Apache Camel 開発ガイド
Apache Camel を使用したアプリケーションの開発
概要
多様性を受け入れるオープンソースの強化
Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。まずは、マスター (master)、スレーブ (slave)、ブラックリスト (blacklist)、ホワイトリスト (whitelist) の 4 つの用語の置き換えから始めます。この取り組みは膨大な作業を要するため、今後の複数のリリースで段階的に用語の置き換えを実施して参ります。詳細は、CTO である Chris Wright のメッセージ をご覧ください。
パート I. エンタープライズ統合パターンの実装
ここでは、Apache Camel を使用してルートを構築する方法を説明します。基本的なビルディングブロックおよび EIP コンポーネントをカバーします。
第1章 ルート定義のためのビルディングブロック
概要
Apache Camel はルートを定義するために、Java DSL と Spring XML DSL の 2 つの ドメイン固有言語 (DSL) をサポートします。ルートを定義するための基本的なビルディングブロックは エンドポイント および プロセッサー で、プロセッサーの動作は通常 式 または論理 述語 によって変更されます。Apache Camel は、さまざまな言語を使用して式や述語を定義できます。
1.1. RouteBuilder クラスの実装
概要
ドメイン固有言語 (DSL) を使用するには、RouteBuilder
クラスを拡張し、その configure()
メソッド (ここにルーティングルールを定義します) を上書きします。
必要な数だけ RouteBuilder
クラスを定義できます。各クラスは一度インスタンス化され、CamelContext
オブジェクトに登録されます。通常、各 RouteBuilder
オブジェクトのライフサイクルは、ルーターをデプロイするコンテナーによって自動的に管理されます。
RouteBuilder クラス
ルート開発者の主なロールは、1 つ以上の RouteBuilder
クラスを実装することです。以下の 2 つの代替 RouteBuilder
クラスを継承できます。
-
org.apache.camel.builder.RouteBuilder
: これは、あらゆる コンテナータイプへのデプロイに適した汎用のRouteBuilder
ベースクラスです。camel-core
アーティファクトで提供されます。 -
org.apache.camel.spring.SpringRouteBuilder
: このベースクラスは、Spring コンテナーに特別に適合されています。特に、以下の Spring 固有の機能に対する追加サポートが提供されます。追加サポートの対象となる機能は、Spring レジストリーでの Bean 検索 (beanRef()
Java DSL コマンドを使用) およびトランザクション (詳細は トランザクションガイド を参照) です。camel-spring
アーティファクトで提供されます。
RouteBuilder
クラスは、ルーティングルールの開始に使用されるメソッドを定義します (例: from()
、intercept()
、および exception()
)。
RouteBuilder の実装
例1.1「RouteBuilder クラスの実装」 は、最小限の RouteBuilder
実装を示しています。configure()
メソッド本文にはルーティングルールが含まれ、各ルールは単一の Java ステートメントです。
例1.1 RouteBuilder クラスの実装
import org.apache.camel.builder.RouteBuilder; public class MyRouteBuilder extends RouteBuilder { public void configure() { // Define routing rules here: from("file:src/data?noop=true").to("file:target/messages"); // More rules can be included, in you like. // ... } }
from(URL1).to(URL2)
ルールの形式は、src/data
ディレクトリーからファイルを読み込み、それを target/messages
ディレクトリーに送信するようルーターに指示します。?noop=true
オプションは、src/data
ディレクトリー内のソースファイルを (削除ではなく) 保持するようにルーターに指示します。
Spring または Blueprint で contextScan
を使用して RouteBuilder
クラスをフィルターする場合、Apache Camel はデフォルトでシングルトン Bean を検索します。ただし、以前の動作をオンにして、新しいオプション includeNonSingletons
でスコープされたプロトタイプを含めることができます。
1.2. 基本的な Java DSL 構文
DSL とは
ドメイン固有言語 (DSL) は、特別な目的のために設計されたミニ言語です。DSL は論理的に完全である必要はありませんが、選択したドメインで適切に問題を記述するのに十分な表現力が必要になります。通常、DSL には専用のパーサー、インタープリター、またはコンパイラーは 必要ありません。DSL コンストラクトがホスト言語 API のコンストラクトに適切にマッピングされていれば、DSL は既存のオブジェクト指向ホスト言語の上に便乗できます。
架空の DSL で以下の一連のコマンドについて考えてみましょう。
command01; command02; command03;
これらのコマンドは、以下のような Java メソッド呼び出しにマップできます。
command01().command02().command03()
ブロックを Java メソッド呼び出しにマップすることもできます。以下に例を示します。
command01().startBlock().command02().command03().endBlock()
DSL 構文は、ホスト言語 API のデータ型によって暗黙的に定義されます。たとえば、Java メソッドの戻り値の型によって、次 (DSL の次のコマンドと同等) をアクティブに呼び出すことのできるメソッドが決定します。
ルータールール構文
Apache Camel は、ルーティングルールを定義する ルーター DSL を定義します。この DSL を使用して、RouteBuilder.configure()
実装のボディーにルールを定義できます。図1.1「ローカルルーティングルール」 は、ローカルルーティングルールを定義する基本的な構文の概要を示しています。
図1.1 ローカルルーティングルール
ローカルルールは、常に from("EndpointURL")
メソッドで開始します。このメソッドは、ルーティングルールのメッセージのソース (コンシューマーエンドポイント) を指定します。その後、ルールに任意の長いプロセッサーチェーンを追加できます (例: filter()
)。通常、to("EndpointURL")
メソッドでルールを終了します。このメソッドは、ルールを通過するメッセージのターゲット (producer endpoint) を指定します。ただし、常に to()
でルールを終了する必要はありません。メッセージのターゲットをルールに指定する別の方法もあります。
特別なプロセッサータイプ (intercept()
、exception()
、または errorHandler()
など) でルールを開始することで、グローバルルーティングルールを定義することもできます。グローバルルールは本ガイドの対象範囲外です。
コンシューマーおよびプロデューサー
ローカルルールは、常に from("EndpointURL")
を使用してコンシューマーエンドポイントを定義して開始します。そして (常にではありませんが) 通常は、to("EndpointURL")
を使用してプロデューサーエンドポイントを定義して終了します。エンドポイント URL である EndpointURL は、デプロイ時に設定された任意のコンポーネントを使用できます。たとえば、ファイルエンドポイント file:MyMessageDirectory
、Apache CXF エンドポイント cxf:MyServiceName
、または Apache ActiveMQ エンドポイント activemq:queue:MyQName
を使用できます。全てのコンポーネント型を網羅するリストは、Apache Camel Component Reference を参照してください。
エクスチェンジ
エクスチェンジ オブジェクトは、メタデータで拡張されたメッセージで設定されます。エクスチェンジはメッセージがルーティングルールを介して伝搬される際に使われる標準形式であるため、 Apache Camel において最も重要です。エクスチェンジの主な設定要素は次のとおりです。
Inメッセージ - エクスチェンジによってカプセル化された現在のメッセージです。エクスチェンジがルートを通過するにつれて、このメッセージは変更される場合があります。そのため、ルート開始時の In メッセージは、通常ルート終了時の Inメッセージとは 異なります。
org.apache.camel.Message
タイプは、以下の部分を含むメッセージの汎用モデルを提供します。- ボディー
- ヘッダー
- アタッチメント
これはメッセージの 汎用 モデルであることを理解することが重要です。Apache Camel は、さまざまなプロトコルおよびエンドポイントをサポートします。したがって、メッセージのボディーまたはヘッダーの形式を標準化することは できません。たとえば、JMS メッセージのボディー形式は、HTTP メッセージまたは Web サービスメッセージのボディーとは全く異なります。このため、ボディーとヘッダーは
Object
タイプと宣言されます。ボディーとヘッダーの元のコンテンツは、エクスチェンジインスタンスを作成するエンドポイントによって決定されます (つまり、from()
コマンドに指定されたエンドポイント)。out メッセージ - 返信メッセージまたは変換されたメッセージの一時的な保持領域です。特定の処理ノード (特に
to()
コマンド) は、In メッセージをリクエストとして処理し、これをプロデューサーエンドポイントに送信し、そのエンドポイントからのリプライを受信することで、現在のメッセージを変更できます。リプライメッセージは、エクスチェンジの Out メッセージスロットに挿入されます。通常、現在のノードで Out メッセージが設定された場合、Apache Camel はエクスチェンジを次のように変更してからルートの次のノードに渡します: In メッセージを破棄し、Out メッセージを In メッセージのスロットに移動します。そのため、リプライは最新のメッセージになります。Apache Camel がノード同士をルート内で接続する方法の詳細は、「パイプライン処理」 を参照してください。
ただし、Out メッセージが異なる方法で扱われる特殊なケースが 1 つあります。ルート開始時のコンシューマーエンドポイントがリプライメッセージを期待している場合、ルートの最終端にある Out メッセージはコンシューマーエンドポイントのリプライメッセージとみなされます (さらに、この場合、最終ノードが Out メッセージを作成 しなければならず、さもなくばコンシューマーエンドポイントはハングしてしまいます)。
メッセージ交換パターン (MEP) - 以下のように、ルート内でのエクスチェンジとエンドポイント間のやり取りに影響を与えます。
- コンシューマーエンドポイント - 元となるエクスチェンジを作成するコンシューマーエンドポイントは、MEP の初期値を設定します。初期値は、コンシューマーエンドポイントがリプライするか (例: InOut MEP)、リプライしないか (例: InOnly MEP) を示します。
-
プロデューサーエンドポイント - MEP は、エクスチェンジがルートで通過するプロデューサーエンドポイントに影響します (例: エクスチェンジが
to()
ノードを通過する場合)。たとえば、現在の MEP が InOnly の場合、to()
ノードはエンドポイントからのリプライを受け取ることを期待しません。場合によっては、エクスチェンジのプロデューサーエンドポイントとのやり取りをカスタマイズするために、現在の MEP を変更する必要があります。詳細は、「エンドポイント」 を参照してください。
- エクスチェンジプロパティー - 現在のメッセージのメタデータが含まれる名前付きプロパティーのリスト。
メッセージ交換パターン
Exchange
オブジェクトを使用すると、メッセージ処理を異なる メッセージ交換パターン に簡単に一般化することができます。たとえば、非同期プロトコルは、コンシューマーエンドポイントからプロデューサーエンドポイントに流れる単一のメッセージで設定される MEP を定義する場合があります (InOnly MEP)。一方、RPC プロトコルは、リクエストメッセージとリプライメッセージで設定される MEP を定義する場合があります (InOut MEP)。現在、Apache Camel は以下の MEP をサポートします。
-
InOnly
-
RobustInOnly
-
InOut
-
InOptionalOut
-
OutOnly
-
RobustOutOnly
-
OutIn
-
OutOptionalIn
これらのメッセージ交換パターンは、列挙型の定数 org.apache.camel.ExchangePattern
によって表されます。
グループ化されたエクスチェンジ
複数のエクスチェンジインスタンスをカプセル化した 1 つのエクスチェンジがあると便利な場合もあります。これに対応する為、グループ化されたエクスチェンジ を使用できます。グループ化されたエクスチェンジは、基本的 Exchange.GROUPED_EXCHANGE
エクスチェンジプロパティーに格納されている Exchange
オブジェクトの java.util.List
が含まれるエクスチェンジインスタンスです。グループ化されたエクスチェンジの使用例は、「Aggregator」 を参照してください。
プロセッサー
プロセッサー は、ルートを通過するエクスチェンジのストリームにアクセスして変更することができるルート内のノードです。プロセッサーは、動作を変更する 式 や 述語 の引数を取ることができます。たとえば、図1.1「ローカルルーティングルール」 で示されたルールには、xpath()
述語を引数とする filter()
プロセッサーが含まれます。
式および述語
式 (文字列またはその他のデータ型への評価) および述語 (true または false への評価) は、組み込みプロセッサー型の引数として頻繁に発生します。たとえば、以下のフィルタールールは、ヘッダー foo
の値が bar
と等しい場合にのみ In メッセージを伝播します。
from("seda:a").filter(header("foo").isEqualTo("bar")).to("seda:b");
header("foo").isEqualTo("bar")
ではフィルターが述語によって修飾されます。メッセージの内容に基づいてより高度な述語や式を作成するために、式言語および述語言語のいずれかを使用できます (パートII「ルーティング式と述語言語」 を参照)。
1.3. Spring XML ファイルのルータースキーマ
名前空間
ルータースキーマは XML DSL を定義し、以下の XML スキーマ名前空間に属します。
http://camel.apache.org/schema/spring
スキーマの場所の指定
ルータースキーマの場所は通常、Apache Web サイトにある最新バージョンのスキーマを参照する http://camel.apache.org/schema/spring/camel-spring.xsd
に指定されます。たとえば、Apache Camel Spring ファイルのルート beans
要素は、通常 例1.2「ルータースキーマの場所の指定」 のように設定されます。
例1.2 ルータースキーマの場所の指定
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <!-- Define your routing rules here --> </camelContext> </beans>
ランタイムスキーマの場所
実行時に、Apache Camel は Spring ファイルで指定されたスキーマの場所からルータースキーマをダウンロードしません。代わりに、Apache Camel は camel-spring
JAR ファイルの root ディレクトリーからスキーマのコピーを自動的に取得します。これにより、Spring ファイルの解析に使用されるスキーマのバージョンが、常に現在のランタイムバージョンと一致するようになります。これは、Apache Web サイトに公開されているスキーマの最新バージョンが、現在使用しているランタイムのバージョンと一致しない場合があるため、重要になります。
XML エディターの使用
通常、フル機能の XML エディターを使用して Spring ファイルを編集することが推奨されます。XML エディターの自動補完機能により、ルータースキーマに準拠する XML の作成がはるかに容易になり、XML の形式が不適切な場合はエディターが即座に警告します。
通常、XML エディターは、xsi:schemaLocation
属性で指定した場所からスキーマをダウンロードすることに 依存しています。編集中に正しいスキーマバージョンを使用するようにするため、通常は camel-spring.xsd
ファイルの特定のバージョンを選択することが推奨されます。たとえば、Apache Camel バージョン 2.3 用の Spring ファイルを編集するには、以下のように beans 要素を修正します。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring-2.3.0.xsd"> ...
編集が完了したら、デフォルトの camel-spring.xsd
に戻します。現在ダウンロードできるスキーマのバージョンを確認するには、Web ページ http://camel.apache.org/schema/spring に移動します。
1.4. エンドポイント
概要
Apache Camel エンドポイントは、ルートにおけるメッセージの水源 (蛇口) とシンク (流し台) です。エンドポイントは一般的なビルディングブロックと言えます。満たす必要がある唯一の要件は、メッセージの水源 (プロデューサーエンドポイント) またはメッセージのシンク (コンシューマーエンドポイント) のいずれかとして機能することです。そのため、Apache Camel でサポートされるエンドポイント型には、HTTP などのプロトコルをサポートするエンドポイントから、定期的な間隔でダミーメッセージを生成する Quartz などの単純なタイマーエンドポイントまで、非常に多様な種類があります。Apache Camel の大きな強みの 1 つは、新しいエンドポイント型を実装するカスタムコンポーネントを、比較的簡単に追加できることです。
エンドポイント URI
エンドポイントは、以下の一般的な形式を持つ エンドポイント URI で識別されます。
scheme:contextPath[?queryOptions]
URI スキームは は、http
などのプロトコルを識別し、contextPath はプロトコルによって解釈される URI の詳細を提供します。さらに、ほとんどのスキームでは、以下の形式で指定される queryOptions (クエリーオプション) を定義できます。
?option01=value01&option02=value02&...
たとえば、以下の HTTP URI を使用して Google 検索エンジンページに接続できます。
http://www.google.com
以下のファイル URI を使用して、C:\temp\src\data
ディレクトリー配下に表示されるすべてのファイルを読み取ることができます。
file://C:/temp/src/data
すべての スキーム がプロトコルを表しているわけではありません。スキーム は、タイマーなどの有用なユーティリティーへのアクセスのみを提供することもあります。たとえば、以下の Timer エンドポイント URI は、1 秒毎 (=1000 ミリ秒) にエクスチェンジを生成します。これを使用して、ルートでアクティビティーをスケジュールできます。
timer://tickTock?period=1000
長いエンドポイント URI の使用
エンドポイント URI は、提供される設定情報がすべて付随することで、かなり長くなることがあります。Red Hat Fuse 6.2 以降では、長い URI での作業をより管理しやすくする方法が 2 つあります。
- エンドポイントの個別設定
エンドポイントは個別に設定でき、ルートから短縮 ID を使用してエンドポイントを参照できます。
<camelContext ...> <endpoint id="foo" uri="ftp://foo@myserver"> <property name="password" value="secret"/> <property name="recursive" value="true"/> <property name="ftpClient.dataTimeout" value="30000"/> <property name="ftpClient.serverLanguageCode" value="fr"/> </endpoint> <route> <from uri="ref:foo"/> ... </route> </camelContext>
URI 内でオプションをいくつか設定し、
property
属性を使用して追加オプションを指定することもできます (または URI からオプションを上書きすることもできます)。<endpoint id="foo" uri="ftp://foo@myserver?recursive=true"> <property name="password" value="secret"/> <property name="ftpClient.dataTimeout" value="30000"/> <property name="ftpClient.serverLanguageCode" value="fr"/> </endpoint>
- 複数行にまたがるエンドポイント設定の分割
改行を使用して URI 属性を分割できます。
<route> <from uri="ftp://foo@myserver?password=secret& recursive=true&ftpClient.dataTimeout=30000& ftpClientConfig.serverLanguageCode=fr"/> <to uri="bean:doSomething"/> </route>
注記&
で区切って、各行に 1 つ以上のオプションを指定できます。
URI での期間指定
Apache Camel コンポーネントの多くは、期間を指定するオプション (タイムアウト値など) を持ちます。デフォルトでは、このような期間を指定するオプションは通常、数値で指定され、ミリ秒単位として解釈されます。ただし、Apache Camel は期間のより読みやすい構文もサポートしており、時、分、秒で期間を表現できます。以下の構文に準拠する文字列を使用すると、人間が読みやすい期間を指定できます。
[NHour(h|hour)][NMin(m|minute)][NSec(s|second)]
角括弧 []
に囲まれた各項は任意であり、表記法 (A|B)
は、A
および B
のどちらかであることを示します。
たとえば、以下のように timer
エンドポイントを 45 分間隔に設定できます。
from("timer:foo?period=45m") .to("log:foo");
また、以下のように、時、分、秒の単位を任意に組み合わせて使用することもできます。
from("timer:foo?period=1h15m") .to("log:foo"); from("timer:bar?period=2h30s") .to("log:bar"); from("timer:bar?period=3h45m58s") .to("log:bar");
URI オプションへの raw 値の指定
デフォルトでは、URI に指定するオプションの値は自動的に URI エンコードされます。場合によっては、これは望ましくない動作である場合があります。たとえば、password オプションを設定する場合は、URI エンコーディング なし で raw 文字列を送信することが推奨されます。
構文 RAW(RawValue)
でオプションの値を指定して、URI エンコーディングをオフにすることができます。以下に例を示します。
from("SourceURI") .to("ftp:joe@myftpserver.com?password=RAW(se+re?t&23)&binary=true")
この例では、パスワードの値はリテラル値 se+re?t&23
として送信されます。
大文字と小文字を区別しない列挙オプション
一部のエンドポイント URI オプションは、Java enum
定数にマッピングされます。たとえば、Log コンポーネントの level
オプションは、INFO
、WARN
、ERROR
などの enum
値を取ることができます。この型変換は大文字と小文字を区別しないため、以下ののいずれかのオプションを使用して Log プロデューサーエンドポイントのログレベルを設定できます。
<to uri="log:foo?level=info"/> <to uri="log:foo?level=INfo"/> <to uri="log:foo?level=InFo"/>
URI リソースの指定
Camel 2.17 以降、XSLT や Velocity などのリソースベースのコンポーネントは、ref:
を接頭辞として使用して、レジストリーからリソースファイルをロードできます。
たとえば、レジストリーに格納された 2 つの Bean それぞれの ID である ifmyvelocityscriptbean
および mysimplescriptbean
は、以下のように参照することで、Bean の内容を使うことができます。
Velocity endpoint: ------------------ from("velocity:ref:myvelocityscriptbean").<rest_of_route>. Language endpoint (for invoking a scripting language): ----------------------------------------------------- from("direct:start") .to("language:simple:ref:mysimplescriptbean") Where Camel implicitly converts the bean to a String.
Apache Camel コンポーネント
各 URI スキーム は本質的にエンドポイントファクトリーとなる Apache Camel コンポーネント にマップされます。つまり、特定のタイプのエンドポイントを使用するには、対応する Apache Camel コンポーネントをランタイムコンテナーにデプロイする必要があります。たとえば、JMS エンドポイントを使用するには、コンテナーに JMS コンポーネントをデプロイします。
Apache Camel は、アプリケーションをさまざまなトランスポートプロトコルおよびサードパーティー製品と統合できる、多種多様なコンポーネントを提供します。たとえば、より一般的に使用されるコンポーネントには、File、JMS、CXF (Web サービス) 、HTTP、Jetty、Direct、および Mock などがあります。サポートされるコンポーネントの完全なリストは、Apache Camel コンポーネントのドキュメント を参照してください。
Apache Camel コンポーネントの大半は、Camel コアとは別にパッケージ化されています。Maven を使用してアプリケーションをビルドする場合、関連するコンポーネントアーティファクトの依存関係を追加するだけで、コンポーネント (およびサードパーティーの依存関係) を簡単にアプリケーションへ追加できます。たとえば、HTTP コンポーネントを含めるには、以下の Maven 依存関係をプロジェクトの POM ファイルに追加します。
<!-- Maven POM File --> <properties> <camel-version>{camelFullVersion}</camel-version> ... </properties> <dependencies> ... <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-http</artifactId> <version>${camel-version}</version> </dependency> ... </dependencies>
以下のコンポーネントは Camel コア (camel-core
アーティファクト内) に組み込まれているので、いつでも利用できます。
- Bean
- Browse
- Dataset
- Direct
- File
- Log
- Mock
- Properties
- Ref
- SEDA
- Timer
- VM
コンシューマーエンドポイント
コンシューマーエンドポイント は、ルートの 先頭 に (つまり from()
DSL コマンドで) 表示されるエンドポイントです。言い換えると、コンシューマーエンドポイントはルート内の処理を開始するロールがあります。新しいエクスチェンジインスタンス (通常は受信または取得したメッセージを基に) を作成し、ルートの残りの部分でエクスチェンジを処理するためのスレッドを提供します。
たとえば、以下の JMS コンシューマーエンドポイントは payments
キューからメッセージをプルし、ルートでメッセージを処理します。
from("jms:queue:payments") .process(SomeProcessor) .to("TargetURI");
または、Spring XML で同等のものを記述するとこのようになります。
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="jms:queue:payments"/> <process ref="someProcessorId"/> <to uri="TargetURI"/> </route> </camelContext>
コンポーネントには、 コンシューマー専用 のものもあります。つまり、それらはコンシューマーエンドポイントの定義のみに使用できます。たとえば、Quartz コンポーネントは、コンシューマーエンドポイントを定義するためにのみ使用されます。以下の Quartz エンドポイントは、1 秒ごと (1000 ミリ秒) にイベントを生成します。
from("quartz://secondTimer?trigger.repeatInterval=1000") .process(SomeProcessor) .to("TargetURI");
必要に応じて、fromF()
Java DSL コマンドを使用して、エンドポイント URI をフォーマットされた文字列として指定できます。たとえば、ユーザー名とパスワードを FTP エンドポイントの URI に指定するには、以下のように Java でルートを記述します。
fromF("ftp:%s@fusesource.com?password=%s", username, password) .process(SomeProcessor) .to("TargetURI");
最初の %s
は文字列 username
の値に置き換えられ、2 番目の %s
は文字列 password
の値に置き換えられます。この文字列のフォーマットメカニズムは String.format()
で実装されており、C 言語の printf()
関数によって提供されるフォーマットに似ています。詳細は java.util.Formatter を参照してください。
プロデューサーエンドポイント
プロデューサーエンドポイント は、ルートの 途中 またはルートの 末尾 で表示されるエンドポイントです (例: to()
DSL コマンド)。つまり、プロデューサーエンドポイントは既存のエクスチェンジオブジェクトを受け取り、エクスチェンジの内容を指定されたエンドポイントに送信します。
たとえば、以下の JMS プロデューサーエンドポイントは、現在のエクスチェンジの内容を指定の JMS キューにプッシュします。
from("SourceURI") .process(SomeProcessor) .to("jms:queue:orderForms");
または、Spring XML で同等のものを記述すると、このようになります。
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="SourceURI"/> <process ref="someProcessorId"/> <to uri="jms:queue:orderForms"/> </route> </camelContext>
一部のコンポーネントは プロデューサー専用 で、プロデューサーエンドポイントを定義するためにのみ使用できます。たとえば、HTTP エンドポイントはプロデューサーエンドポイントを定義するためにのみ使用されます。
from("SourceURI") .process(SomeProcessor) .to("http://www.google.com/search?hl=en&q=camel+router");
必要に応じて、toF()
Java DSL コマンドを使用して、エンドポイント URI をフォーマットされた文字列として指定できます。たとえば、カスタム Google クエリーを HTTP URI に置き換えるには、以下のように Java でルートを記述します。
from("SourceURI") .process(SomeProcessor) .toF("http://www.google.com/search?hl=en&q=%s", myGoogleQuery);
%s
と書かれた箇所が、カスタムクエリー文字列 myGoogleQuery
に置き換えられます。詳細は java.util.Formatter を参照してください。
1.5. プロセッサー
概要
ルーターが単にコンシューマーエンドポイントをプロデューサーエンドポイントに接続するだけでなく、より複雑なことを実行できるようにするため、プロセッサー をルートに追加することができます。プロセッサーは、ルーティングルールに挿入して、ルールを通過するメッセージの任意処理を実行するコマンドです。Apache Camel は、表1.1「Apache Camel プロセッサー」 に示されるように、さまざまなプロセッサーを提供します。
Java DSL | XML DSL | 説明 |
---|---|---|
|
| 「Aggregator」: 複数の受信エクスチェンジを単一のエクスチェンジに組み合わせるアグリゲーターを作成します。 |
|
| アスペクト指向プログラミング (AOP) を使用して、指定されたサブルートの前後で作業を行います。 |
|
| Java オブジェクト (または Bean) でメソッドを呼び出して、現在のエクスチェンジを処理します。「Bean インテグレーション」を参照してください。 |
|
|
「Content-Based Router」: |
|
| In メッセージボディーを、指定された型に変換します。 |
|
| 「Delayer」: ルートの後続へエクスチェンジを伝搬するのを遅延します。 |
|
|
|
| 該当なし | 現在のコマンドブロックを終了します。 |
|
| 「Content Enricher」: 現在のエクスチェンジと、指定された プロデューサー エンドポイント URI からリクエストされたデータを統合します。 |
|
| 「Message Filter」: 述語式を使用して受信エクスチェンジをフィルタリングします。 |
|
| 「Idempotent Consumer」: 重複メッセージを抑制するストラテジーを実装します。 |
|
| 特定のルートノードで継承されたエラーハンドラーを無効にするために使用できるブール値オプション (Java DSL でサブ句として定義され、XML DSL の属性として定義) 。 |
|
| 現在のエクスチェンジの MEP を InOnly (引数がない場合) に設定するか、指定されたエンドポイントへエクスチェンジを InOnly として送信します。 |
|
| 現在のエクスチェンジの MEP を InOut (引数がない場合) に設定するか、指定されたエンドポイントへエクスチェンジを InOut として送信します。 |
|
| 「Load Balancer」: エンドポイントのコレクションに対する負荷分散を実装します。 |
|
| コンソールにメッセージを記録します。 |
|
| 「Loop」: 各エクスチェンジをルートの後続に繰り返し再送信します。 |
|
|
(トランザクション) 現在のトランザクションをロールバックオンリーにマークします (例外は発生しません)。XML DSL では、このオプションは |
|
|
(トランザクション) 1 つ以上のトランザクションがこのスレッドに関連付けられてから一時停止されている場合、このコマンドは最新のトランザクションをロールバックオンリーにマークします (例外は発生しません)。XML DSL では、このオプションは |
|
| 指定されたデータフォーマットを使用して低レベルまたはバイナリーフォーマットに変換し、特定のトランスポートプロトコルで送信できるようにします。 |
|
| 「Multicast」: 現在のエクスチェンジを複数の宛先にマルチキャストし、各宛先がエクスチェンジの独自のコピーを取得します。 |
|
|
メインルートの完了後に実行されるサブルート (Java DSL |
|
|
指定された例外が発生するたびに実行されるサブルート (Java DSL |
|
| 「パイプとフィルター」: あるエンドポイントの出力が次のエンドポイントの入力となるように、一連のエンドポイントにエクスチェンジを送ります。「パイプライン処理」も併せて参照してください。 |
|
| 現在のルートにポリシーを適用します (現時点ではトランザクションポリシーにのみ使用されます)。Apache Karaf Transaction Guide を参照してください。 |
|
| 「Content Enricher」: 現在のエクスチェンジと、指定された コンシューマー エンドポイント URI からポーリングされたデータを統合します。 |
|
| 現在のエクスチェンジでカスタムプロセッサーを実行します。「カスタムプロセッサー」 および パートIII「高度な Camel プログラミング」 を参照してください。 |
|
| 「受信者リスト」: エクスチェンジを、実行時に算出される (たとえば、ヘッダーの内容に基づいて) 受信者のリストに送信します。 |
|
| 指定したヘッダーをエクスチェンジの In メッセージから削除します。 |
|
|
指定したパターンに一致するヘッダーをエクスチェンジの In メッセージから削除します。パターンにはフォームを持たせることができます。 |
|
| エクスチェンジから、指定したエクスチェンジプロパティーを削除します。 |
|
|
指定したパターンに一致するプロパティーをエクスチェンジから削除します。コンマで区切られた 1 つ以上の文字列のリストを引数として取ります。最初の文字列はパターンです (上記の |
|
| 「Resequencer」: 指定されたコンパレータの動作に基づき、受信エクスチェンジの順序を変更します。バッチ モードと ストリーム モードをサポートします。 |
|
| (トランザクション) 現在のトランザクションをロールバックオンリーにマークします (デフォルトでは例外も発生)。Apache Karaf Transaction Guide を参照してください。 |
|
| 「Routing Slip」: 任意のヘッダーから抽出したエンドポイント URI のリストに基づいて動的に構築されたパイプラインで、エクスチェンジをルーティングします。 |
|
| サンプリングスロットラーを作成し、ルート上のトラフィックからエクスチェンジのサンプルを抽出できるようにします。 |
|
| エクスチェンジの In メッセージのメッセージボディーを設定します。 |
|
| 現在のエクスチェンジの MEP を指定された値に設定します。「メッセージ交換パターン」を参照してください。 |
|
| エクスチェンジの In メッセージに指定したヘッダーを設定します。 |
|
| エクスチェンジの Out メッセージに指定したヘッダーを設定します。 |
|
| 指定したエクスチェンジプロパティーを設定します。 |
|
| In メッセージボディーの内容を並べ替えます (カスタムコンパレーターをオプションで指定できます) 。 |
|
| 「Splitter」: 現在のエクスチェンジを一連のエクスチェンジに分割します。分割された各エクスチェンジには元のメッセージボディーの断片が含まれます。 |
|
| 現在のエクスチェンジのルーティングを停止し、完了したとマークします。 |
|
| ルートの後続部分を並列に処理するためにスレッドプールを作成します。 |
|
| 「Throttler」: フローレートを指定レベルに制限します (1 秒あたりのエクスチェンジ数) 。 |
|
| 指定された Java 例外を出力します。 |
|
| エクスチェンジを 1 つ以上のエンドポイントに送信します。「パイプライン処理」を参照してください。 |
| 該当なし |
文字列フォーマットを使用して、エクスチェンジをエンドポイントに送信します。つまり、エンドポイント URI 文字列は C 言語の |
|
| ルートの後続部分を囲む Spring トランザクションスコープを作成します。Apache Karaf Transaction Guide を参照してください。 |
|
| 「メッセージトランスレーター」: In メッセージヘッダーを Out メッセージヘッダーにコピーし、Out メッセージボディーを指定された値に設定します。 |
|
| 指定したデータフォーマットを使用して、In メッセージボディーを低レベルまたはバイナリー形式から高レベル形式に変換します。 |
|
|
述語式を取り、現在のメッセージが有効かどうかを検証します。述語が |
|
|
「Wire Tap」: |
サンプルプロセッサー
ルートでプロセッサーを使用する方法をある程度理解するには、以下の例を参照してください。
Choice
choice()
プロセッサーは、受信メッセージを別のプロデューサーエンドポイントにルーティングするために使用される条件文です。代替となる各プロデューサーエンドポイントの前には、述語の引数を取る when()
メソッドがあります。述語が true の場合、後続のターゲットが選択されます。そうでない場合、ルール内の次の when()
メソッドに処理が進みます。たとえば、以下の choice()
プロセッサーは Predicate1 および Predicate2 の値に応じて、受信メッセージを Target1、Target2、または Target3 のいずれかに転送します。
from("SourceURL") .choice() .when(Predicate1).to("Target1") .when(Predicate2).to("Target2") .otherwise().to("Target3");
または、Spring XML で同等のものを記述すると、このようになります。
<camelContext id="buildSimpleRouteWithChoice" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="SourceURL"/> <choice> <when> <!-- First predicate --> <simple>header.foo = 'bar'</simple> <to uri="Target1"/> </when> <when> <!-- Second predicate --> <simple>header.foo = 'manchu'</simple> <to uri="Target2"/> </when> <otherwise> <to uri="Target3"/> </otherwise> </choice> </route> </camelContext>
Java DSL には、endChoice()
コマンドを使用する必要がある可能性がある特殊なケースがあります。標準の Apache Camel プロセッサーの中には、特殊なサブ句を使用して追加のパラメーターを指定でき、通常は end()
コマンドで終了する追加レベルのネストを効果的に展開します。たとえば、ロードバランサー句を loadBalance().roundRobin().to("mock:foo").to("mock:bar").end()
として指定できます。これにより、mock:foo
と mock:bar
エンドポイント間でメッセージの負荷分散が行われます。ただし、ロードバランサー句が choice の条件に組み込まれている場合は、以下のように endChoice()
コマンドを使用して句を終了する必要があります。
from("direct:start") .choice() .when(bodyAs(String.class).contains("Camel")) .loadBalance().roundRobin().to("mock:foo").to("mock:bar").endChoice() .otherwise() .to("mock:result");
フィルター
filter()
プロセッサーを使用すると、必要のないメッセージがプロデューサーエンドポイントに到達しないようにすることができます。述語の引数を 1 つ取ります。述語が true の場合、メッセージエクスチェンジはプロデューサーに対して許可されます。述語が false の場合、メッセージエクスチェンジはブロックされます。たとえば、以下のフィルターは、受信メッセージに bar
の値を持つヘッダー foo
が含まれない限り、メッセージエクスチェンジをブロックします。
from("SourceURL").filter(header("foo").isEqualTo("bar")).to("TargetURL");
または、Spring XML で同等のものを記述すると、このようになります。
<camelContext id="filterRoute" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="SourceURL"/> <filter> <simple>header.foo = 'bar'</simple> <to uri="TargetURL"/> </filter> </route> </camelContext>
Throttler
throttle()
プロセッサーは、プロデューサーエンドポイントがオーバーロードしないようにします。スロットラーは、1 秒間に通過できるメッセージの数を制限することで機能します。受信メッセージが指定されたレートを超える場合、スロットラーは超過したメッセージをバッファーに蓄積し、ゆっくりとプロデューサーエンドポイントに送信します。たとえば、スループットのレートを毎秒 100 メッセージに制限するには、以下のルールを定義します。
from("SourceURL").throttle(100).to("TargetURL");
または、Spring XML で同等のものを記述すると、このようになります。
<camelContext id="throttleRoute" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="SourceURL"/> <throttle maximumRequestsPerPeriod="100" timePeriodMillis="1000"> <to uri="TargetURL"/> </throttle> </route> </camelContext>
カスタムプロセッサー
ここに記載されている標準プロセッサーがいずれも必要な機能を提供しない場合は、いつでも独自のカスタムプロセッサーを定義できます。カスタムプロセッサーを作成するには、org.apache.camel.Processor
インターフェイスを実装し、process()
メソッドを上書きするクラスを定義します。以下のカスタムプロセッサー MyProcessor
は、受信メッセージから、ヘッダー foo
を削除します。
例1.3 カスタムプロセッサークラスの実装
public class MyProcessor implements org.apache.camel.Processor { public void process(org.apache.camel.Exchange exchange) { inMessage = exchange.getIn(); if (inMessage != null) { inMessage.removeHeader("foo"); } } };
カスタムプロセッサーをルータールールに挿入するには、process()
メソッドを呼び出します。このメソッドは、ルールにプロセッサーを挿入するための一般的なメカニズムを提供します。たとえば、以下のルールは、例1.3「カスタムプロセッサークラスの実装」 で定義されたプロセッサーを呼び出します。
org.apache.camel.Processor myProc = new MyProcessor(); from("SourceURL").process(myProc).to("TargetURL");
第2章 ルート構築の基本原則
概要
Apache Camel は、ルートの中で繋ぎ合わせられる複数のプロセッサーおよびコンポーネントを提供します。本章では、提供されるビルディングブロックを使用してルートを構築する原則を説明します。
2.1. パイプライン処理
概要
Apache Camel では、パイプライン処理はルート定義でノードを接続するための主要なパラダイムです。パイプラインの概念は、おそらく UNIX オペレーティングシステムのユーザーには最も馴染のあるものでしょう。オペレーティングシステムのコマンドを結合するために使用しているからです。たとえば、ls | more
はディレクトリー一覧 ls
をページスクロールのユーティリティー more
にパイプするコマンドの例です。パイプラインの基本的な概念は、あるコマンドの 出力 が次の 入力 に読み込まれることです。ルートにおいては、あるプロセッサーからの Out メッセージが次のプロセッサーの In メッセージにコピーされることに相当します。
プロセッサーノード
最初のエンドポイントを除き、ルートのすべてのノードは プロセッサー で、org.apache.camel.Processor
インターフェイスを継承します。つまり、プロセッサーが DSL ルートの基本ビルディングブロックを設定します。たとえば、filter()
、delayer()
、setBody()
、setHeader()
、および to()
などの DSL コマンドはすべてプロセッサーを表します。プロセッサーがどのように接続しあってルートを設定するかを考えるときに、2 つの異なる処理アプローチを区別することが重要になります。
最初のアプローチは、図2.1「In メッセージを変更するプロセッサー」 にあるように、プロセッサーが単純にエクスチェンジの In メッセージを変更する方法です 。この場合、エクスチェンジの Out メッセージは null
のままになります。
図2.1 In メッセージを変更するプロセッサー
以下のルートは、BillingSystem
ヘッダーを追加 (または変更) して現在の In メッセージを修正する setHeader()
コマンドを示しています。
from("activemq:orderQueue") .setHeader("BillingSystem", xpath("/order/billingSystem")) .to("activemq:billingQueue");
2 つ目のアプローチは、図2.2「Out メッセージを作成するプロセッサー」 にあるように、プロセッサーが処理の結果として Out メッセージを作成する方法です。
図2.2 Out メッセージを作成するプロセッサー
以下のルートは、文字列 DummyBody
が含まれるメッセージボディーを持った Out メッセージを作成する transform()
コマンドを示しています。
from("activemq:orderQueue") .transform(constant("DummyBody")) .to("activemq:billingQueue");
constant("DummyBody")
は定数式を表します。transform()
の引数の型は式である必要があるため、文字列 DummyBody
を直接渡すことはできません。
InOnly エクスチェンジのパイプライン
図2.3「InOnly エクスチェンジのサンプルパイプライン」 は、 InOnly エクスチェンジのプロセッサーパイプラインの例を示しています。プロセッサー A は In メッセージを変更し、プロセッサー B および C は Out メッセージを作成します。ルートビルダーが、図に示されているようにプロセッサー同士を繋ぎ合わせます。特にプロセッサー B と C は パイプライン の形式で繋がれています。つまり、エクスチェンジをプロセッサー C に読み込ませる前にプロセッサー B の Out メッセージが In メッセージに移動され、エクスチェンジをプロデューサーエンドポイントに読み込ませる前にプロセッサー C の Out メッセージが In メッセージに移動されています。したがって、図2.3「InOnly エクスチェンジのサンプルパイプライン」 に示されるようにプロセッサーの出力と入力は連続したパイプラインに結合されています。
図2.3 InOnly エクスチェンジのサンプルパイプライン
Apache Camel はデフォルトでパイプラインパターンを使用するため、特別な構文を使用してルートにパイプラインを作成する必要はありません。たとえば、以下のルートは userdataQueue
キューからメッセージを取り出し、(テキスト形式で顧客アドレスを生成するために) Velocity テンプレートを通してメッセージをパイプ処理し、生成されたテキストアドレスをキュー envelopeAddresses
に送信します。
from("activemq:userdataQueue") .to(ExchangePattern.InOut, "velocity:file:AdressTemplate.vm") .to("activemq:envelopeAddresses");
Velocity エンドポイントである velocity:file:AddressTemplate.vm
は、ファイルシステム内の Velocity テンプレートファイルの file:AddressTemplate.vm
の場所を指定します。この to()
コマンドは、エクスチェンジを Velocity エンドポイントに送信する前に、エクスチェンジパターンをInOut に変更し、その後 InOnly に戻します。Velocity エンドポイントの詳細は、Apache Camel Component Reference Guide の Velocity を参照してください。
InOut エクスチェンジのパイプライン
図2.4「InOut エクスチェンジのサンプルパイプライン」 は、通常リモートプロシージャーコール (RPC) のセマンティクスをサポートするときに使用する、 InOut エクスチェンジ用のプロセッサーパイプラインの例を示しています。プロセッサー A、B、および C はパイプライン形式で結合され、各プロセッサーの出力が次の入力に読み込まれます。プロデューサーエンドポイントによって生成された最後の Out メッセージは、コンシューマーエンドポイントまで遡って、元のリクエストへの返信として提供されます。
図2.4 InOut エクスチェンジのサンプルパイプライン
InOut 交換パターンをサポートするには、ルートの最後のノード (プロデューサーエンドポイントかその他の種類のプロセッサーかに限らず) が Out メッセージを作成することが 必須 です。そうでない場合は、コンシューマーエンドポイントに接続するクライアントがハングし、リプライメッセージを無期限に待ち続けることになります。すべてのプロデューサーエンドポイントが Out メッセージを作成するわけではないことに注意してください。
受信 HTTP リクエストを処理し、支払い要求を処理する以下のルートを見てみましょう。
from("jetty:http://localhost:8080/foo") .to("cxf:bean:addAccountDetails") .to("cxf:bean:getCreditRating") .to("cxf:bean:processTransaction");
受信支払い要求は、Web サービスのパイプライン、cxf:bean:addAccountDetails
、cxf:bean:getCreditRating
、および cxf:bean:processTransaction
を介して渡すことで処理されます。最後の Web サービス processTransaction
が生成する応答 (Out メッセージ) は、Jetty エンドポイント経由で返信されます。
パイプラインがエンドポイントのシーケンスで設定される場合は、以下の代替構文を使用することもできます。
from("jetty:http://localhost:8080/foo") .pipeline("cxf:bean:addAccountDetails", "cxf:bean:getCreditRating", "cxf:bean:processTransaction");
InOptionalOut エクスチェンジのパイプライン
InOptionalOut エクスチェンジのパイプラインは、図2.4「InOut エクスチェンジのサンプルパイプライン」 のパイプラインと基本的に同じです。InOut と InOptionalOut の相違点は、 InOptionalOut 交換パターンのエクスチェンジが応答として null Out メッセージを使用することが可能であることです。つまり、InOptionalOut エクスチェンジの場合、null
Out メッセージが、パイプラインの次のノードの In メッセージにコピーされます。一方、InOut エクスチェンジの場合、null
Out メッセージは破棄され、代わりに現在のノードの元の In メッセージが、次のノードの In メッセージにコピーされます。
2.2. 複数の入力
概要
標準的なルートは、Java DSL の from(EndpointURL)
構文を使用して、1 つのエンドポイントから入力を受け取ります。しかし、ルートに複数の入力を定義する必要がある場合はどうすればよいでしょうか。Apache Camel では、ルートに複数の入力を指定する複数の方法があります。選択肢は、エクスチェンジを独立して処理するか、または異なる入力からのエクスチェンジを何らかの方法で組み合わせるか (この場合は、「Content Enricher パターン」 を使います) によって異なります。
複数の独立した入力
複数の入力を指定する最も簡単な方法は、from()
DSL コマンドのマルチ引数形式を使用することです。以下に例を示します。
from("URI1", "URI2", "URI3").to("DestinationUri");
または、以下の同等の構文を使用できます。
from("URI1").from("URI2").from("URI3").to("DestinationUri");
これらの両方の例で、各入力エンドポイント URI1 、URI2 、および URI3 からのエクスチェンジは、相互に独立に、別個のスレッドで処理されます。実際、上記のルートは以下の 3 つに分かれたルートと同等であると考えることができます。
from("URI1").to("DestinationUri"); from("URI2").to("DestinationUri"); from("URI3").to("DestinationUri");
セグメント化されたルート
たとえば、2 つの異なるメッセージングシステムからの受信メッセージをマージし、同じルートを使用して処理する場合があります。ほとんどの場合、図2.5「セグメント化されたルートによる複数入力の処理」 に示されるように、ルートをセグメントに分割して複数の入力に対応できます。
図2.5 セグメント化されたルートによる複数入力の処理
ルートの最初のセグメントは、たとえば activemq:Nyse
や activemq:Nasdaq
といったいくつかの外部キューから入力を取得し、その受信エクスチェンジを内部エンドポイント InternalUrl に送信します。2 つ目のルートセグメントは、受信エクスチェンジを内部エンドポイントから取得し、宛先キュー activemq:USTxn
に送信することで、受信エクスチェンジをマージします。InternalUrl は、ルーターのアプリケーション 内 でのみ使用することが意図されたエンドポイントの URL です。以下のタイプのエンドポイントが内部使用に適しています。
これらのエンドポイントの主な目的は、ルートの異なるセグメントをまとめることにあります。これらはすべて、複数の入力を単一のルートにマージする効果的な方法を提供します。
Direct エンドポイント
direct コンポーネントは、複数のルートを繋ぎ合わせる最も簡単なメカニズムを提供します。direct コンポーネントのイベントモデルは 同期型 であり、ルートの後続のセグメントは最初のセグメントと同じスレッドで実行されます。direct URL の一般的な形式は direct:EndpointID
です。エンドポイント ID である EndpointID は、エンドポイントのインスタンスを識別する一意の英数字の文字列です。
たとえば、2 つのメッセージキュー activemq:Nyse
と activemq:Nasdaq
から入力を受け取り、それらを単一のメッセージキュー activemq:USTxn
にマージする場合、以下のルートセットを定義することで実行できます。
from("activemq:Nyse").to("direct:mergeTxns"); from("activemq:Nasdaq").to("direct:mergeTxns"); from("direct:mergeTxns").to("activemq:USTxn");
最初の 2 つのルートはメッセージキュー Nyse
と Nasdaq
から入力を受け取り、それらをエンドポイント direct:mergeTxns
に送信します。最後のキューは、前の 2 つのキューからの入力を組み合わせ、組み合わせたメッセージストリームを activemq:USTxn
キューに送信します。
direct エンドポイントの実装は、以下のように動作します。エクスチェンジがプロデューサーエンドポイント (例: to("direct:mergeTxns")
) に到達するたびに、direct エンドポイントは、同じエンドポイント ID (例: from("direct:mergeTxns")
) を持つすべてのコンシューマーエンドポイントに直接エクスチェンジを渡します。direct エンドポイントは、同じ Java 仮想マシン (JVM) インスタンス内の同じ CamelContext
に属するルート間の通信にのみ使用できます。
SEDA エンドポイント
SEDA コンポーネントは、複数のルートを繋ぎ合わせるもう 1 つのメカニズムを提供します。これは direct コンポーネントと同様の使い方ができますが、以下のように基盤となるイベントとスレッドのモデルが異なります。
- SEDA エンドポイントの処理は同期されません。つまり、エクスチェンジを SEDA プロデューサーエンドポイントに送信すると、ルート内の前のプロセッサーに制御がすぐに戻されます。
-
SEDA エンドポイントはキューバッファー (
java.util.concurrent.BlockingQueue
型) を持ち、次のルートセグメントによって処理される前の受信エクスチェンジをすべて格納しています。 - 各 SEDA コンシューマーエンドポイントは、ブロッキングキューからのエクスチェンジオブジェクトを処理するためにスレッドプール (デフォルトのサイズは 5) を作成します。
- SEDA コンポーネントは、競合コンシューマー (competing consumers) パターンをサポートします。これは、特定のエンドポイントに複数のコンシューマーが接続している場合でも、各受信エクスチェンジが 1 度だけ処理されることを保証するものです。
SEDA エンドポイントを使用する主な利点の 1 つは、組み込みのコンシューマースレッドプールにより、ルートの応答性が向上することです。株式取引の例は、以下のように、direct エンドポイントの代わりに SEDA エンドポイントを使用するように書き換えられます。
from("activemq:Nyse").to("seda:mergeTxns"); from("activemq:Nasdaq").to("seda:mergeTxns"); from("seda:mergeTxns").to("activemq:USTxn");
この例と direct の例の主な相違点は、SEDA を使用する場合、2 番目のルートセグメント (seda:mergeTxns
から activemq:USTxn
) が 5 つのスレッドのプールで処理される点です。
SEDA は単にルートセグメントを繋ぎ合わせるだけではありません。段階的イベント駆動型アーキテクチャー (staged event-driven architecture、SEDA) は、より管理しやすいマルチスレッドアプリケーションを構築するための設計哲学を含んでいます。Apache Camel の SEDA コンポーネントの目的は、この設計哲学をアプリケーションに適用できるようにすることです。SEDA の詳細は、http://www.eecs.harvard.edu/~mdw/proj/seda/ を参照してください。
VM エンドポイント
VM コンポーネントは SEDA エンドポイントと非常に似ています。唯一の違いは、SEDA コンポーネントが同じ CamelContext
内のルートセグメントの繋ぎ合わせに限定されるのに対し、VM コンポーネントでは、同じ Java 仮想マシン内で実行されている限り、異なる Apache Camel アプリケーションからのルートを繋ぎ合わせられることです。
株式取引の例は、以下のように、 SEDA エンドポイントの代わりに VM エンドポイントを使用するように書き換えられます。
from("activemq:Nyse").to("vm:mergeTxns"); from("activemq:Nasdaq").to("vm:mergeTxns");
そして、別のルーターアプリケーション (同じ Java 仮想マシンで実行されている) において、以下のようにルートの 2 つ目のセグメントを定義できます。
from("vm:mergeTxns").to("activemq:USTxn");
Content Enricher パターン
Content Enricher パターンは、これまでと根本的に異なる方法でルートへの複数入力の処理を定義します。エクスチェンジが Enricher プロセッサーに入ると、Enricher は外部リソースにアクセスして情報を取得し、その情報を元のメッセージに追加します。このパターンでは、外部リソースが実質的にメッセージへの 2 つ目の入力を表しています。
たとえば、信用リクエストを処理するアプリケーションを作成している場合に、信用リクエストを処理する前に、それを顧客に対して信用格付けを割り当てるデータ (格付けデータはディレクトリー src/data/ratings
のファイルに格納されている) で拡張する必要があります。以下のように、pollEnrich()
パターンと GroupedExchangeAggregationStrategy
集約ストラテジーを使用して、受信信用リクエストと格付けファイルのデータを組み合わせることができます。
from("jms:queue:creditRequests") .pollEnrich("file:src/data/ratings?noop=true", new GroupedExchangeAggregationStrategy()) .bean(new MergeCreditRequestAndRatings(), "merge") .to("jms:queue:reformattedRequests");
GroupedExchangeAggregationStrategy
クラスは、org.apache.camel.processor.aggregate
パッケージの標準集約ストラテジーで、各新しいエクスチェンジを java.util.List
インスタンスに追加し、生成されるリストを Exchange.GROUPED_EXCHANGE
エクスチェンジプロパティーに保存します。この場合、リストには、(creditRequests
JMS キューからの) 元のエクスチェンジと (file エンドポイントからの) Enricher エクスチェンジの 2 つの要素が含まれます。
グループ化されたエクスチェンジにアクセスするには、以下のようなコードを使用します。
public class MergeCreditRequestAndRatings { public void merge(Exchange ex) { // Obtain the grouped exchange List<Exchange> list = ex.getProperty(Exchange.GROUPED_EXCHANGE, List.class); // Get the exchanges from the grouped exchange Exchange originalEx = list.get(0); Exchange ratingsEx = list.get(1); // Merge the exchanges ... } }
このアプリケーションへの別のアプローチとしては、データをマージするコードをカスタム集約ストラテジークラスに直接実装することが考えられます。
Content Enricher パターンの詳細は、「Content Enricher」 を参照してください。
2.3. 例外処理
概要
Apache Camel はいくつかの異なるメカニズムを提供しており、異なるレベルの粒度で例外を処理することができます。まず、doTry
、doCatch
、および doFinally
を使用してルート内で例外を処理できます。また、onException
を使用して、各例外型に対して実行するアクションを指定し、RouteBuilder
内のすべてのルートにそのルールを適用することもできます。または、errorHandler
を使用して、すべての 例外型に対して実行するアクションを指定し、そのルールを RouteBuilder
内のすべてのルートに適用することもできます。
例外処理の詳細は、「Dead Letter Channel」 を参照してください。
2.3.1. onException 句
概要
onException
句は、1 つ以上のルートで発生する例外をトラップするための強力なメカニズムです。これは型固有のもので、異なる例外型を処理するための個別のアクションを定義することができます。基本的にルートと同じ (実際には、若干拡張された) 構文でアクションを定義できるため、例外を処理する方法にかなりの柔軟性が得られます。また、トラップモデルをベースにしていることにより、1 つの onException
句で任意のルート内の任意のノードで発生した例外を処理できます。
onException を使用した例外のトラップ
onException
句は、例外をキャッチするのではなく、トラップ するメカニズムです。つまり、一度 onException
句を定義すると、ルート内の任意の地点で発生する例外がトラップされます。これは、特定のコードフラグメントが try ブロックで 明示的 に囲まれている場合にのみ例外がキャッチされる、Java の try/catch メカニズムとは対照的です。
onException
句を定義すると、Apache Camel ランタイムが各ルートノードを暗黙的に try ブロックで囲んでしまいます。このため、onException
句はルートの任意の地点で例外をトラップすることができます。ただし、このラッピングは自動的に行われ、ルート定義には表示されません。
Java DSL の例
以下の Java DSL の例では、onException
句は RouteBuilder
クラスで定義されているすべてのルートに適用されます。いずれかのルート (from("seda:inputA")
または from("seda:inputB")
) の処理中に ValidationException
例外が発生すると、onException
句はその例外をトラップし、現在のエクスチェンジを validationFailed
JMS キュー (デッドレターキューとして機能する) にリダイレクトします。
// Java public class MyRouteBuilder extends RouteBuilder { public void configure() { onException(ValidationException.class) .to("activemq:validationFailed"); from("seda:inputA") .to("validation:foo/bar.xsd", "activemq:someQueue"); from("seda:inputB").to("direct:foo") .to("rnc:mySchema.rnc", "activemq:anotherQueue"); } }
XML DSL の例
上記の例は、exception 句を定義する onException
要素を使用して、以下のように XML DSL で表すこともできます。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <camelContext xmlns="http://camel.apache.org/schema/spring"> <onException> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException> <route> <from uri="seda:inputA"/> <to uri="validation:foo/bar.xsd"/> <to uri="activemq:someQueue"/> </route> <route> <from uri="seda:inputB"/> <to uri="rnc:mySchema.rnc"/> <to uri="activemq:anotherQueue"/> </route> </camelContext> </beans>
複数の例外のトラップ
複数の onException
句を定義して、RouteBuilder
スコープ内で例外をトラップすることができます。これにより、例外に応じて異なるアクションを実行できます。たとえば、以下の Java DSL で定義された一連の onException
句は、ValidationException
、IOException
、および Exception
の異なるデッドレター宛先を定義します。
onException(ValidationException.class).to("activemq:validationFailed"); onException(java.io.IOException.class).to("activemq:ioExceptions"); onException(Exception.class).to("activemq:exceptions");
以下のように、XML DSL で同じ一連の onException
句を定義することができます。
<onException> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException> <onException> <exception>java.io.IOException</exception> <to uri="activemq:ioExceptions"/> </onException> <onException> <exception>java.lang.Exception</exception> <to uri="activemq:exceptions"/> </onException>
また、複数の例外をグループ化して、同じ onException
句でトラップすることもできます。Java DSL では、以下のように複数の例外をグループ化できます。
onException(ValidationException.class, BuesinessException.class) .to("activemq:validationFailed");
XML DSL では、以下のように onException
要素内に複数の exception
要素を定義することで、複数の例外をグループ化できます。
<onException> <exception>com.mycompany.ValidationException</exception> <exception>com.mycompany.BuesinessException</exception> <to uri="activemq:validationFailed"/> </onException>
複数の例外をトラップする場合、onException
句の順序は重要です。Apache Camel はまず、発生した例外を最初の句に対して一致しようと試みます。最初の句が一致しない場合、次の onException
句が試行され、一致するものが見つかるまで続きます。各々の一致するかどうかの試行は、以下のアルゴリズムで制御されます。
発生する例外が チェーン例外 (例外がキャッチされて別の例外として出力されたもの) である場合、最もネストされた例外型が最初に一致の基準となります。この例外は、以下のようにテストされます。
-
テスト対象例外が正確に
onException
句で指定された型を持っている場合 (instanceof
によってテストされる) は、一致が起こります。 -
テスト対象例外が
onException
句で指定された型のサブタイプである場合、一致が起こります。
-
テスト対象例外が正確に
- 最もネストされた例外が一致しなかった場合、チェーンの次の例外 (ラップしている例外) がテストされます。このテストは、一致が起こるかチェーンの最後に到達するまで継続します。
throwException EIP を使用すると、Simple 言語の式から新しい例外インスタンスを生成できます。現在のエクスチェンジから利用可能な情報に基づいて、動的に生成することができます。以下に例を示します。
<throwException exceptionType="java.lang.IllegalArgumentException" message="${body}"/>
デッドレターチャネル
これまでの基本的な onException
の使用例は、すべて デッドレターチャネル パターンを利用していました。つまり、onException
句が例外をトラップすると、現在のエクスチェンジは特別な宛先 (デッドレターチャネル) にルーティングされます。デッドレターチャネルは、処理されて いない 失敗したメッセージの保持領域として機能します。管理者は後でメッセージを検査し、どのようなアクションを取る必要があるかを決定できます。
チャネルパターンの詳細は、「Dead Letter Channel」 を参照してください。
元のメッセージの使用
ルートの途中で例外が発生した時点では、エクスチェンジ内のメッセージが大幅に変更されている可能性があります (人間には判読できなくなっている場合もあります) 。多くの場合、管理者にとっては、デッドレターキューに表示されるメッセージがルートの開始時に受信したままの 元 のメッセージであれば、どのような修正アクションをとるべきか決定するのが簡単になります。useOriginalMessage
オプションはデフォルトでは false
に設定されますが、エラーハンドラーに設定されている場合には自動的に有効になります。
useOriginalMessage
オプションは、メッセージを複数のエンドポイントに送信する Camel ルートに適用したり、メッセージを分割したりすると、予期せぬ動作をすることがあります。中間処理ステップが元のメッセージを変更する Multicast、Splitter、または RecipientList のルートでは、元のメッセージは保持されない場合があります。
Java DSL では、エクスチェンジのメッセージを元のメッセージで置き換えることができます。setAllowUseOriginalMessage()
を true
に設定し、以下のように useOriginalMessage()
DSL コマンドを使用します。
onException(ValidationException.class) .useOriginalMessage() .to("activemq:validationFailed");
XML DSL では、以下のように onException
要素の useOriginalMessage
属性を設定することで、元のメッセージを取得できます。
<onException useOriginalMessage="true"> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException>
setAllowUseOriginalMessage()
オプションが true
に設定されている場合、Camel はルートの開始時に元のメッセージのコピーを作成します。これにより、useOriginalMessage()
の呼び出し時に元のメッセージが利用できることを保証します。しかし、setAllowUseOriginalMessage()
オプションが Camel コンテキストで false
(デフォルト) に設定されている場合、元のメッセージにはアクセス できず、useOriginalMessage()
を呼び出すことができません。
デフォルトの動作がこうなっている理由は、大きなメッセージを処理する際にパフォーマンスを最適化するためです。
2.18 より前の Camel バージョンでは、allowUseOriginalMessage
のデフォルト設定は true です。
再配信ポリシー
例外が発生したらすぐにメッセージの処理を中断して諦める代わりに、Apache Camel では例外が発生した時点でメッセージを 再送 するオプションを利用できます。タイムアウトが発生したり、一時的な障害が発生したりするネットワークシステムでは、元の例外が発生してからすぐに再送することで、失敗したメッセージが正常に処理されることがよくあります。
Apache Camel の再配信は、例外の発生後にメッセージを再送するさまざまなストラテジーをサポートします。再配信を設定する際に最も重要なオプションには、以下のものがあります。
maximumRedeliveries()
-
再配信を試行できる最大回数を指定します (デフォルトは
0
)。負の値は、再配信がいつまでも試行されることを意味します (無限の値と同等です) 。 retryWhile()
Apache Camel が再配信を続行すべきかどうかを決定する述語 (
Predicate
型) を指定します。述語が現在のエクスチェンジ上でtrue
と評価されると、再配信が試行されます。そうでない場合は再配信が停止され、それ以上の再配信の試みは行われません。このオプションは
maximumRedeliveries()
オプションよりも優先されます。
Java DSL では、再配信ポリシーのオプションは、onException
句内の DSL コマンドを使用して指定します。たとえば、以下のように、エクスチェンジが validationFailed
デッドレターキューに送信される前に、最大 6 回の再配信を指定できます。
onException(ValidationException.class) .maximumRedeliveries(6) .retryAttemptedLogLevel(org.apache.camel.LogginLevel.WARN) .to("activemq:validationFailed");
XML DSL では、redeliveryPolicy
要素に属性を設定することで再配信ポリシーオプションを指定します。たとえば、上記のルートは以下のように XML DSL で表現できます。
<onException useOriginalMessage="true"> <exception>com.mycompany.ValidationException</exception> <redeliveryPolicy maximumRedeliveries="6"/> <to uri="activemq:validationFailed"/> </onException>
再配信オプションが設定された後のルートの後半部分は、最後の再配信の試みが失敗するまで処理されません。すべての再配信オプションの詳細については、「Dead Letter Channel」 を参照してください。
もう 1 つの方法として、redeliveryPolicyProfile
インスタンスで再配信ポリシーオプションを指定することもできます。その後、onException
要素の redeliverPolicyRef
属性を使用して、redeliveryPolicyProfile
インスタンスを参照できます。たとえば、上記のルートは以下のように表現できます。
<redeliveryPolicyProfile id="redelivPolicy" maximumRedeliveries="6" retryAttemptedLogLevel="WARN"/> <onException useOriginalMessage="true" redeliveryPolicyRef="redelivPolicy"> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException>
複数の onException
句で同じ再配信ポリシーを再利用する場合は、redeliveryPolicyProfile
を使用するアプローチが便利です。
条件付きトラップ
onWhen
オプションを指定することで、onException
による例外のトラップを条件付きにすることができます。onException
句で onWhen
オプションを指定すると、発生した例外が句と一致し、かつ、onWhen
述語が現在のエクスチェンジで true
に評価された場合にのみ一致が起こります。
たとえば、以下の Java DSL フラグメントでは、発生する例外が MyUserException
に一致し、user
ヘッダーが現在のエクスチェンジで null でない場合にのみ、最初の onException
句が実行されます。
// Java // Here we define onException() to catch MyUserException when // there is a header[user] on the exchange that is not null onException(MyUserException.class) .onWhen(header("user").isNotNull()) .maximumRedeliveries(2) .to(ERROR_USER_QUEUE); // Here we define onException to catch MyUserException as a kind // of fallback when the above did not match. // Noitce: The order how we have defined these onException is // important as Camel will resolve in the same order as they // have been defined onException(MyUserException.class) .maximumRedeliveries(2) .to(ERROR_QUEUE);
上記の onException
句は、以下のように XML DSL で表現できます。
<redeliveryPolicyProfile id="twoRedeliveries" maximumRedeliveries="2"/> <onException redeliveryPolicyRef="twoRedeliveries"> <exception>com.mycompany.MyUserException</exception> <onWhen> <simple>${header.user} != null</simple> </onWhen> <to uri="activemq:error_user_queue"/> </onException> <onException redeliveryPolicyRef="twoRedeliveries"> <exception>com.mycompany.MyUserException</exception> <to uri="activemq:error_queue"/> </onException>
例外の処理
デフォルトでは、ルートの途中で例外が発生すると、現在のエクスチェンジの処理が中断され、発生した例外がルート先頭のコンシューマーエンドポイントに伝播されます。onException
句がトリガーされても、発生した例外が伝播される前に onException
句がいくつかの処理を実行することを除き、この動作は基本的に同じです。
しかし、このデフォルトの動作が例外を処理する唯一の方法ではありません。以下のように、onException
には例外処理の動作を変更するさまざまなオプションが用意されています。
-
例外再出力の抑制 -
onException
句が完了した後に、再出力された例外を抑制するオプションがあります。つまり、この場合、例外はルート先頭のコンシューマーエンドポイントまで伝播しません。 - 継続的な処理 - 例外が発生した時点からエクスチェンジの通常の処理を再開するオプションがあります。このアプローチでは、暗黙的に例外の再出力も抑制されます。
- レスポンスの送信 - ルート先頭にあるコンシューマーエンドポイントがリプライを期待する (つまり InOut MEP を持つ) 特別なケースでは、例外をコンシューマーエンドポイントに伝播するのではなく、カスタムのフォールトリプライメッセージを作成する場合があります。
例外再出力の抑制
現在の例外が再出力され、コンシューマーエンドポイントに伝播されないようにするには、以下のように Java DSL で handled()
オプションを true
に設定します。
onException(ValidationException.class) .handled(true) .to("activemq:validationFailed");
Java DSL では、handled()
オプションの引数はブール型、Predicate
型、または Expression
型のいずれかを取ります (非ブール型の式は、それが非 null 値として評価された場合には true
と解釈されます)。
以下のように handled
要素を使用して、XML DSL で同じルートを設定して再出力した例外を抑制できます。
<onException> <exception>com.mycompany.ValidationException</exception> <handled> <constant>true</constant> </handled> <to uri="activemq:validationFailed"/> </onException>
処理の継続
例外が最初に発生したルート内のポイントから現在のメッセージの処理を続行するには、以下のように Java DSL で continued
オプションを true
に設定します。
onException(ValidationException.class) .continued(true);
Java DSL では、continued()
オプションの引数はブール型、Predicate
型、または Expression
型のいずれかを取ります (非ブール型の式は、それが非 null 値として評価された場合には true
と解釈されます)。
以下のように continued
要素を使用して、XML DSL で同じルートを設定できます。
<onException> <exception>com.mycompany.ValidationException</exception> <continued> <constant>true</constant> </continued> </onException>
レスポンスの送信
ルートを開始するコンシューマーエンドポイントがリプライを期待している場合、単に発生した例外をコンシューマーに伝播するのではなく、カスタムのフォールトリプライメッセージを作成する場合があります。この場合、2 つのステップが必要になります。まず、handled
オプションを使用して再出力例外を抑制し、次にエクスチェンジの Out メッセージスロットにカスタムのフォールトメッセージを設定します。
たとえば、以下の Java DSL フラグメントは、MyFunctionalException
例外が発生するたびに、テキスト文字列 Sorry
を含むリプライメッセージを送信する方法を示しています。
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body as Sorry. onException(MyFunctionalException.class) .handled(true) .transform().constant("Sorry");
クライアントにフォールトレスポンスを送信する場合、例外メッセージのテキストをレスポンスに組み込みたいことがよくあります。exceptionMessage()
ビルダーメソッドを使用して、現在の例外メッセージのテキストにアクセスできます。たとえば、以下のように MyFunctionalException
例外が発生するたびに、例外メッセージのテキストのみを含むリプライを送信できます。
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body and return the exception message onException(MyFunctionalException.class) .handled(true) .transform(exceptionMessage());
例外メッセージのテキストは、Simple 言語からも exception.message
変数を介してアクセスできます。たとえば、以下のように現在の例外のテキストをリプライメッセージに埋め込むことができます。
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body and return a nice message // using the simple language where we want insert the exception message onException(MyFunctionalException.class) .handled(true) .transform().simple("Error reported: ${exception.message} - cannot process this message.");
上記の onException
句は、以下のように XML DSL で表現できます。
<onException> <exception>com.mycompany.MyFunctionalException</exception> <handled> <constant>true</constant> </handled> <transform> <simple>Error reported: ${exception.message} - cannot process this message.</simple> </transform> </onException>
例外処理中に発生した例外
既存の例外の処理中に発生した例外 (つまり、onException
句の処理中に発生した例外) は、特別な方法で処理されます。このような例外は、特別なフォールバック例外ハンドラーによって処理されます。例外は以下のように処理されます。
- 既存の例外ハンドラーはすべて無視され、処理は直ちに失敗します。
- 新しい例外がログに記録されます。
- 新しい例外がエクスチェンジオブジェクトに設定されます。
このシンプルな戦略は、onException
句が無限ループに閉じ込められるような複雑な障害のシナリオを回避します。
スコープ
OnException
句は、以下のスコープのいずれかで有効になります。
RouteBuilder scope:
RouteBuilder.configure()
メソッド内で独立した文として定義されたonException
句は、そのRouteBuilder
インスタンスで定義されたすべてのルートに影響します。一方、これらのonException
句は他のRouteBuilder
インスタンス内で定義されたルートに対する 影響はありません。onException
句は、ルート定義の前に表示する 必要があります。この時点までのすべての例は、
RouteBuilder
スコープを使用して定義されます。-
Route スコープ -
onException
句をルート内に直接埋め込むこともできます。onException 句は、それらが定義されているルートに のみ 影響します。
Route スコープ
ルート定義内のどこにでも onException
句を埋め込むことができますが、end()
DSL コマンドを使用して埋め込んだ onException
句を終了する必要があります。
たとえば、以下のように Java DSL で埋め込み onException
句を定義できます。
// Java from("direct:start") .onException(OrderFailedException.class) .maximumRedeliveries(1) .handled(true) .beanRef("orderService", "orderFailed") .to("mock:error") .end() .beanRef("orderService", "handleOrder") .to("mock:result");
XML DSL では、埋め込み onException
句を以下のように定義できます。
<route errorHandlerRef="deadLetter"> <from uri="direct:start"/> <onException> <exception>com.mycompany.OrderFailedException</exception> <redeliveryPolicy maximumRedeliveries="1"/> <handled> <constant>true</constant> </handled> <bean ref="orderService" method="orderFailed"/> <to uri="mock:error"/> </onException> <bean ref="orderService" method="handleOrder"/> <to uri="mock:result"/> </route>
2.3.2. エラーハンドラー
概要
errorHandler()
句は、このメカニズムが異なる例外型を識別 できない 点を除いて、onException
句と同様の機能を提供します。errorHandler()
句は、Apache Camel が提供する元々の例外処理メカニズムで、onException
句が実装される前から利用可能でした。
Java DSL の例
errorHandler()
句は RouteBuilder
クラスで定義され、その RouteBuilder
クラスのすべてのルートに適用されます。これは、該当するルートのいずれかで例外が その種類に関わらず 発生するたびに実行されます。たとえば、失敗したすべてのエクスチェンジを ActiveMQ の deadLetter
キューにルーティングするエラーハンドラーを定義するには、以下のように RouteBuilder
を定義します。
public class MyRouteBuilder extends RouteBuilder { public void configure() { errorHandler(deadLetterChannel("activemq:deadLetter")); // The preceding error handler applies // to all of the following routes: from("activemq:orderQueue") .to("pop3://fulfillment@acme.com"); from("file:src/data?noop=true") .to("file:target/messages"); // ... } }
ただし、デッドレターチャネルへのリダイレクトは、再配信の試行が終了するまで発生しません。
XML DSL の例
XML DSL では、errorHandler
要素を使用して、camelContext
スコープ内にエラーハンドラーを定義します。たとえば、失敗したすべてのエクスチェンジを ActiveMQ deadLetter
キューにルーティングするエラーハンドラーを定義するには、以下のように errorHandler
要素を定義します。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <camelContext xmlns="http://camel.apache.org/schema/spring"> <errorHandler type="DeadLetterChannel" deadLetterUri="activemq:deadLetter"/> <route> <from uri="activemq:orderQueue"/> <to uri="pop3://fulfillment@acme.com"/> </route> <route> <from uri="file:src/data?noop=true"/> <to uri="file:target/messages"/> </route> </camelContext> </beans>
エラーハンドラーの種類
表2.1「エラーハンドラーの種類」 では、定義可能なさまざまな種類のエラーハンドラーの概要を説明します。
Java DSL ビルダー | XML DSL Type 属性 | 説明 |
---|---|---|
|
| 例外を呼び出し元に戻し、再配信ポリシーをサポートしますが、デッドレターキューはサポートされません。 |
|
| デフォルトのエラーハンドラーと同じ機能をサポートし、さらにデッドレターキューもサポートします。 |
|
| 例外が発生するたびに例外テキストをログに記録します。 |
|
| エラーハンドラーを無効にするために使用できるダミーのハンドラー実装。 |
| トランザクションが有効化されたルートのエラーハンドラー。トランザクションが有効化されたルートでは、デフォルトのトランザクションエラーハンドラーインスタンスが自動的に使用されます。 |
2.3.3. doTry、doCatch、および doFinally
概要
ルート内で例外を処理するには、Java の try
、catch
、および finally
ブロックと同様の方法で例外を処理する、doTry
、doCatch
、および doFinally
句の組み合わせを使用できます。
doCatch と Java における catch の類似点
通常、ルート定義内の doCatch()
句は、Java コードの catch()
文と同様の動作をします。具体的には、以下の機能が doCatch()
句でサポートされています。
複数の doCatch 句 - 1 つの
doTry
ブロック内に複数のdoCatch
句を持たせることができます。このdoCatch
句は、Java のcatch()
文と同様に、表示される順序でテストされます。Apache Camel は、出力された例外に一致する最初のdoCatch
句を実行します。注記このアルゴリズムは、
onException
句で使用される例外一致アルゴリズムとは異なります。詳細は 「onException 句」 を参照してください。-
例外 の再出力 — コンストラクトを使用して
doCatch
句内から現在の例外を再出力できます (「doCatch での例外の再出力」)。
doCatch の特別機能
ただし、doCatch()
句には Java の catch()
文に類似するものがない特別な機能がいくつかあります。以下の機能は、doCatch()
に固有のものです。
-
条件キャッチ -
doCatch
句にonWhen
サブ句を追加することで、例外を条件付きでキャッチできます (「onWhen による条件付き例外キャッチ」 を参照)。
例
以下の例は、Java DSL で doTry
ブロックを書く方法を示しています。doCatch()
句は、IOException
例外または IllegalStateException
例外のいずれかが発生した場合に実行され、doFinally()
句は例外が発生したかどうかに関係なく、常に 実行されます。
from("direct:start") .doTry() .process(new ProcessorFail()) .to("mock:result") .doCatch(IOException.class, IllegalStateException.class) .to("mock:catch") .doFinally() .to("mock:finally") .end();
または、Spring XML で同等のものを記述するとこのようになります。
<route> <from uri="direct:start"/> <!-- here the try starts. its a try .. catch .. finally just as regular java code --> <doTry> <process ref="processorFail"/> <to uri="mock:result"/> <doCatch> <!-- catch multiple exceptions --> <exception>java.io.IOException</exception> <exception>java.lang.IllegalStateException</exception> <to uri="mock:catch"/> </doCatch> <doFinally> <to uri="mock:finally"/> </doFinally> </doTry> </route>
doCatch での例外の再出力
次のように、コンストラクトを使用して、doCatch ()
句で例外を再出力することができます。
from("direct:start") .doTry() .process(new ProcessorFail()) .to("mock:result") .doCatch(IOException.class) .to("mock:io") // Rethrow the exception using a construct instead ofhandled(false)
which is deprecated in adoTry/doCatch
clause. .throwException(new IllegalArgumentException("Forced")) .doCatch(Exception.class) // Catch all other exceptions. .to("mock:error") .end();
doTry/doCatch
句で非推奨となった handled (false)
の代わりにプロセッサーを使用して例外を再出力することもできます。
.process(exchange -> {throw exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);})
上記の例では、IOException
が doCatch()
にキャッチされると、現在のエクスチェンジが mock:io
エンドポイントに送信され、その後に IOException
が再出力されます。これにより、ルートの先頭 (from()
コマンド) にあるコンシューマーエンドポイントにも例外を処理する機会が与えられます。
以下の例では、Spring XML で同じルートを定義する方法を示しています。
<route> <from uri="direct:start"/> <doTry> <process ref="processorFail"/> <to uri="mock:result"/> <doCatch> <to uri="mock:io"/> <throwException message="Forced" exceptionType="java.lang.IllegalArgumentException"/> </doCatch> <doCatch> <!-- Catch all other exceptions. --> <exception>java.lang.Exception</exception> <to uri="mock:error"/> </doCatch> </doTry> </route>
onWhen による条件付き例外キャッチ
Apache Camel の doCatch()
句の特別な機能として、実行時に評価される式に基づいて例外のキャッチを条件付けすることができます。つまり、doCatch(ExceptionList).doWhen(Expression)
の形式の句を使用して例外をキャッチした場合、述語の式 Expression が実行時に true
に評価された場合にのみ例外がキャッチされます。
たとえば、以下の doTry
ブロックは、例外メッセージが単語 Severe
を含む場合にのみ、例外 IOException
と IllegalStateException
をキャッチします。
from("direct:start")
.doTry()
.process(new ProcessorFail())
.to("mock:result")
.doCatch(IOException.class, IllegalStateException.class)
.onWhen(exceptionMessage().contains("Severe"))
.to("mock:catch")
.doCatch(CamelExchangeException.class)
.to("mock:catchCamel")
.doFinally()
.to("mock:finally")
.end();
または、Spring XML で同等のものを記述するとこのようになります。
<route> <from uri="direct:start"/> <doTry> <process ref="processorFail"/> <to uri="mock:result"/> <doCatch> <exception>java.io.IOException</exception> <exception>java.lang.IllegalStateException</exception> <onWhen> <simple>${exception.message} contains 'Severe'</simple> </onWhen> <to uri="mock:catch"/> </doCatch> <doCatch> <exception>org.apache.camel.CamelExchangeException</exception> <to uri="mock:catchCamel"/> </doCatch> <doFinally> <to uri="mock:finally"/> </doFinally> </doTry> </route>
doTry のネストされた条件
Camel の例外処理を JavaDSL ルートに追加するためのオプションは複数あります。dotry()
は例外を処理するための try または catch ブロックを作成します。これはルート固有のエラー処理に役立ちます。
ChoiceDefinition
内部で例外をキャッチする場合は、以下のように doTry
ブロックを使用できます。
from("direct:wayne-get-token").setExchangePattern(ExchangePattern.InOut) .doTry() .to("https4://wayne-token-service") .choice() .when().simple("${header.CamelHttpResponseCode} == '200'") .convertBodyTo(String.class) .setHeader("wayne-token").groovy("body.replaceAll('\"','')") .log(">> Wayne Token : ${header.wayne-token}") .endChoice() .doCatch(java.lang.Class (java.lang.Exception>) .log(">> Exception") .endDoTry(); from("direct:wayne-get-token").setExchangePattern(ExchangePattern.InOut) .doTry() .to("https4://wayne-token-service") .doCatch(Exception.class) .log(">> Exception") .endDoTry();
2.3.4. SOAP 例外の伝播
概要
Camel CXF コンポーネントは Apache CXF とのインテグレーションを提供し、Apache Camel のエンドポイントから SOAP メッセージを送受信できます。XML で Apache Camel のエンドポイントを簡単に定義でき、それをエンドポイントの Bean ID を使用してルート内で参照できます。詳細は、Apache Camel Component Reference Guide の CXF を参照してください。
スタックトレース情報を伝播させる方法
Java の例外がサーバー側で発生したときに、例外のスタックトレースがフォールトメッセージにマーシャリングされてクライアントに返されるように、CXF エンドポイントを設定することができます。この機能を有効にするには、以下のように cxfEndpoint
要素で、dataFormat
を PAYLOAD
に設定し、faultStackTraceEnabled
プロパティーを true
に設定します。
<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage" wsdlURL="ship.wsdl" endpointName="s:TestSoapEndpoint" serviceName="s:TestService" xmlns:s="http://test"> <cxf:properties> <!-- enable sending the stack trace back to client; the default value is false--> <entry key="faultStackTraceEnabled" value="true" /> <entry key="dataFormat" value="PAYLOAD" /> </cxf:properties> </cxf:cxfEndpoint>
セキュリティー上の理由から、スタックトレースには原因となる例外 (つまりスタックトレースの Caused by
以降の部分) は含まれません。スタックトレースに原因となる例外を含めたい場合は、以下のように cxfEndpoint
要素の exceptionMessageCauseEnabled
プロパティーを true
に設定します。
<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage" wsdlURL="ship.wsdl" endpointName="s:TestSoapEndpoint" serviceName="s:TestService" xmlns:s="http://test"> <cxf:properties> <!-- enable to show the cause exception message and the default value is false --> <entry key="exceptionMessageCauseEnabled" value="true" /> <!-- enable to send the stack trace back to client, the default value is false--> <entry key="faultStackTraceEnabled" value="true" /> <entry key="dataFormat" value="PAYLOAD" /> </cxf:properties> </cxf:cxfEndpoint>
exceptionMessageCauseEnabled
フラグは、テストおよび診断目的でのみ有効にしてください。サーバーにおいて例外の元の原因を隠すことで、敵対的なユーザーがサーバーを調査しにくくするのが、通常の実践的なやり方です。
2.4. Bean インテグレーション
概要
Bean インテグレーションは、任意の Java オブジェクトを使用してメッセージを処理するための汎用のメカニズムを提供します。Bean の参照をルートに挿入すると、Java オブジェクトの任意のメソッドを呼び出して、受信エクスチェンジにアクセスしたり変更したりすることができます。エクスチェンジの内容を Bean メソッドのパラメーターと戻り値にマッピングするメカニズムは、パラメーターバインディング と呼ばれます。パラメーターバインディングは、メソッドのパラメーターを初期化するために、以下のアプローチの任意の組み合わせを使用することができます。
- 規約に従ったメソッドシグネチャー - メソッドシグネチャーが特定の規約に準拠している場合、パラメーターバインディングは Java リフレクションを使用して、どのパラメーターを渡すかを決定できます。
- アノテーションと依存性注入 - より柔軟なバインディングメカニズムが必要な場合は、Java アノテーションを使用してメソッドの引数に何を注入するかを指定します。この依存性注入メカニズムは、Spring 2.5 のコンポーネントスキャンに基づきます。通常、Apache Camel アプリケーションを Spring コンテナーにデプロイする場合、依存性注入メカニズムは自動的に機能します。
- 明示的に指定したパラメーター - Bean が呼び出される段階で、パラメーターを明示的に (定数として、または Simple 言語を使用して) 指定できます。
Bean レジストリー
Bean は Bean レジストリー を介してアクセスできます。Bean レジストリーは、クラス名または Bean ID のいずれかをキーとして Bean を検索できるサービスです。Bean レジストリーにエントリーを作成する方法は、基盤となるフレームワーク (たとえばプレーンな Java、Spring、Guice、または Blueprint など) によって異なります。レジストリーのエントリーは通常暗黙的に作成されます (例: Spring XML ファイルで Spring Bean をインスタンス化するときなど)。
レジストリープラグインストラテジー
Apache Camel は Bean レジストリーのプラグインストラテジーを実装しており、基盤となるレジストリー実装から透過的に Bean にアクセスするためのインテグレーション層を定義しています。そのため、表2.2「レジストリープラグイン」 に示されるように、Apache Camel アプリケーションをさまざまな Bean レジストリーと統合させることが可能です。
レジストリー実装 | レジストリープラグインのある Camel コンポーネント |
---|---|
Spring Bean レジストリー |
|
Guice Bean レジストリー |
|
Blueprint Bean レジストリー |
|
OSGi サービスレジストリー | OSGi コンテナーにデプロイされている |
JNDI レジストリー |
通常、関連する Bean レジストリーが自動的にインストールされるため、Bean レジストリーの設定を自ら行なう必要はありません。たとえば、Spring フレームワークを使用してルートを定義する場合、Spring ApplicationContextRegistry
プラグインは現在の CamelContext
インスタンスに自動的にインストールされます。
OSGi コンテナーへのデプロイは特別なケースになります。Apache Camel ルートが OSGi コンテナーにデプロイされると、CamelContext
が Bean インスタンスの解決のためにレジストリーチェーンを自動的に設定します。レジストリーチェーンは OSGi レジストリーと、それに続く Blueprint (または Spring) レジストリーで設定されます。
Java で作成された Bean へのアクセス
Java Bean (Plain Old Java Object または POJO) を使用してエクスチェンジオブジェクトを処理するには、インバウンドエクスチェンジを Java オブジェクトのメソッドにバインドする bean()
プロセッサーを使用します。たとえば、MyBeanProcessor
クラスを使用してインバウンドエクスチェンジを処理するには、以下のようにルートを定義します。
from("file:data/inbound") .bean(MyBeanProcessor.class, "processBody") .to("file:data/outbound");
bean()
プロセッサーは MyBeanProcessor
型のインスタンスを作成し、processBody()
メソッドを呼び出してインバウンドエクスチェンジを処理します。単一のルートからのみ MyBeanProcessor
インスタンスにアクセスする場合には、この方法が適切です。しかし、複数のルートから同じ MyBeanProcessor
インスタンスにアクセスする場合は、Object
型を最初の引数として取る bean()
のバリアントを使用します。以下に例を示します。
MyBeanProcessor myBean = new MyBeanProcessor(); from("file:data/inbound") .bean(myBean, "processBody") .to("file:data/outbound"); from("activemq:inboundData") .bean(myBean, "processBody") .to("activemq:outboundData");
オーバーロードされた Bean メソッドへのアクセス
Bean がオーバーロードされた複数のメソッドを定義する場合、メソッド名とそのパラメーター型を指定して、どのオーバーロードされたメソッドを呼び出すかを選択できます。たとえば、MyBeanBrocessor
クラスに 2 つのオーバーロードされたメソッド processBody(String)
および processBody(String,String)
がある場合、後者のオーバーロードされたメソッドを以下のように呼び出すことができます。
from("file:data/inbound") .bean(MyBeanProcessor.class, "processBody(String,String)") .to("file:data/outbound");
または、各パラメーターの型を明示的に指定するのではなく、受け取るパラメーターの数でメソッドを特定する場合は、ワイルドカード文字 *
を使用できます。たとえば、パラメーターの正確な型に関係なく、2 つのパラメーターを取る名前が processBody
のメソッドを呼び出すには、以下のように bean()
プロセッサーを呼び出します。
from("file:data/inbound") .bean(MyBeanProcessor.class, "processBody(*,*)") .to("file:data/outbound");
メソッドを指定する場合、単純な修飾なしの型名 (例: processBody(Exchange)
) または完全修飾型名 (例: processBody(org.apache.camel.Exchange)
) のいずれかを使用できます。
現在の実装では、指定された型名はパラメーター型に完全に一致する必要があります。型の継承は考慮されません。
パラメーターの明示的な指定
Bean メソッドを呼び出す際に、パラメーター値を明示的に指定できます。以下の単純な型の値を渡すことができます。
-
ブール値:
true
またはfalse
-
数値:
123
、7
など -
文字列:
'In single quotes'
または"In double quotes"
-
Null オブジェクト:
null
以下の例は、同じメソッド呼び出しの中で明示的なパラメーター値と型指定子を混在させる方法を示しています。
from("file:data/inbound") .bean(MyBeanProcessor.class, "processBody(String, 'Sample string value', true, 7)") .to("file:data/outbound");
上記の例では、最初のパラメーターの値はパラメーターバインディングアノテーションによって決定されます (「基本アノテーション」 を参照)。
単純な型の値の他に、Simple 言語 (30章Simple 言語) を使用してパラメーター値を指定することもできます。これは、パラメーター値を指定する際に Simple 言語の完全な機能が利用可能である ことを意味します。たとえば、メッセージボディーと title
ヘッダーの値を Bean メソッドに渡すには、以下のようにします。
from("file:data/inbound") .bean(MyBeanProcessor.class, "processBodyAndHeader(${body},${header.title})") .to("file:data/outbound");
ヘッダーのハッシュマップ全体をパラメーターとして渡すこともできます。たとえば、以下の例では、2 つ目のメソッドパラメーターは java.util.Map
型として宣言する必要があります。
from("file:data/inbound") .bean(MyBeanProcessor.class, "processBodyAndAllHeaders(${body},${header})") .to("file:data/outbound");
Apache Camel 2.19 のリリースから、Bean メソッド呼び出しから null を返すことで、常にメッセージボディーが null 値として設定されるようになりました。
基本的なメソッドシグネチャー
エクスチェンジを Bean メソッドにバインドするには、特定の規約に準拠するメソッドシグネチャーを定義します。特に、メソッドシグネチャーには 2 つの基本的な規約があります。
メッセージボディーを処理するメソッドシグネチャー
受信メッセージボディーにアクセスしたり、これを変更したりする Bean メソッドを実装する場合は、単一の String
引数を取り、String
値を返すメソッドシグネチャーを定義する必要があります。以下に例を示します。
// Java package com.acme; public class MyBeanProcessor { public String processBody(String body) { // Do whatever you like to 'body'... return newBody; } }
エクスチェンジを処理するメソッドシグネチャー
より柔軟性を高めるために、受信エクスチェンジにアクセスする Bean メソッドを実装できます。これにより、すべてのヘッダー、ボディー、エクスチェンジプロパティーにアクセスしたり、変更したりすることができます。エクスチェンジの処理には、メソッドシグネチャーは単一の org.apache.camel.Exchange
パラメーターを取り、void
を返します。以下に例を示します。
// Java package com.acme; public class MyBeanProcessor { public void processExchange(Exchange exchange) { // Do whatever you like to 'exchange'... exchange.getIn().setBody("Here is a new message body!"); } }
Spring XML から Spring Bean へのアクセス
Java で Bean インスタンスを作成する代わりに、Spring XML を使用してインスタンスを作成できます。実際、ルートを XML で定義している場合には、これが唯一の実行可能な方法です。XML で Bean を定義するには、標準の Spring bean
要素を使用します。以下の例は、MyBeanProcessor
のインスタンスを作成する方法を示しています。
<beans ...> ... <bean id="myBeanId" class="com.acme.MyBeanProcessor"/> </beans>
Spring 構文を使用して、データを Bean のコンストラクター引数に渡すこともできます。Spring bean
要素の使用方法に関する詳細は、Spring リファレンスガイドの The IoC Container を参照してください。
bean
要素を使用してオブジェクトインスタンスを作成する場合、Bean の ID (bean
要素の id
属性の値) を使用すると後でオブジェクトインスタンスを参照できます。たとえば、ID が myBeanId
と同じ bean
要素がある場合は、以下のように beanRef()
プロセッサーを使用して Java DSL ルート内で Bean を参照できます。
from("file:data/inbound").beanRef("myBeanId", "processBody").to("file:data/outbound");
beanRef()
プロセッサーは、指定された Bean インスタンスで MyBeanProcessor.processBody()
メソッドを呼び出します。
Spring XML ルート内から、Camel スキーマの bean
要素を使用して Bean を呼び出すこともできます。以下に例を示します。
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="file:data/inbound"/> <bean ref="myBeanId" method="processBody"/> <to uri="file:data/outbound"/> </route> </camelContext>
効率を若干向上させるために、cache
オプションを true
に設定して、Bean が使用されるたびにレジストリーを検索しないようにすることもできます。たとえば、キャッシュを有効にするには、以下のように bean
要素の cache
属性を設定します。
<bean ref="myBeanId" method="processBody" cache="true"/>
Java からの Spring Bean へのアクセス
Spring bean
要素を使用してオブジェクトインスタンスを作成する場合、Bean の ID (bean
要素の id
属性の値) を使用して Java からオブジェクトインスタンスを参照できます。たとえば、ID が myBeanId
と同じ bean
要素がある場合は、以下のように beanRef()
プロセッサーを使用して Java DSL ルート内で Bean を参照できます。
from("file:data/inbound").beanRef("myBeanId", "processBody").to("file:data/outbound");
または、以下のように @BeanInject
アノテーションを使用して、依存性注入によって Spring Bean を参照することもできます。
// Java import org.apache.camel.@BeanInject; ... public class MyRouteBuilder extends RouteBuilder { @BeanInject("myBeanId") com.acme.MyBeanProcessor bean; public void configure() throws Exception { .. } }
@BeanInject
アノテーションから Bean ID を省略した場合、Camel は型別にレジストリーを検索しますが、これは指定された型の Bean が 1 つだけの場合にのみ機能します。たとえば、com.acme.MyBeanProcessor
型の Bean を検索して依存性注入するには、以下を実行します。
@BeanInject com.acme.MyBeanProcessor bean;
Spring XML における Bean のシャットダウン順序
Camel コンテキストで使用される Bean の場合、通常、正しいシャットダウンの順序は次のようになります。
-
camelContext
インスタンスをシャットダウンします。 - 使用された Bean をシャットダウンします。
このシャットダウン順序が逆の場合、Camel コンテキストがすでに破棄された Bean にアクセスしようとすることがあります (直接エラーになるか、または Camel コンテキストが破棄されている間に見つからなかった Bean を作成しようして、結局エラーになるかのどちらかです)。Spring XML のデフォルトのシャットダウン順序は、Bean と camelContext
が Spring XML ファイルの中で出現する順序によって異なります。誤ったシャットダウン順序によるランダムなエラーを回避するため、camelContext
は Spring XML ファイルの他の Bean よりも 前に シャットダウンするように設定されています。これは Apache Camel 2.13.0 以降のデフォルトの動作です。
この動作を変更 (Camel コンテキストが他の Bean の前に強制的にシャットダウン されない ように) する必要がある場合は、camelContext
要素の shutdownEager
属性を false
に設定します。この場合、Spring の depends-on
属性を使用して、シャットダウンの順序をより詳細に制御することもできます。
パラメーターバインディングアノテーション
「基本的なメソッドシグネチャー」 で説明されている基本的なパラメーターバインディングは、必ずしも便利に使えるとは限りません。たとえば、何らかのデータ操作を行うレガシーな Java クラスがある場合、インバウンドエクスチェンジからデータを抽出し、既存のメソッドシグネチャーの引数にマップする必要があるかもしれません。このようなパラメーターバインディングには、Apache Camel は以下のような Java アノテーションを提供します。
基本アノテーション
表2.3「基本の Bean アノテーション」 は、Bean メソッドの引数にメッセージデータを依存性注入するために使用できる org.apache.camel
Java パッケージのアノテーションを示しています。
アノテーション | 意味 | パラメーター |
---|---|---|
| アタッチメントのリストにバインドします。 | |
| インバウンドメッセージのボディーにバインドします。 | |
| インバウンドメッセージのヘッダーにバインドします。 | ヘッダーの文字列名。 |
|
インバウンドメッセージヘッダーの | |
|
アウトバウンドメッセージヘッダーの | |
| 名前のあるエクスチェンジプロパティーにバインドします。 | プロパティーの文字列名。 |
|
エクスチェンジプロパティーの |
たとえば、以下のクラスは基本アノテーションを使用してメッセージデータを processExchange()
メソッド引数に依存性注入する方法を示しています。
// Java import org.apache.camel.*; public class MyBeanProcessor { public void processExchange( @Header(name="user") String user, @Body String body, Exchange exchange ) { // Do whatever you like to 'exchange'... exchange.getIn().setBody(body + "UserName = " + user); } }
アノテーションがどのようにデフォルトの規約と混在できるかに注目してください。パラメーターバインディングは、アノテーションが付けられた引数を依存性注入するだけでなく、エクスチェンジオブジェクトも org.apache.camel.Exchange
引数に自動的に依存性注入します。
式言語アノテーション
式言語アノテーションは、メッセージデータを Bean メソッドの引数に依存性注入する強力なメカニズムを提供します。これらのアノテーションを使用すると、任意のスクリプト言語で書かれた任意のスクリプトを呼び出して、インバウンドエクスチェンジからデータを抽出し、メソッド引数に注入することができます。表2.4「式言語アノテーション」 は、Bean メソッドの引数にメッセージデータを依存性注入するために使用できる org.apache.camel.language
パッケージ (およびコア以外のアノテーションのサブパッケージ) のアノテーションを示しています。
アノテーション | 説明 |
---|---|
| Bean 式を注入します。 |
| Constant 式を注入します。 |
| EL 式を注入します。 |
| Groovy 式を注入します。 |
| ヘッダー式を注入します。 |
| JavaScript 式を注入します。 |
| OGNL 式を注入します。 |
| PHP 式を注入します。 |
| Python 式を注入します。 |
| Ruby 式を注入します。 |
| Simple 式を注入します。 |
| XPath 式を注入します。 |
| XQuery 式を注入します。 |
たとえば、以下のクラスは、XML 形式の受信メッセージのボディーからユーザー名とパスワードを抽出するために @XPath
アノテーションを使用する方法を示しています。
// Java import org.apache.camel.language.*; public class MyBeanProcessor { public void checkCredentials( @XPath("/credentials/username/text()") String user, @XPath("/credentials/password/text()") String pass ) { // Check the user/pass credentials... ... } }
@Bean
アノテーションは、特殊なケースになります。登録された Bean の呼び出し結果を依存性注入できるためです。たとえば、相関 ID をメソッド引数に依存性注入するには、以下のように @Bean
アノテーションを使用して ID 生成クラスを呼び出します。
// Java import org.apache.camel.language.*; public class MyBeanProcessor { public void processCorrelatedMsg( @Bean("myCorrIdGenerator") String corrId, @Body String body ) { // Check the user/pass credentials... ... } }
文字列 myCorrIdGenerator
は ID 生成インスタンスの Bean ID です。ID 生成クラスは、以下のように Spring の bean
要素を使用してインスタンス化できます。
<beans ...> ... <bean id="myCorrIdGenerator" class="com.acme.MyIdGenerator"/> </beans>
MyIdGenerator
クラスは以下のように定義することができます。
// Java package com.acme; public class MyIdGenerator { private UserManager userManager; public String generate( @Header(name = "user") String user, @Body String payload ) throws Exception { User user = userManager.lookupUser(user); String userId = user.getPrimaryId(); String id = userId + generateHashCodeForPayload(payload); return id; } }
参照された Bean クラス MyIdGenerator
でアノテーションを使用することもできます。generate()
メソッドシグネチャーに対する唯一の制限は、@Bean
アノテーションが付けられた引数に依存性注入するために正しい型を返す必要があることです。@Bean
アノテーションではメソッド名を指定できないため、依存性注入メカニズムは単純に参照された Bean の戻り値型が一致する最初のメソッドを呼び出します。
言語アノテーションのいくつかはコアコンポーネントで利用できます (@Bean
、@Constant
、@Simple
、および @XPath
)。しかし、コア以外のコンポーネントの場合、該当するコンポーネントをロードしておく必要があります。たとえば、OGNL スクリプトを使用するには、camel-ognl
コンポーネントをロードする必要があります。
継承されたアノテーション
パラメーターバインディングアノテーションは、インターフェイスまたはスーパークラスから継承できます。たとえば、以下のように Header
アノテーションと Body
アノテーションの付いた Java インターフェイスを定義したとします。
// Java import org.apache.camel.*; public interface MyBeanProcessorIntf { void processExchange( @Header(name="user") String user, @Body String body, Exchange exchange ); }
実装クラス MyBeanProcessor
で定義されたオーバーロードされたメソッドは、以下のように基本インターフェイスに定義されたアノテーションを継承します。
// Java import org.apache.camel.*; public class MyBeanProcessor implements MyBeanProcessorIntf { public void processExchange( String user, // Inherits Header annotation String body, // Inherits Body annotation Exchange exchange ) { ... } }
インターフェイスの実装
Java インターフェイスを実装するクラスは、多くの場合、protected
、private
、または package-only
の範囲となります。このように制限された実装クラスのメソッドを呼び出す場合、Bean バインディングはフォールバックして、公開アクセス可能な対応するインターフェイスメソッドを呼び出します。
たとえば、以下のパブリック BeanIntf
インターフェイスについて考えてみましょう。
// Java public interface BeanIntf { void processBodyAndHeader(String body, String title); }
BeanIntf
インターフェイスは、以下の protected な BeanIntfImpl
クラスによって実装されます。
// Java protected class BeanIntfImpl implements BeanIntf { void processBodyAndHeader(String body, String title) { ... } }
以下の Bean 呼び出しは、フォールバックして public な BeanIntf.processBodyAndHeader
メソッドを呼び出します。
from("file:data/inbound") .bean(BeanIntfImpl.class, "processBodyAndHeader(${body}, ${header.title})") .to("file:data/outbound");
static メソッドの呼び出し
Bean インテグレーションには、関連付けられたクラスのインスタンスを作成 せずに static メソッドを呼び出す機能があります。たとえば、static メソッド changeSomething()
を定義した以下の Java クラスについて考えてみましょう。
// Java ... public final class MyStaticClass { private MyStaticClass() { } public static String changeSomething(String s) { if ("Hello World".equals(s)) { return "Bye World"; } return null; } public void doSomething() { // noop } }
以下のように、Bean インテグレーションを使用して static changeSomething
メソッドを呼び出すことができます。
from("direct:a") *.bean(MyStaticClass.class, "changeSomething")* .to("mock:a");
この構文は、通常の関数の呼び出しと同じように見えますが、Bean インテグレーションは Java のリフレクションを利用してこのメソッドを static と識別し、MyStaticClass
をインスタンス化 せずに メソッドの呼び出しに進むことに留意してください。
OSGi サービスの呼び出し
ルートが Red Hat Fuse コンテナーにデプロイされた特別なケースでは、Bean インテグレーションを使用して OSGi サービスを直接呼び出すことができます。たとえば、OSGi コンテナーのバンドルのいずれかがサービス org.fusesource.example.HelloWorldOsgiService
をエクスポートしているとすると、以下のような Bean インテグレーションのコードを使用して sayHello
メソッドを呼び出すことができます。
from("file:data/inbound") .bean(org.fusesource.example.HelloWorldOsgiService.class, "sayHello") .to("file:data/outbound");
以下のように Bean コンポーネントを使用して、Spring または Blueprint XML ファイル内から OSGi サービスを呼び出すこともできます。
<to uri="bean:org.fusesource.example.HelloWorldOsgiService?method=sayHello"/>
これが動作する仕組みは、Apache Camel が OSGi コンテナーにデプロイされる際にレジストリーのチェーンを設定することによります。まず、OSGi サービスレジストリーで指定のクラス名を検索します。検索に失敗した場合、ローカルの Spring DM または Blueprint レジストリーにフォールバックします。
2.5. エクスチェンジインスタンスの作成
概要
Java コード (たとえば Bean クラスやプロセッサークラス) でメッセージを処理している際に、新しいエクスチェンジインスタンスの生成が必要になることがあります。Exchange
オブジェクトを作成する必要がある場合は、ここで説明するように ExchangeBuilder
クラスのメソッドを呼び出すことが最も簡単な方法になります。
ExchangeBuilder クラス
ExchangeBuilder
クラスの完全修飾名は以下の通りです。
org.apache.camel.builder.ExchangeBuilder
ExchangeBuilder
は、エクスチェンジオブジェクトの構築を開始するために使用できる static メソッド anExchange
を公開しています。
例
たとえば、以下のコードは、メッセージボディーに文字列 Hello World!
を持ち、ヘッダーにユーザー名とパスワードのクレデンシャルを含んだ新しいエクスチェンジオブジェクトを作成します。
// Java import org.apache.camel.Exchange; import org.apache.camel.builder.ExchangeBuilder; ... Exchange exch = ExchangeBuilder.anExchange(camelCtx) .withBody("Hello World!") .withHeader("username", "jdoe") .withHeader("password", "pass") .build();
ExchangeBuilder のメソッド
ExchangeBuilder
クラスは以下のメソッドをサポートします。
ExchangeBuilder anExchange(CamelContext context)
- (static メソッド) エクスチェンジオブジェクトの構築を開始します。
Exchange build()
- エクスチェンジを構築します。
ExchangeBuilder withBody(Object body)
- エクスチェンジにメッセージボディーを設定します (つまり、エクスチェンジの In メッセージボディーを設定します) 。
ExchangeBuilder withHeader(String key, Object value)
- エクスチェンジにヘッダーを設定します (つまり、エクスチェンジの In メッセージにヘッダーを設定します) 。
ExchangeBuilder withPattern(ExchangePattern pattern)
- エクスチェンジに交換パターンを設定します。
ExchangeBuilder withProperty(String key, Object value)
- エクスチェンジにプロパティーを設定します。
2.6. メッセージコンテンツの変換
概要
Apache Camel は、メッセージコンテンツを変換するためのさまざまなアプローチをサポートしています。Apache Camel は、メッセージコンテンツを変更するためのシンプルなネイティブ API に加えて、いくつかの異なるサードパーティーライブラリーや変換のための標準とのインテグレーションをサポートしています。
2.6.1. シンプルなメッセージ変換
概要
Java DSL にはビルトインの API があり、送受信メッセージのシンプルな変換を実行できます。たとえば、例2.1「受信メッセージのシンプルな変換」 に示すルールは、受信メッセージのボディー部の末尾にテキスト World!
を追加します。
例2.1 受信メッセージのシンプルな変換
from("SourceURL").setBody(body().append(" World!")).to("TargetURL");
ここで、setBody()
コマンドは受信メッセージボディーのコンテンツを置き換えます。
シンプルな変換の API
以下の API クラスを使用して、ルーターのルールによってメッセージコンテンツのシンプルな変換を実行できます。
-
org.apache.camel.model.ProcessorDefinition
-
org.apache.camel.builder.Builder
-
org.apache.camel.builder.ValueBuilder
ProcessorDefinition クラス
org.apache.camel.model.ProcessorDefinition
クラスは、ルーターのルールに直接挿入できる DSL コマンドを定義しています (例: 例2.1「受信メッセージのシンプルな変換」 の setBody()
コマンド)。表2.5「ProcessorDefinition クラスの変換メソッド」 は、メッセージコンテンツの変換に関係のある ProcessorDefinition
のメソッドを示しています。
メソッド | 説明 |
---|---|
| IN メッセージのボディーを指定の型に変換します。 |
| FAULT メッセージのヘッダーを削除するプロセッサーを追加します。 |
| IN メッセージ上のヘッダーを削除するプロセッサーを追加します。 |
| エクスチェンジプロパティーを削除するプロセッサーを追加します。 |
| IN メッセージのボディーをセットするプロセッサーを追加します。 |
| FAULT メッセージのボディーをセットするプロセッサーを追加します。 |
| FAULT メッセージにヘッダーをセットするプロセッサーを追加します。 |
| IN メッセージにヘッダーをセットするプロセッサーを追加します。 |
| IN メッセージにヘッダーをセットするプロセッサーを追加します。 |
| OUT メッセージにヘッダーをセットするプロセッサーを追加します。 |
| OUT メッセージにヘッダーをセットするプロセッサーを追加します。 |
| エクスチェンジプロパティーをセットするプロセッサーを追加します。 |
| エクスチェンジプロパティーをセットするプロセッサーを追加します。 |
| OUT メッセージのボディーをセットするプロセッサーを追加します。 |
| OUT メッセージのボディーをセットするプロセッサーを追加します。 |
Builder クラス
org.apache.camel.builder.Builder
クラスは、式または述語が想定される文脈でのメッセージコンテンツへのアクセスを提供します。つまり、Builder
のメソッドは通常 DSL コマンドの 引数 の中で呼び出されます (例: 例2.1「受信メッセージのシンプルな変換」 の body()
コマンド)。表2.6「Builder クラスのメソッド」 では、Builder
クラスで利用可能な static メソッドをまとめています。
メソッド | 説明 |
---|---|
| エクスチェンジのインバウンドボディーに対する述語および値ビルダーを返します。 |
| インバウンドメッセージのボディーを特定の型として、それに対する述語および値ビルダーを返します。 |
| 定数式を返します。 |
| エクスチェンジのフォールトボディーに対する述語および値ビルダーを返します。 |
| フォールトメッセージのボディーを特定の型として、それに対する述語および値ビルダーを返します。 |
| エクスチェンジのヘッダーに対する述語および値ビルダーを返します。 |
| エクスチェンジのアウトバウンドボディーに対する述語および値ビルダーを返します。 |
| アウトバウンドメッセージのボディーを特定の型として、それに対する述語および値ビルダーを返します。 |
| エクスチェンジのプロパティーに対する述語および値ビルダーを返します。 |
| 正規表現のすべての出現箇所を、指定した置換文字列で置き換える式を返します。 |
| 正規表現のすべての出現箇所を、指定した置換文字列で置き換える式を返します。 |
| 指定したエンドポイント URI にエクスチェンジを送信する式を返します。 |
| 指定のシステムプロパティーの式を返します。 |
| 指定のシステムプロパティーの式を返します。 |
ValueBuilder クラス
org.apache.camel.builder.ValueBuilder
クラスを使用すると、Builder
メソッドによって返される値を変更できます。つまり、 ValueBuilder
のメソッドは、メッセージコンテンツを変更するシンプルな方法を提供します。表2.7「ValueBuilder クラスの変更メソッド」 では、ValueBuilder
クラスで利用可能なメソッドをまとめています。この表では、呼び出された値を変更するために使用されるメソッドのみが示されています (詳細は API Reference ドキュメントを参照してください) 。
メソッド | 説明 |
---|---|
| 指定された値をこの式の文字列評価に追加します。 |
| 左辺の式に右辺の式の値が含まれた述語を作成します。 |
| 登録された型コンバーターを使用して、現在の値を指定の型に変換します。 |
| 登録された型コンバーターを使用して、現在の値を String に変換します。 |
| |
| |
| |
| |
|
現在の値が指定の |
|
現在の値が指定の |
|
現在の値が指定の |
| 現在の値が指定の型のインスタンスである場合、true を返します。 |
|
現在の値が指定の |
|
現在の値が指定の |
|
現在の値が指定の |
|
現在の値が |
|
現在の値が |
| |
| 述語の引数を否定にします。 |
| この式の文字列評価を指定された値に追加します。 |
| |
| 正規表現のすべての出現箇所を、指定した置換文字列で置き換えます。 |
| 正規表現のすべての出現箇所を、指定した置換文字列で置き換えます。 |
| 指定の正規表現を使用してこの式の文字列変換をトークン化します。 |
| 指定されたコンパレーターを使用して現在の値をソートします。 |
|
現在の値が |
| コンマのトークン区切り文字を使用してこの式の文字列変換をトークン化します。 |
| 指定のトークン区切り文字を使用してこの式の文字列変換をトークン化します。 |
2.6.2. マーシャリングとアンマーシャリング
Java DSL コマンド
以下のコマンドを使用して、低レベルのメッセージ形式と高レベルのメッセージ形式の間で変換を行うことができます。
-
marshal()
- 高レベルのデータフォーマットを低レベルのデータフォーマットに変換します。 -
unmarshal()
- 低レベルのデータフォーマットを高レベルのデータフォーマットに変換します。
データ形式
Apache Camel は、以下のデータフォーマットのマーシャリングおよびアンマーシャリングをサポートします。
- Java シリアライゼーション
- JAXB
- XMLBeans
- XStream
Java シリアライゼーション
Java オブジェクトをバイナリーデータの Blob に変換できるようにします。このデータフォーマットでは、アンマーシャリングはバイナリー Blob を Java オブジェクトに変換し、マーシャリングは Java オブジェクトをバイナリー Blob に変換します。たとえば、エンドポイント SourceURL からシリアライズされた Java オブジェクトを読み込み、これを Java オブジェクトに変換するには、以下のようなルールを使用します。
from("SourceURL").unmarshal().serialization() .<FurtherProcessing>.to("TargetURL");
または、Spring XML では以下の通りです。
<camelContext id="serialization" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="SourceURL"/> <unmarshal> <serialization/> </unmarshal> <to uri="TargetURL"/> </route> </camelContext>
JAXB
XML スキーマ型と Java 型間のマッピングを提供します (https://jaxb.dev.java.net/ を参照してください)。JAXB では、アンマーシャリングは XML データ型を Java オブジェクトに変換し、マーシャリングは Java オブジェクトを XML データ型に変換します。JAXB データフォーマットを使用する前に、JAXB コンパイラーを使用して XML スキーマをコンパイルし、スキーマの XML データ型を表す Java クラスを生成する必要があります。これはスキーマの バインディング と呼ばれます。スキーマをバインドした後に、以下のようなコードを使用して XML データを Java オブジェクトにアンマーシャリングするルールを定義します。
org.apache.camel.spi.DataFormat jaxb = new org.apache.camel.converter.jaxb.JaxbDataFormat("GeneratedPackageName"); from("SourceURL").unmarshal(jaxb) .<FurtherProcessing>.to("TargetURL");
GeneratedPackagename は JAXB コンパイラーによって生成された Java パッケージの名前で、XML スキーマを表す Java クラスが含まれます。
または、Spring XML では以下の通りです。
<camelContext id="jaxb" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="SourceURL"/> <unmarshal> <jaxb prettyPrint="true" contextPath="GeneratedPackageName"/> </unmarshal> <to uri="TargetURL"/> </route> </camelContext>
XMLBeans
XML スキーマ型と Java 型間の代替的なマッピングを提供します (http://xmlbeans.apache.org/ を参照してください) 。XMLBean では、アンマーシャリングは XML データ型を Java オブジェクトに変換し、マーシャリングは Java オブジェクトを XML データ型に変換します。たとえば、XMLBean を使用して XML データを Java オブジェクトにアンマーシャリングするには、以下のようなコードを使用します。
from("SourceURL").unmarshal().xmlBeans() .<FurtherProcessing>.to("TargetURL");
または、Spring XML では以下の通りです。
<camelContext id="xmlBeans" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="SourceURL"/> <unmarshal> <xmlBeans prettyPrint="true"/> </unmarshal> <to uri="TargetURL"/> </route> </camelContext>
XStream
XML 型と Java 型間のもう 1 つのマッピングを提供します (http://www.xml.com/pub/a/2004/08/18/xstream.html を参照してください) 。XStream はシリアライゼーションライブラリー (Java シリアライゼーションなど) で、Java オブジェクトを XML に変換できるものです。XStream では、アンマーシャリングは XML データ型を Java オブジェクトに変換し、マーシャリングは Java オブジェクトを XML データ型に変換します。
from("SourceURL").unmarshal().xstream() .<FurtherProcessing>.to("TargetURL");
XStream データフォーマットは現在 Spring XML ではサポート されません。
2.6.3. エンドポイントバインディング
バインディングの概要
Apache Camel において、バインディング とは、エンドポイントにコントラクトを結び付ける方法です。たとえば Data Format、Content Enricher、または検証ステップを適用することでコントラクトを結び付けます。入力メッセージには条件または変換が適用され、出力メッセージには補完的な条件または変換が適用されます。
DataFormatBinding
DataFormatBinding
クラスは、特定のデータフォーマットをマーシャリングしたりアンマーシャリングしたりするバインディングを定義する場合に有効です (「マーシャリングとアンマーシャリング」 を参照)。この場合、バインディングの作成に必要なのは、コンストラクターに必要なデータフォーマットへの参照を渡して DataFormatBinding
インスタンスを作成することだけです。
たとえば、例2.2「JAXB バインディング」 の XML DSL スニペットは、Apache Camel エンドポイントに関連付けられたときに、JAXB データフォーマットをマーシャリングおよびアンマーシャリングできるバインディング (ID は jaxb
) を示しています。
例2.2 JAXB バインディング
<beans ... >
...
<bean id="jaxb" class="org.apache.camel.processor.binding.DataFormatBinding">
<constructor-arg ref="jaxbformat"/>
</bean>
<bean id="jaxbformat" class="org.apache.camel.model.dataformat.JaxbDataFormat">
<property name="prettyPrint" value="true"/>
<property name="contextPath" value="org.apache.camel.example"/>
</bean>
</beans>
バインディングとエンドポイントの関連付け
エンドポイントとバインディングを関連付けるには、以下の方法を使用できます。
Binding URI
バインディングをエンドポイントに関連付けるには、エンドポイント URI に binding:NameOfBinding
を接頭辞として付けます。ここで NameOfBinding
は、バインディングの Bean ID になります (例: Spring XML で作成されたバインディング Bean の ID)。
たとえば、以下の例では、ActiveMQ エンドポイントを 例2.2「JAXB バインディング」 で定義された JAXB バインディングに関連付ける方法を示しています。
<beans ...> ... <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="binding:jaxb:activemq:orderQueue"/> <to uri="binding:jaxb:activemq:otherQueue"/> </route> </camelContext> ... </beans>
BindingComponent
接頭辞を使用してバインディングをエンドポイントに関連付ける代わりに、関連付けを暗黙的に行い、バインディングが URI に表示されないようにすることもできます。暗黙的なバインディングを持たない既存のエンドポイントの場合、最も簡単な方法は BindingComponent
クラスを使用してエンドポイントをラップすることです。
たとえば、jaxb
バインディングを activemq
エンドポイントに関連付けるには、以下のように新しい BindingComponent
インスタンスを定義します。
<beans ... >
...
<bean id="jaxbmq" class="org.apache.camel.component.binding.BindingComponent">
<constructor-arg ref="jaxb"/>
<constructor-arg value="activemq:foo."/>
</bean>
<bean id="jaxb" class="org.apache.camel.processor.binding.DataFormatBinding">
<constructor-arg ref="jaxbformat"/>
</bean>
<bean id="jaxbformat" class="org.apache.camel.model.dataformat.JaxbDataFormat">
<property name="prettyPrint" value="true"/>
<property name="contextPath" value="org.apache.camel.example"/>
</bean>
</beans>
jaxbmq
の 2 つ目のコンストラクター引数 (オプション) で URI 接頭辞を定義します。これで、この jaxbmq
ID をエンドポイント URI のスキームとして使用できるようになりました。たとえば、このバインディングコンポーネントを使用して以下のルートを定義できます。
<beans ...> ... <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="jaxbmq:firstQueue"/> <to uri="jaxbmq:otherQueue"/> </route> </camelContext> ... </beans>
上記のルートは、Binding URI のアプローチを使用する以下のルートと同じです。
<beans ...> ... <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="binding:jaxb:activemq:foo.firstQueue"/> <to uri="binding:jaxb:activemq:foo.otherQueue"/> </route> </camelContext> ... </beans>
カスタムの Apache Camel コンポーネントを実装する開発者は、org.apache.camel.spi.HasBinding
インターフェイスを継承するエンドポイントクラスを実装することで、これを実現できます。
BindingComponent コンストラクター
BindingComponent
クラスは以下のコンストラクターをサポートします。
public BindingComponent()
- 無引数の形式です。プロパティー注入を使用してバインディングコンポーネントインスタンスを設定します。
public BindingComponent(Binding binding)
-
このバインディングコンポーネントを指定された
Binding
オブジェクトのbinding
に関連付けます。 public BindingComponent(Binding binding, String uriPrefix)
-
このバインディングコンポーネントを指定された
Binding
オブジェクトのbinding
、および URI 接頭辞uriPrefix
に関連付けます。これが、最も一般的に使用されるコンストラクターです。 public BindingComponent(Binding binding, String uriPrefix, String uriPostfix)
-
このコンストラクターは、追加の URI ポストフィックス
uriPostfix
引数をサポートします。これは、このバインディングコンポーネントを使用して定義された URI に自動的に追加されます。
カスタムバインディングの実装
マーシャリングおよびアンマーシャリングのデータフォーマットに使用される DataFormatBinding
に加えて、独自のカスタムバインディングを実装することができます。カスタムバインディングを以下のように定義します。
-
org.apache.camel.Processor
クラスを実装して、(from
要素に登場) コンシューマーエンドポイントで受信するメッセージに対して変換を行います。 -
補完関係となる
org.apache.camel.Processor
クラスを実装して、プロデューサーエンドポイント (to
要素に登場) から送信されるメッセージに対して逆変換を行います。 -
org.apache.camel.spi.Binding
インターフェイスを実装します。これは上記のプロセッサーインスタンスのファクトリーとして機能します。
Binding インターフェイス
例2.3「org.apache.camel.spi.Binding インターフェイス」 は org.apache.camel.spi.Binding
インターフェイスの定義を示しています。このインタフェースは、カスタムバインディングを定義するために実装する必要があります。
例2.3 org.apache.camel.spi.Binding インターフェイス
// Java package org.apache.camel.spi; import org.apache.camel.Processor; /** * Represents a <a href="http://camel.apache.org/binding.html">Binding</a> or contract * which can be applied to an Endpoint; such as ensuring that a particular * <a href="http://camel.apache.org/data-format.html">Data Format</a> is used on messages in and out of an endpoint. */ public interface Binding { /** * Returns a new {@link Processor} which is used by a producer on an endpoint to implement * the producer side binding before the message is sent to the underlying endpoint. */ Processor createProduceProcessor(); /** * Returns a new {@link Processor} which is used by a consumer on an endpoint to process the * message with the binding before its passed to the endpoint consumer producer. */ Processor createConsumeProcessor(); }
バインディングを使うタイミング
バインディングは、多くの異なるエンドポイントに同じ種類の変換を適用する必要がある場合に有効です。
2.7. プロパティープレースホルダー
概要
プロパティープレースホルダー機能は、さまざまなコンテキスト (エンドポイント URI や XML DSL 要素の属性など) で文字列を置き換えるために使用できます。プレースホルダーの設定は Java プロパティーファイルに格納されます。この機能は、異なる Apache Camel アプリケーション間で設定を共有する場合や、特定の設定を一元管理する場合に役立ちます。
たとえば、以下のルートはリクエストを Web サーバーに送信します。この Web サーバーのホストとポートは、プレースホルダーの {{remote.host}}
と {{remote.port}}
に置き換えられます。
from("direct:start").to("http://{{remote.host}}:{{remote.port}}");
プレースホルダーの値は、以下のように Java プロパティーファイルに定義されています。
# Java properties file remote.host=myserver.com remote.port=8080
プロパティープレースホルダーはエンコーディングオプションをサポートし、UTF-8 などの特定の文字セットを使用して、.properties
ファイルの読み取りを可能にします。ただし、デフォルトでは ISO-8859-1 文字セットによる実装になります。
PropertyPlaceholders
を使用した Apache Camel では、以下がサポートされます。
- 検索するキーと共にデフォルト値を指定する。
-
すべてのプレースホルダーキーがデフォルト値で設定されていて、それらが使用される場合、
PropertiesComponent
を定義する必要がない。 サードパーティー関数を使用してプロパティー値を検索する。これにより、独自のロジックを実装できます。
注記プロパティー値を検索する関数として、標準で OS 環境変数、JVM システムプロパティー、サービス名イディオムの 3 つを提供します。
プロパティーファイル
プロパティー設定は 1 つ以上の Java プロパティーファイルに格納され、標準の Java プロパティーファイル形式に準拠する必要があります。各プロパティー設定は、それぞれ独立した行に Key=Value
の形式で表示されます。空白以外の最初の文字が #
または !
から始まる行は、コメントとして扱われます。
たとえば、プロパティーファイルは 例2.4「プロパティーファイルの例」 のような内容になります。
例2.4 プロパティーファイルの例
# Property placeholder settings # (in Java properties file format) cool.end=mock:result cool.result=result cool.concat=mock:{{cool.result}} cool.start=direct:cool cool.showid=true cheese.end=mock:cheese cheese.quote=Camel rocks cheese.type=Gouda bean.foo=foo bean.bar=bar
プロパティーの解決
Properties コンポーネントは、ルート定義の中で使用を開始する前に、1 つ以上のプロパティーファイルのロケーションを指定して設定しておく必要があります。以下のいずれかのリゾルバーを使用して、プロパティー値を提供する必要があります。
classpath:PathName,PathName,…
- (デフォルト) クラスパス上のロケーションを指定します。PathName は、フォワードスラッシュを使用して区切られたファイルパス名です。
file:PathName,PathName,…
- ファイルシステムのロケーションを指定します。PathName はフォワードスラッシュを使用して区切られたファイルパス名です。
ref:BeanID
-
レジストリーの
java.util.Properties
オブジェクトの ID を指定します。 blueprint:BeanID
-
cm:property-placeholder
Bean の ID を指定します。この Bean は、OSGi Blueprint ファイルのコンテキスト内で、OSGi Configuration Admin サービスで定義されたプロパティーにアクセスするために使用されます。詳細は、「OSGi Blueprint プロパティープレースホルダーとの統合」 を参照してください。
たとえば、クラスパス上にある com/fusesource/cheese.properties
プロパティーファイルと com/fusesource/bar.properties
プロパティーファイルを指定するには、以下のようなロケーション文字列を使用します。
com/fusesource/cheese.properties,com/fusesource/bar.properties
クラスパスリゾルバーはデフォルトで使用されるため、この例では classpath:
接頭辞は省略できます。
システムプロパティーと環境変数を使用したロケーションの指定
ロケーション PathName に Java システムプロパティーおよび O/S 環境変数を埋め込むことができます。
Java システムプロパティーは、${PropertyName}
構文を使用してロケーションリゾルバーに埋め込むことができます。たとえば、Red Hat Fuse のルートディレクトリーが Java システムプロパティー karaf.home
に保存されている場合、以下のようにそのディレクトリーの値をファイルロケーションに埋め込むことができます。
file:${karaf.home}/etc/foo.properties
O/S 環境変数は、${env:VarName}
構文を使用してロケーションリゾルバーに埋め込むことができます。たとえば、Fuse のルートディレクトリーが環境変数 SMX_HOME
に保存されている場合、以下のようにそのディレクトリーの値をファイルロケーションに埋め込むことができます。
file:${env:SMX_HOME}/etc/foo.properties
Properties コンポーネントの設定
プロパティープレースホルダーの使用を開始する前に、1 つ以上のプロパティーファイルのロケーションを指定して、Properties コンポーネントを設定する必要があります。
Java DSL では、以下のように Properties コンポーネントにプロパティーファイルのロケーションを設定できます。
// Java import org.apache.camel.component.properties.PropertiesComponent; ... PropertiesComponent pc = new PropertiesComponent(); pc.setLocation("com/fusesource/cheese.properties,com/fusesource/bar.properties"); context.addComponent("properties", pc);
addComponent()
の呼び出しに示されているように、Properties コンポーネントの名前を properties
に設定する 必要があります。
XML DSL では、以下のように専用の propertyPlacholder
要素を使用して Properties コンポーネントを設定できます。
<camelContext ...> <propertyPlaceholder id="properties" location="com/fusesource/cheese.properties,com/fusesource/bar.properties" /> </camelContext>
Properties コンポーネントの初期化時に見つからない .properties
ファイルを、Properties コンポーネントに無視させる場合は、ignoreMissingLocation
オプションを true
に設定します (通常、.properties
ファイルが見つからない場合はエラーが発生します)。
また、Java システムプロパティーまたは O/S 環境変数を使用して、指定されたロケーションが見つからないときに Properties コンポーネントが無視するようにする場合も、ignoreMissingLocation
オプションを true
に設定することができます。
プレースホルダー構文
Properties コンポーネントは、設定後は (適切なコンテキストで) プレースホルダーを自動的に置き換えます。プレースホルダーの構文は、以下のようにコンテキストによって異なります。
-
エンドポイント URI および Spring XML ファイル: プレースホルダーは
{{Key}}
のように指定します。 XML DSL の属性設定時:
xs:string
属性は以下の構文で設定します。AttributeName="{{Key}}"
その他の属性タイプ (
xs:int
またはxs:boolean
など) は、以下の構文を使用して設定する必要があります。prop:AttributeName="Key"
prop
は、http://camel.apache.org/schema/placeholder
名前空間に関連付けられています。Java DSL の EIP オプション設定時: Java DSL でエンタープライズ統合パターン (EIP) コマンドにオプションを設定するには、流れるような DSL に以下の
placeholder()
句を追加します。.placeholder("OptionName", "Key")
-
Simple 言語式: プレースホルダーは
${properties:Key}
. のように指定します。
エンドポイント URI 内での置換
ルートの中でエンドポイント URI 文字列が現れると、そのエンドポイント URI を構文解析する最初のステップは、常にプロパティープレースホルダーパーサーを適用することです。プレースホルダーパーサーは、二重かっこ {{Key}}
の間に表示されるプロパティー名を自動的に置換します。たとえば、例2.4「プロパティーファイルの例」 にあるプロパティー設定では、以下のようにルートを定義できます。
from("{{cool.start}}") .to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}") .to("mock:{{cool.result}}");
デフォルトでは、プレースホルダーパーサーはレジストリーから properties
Bean ID を検索し、Properties コンポーネントを検索します。必要であれば、エンドポイント URI でスキーマを明示的に指定できます。たとえば、各エンドポイント URI に接頭辞 properties:
を付けて、以下のように同等のルートを定義できます。
from("properties:{{cool.start}}") .to("properties:log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}") .to("properties:mock:{{cool.result}}");
スキーマを明示的に指定する場合、Properties コンポーネントにオプションを指定することもできます。たとえば、プロパティーファイルのロケーションを上書きするために、以下のように location
オプションを設定できます。
from("direct:start").to("properties:{{bar.end}}?location=com/mycompany/bar.properties");
Spring XML ファイル内での置換
XML DSL で DSL 要素のさまざまな属性を設定するために、プロパティープレースホルダーを使用することもできます。このコンテキストにおいても、プレースホルダー構文には二重かっこ {{Key}}
を使用します。たとえば、以下のようにプロパティープレースホルダーを使用して jmxAgent
要素を定義できます。
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <propertyPlaceholder id="properties" location="org/apache/camel/spring/jmx.properties"/> <!-- we can use property placeholders when we define the JMX agent --> <jmxAgent id="agent" registryPort="{{myjmx.port}}" usePlatformMBeanServer="{{myjmx.usePlatform}}" createConnector="true" statisticsLevel="RoutesOnly" /> <route> <from uri="seda:start"/> <to uri="mock:result"/> </route> </camelContext>
XML DSL 属性値の置換
xs:string
型の属性値を指定するには、通常のプレースホルダー構文を使用できます (例: <jmxAgent registryPort="{{myjmx.port}}" …>
)。しかし、他の型の属性 (例: xs:int
や xs:boolean
) については、特別な構文 prop:AttributeName="Key"
を使用する必要があります。
たとえば、プロパティーファイルで stop.flag
プロパティーの値が true
に定義されている場合、このプロパティーを使用して以下のように stopOnException
ブール値属性を設定できます。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:prop="http://camel.apache.org/schema/placeholder" ... > <bean id="illegal" class="java.lang.IllegalArgumentException"> <constructor-arg index="0" value="Good grief!"/> </bean> <camelContext xmlns="http://camel.apache.org/schema/spring"> <propertyPlaceholder id="properties" location="classpath:org/apache/camel/component/properties/myprop.properties" xmlns="http://camel.apache.org/schema/spring"/> <route> <from uri="direct:start"/> <multicast prop:stopOnException="stop.flag"> <to uri="mock:a"/> <throwException ref="damn"/> <to uri="mock:b"/> </multicast> </route> </camelContext> </beans>
prop
接頭辞は、前述の例の beans
要素に示されるように、Spring ファイルの http://camel.apache.org/schema/placeholder
名前空間に明示的に割り当てられている必要があります。
Java DSL における EIP オプションの置換
Java DSL で EIP コマンドを呼び出す場合は、placeholder("OptionName", "Key")
の形式のサブ句を追加することで、プロパティープレースホルダーの値を使用した EIP オプションの設定ができます。
たとえば、プロパティーファイルで stop.flag
プロパティーの値が true
に定義されている場合、このプロパティーを使用して以下のように Multicast EIP の stopOnException
オプションを設定できます。
from("direct:start") .multicast().placeholder("stopOnException", "stop.flag") .to("mock:a").throwException(new IllegalAccessException("Damn")).to("mock:b");
Simple 言語式内での置換
Simple 言語の式の中でプロパティープレースホルダーを置換することもできますが、この場合プレースホルダーの構文は ${properties:Key}
になります。たとえば、以下のようにして Simple 式内の cheese.quote
プレースホルダーを置換できます。
from("direct:start") .transform().simple("Hi ${body} do you think ${properties:cheese.quote}?");
構文 ${properties:Key:DefaultVal}
を使用すると、プロパティーのデフォルト値を指定できます。以下に例を示します。
from("direct:start") .transform().simple("Hi ${body} do you think ${properties:cheese.quote:cheese is good}?");
構文 ${properties-location:Location:Key}
を使用して、プロパティーファイルのロケーションをオーバーライドすることもできます。たとえば、com/mycompany/bar.properties
プロパティーファイルの設定を使用して、bar.quote
プレースホルダーを置き換えるには、以下のように Simple 式を定義します。
from("direct:start") .transform().simple("Hi ${body}. ${properties-location:com/mycompany/bar.properties:bar.quote}.");
XML DSL 内でのプロパティープレースホルダーの使用
以前のリリースでは、 XML DSL のプレースホルダーをサポートするために xs:string
型の属性が使用されていました。たとえば、timeout 属性は xs:int
型になります。したがって、文字列の値をプレースホルダーキーとして設定することはできませんでした。
Apache Camel 2.7 以降、特別なプレースホルダーの名前空間を使用することでそれが可能になりました。以下の例は、その名前空間を使うための prop 接頭辞を示しています。これにより、XML DSL の属性に prop 接頭辞を付けて使用できます。
Multicast において、キー stop
を使用してプレースホルダーの値をオプション stopOnException
に設定しています。また、プロパティーファイルの中で以下の値を定義しています。
stop=true
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:prop="http://camel.apache.org/schema/placeholder" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd "> <!-- Notice in the declaration above, we have defined the prop prefix as the Camel placeholder namespace --> <bean id="damn" class="java.lang.IllegalArgumentException"> <constructor-arg index="0" value="Damn"/> </bean> <camelContext xmlns="http://camel.apache.org/schema/spring"> <propertyPlaceholder id="properties" location="classpath:org/apache/camel/component/properties/myprop.properties" xmlns="http://camel.apache.org/schema/spring"/> <route> <from uri="direct:start"/> <!-- use prop namespace, to define a property placeholder, which maps to option stopOnException={{stop}} --> <multicast prop:stopOnException="stop"> <to uri="mock:a"/> <throwException ref="damn"/> <to uri="mock:b"/> </multicast> </route> </camelContext> </beans>
OSGi Blueprint プロパティープレースホルダーとの統合
Red Hat Fuse の OSGi コンテナーにルートをデプロイする場合、Apache Camel プロパティープレースホルダーのメカニズムを Fuse が持つ Blueprint プロパティープレースホルダーのメカニズムと統合できます (実際には、この統合はデフォルトで有効になっています) 。統合のセットアップには、基本的に以下の 2 つの方法があります。
暗黙的な Blueprint の統合
OSGi Blueprint ファイル内で camelContext
要素を定義すると、Apache Camel プロパティープレースホルダーのメカニズムは自動的に Blueprint プロパティープレースホルダーのメカニズムと統合します。つまり、camelContext
のスコープ内に現れる Apache Camel 構文に従ったプレースホルダー (例: {{cool.end}}
) は、暗黙的に blueprint property placeholder のメカニズムを検索することで解決されます。
たとえば、OSGi Blueprint ファイルで定義された以下のようなルートがあるとします。ルートの最後のエンドポイントは、プロパティープレースホルダー {{result}}
で定義されています。
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"> <!-- OSGI blueprint property placeholder --> <cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint"> <!-- list some properties for this test --> <cm:default-properties> <cm:property name="result" value="mock:result"/> </cm:default-properties> </cm:property-placeholder> <camelContext xmlns="http://camel.apache.org/schema/blueprint"> <!-- in the route we can use {{ }} placeholders which will look up in blueprint, as Camel will auto detect the OSGi blueprint property placeholder and use it --> <route> <from uri="direct:start"/> <to uri="mock:foo"/> <to uri="{{result}}"/> </route> </camelContext> </blueprint>
Blueprint プロパティープレースホルダーのメカニズムは、cm:property-placeholder
Bean を作成することで初期化されます。上記の例では、cm:property-placeholder
Bean は camel.blueprint
永続化 ID に関連付けられています。永続化 ID は、OSGi Configuration Admin サービスから関連するプロパティーのグループを参照する標準的な方法です。つまり、cm:property-placeholder
Bean は、camel.blueprint
永続化 ID の下で定義されたすべてのプロパティーへのアクセスを提供します。一部のプロパティーにデフォルト値を指定することもできます (ネストされた cm:property
要素を使用します)。
Blueprint のコンテキストでは、Apache Camel プレースホルダーのメカニズムは Bean レジストリー内の cm:property-placeholder
インスタンスを検索します。このインスタンスが見つかると、Apache Camel プレースホルダーのメカニズムと自動的に統合され、{{result}}
のようなプレースホルダーは Blueprint プロパティープレースホルダーのメカニズムに対して (この例では myblueprint.placeholder
Bean を通して) キーを検索することで解決されます。
デフォルトの Blueprint プレースホルダー構文 (Blueprint プロパティーに直接アクセスする) は ${Key}
です。そのため、camelContext
要素の 範囲外 では、使用しなければならないプレースホルダー構文は ${Key}
になります。しかし、camelContext
要素の 範囲内 では、使用しなければならないプレースホルダー構文は {{Key}}
になります。
明示的な Blueprint の統合
Apache Camel プロパティープレースホルダーのメカニズムがプロパティーを探す場所をさらに制御する場合は、propertyPlaceholder
要素を定義してリゾルバーのロケーションを明示的に指定できます。
たとえば、以下の Blueprint の設定について考えるとします。この例は、明示的に propertyPlaceholder
インスタンスを作成している点が前述の例とは異なっています。
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 ">https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"> <!-- OSGI blueprint property placeholder --> <cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint"> <!-- list some properties for this test --> <cm:default-properties> <cm:property name="result" value="mock:result"/> </cm:default-properties> </cm:property-placeholder> <camelContext xmlns="http://camel.apache.org/schema/blueprint"> <!-- using Camel properties component and refer to the blueprint property placeholder by its id --> <propertyPlaceholder id="properties" location="blueprint:myblueprint.placeholder"/> <!-- in the route we can use {{ }} placeholders which will lookup in blueprint --> <route> <from uri="direct:start"/> <to uri="mock:foo"/> <to uri="{{result}}"/> </route> </camelContext> </blueprint>
前述の例では、propertyPlaceholder
要素は、場所を blueprint:myblueprint.placeholder
に設定することにより、使用する cm:property-placeholder
Bean を明示的に指定します。つまり、blueprint:
リゾルバーは、cm:property-placeholder
Bean の ID myblueprint.placeholder
を明示的に参照します。
このスタイルによる設定は、Blueprint ファイルに複数の cm:property-placeholder
Bean が定義されていて、どれを使用すべきかを指定する必要がある場合に有効です。また、ロケーションをコンマ区切りのリストで指定することで、複数のロケーションからプロパティーを取得することも可能になります。たとえば、cm:property-placeholder
Bean とクラスパス上のプロパティーファイル myproperties.properties
の両方からプロパティーを検索する場合、以下のように propertyPlaceholder
要素を定義します。
<propertyPlaceholder id="properties" location="blueprint:myblueprint.placeholder,classpath:myproperties.properties"/>
Spring プロパティープレースホルダーとの統合
Spring XML ファイルの XML DSL を使用して Apache Camel アプリケーションを定義している場合、org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer
型の Spring Bean を宣言することで、Apache Camel プロパティープレースホルダーのメカニズムを Spring プロパティープレースホルダーのメカニズムと統合できます。
BridgePropertyPlaceholderConfigurer
を定義します。これは、Spring XML ファイルの Apache Camel の propertyPlaceholder
要素と Spring の ctx:property-placeholder
要素の両方を置き換えます。その後、Spring の ${PropName}
構文または Apache Camel の {{PropName}}
構文のいずれかを使用して、設定したプロパティーを参照できます。
たとえば、cheese.properties
ファイルからプロパティー設定を読み取るブリッジプロパティープレースホルダーを定義するとします。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ctx="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Bridge Spring property placeholder with Camel --> <!-- Do not use <ctx:property-placeholder ... > at the same time --> <bean id="bridgePropertyPlaceholder" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer"> <property name="location" value="classpath:org/apache/camel/component/properties/cheese.properties"/> </bean> <!-- A bean that uses Spring property placeholder --> <!-- The ${hi} is a spring property placeholder --> <bean id="hello" class="org.apache.camel.component.properties.HelloBean"> <property name="greeting" value="${hi}"/> </bean> <camelContext xmlns="http://camel.apache.org/schema/spring"> <!-- Use Camel's property placeholder {{ }} style --> <route> <from uri="direct:{{cool.bar}}"/> <bean ref="hello"/> <to uri="{{cool.end}}"/> </route> </camelContext> </beans>
または、Spring プロパティーファイルを指すように、BridgePropertyPlaceholderConfigurer
の location
属性を設定することもできます。Spring プロパティーファイルの構文は完全にサポートされます。
2.8. スレッドモデル
Java スレッドプール API
Apache Camel のスレッドモデルは、強力な Java 並行処理 API パッケージ java.util.concurrent に基づいています。この API は、Sun の JDK 1.5 で初めて利用可能になったものです。この API の主要なインターフェイスは、スレッドプールを表す ExecutorService
インターフェイスです。並行処理 API を使用すると、幅広いシナリオに対応する、さまざまな種類のスレッドプールを作成できます。
Apache Camel スレッドプール API
Apache Camel スレッドプール API は Java 並行処理 API 上で構築されており、Apache Camel アプリケーションのすべてのスレッドプールに対して中心的なファクトリー (org.apache.camel.spi.ExecutorServiceManager
型) が提供されます。このような方法でスレッドプールの作成を一元化することには、以下のようにいくつかの利点があります。
- ユーティリティークラスを使用することで、スレッドプールの作成を簡素化できる。
- スレッドプールを正常なシャットダウンに統合できる。
- スレッドに有益な名前を自動的に付与できる。これは、ロギングと管理に役立ちます。
コンポーネントのスレッドモデル
SEDA、JMS、Jetty など、Apache Camel コンポーネントには本質的にマルチスレッドなものがあります。こうしたコンポーネントはすべて Apache Camel のスレッドモデルとスレッドプール API を使用して実装されています。
独自の Apache Camel コンポーネントを実装しようとしている場合、マルチスレッドなコードは Apache Camel のスレッドモデルと統合することが推奨されます。たとえば、コンポーネントにスレッドプールが必要な場合は、CamelContext の ExecutorServiceManager
オブジェクトを使用して作成することが推奨されます。
プロセッサーのスレッドモデル
Apache Camel の標準プロセッサーの中には、デフォルトで独自のスレッドプールを作成するものがあります。これらのスレッド対応プロセッサーも Apache Camel のスレッドモデルと統合されており、使用するスレッドプールのカスタマイズを可能にするさまざまなオプションを提供しています。
表2.8「プロセッサーのマルチスレッドオプション」 では、Apache Camel に組み込まれているスレッド対応のプロセッサーでスレッドプールを制御および設定するためのさまざまなオプションをまとめています。
プロセッサー | Java DSL | XML DSL |
---|---|---|
|
parallelProcessing() executorService() executorServiceRef() |
@parallelProcessing @executorServiceRef |
|
parallelProcessing() executorService() executorServiceRef() |
@parallelProcessing @executorServiceRef |
|
parallelProcessing() executorService() executorServiceRef() |
@parallelProcessing @executorServiceRef |
|
parallelProcessing() executorService() executorServiceRef() |
@parallelProcessing @executorServiceRef |
|
executorService() executorServiceRef() poolSize() maxPoolSize() keepAliveTime() timeUnit() maxQueueSize() rejectedPolicy() |
@executorServiceRef @poolSize @maxPoolSize @keepAliveTime @timeUnit @maxQueueSize @rejectedPolicy |
|
wireTap(String uri, ExecutorService executorService) wireTap(String uri, String executorServiceRef) |
@executorServiceRef |
threads DSL オプション
threads
プロセッサーは汎用の DSL コマンドであり、ルートにスレッドプールを導入するために使用できます。スレッドプールをカスタマイズするために、以下のオプションをサポートしています。
poolSize()
- プールの最小スレッド数 (および初期プールサイズ)。
maxPoolSize()
- プールの最大スレッド数。
keepAliveTime()
- スレッドがこの期間 (秒単位で指定) よりも長い間アイドル状態になっている場合、スレッドを終了させる。
timeUnit()
-
keep alive の時間単位。
java.util.concurrent.TimeUnit
タイプを使用して指定します。 maxQueueSize()
- このスレッドプールが受信タスクキューに保持できる保留中の最大タスク数。
rejectedPolicy()
- 受信タスクキューが満杯の場合に実行すべきアクションを指定する。表2.10「スレッドプールビルダーのオプション」 を参照してください。
前述のスレッドプールのオプションは、executorServiceRef
オプションと互換性が ありません (たとえば、これらのオプションを使用して、executorServiceRef
オプションで参照されるスレッドプールの設定を上書きすることはできません)。この DSL の制約は Apache Camel が検証することで強制されます。
デフォルトのスレッドプールの作成
スレッド対応プロセッサーに対してデフォルトのスレッドプールを作成するには、parallelProcessing
オプションを有効にします。Java DSL では parallelProcessing()
サブ句を、XML DSL では parallelProcessing
属性を使用します。
たとえば、Java DSL では、以下のようにデフォルトのスレッドプールを使用して multicast プロセッサーを呼び出すことができます (スレッドプールはマルチキャストの宛先を同時に処理するために使用されます)。
from("direct:start") .multicast().parallelProcessing() .to("mock:first") .to("mock:second") .to("mock:third");
以下のように XML DSL で同じルートを定義できます。
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start"/> <multicast parallelProcessing="true"> <to uri="mock:first"/> <to uri="mock:second"/> <to uri="mock:third"/> </multicast> </route> </camelContext>
デフォルトスレッドプールプロファイルの設定
デフォルトのスレッドプールは、スレッドファクトリーがデフォルトスレッドプールプロファイル から設定を取得することによって自動的に作成されます。デフォルトスレッドプールプロファイルが持つ設定は、 表2.9「デフォルトスレッドプールプロファイルの設定」 に示す通りです (これらの設定はアプリケーションコードによって変更されていないことを前提とします)。
スレッドオプション | デフォルト値 |
---|---|
|
|
|
|
|
|
|
|
|
|
デフォルトスレッドプールプロファイルの変更
デフォルトスレッドプールプロファイルの設定を変更することで、後続のすべてのデフォルトスレッドプールをカスタムの設定で作成することができます。プロファイルは Java または Spring XML のどちらでも変更できます。
たとえば、Java DSL では、以下のようにデフォルトスレッドプールプロファイルの poolSize
オプションと maxQueueSize
オプションをカスタマイズできます。
// Java import org.apache.camel.spi.ExecutorServiceManager; import org.apache.camel.spi.ThreadPoolProfile; ... ExecutorServiceManager manager = context.getExecutorServiceManager(); ThreadPoolProfile defaultProfile = manager.getDefaultThreadPoolProfile(); // Now, customize the profile settings. defaultProfile.setPoolSize(3); defaultProfile.setMaxQueueSize(100); ...
XML DSL では、以下のようにデフォルトスレッドプールプロファイルをカスタマイズできます。
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<threadPoolProfile
id="changedProfile"
defaultProfile="true"
poolSize="3"
maxQueueSize="100"/>
...
</camelContext>
前述の XML DSL の例では defaultProfile
属性を true
に設定することが不可欠です。そうしないと、そのスレッドプールプロファイルはデフォルトスレッドプールプロファイルを置き換えるのではなく、カスタムのスレッドプールプロファイルとして扱われてしまいます (「カスタムスレッドプールプロファイルの作成」 を参照)。
プロセッサーのスレッドプールのカスタマイズ
executorService
または executorServiceRef
オプションのいずれかを使用 (parallelProcessing
オプションの代わりにこれらのオプションを使用) することで、スレッド対応のプロセッサーにスレッドプールを直接指定することもできます。以下のように、プロセッサーのスレッドプールをカスタマイズするには 2 つの方法があります。
-
カスタムスレッドプールの値の指定 -
ExecutorService
(スレッドプール) インスタンスを明示的に作成し、これをexecutorService
オプションに渡します。 -
カスタムスレッドプールプロファイルの指定 - カスタムスレッドプールファクトリーを作成して登録します。
executorServiceRef
オプションを使用してこのファクトリーを参照すると、プロセッサーは自動的にそのファクトリーを使用して、カスタムスレッドプールインスタンスを作成します。
Bean ID を executorServiceRef
オプションに渡すと、スレッド対応のプロセッサーはまずその ID を持つカスタムスレッドプールをレジストリーの中から検索します。その ID でスレッドプールが登録されていない場合、プロセッサーはレジストリーの中からカスタムスレッドプールプロファイルを検索し、そのカスタムスレッドプールプロファイルを使用してカスタムスレッドプールをインスタンス化します。
カスタムスレッドプールの作成
カスタムスレッドプールは、java.util.concurrent.ExecutorService 型の任意のスレッドプールです。Apache Camel では、スレッドプールインスタンスを作成する上で以下の方法が推奨されています。
-
org.apache.camel.builder.ThreadPoolBuilder
ユーティリティーを使用して、スレッドプールクラスをビルドします。 -
現在の
CamelContext
からorg.apache.camel.spi.ExecutorServiceManager
インスタンスを使用して、スレッドプールクラスを作成します。
ThreadPoolBuilder
は実際には ExecutorServiceManager
インスタンスを使用して定義されているため、究極的には 2 つのアプローチには大きな違いはありません。通常、ThreadPoolBuilder
が推奨されます。これは、より単純なアプローチを提供するためです。ただし、少なくとも 1 種類のスレッド (ScheduledExecutorService
) は、ExecutorServiceManager
インスタンスに直接アクセスする方法でしか作成できません。
表2.10「スレッドプールビルダーのオプション」 は、ThreadPoolBuilder
クラスがサポートするオプションを示します。これらのオプションは、新しいカスタムスレッドプールを定義する際に設定できます。
Builder オプション | 説明 |
---|---|
|
このスレッドプールが受信タスクキューに保持できる保留中の最大タスク数を設定します。 |
| プールの最小スレッド数を設定します (これは初期プールサイズにもなります)。デフォルト値はデフォルトスレッドプールプロファイルから取得されます。 |
| プールで使用できる最大スレッド数を設定します。デフォルト値はデフォルトスレッドプールプロファイルから取得されます。 |
| スレッドがこの期間 (秒単位で指定) よりも長い間アイドル状態になっている場合、スレッドを終了させる。これにより、負荷が軽くなるとスレッドプールが縮小されます。デフォルト値はデフォルトスレッドプールプロファイルから取得されます。 |
| 受信タスクキューが満杯の場合に実行すべきアクションを指定する。以下の 4 つの値から指定できます。
|
|
カスタムスレッドプールの構築を終了し、 |
Java DSL では、以下のように ThreadPoolBuilder
を使用してカスタムスレッドプールを定義できます。
// Java import org.apache.camel.builder.ThreadPoolBuilder; import java.util.concurrent.ExecutorService; ... ThreadPoolBuilder poolBuilder = new ThreadPoolBuilder(context); ExecutorService customPool = poolBuilder.poolSize(5).maxPoolSize(5).maxQueueSize(100).build("customPool"); ... from("direct:start") .multicast().executorService(customPool) .to("mock:first") .to("mock:second") .to("mock:third");
オブジェクト参照 customPool
を直接 executorService()
オプションに渡す代わりに、以下のように Bean ID を executorServiceRef()
オプションに渡すことで、レジストリーの中からスレッドプールを検索できます。
// Java from("direct:start") .multicast().executorServiceRef("customPool") .to("mock:first") .to("mock:second") .to("mock:third");
XML DSL では、threadPool
要素を使用して ThreadPoolBuilder
にアクセスします。その後、以下のように executorServiceRef
属性を使用して Spring レジストリーでスレッドプールを ID で検索することで、カスタムスレッドプールを参照できます。
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <threadPool id="customPool" poolSize="5" maxPoolSize="5" maxQueueSize="100" /> <route> <from uri="direct:start"/> <multicast executorServiceRef="customPool"> <to uri="mock:first"/> <to uri="mock:second"/> <to uri="mock:third"/> </multicast> </route> </camelContext>
カスタムスレッドプールプロファイルの作成
多くのカスタムスレッドプールインスタンスを作成する場合は、スレッドプールのファクトリーとして機能するカスタムスレッドプールプロファイルを定義しておくと便利です。スレッド対応プロセッサーからスレッドプールプロファイルを参照するだけで、プロセッサーは自動的にそのプロファイルを使用して新しいスレッドプールインスタンスを作成します。カスタムスレッドプールプロファイルは、Java DSL または XML DSL のどちらでも定義できます。
たとえば、Java DSL では、以下のようにして Bean ID customProfile
を持つカスタムスレッドプールプロファイルを作成し、ルート内でそのプロファイルを参照できます。
// Java import org.apache.camel.spi.ThreadPoolProfile; import org.apache.camel.impl.ThreadPoolProfileSupport; ... // Create the custom thread pool profile ThreadPoolProfile customProfile = new ThreadPoolProfileSupport("customProfile"); customProfile.setPoolSize(5); customProfile.setMaxPoolSize(5); customProfile.setMaxQueueSize(100); context.getExecutorServiceManager().registerThreadPoolProfile(customProfile); ... // Reference the custom thread pool profile in a route from("direct:start") .multicast().executorServiceRef("customProfile") .to("mock:first") .to("mock:second") .to("mock:third");
XML DSL では、threadPoolProfile
要素を使用してカスタムプールプロファイルを作成します (これはデフォルトスレッドプールプロファイル ではない ため、ここでは defaultProfile
オプションをデフォルトで false
に設定します)。以下のようにして、Bean ID customProfile
を持つカスタムスレッドプールプロファイルを作成し、ルート内でそのプロファイルを参照できます。
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <threadPoolProfile id="customProfile" poolSize="5" maxPoolSize="5" maxQueueSize="100" /> <route> <from uri="direct:start"/> <multicast executorServiceRef="customProfile"> <to uri="mock:first"/> <to uri="mock:second"/> <to uri="mock:third"/> </multicast> </route> </camelContext>
コンポーネント間でのスレッドプールの共有
File や FTP など、標準のポーリングベースのコンポーネントの中には、使用するスレッドプールを指定できるものがあります。これにより、異なるコンポーネントが同じスレッドプールを共有することが可能となり、JVM 内のスレッド総数を削減することができます。
たとえば、Apache Camel Component Reference Guide の File2 と Apache Camel Component Reference Guide の Ftp2 は、どちらも scheduledExecutorService
プロパティーを公開しており、コンポーネントの ExecutorService
オブジェクトを指定するために使用できます。
スレッド名のカスタマイズ
アプリケーションのログをより読みやすくするために、スレッド名 (ログ内でスレッドを識別するために使用されるもの) をカスタマイズすることが推奨されます。スレッド名をカスタマイズするには、ExecutorServiceStrategy
クラスまたは ExecutorServiceManager
クラスの setThreadNamePattern
メソッドを呼び出すことで、スレッド名パターン を設定します。または、スレッド名パターンを設定するより簡単な方法として、CamelContext
オブジェクトに threadNamePattern
プロパティーを設定する方法もあります。
スレッド名パターンでは、以下のプレースホルダーが使用できます。
#camelId#
-
現在の
CamelContext
の名前。 #counter#
- インクリメントカウンターとして実装された一意のスレッド ID。
#name#
- 通常の Camel スレッド名。
#longName#
- 長いスレッド名。エンドポイントパラメーターなどを含めることができる。
以下は、スレッド名パターンの典型的な例です。
Camel (#camelId#) thread #counter# - #name#
以下の例は、XML DSL を使用して Camel コンテキストに threadNamePattern
属性を設定する方法を示しています。
<camelContext xmlns="http://camel.apache.org/schema/spring"
threadNamePattern="Riding the thread #counter#" >
<route>
<from uri="seda:start"/>
<to uri="log:result"/>
<to uri="mock:result"/>
</route>
</camelContext>
2.9. ルートの起動およびシャットダウンの制御
概要
デフォルトでは、Apache Camel アプリケーション (CamelContext
インスタンスで表される) の起動時にルートが自動的に起動し、Apache Camel アプリケーションのシャットダウン時にルートは自動的にシャットダウンします。クリティカルでないデプロイメントについては、シャットダウン順序の詳細は通常それほど重要ではありません。しかし、本番環境では、データ損失を回避するために、シャットダウン時に残っているタスクを完了させることが重要になります。また、通常、依存関係の順序違反 (実行中のタスクが完了できなくなる) を防止するために、ルートがシャットダウンする順番を制御したくなることもあります。
このため、Apache Camel はアプリケーションの 正常なシャットダウン をサポートする機能を提供しています。正常なシャットダウンにより、ルートの停止と起動を完全に制御し、ルートのシャットダウン順序を制御し、現在実行中のタスクを完了まで実行できるようになります。
ルート ID の設定
ルート ID を各ルートに割り当てるのが良いプラクティスです。ルート ID を設定すると、ロギングメッセージや管理機能がよりわかりやすくなるだけでなく、ルートの停止と起動の制御がより行いやすくなります。
たとえば、Java DSL では、以下のように routeId()
コマンドを実行して、ルート ID myCustomerRouteId
をルートに割り当てることができます。
from("SourceURI").routeId("myCustomRouteId").process(...).to(TargetURI);
XML DSL では、以下のように route
要素の id
属性を設定します。
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring"> <route id="myCustomRouteId" > <from uri="SourceURI"/> <process ref="someProcessorId"/> <to uri="TargetURI"/> </route> </camelContext>
ルートの自動起動の無効化
デフォルトでは、起動時に CamelContext が認識しているすべてのルートは自動的に起動されます。しかし、特定のルートの起動を手動で制御する場合は、そのルートの自動起動を無効にできます。
Java DSL ルートが自動的に起動するかどうかを制御するには、boolean
引数 (true
または false
) か、String
引数 (true
または false
) のいずれかで、autoStartup
コマンドを呼び出します。たとえば、以下のように Java DSL でルートの自動起動を無効にできます。
from("SourceURI") .routeId("nonAuto") .autoStartup(false) .to(TargetURI);
XML DSL では、以下のように route
要素の autoStartup
属性を false
に設定して、ルートの自動起動を無効にできます。
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring"> <route id="nonAuto" autoStartup="false"> <from uri="SourceURI"/> <to uri="TargetURI"/> </route> </camelContext>
手動によるルートの起動および停止
Java において、CamelContext
インスタンスの startRoute()
および stopRoute()
メソッドを呼び出すことにより、ルートを手動でいつでも起動または停止できます。たとえば、ルート ID nonAuto
を持つルートを開始するには、以下のように CamelContext
インスタンス context
で startRoute()
メソッドを呼び出します。
// Java context.startRoute("nonAuto");
ルート ID nonAuto
を持つルートを停止するには、以下のように CamelContext
インスタンス context
で stopRoute()
メソッドを呼び出します。
// Java context.stopRoute("nonAuto");
ルートの起動順序
デフォルトでは、Apache Camel はルートを非決定論的な順序で起動します。しかし、アプリケーションによっては、起動順序を制御することが重要になる場合があります。Java DSL で起動順序を制御するには、startupOrder()
コマンドを使用します。このコマンドは、正の整数値を引数として取ります。整数値が最も小さいルートが最初に起動し、それ以降、起動順序の値が小さいものから順番にルートが起動します。
たとえば、以下の例では最初の 2 つのルートが seda:buffer
エンドポイントを通して繋ぎ合わされています。以下のように起動順序 (それぞれ 2 と 1) を割り当てることで、最初のルートセグメントを 2 つ目のルートセグメントの 後に 起動させることができます。
例2.5 Java DSL の起動順序
from("jetty:http://fooserver:8080") .routeId("first") .startupOrder(2) .to("seda:buffer"); from("seda:buffer") .routeId("second") .startupOrder(1) .to("mock:result"); // This route's startup order is unspecified from("jms:queue:foo").to("jms:queue:bar");
または、Spring XML では、以下のように route
要素の startupOrder
属性を設定することで、同様の効果を得ることができます。
例2.6 XML DSL の起動順序
<route id="first" startupOrder="2"> <from uri="jetty:http://fooserver:8080"/> <to uri="seda:buffer"/> </route> <route id="second" startupOrder="1"> <from uri="seda:buffer"/> <to uri="mock:result"/> </route> <!-- This route's startup order is unspecified --> <route> <from uri="jms:queue:foo"/> <to uri="jms:queue:bar"/> </route>
各ルートには、一意の 起動順序の値を割り当てる必要があります。値は 1000 未満の正の整数から選択できます。1000 以上の値は Apache Camel 用に予約されており、明示的な起動値を持たないルートにこれらの値が自動的に割り当てられます。たとえば、前述の例の最後のルートは自動的に起動値 1000 が割り当てられます (これにより最初の 2 つのルートの後に起動することになります)。
シャットダウンシーケンス
CamelContext
インスタンスがシャットダウンしているとき、Apache Camel はプラグ可能な シャットダウンストラテジー を使用してシャットダウンシーケンスを制御します。デフォルトのシャットダウンストラテジーは、以下のシャットダウンシーケンスを実装します。
- ルートが起動順序の 逆順 でシャットダウンされる。
- 通常、シャットダウンストラテジーは、現在アクティブなエクスチェンジの処理が終了するまで待機する。ただし、実行中タスクの取り扱いは設定可能。
- 全体的なシャットダウンシーケンスには、タイムアウトの上限がある (デフォルトは 300 秒)。シャットダウンシーケンスがこのタイムアウトを超えると、シャットダウンストラテジーは、一部のタスクが実行中であっても強制的にシャットダウンを実行する。
ルートのシャットダウン順序
ルートは起動順序の逆順でシャットダウンされます。つまり、起動順序が startupOrder()
コマンド (Java DSL) または startupOrder
属性 (XML DSL) を使用して定義されている場合、最初にシャットダウンするルートは、起動順として割り当てられた 最大の 整数値を持つルートであり、最後にシャットダウンするルートは、起動順として割り当てられた 最小の 整数値を持つルートになります。
たとえば、例2.5「Java DSL の起動順序」 では、最初にシャットダウンするルートセグメントは ID first
のルートで、2 番目にシャットダウンするルートセグメントは ID second
のルートになります。この例は、ルートをシャットダウンする際に守るべき一般的なルールを示しています。つまり、外部からアクセス可能なコンシューマーエンドポイントを公開するルートは最初にシャットダウンする必要がある、ということです。これは、残りのルートグラフを流通するメッセージのフローを調整するスロットルとなるためです。
Apache Camel は、オプション shutdownRoute(Defer)
も提供しています。これにより、ルートを (起動順の値を上書きして) 最後にシャットダウンするように指定できます。ただし、このオプションが必要になることはほとんどありません。このオプションは主に、ルートが起動順序と 同じ 順序でシャットダウンしていた Apache Camel の以前のバージョン (2.3 以前) への回避策として必要だったものです。
ルート内で実行中のタスクのシャットダウン
シャットダウンの開始時にルートが依然としてメッセージを処理している場合、シャットダウンストラテジーは通常、現在アクティブなエクスチェンジの処理が終了するまで待機してからルートをシャットダウンします。この動作は、ルート毎に shutdownRunningTask
オプションを使用することで設定できます。このオプションは以下のいずれかの値を取ります。
ShutdownRunningTask.CompleteCurrentTaskOnly
- (デフォルト) 通常、ルートは一度に 1 つのメッセージのみを処理するため、現在のタスクが完了した後にルートを安全にシャットダウンできます。
ShutdownRunningTask.CompleteAllTasks
- バッチコンシューマー を正常にシャットダウンするには、このオプションを指定します。一部のコンシューマーエンドポイント (File、FTP、Mail、iBATIS、JPA など) は一度に複数のメッセージを一括して処理します。これらのエンドポイントでは、現在のバッチのすべてのメッセージが完了するまで待機することが推奨されます。
たとえば、File コンシューマーエンドポイントを正常にシャットダウンするには、以下の Java DSL フラグメントのように CompleteAllTasks
オプションを指定する必要があります。
// Java
public void configure() throws Exception {
from("file:target/pending")
.routeId("first").startupOrder(2)
.shutdownRunningTask(ShutdownRunningTask.CompleteAllTasks)
.delay(1000).to("seda:foo");
from("seda:foo")
.routeId("second").startupOrder(1)
.to("mock:bar");
}
同じルートを XML DSL では以下のように定義できます。
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<!-- let this route complete all its pending messages when asked to shut down -->
<route id="first"
startupOrder="2"
shutdownRunningTask="CompleteAllTasks">
<from uri="file:target/pending"/>
<delay><constant>1000</constant></delay>
<to uri="seda:foo"/>
</route>
<route id="second" startupOrder="1">
<from uri="seda:foo"/>
<to uri="mock:bar"/>
</route>
</camelContext>
シャットダウンのタイムアウト
シャットダウンタイムアウトのデフォルト値は 300 秒です。シャットダウンストラテジーの setTimeout()
メソッドを呼び出すことで、タイムアウトの値を変更できます。たとえば、以下のようにタイムアウト値を 600 秒に変更できます。
// Java // context = CamelContext instance context.getShutdownStrategy().setTimeout(600);
カスタムコンポーネントとの統合
カスタムの Apache Camel コンポーネント (同じく org.apache.camel.Service
インターフェイスから継承する) を実装している場合、org.apache.camel.spi.ShutdownPrepared
インターフェイスを実装することで、カスタムコードがシャットダウン通知を受け取るようにすることができます。これにより、コンポーネントはシャットダウンに備えてカスタムコードを実行できるようになります。
2.9.1. RouteIdFactory
コンシューマーエンドポイントに基づいて、ルート ID に論理名を割り当てられる RouteIdFactory
を追加することができます。
たとえば、seda または direct コンポーネントをルートの入力として持つルートを使用するとき、以下のようにそれらの名前をルート ID として使用する場合があります。
- direct:foo - foo
- seda:bar - bar
- jms:orders - orders
自動的に割り当てられた名前を使用する代わりに、NodeIdFactory
を用いてルートに論理名を割り当てることができます。また、ルート URL の context-path を名前として使用することもできます。たとえば、以下を実行して RouteIDFactory
を使用します。
context.setNodeIdFactory(new RouteIdFactory());
REST エンドポイントからカスタムルート ID を取得することもできます。
2.10. 定期実行ルートポリシー
2.10.1. 定期実行ルートポリシーの概要
概要
定期実行ルートポリシーは、実行時にルートに影響するイベントをトリガーすることができます。特に、現在利用可能な実装では、ポリシーで指定された任意の時刻 (または複数時刻) にルートを開始、停止、中断、または再開することができます。
タスクのスケジューリング
定時実行ルートポリシーは、次のようなイベントを発生させることができます。
- ルートの開始: 指定した時刻 (または複数時刻) でルートを開始します。このイベントは、ルートが現在停止状態にあり、起動を待っている場合にのみ有効になります。
- ルートの停止: 指定した時刻 (または複数時刻) でルートを停止します。このイベントは、ルートが現在アクティブの場合にのみ有効になります。
-
ルートの一時停止: ルートの先頭にあるコンシューマーエンドポイントを (
from()
で指定したように) 一時的に非アクティブにします。ルートの残りの部分はアクティブですが、クライアントはルートに新しいメッセージを送信することはできません。 - ルートの再開 : ルートの先頭にあるコンシューマーエンドポイントを再アクティブにし、ルートを完全にアクティブな状態に戻します。
Quartz コンポーネント
Quartz コンポーネントは、ジョブスケジューラーのオープンソース実装である Terratania の Quartz をベースにした Timer コンポーネントです。Quartz コンポーネントは、単純な定期実行ルートポリシーと cron 定期実行ルートポリシーの両方の基礎となる実装を提供しています。
2.10.2. 単純な定期実行ルートポリシー
概要
定期実行ルートポリシーは、ルートの開始、停止、一時停止、および再開を可能にするルートポリシーで、これらのイベントのタイミングは、初回イベントの発生時刻で指定し、(オプションで) その後の繰り返し回数も指定できます。単純な定期実行ルートポリシーを定義するには、以下のクラスのインスタンスを作成します。
org.apache.camel.routepolicy.quartz.SimpleScheduledRoutePolicy
依存関係
単純な定期実行ルートポリシーは、Quartz コンポーネント camel-quartz
に依存しています。たとえば、Maven をビルドシステムとして使用する場合は、アーティファクト camel-quartz
の依存関係を追加してください。
Java DSL の例
例2.7「単純な定期実行ルートの Java DSL の例」 は、Java DSL を使用して起動するルートをスケジュールする方法を示しています。初回の起動時刻 startTime
は、現在時刻から 3 秒後に設定されています。また、このポリシーは、初期起動時刻の 3 秒後に、2 回目のルートを開始するように設定されています。これは、routeStartRepeatCount
を 1 に設定し、routeStartRepeatInterval
を 3000 ミリ秒に設定することで設定されます。
Java DSL では、ルート内で routePolicy()
DSL コマンドを呼び出して、ルートポリシーをルートにアタッチします。
例2.7 単純な定期実行ルートの Java DSL の例
// Java
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
long startTime = System.currentTimeMillis() + 3000L;
policy.setRouteStartDate(new Date(startTime));
policy.setRouteStartRepeatCount(1);
policy.setRouteStartRepeatInterval(3000);
from("direct:start")
.routeId("test")
.routePolicy(policy)
.to("mock:success");
複数の引数を指定して routePolicy()
を呼び出すことで、ルート上に複数のポリシーを指定することができます。
XML DSL の例
例2.8「単純な定期実行ルートの XML DSL の例」 は、XML DSL を使用して起動するルートをスケジュールする方法を示しています。
XML DSL では、route
要素に routePolicyRef
属性を設定して、ルートポリシーをルートにアタッチします。
例2.8 単純な定期実行ルートの XML DSL の例
<bean id="date" class="java.util.Data"/>
<bean id="startPolicy" class="org.apache.camel.routepolicy.quartz.SimpleScheduledRoutePolicy">
<property name="routeStartDate" ref="date"/>
<property name="routeStartRepeatCount" value="1"/>
<property name="routeStartRepeatInterval" value="3000"/>
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route id="myroute" routePolicyRef="startPolicy">
<from uri="direct:start"/>
<to uri="mock:success"/>
</route>
</camelContext>
routePolicyRef
の値をコンマ区切りの Bean ID リストとして設定することで、ルート上に複数のポリシーを指定できます。
日付と時刻の定義
単純な定期実行ルートポリシーで使用されるトリガーの初動時刻は、java.util.Date
タイプを使用して指定します。Date
インスタンスを定義する最も柔軟な方法は、java.util.GregorianCalendar クラスを使用することです。GregorianCalendar
クラスの便利なコンストラクターとメソッドを使用して日付を定義し、GregorianCalendar.getTime()
を呼び出して Date
インスタンスを取得します。
たとえば、2011 年 1 月 1 日正午の日時を定義するには、以下のように GregorianCalendar
コンストラクターを呼び出します。
// Java import java.util.GregorianCalendar; import java.util.Calendar; ... GregorianCalendar gc = new GregorianCalendar( 2011, Calendar.JANUARY, 1, 12, // hourOfDay 0, // minutes 0 // seconds ); java.util.Date triggerDate = gc.getTime();
GregorianCalendar
クラスは、異なるタイムゾーンの時間の定義もサポートします。デフォルトでは、コンピューターのローカルタイムゾーンを使用します。
正常シャットダウン
単純な定期実行ルートポリシーを設定してルートを停止すると、ルートの停止アルゴリズムが自動的に正常シャットダウンの手順に統合されます (「ルートの起動およびシャットダウンの制御」 を参照)。よって、タスクは現在のエクスチェンジが処理を完了するまで待機してから、ルートをシャットダウンします。ただし、タイムアウトを設定することで、ルートがエクスチェンジの処理を終了したかどうかにかかわらず、指定した時間後にルートを強制的に停止することができます。
タイムアウト時の処理中エクスチェンジのロギング
指定のタイムアウト期間内に正常シャットダウンが適切に行われなかった場合、Apache Camel はより強行なシャットダウンを実行します。ルートやスレッドプールなどを強制的にシャットダウンします。
タイムアウト後、Apache Camel は現在処理中のエクスチェンジの情報をログに記録します。エクスチェンジの元および現在のルートをログに記録します。
たとえば、以下のログは、1 つの処理中のエクスチェンジがあり、その元のルートは route1 で、現在 delay1 ノードの同じ route1 にあることを表しています。
正常なシャットダウン中に、org.apache.camel.impl.DefaultShutdownStrategy
で DEBUG ロギングレベルを有効にすると、同じインフライトエクスチェンジ情報がログに記録されます。
2015-01-12 13:23:23,656 [- ShutdownTask] INFO DefaultShutdownStrategy - There are 1 inflight exchanges: InflightExchange: [exchangeId=ID-davsclaus-air-62213-1421065401253-0-3, fromRouteId=route1, routeId=route1, nodeId=delay1, elapsed=2007, duration=2017]
これらのログを表示したくない場合は、logInflightExchangesOnTimeout
オプションを false に設定してこれをオフにできます。
context.getShutdownStrategegy().setLogInflightExchangesOnTimeout(false);
タスクのスケジューリング
単純な定期実行ルートポリシーを使用して、以下のスケジュールタスクのいずれかを定義することができます。
ルートの開始
次の表は、ルートの開始を 1 回以上スケジューリングするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | ルートの初回起動日時を指定します。 |
|
|
| 0 以外の値に設定すると、ルートの開始回数が指定されます。 |
|
|
| 開始の間隔 (ミリ秒単位) を指定します。 |
ルートの停止
次の表は、ルートの停止を 1 回以上スケジューリングするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | ルートの初回停止日時を指定します。 |
|
|
| 0 以外の値に設定すると、ルートの停止回数が指定されます。 |
|
|
| 停止の間隔 (ミリ秒単位) を指定します。 |
|
|
| ルートを強制停止する前に、現在のエクスチェンジの処理が終了するまで待つ時間 (猶予期間) を指定します。猶予期間が無限の場合は 0 に設定します。 |
|
|
| 猶予期間の時間単位を指定します。 |
ルートの一時停止
次の表は、ルートの一時停止を 1 回以上スケジュールするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | ルートが初めて一時停止される日時を指定します。 |
|
| 0 | 0 以外の値に設定すると、ルートが一時停止される回数が指定されます。 |
|
| 0 | 一時停止の間隔 (ミリ秒単位) を指定します。 |
ルートの再開
次の表は、ルートの再開を 1 回以上スケジュールするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | ルートの初回再開日時を指定します。 |
|
| 0 | 0 以外の値に設定すると、ルートの再開回数が指定されます。 |
|
| 0 | 再開の間隔をミリ秒単位で指定します。 |
2.10.3. cron 定期実行ルートポリシー
概要
cron 定期実行ルートポリシーは、cron 式で指定することで、ルートの開始、停止、一時停止、および再開を可能にするルートポリシーです。cron 定期実行ルートポリシーを定義するには、以下のクラスのインスタンスを作成します。
org.apache.camel.routepolicy.quartz.CronScheduledRoutePolicy
依存関係
単純な定期実行ルートポリシーは、Quartz コンポーネント camel-quartz
に依存しています。たとえば、Maven をビルドシステムとして使用する場合は、アーティファクト camel-quartz
の依存関係を追加してください。
Java DSL の例
例2.9「cron 定期実行ルートの Java DSL の例」 は、Java DSL を使用して起動するルートをスケジュールする方法を示しています。このポリシーは、3 秒ごとに開始イベントをトリガーする cron 式 \*/3 * * * * ?
で設定されています。
Java DSL では、ルート内で routePolicy()
DSL コマンドを呼び出して、ルートポリシーをルートにアタッチします。
例2.9 cron 定期実行ルートの Java DSL の例
// Java CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); policy.setRouteStartTime("*/3 * * * * ?"); from("direct:start") .routeId("test") .routePolicy(policy) .to("mock:success");;
複数の引数を指定して routePolicy()
を呼び出すことで、ルート上に複数のポリシーを指定することができます。
XML DSL の例
例2.10「cron 定期実行ルートの XML DSL の例」 は、XML DSL を使用して起動するルートをスケジュールする方法を示しています。
XML DSL では、route
要素に routePolicyRef
属性を設定して、ルートポリシーをルートにアタッチします。
例2.10 cron 定期実行ルートの XML DSL の例
<bean id="date" class="org.apache.camel.routepolicy.quartz.SimpleDate"/> <bean id="startPolicy" class="org.apache.camel.routepolicy.quartz.CronScheduledRoutePolicy"> <property name="routeStartTime" value="*/3 * * * * ?"/> </bean> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route id="testRoute" routePolicyRef="startPolicy"> <from uri="direct:start"/> <to uri="mock:success"/> </route> </camelContext>
routePolicyRef
の値をコンマ区切りの Bean ID リストとして設定することで、ルート上に複数のポリシーを指定できます。
cron 式の定義
cron 式 の構文は、UNIX システム上でバックグラウンドで実行するジョブをスケジュールする UNIX cron
ユーティリティーに由来しています。cron 式は、日付と時刻にワイルドカードを使用することで、単一のイベントまたは定期的に繰り返される複数のイベントを効果的に指定することができる構文です。
cron 式は、以下の順序の 6 または 7 個のフィールドで設定されます。
Seconds Minutes Hours DayOfMonth Month DayOfWeek [Year]
Year
フィールドは任意のフィールドで、一度だけ発生するイベントを定義する場合を除き、通常は省略できます。各フィールドは、リテラルと特殊文字の組み合わせで設定されます。たとえば、以下の cron 式は、毎日 1 回夜 12 時に発生するイベントを指定します。
0 0 24 * * ?
*
文字は、フィールドのすべての値にマッチするワイルドカードです。したがって、上記の式は毎月の毎日を意味します。?
の文字は、フィールドの無視を意味するダミーのプレースホルダーです。DayOfMonth
フィールド または DayOfWeek
フィールドの両方を同時に指定するのは論理的ではないので、常にどちらかのフィールドにこの文字を指定します。たとえば、1 日 1 回発生するイベントを、月曜日から金曜日までのみスケジュールする場合は、以下の cron 式を使用します。
0 0 24 ? * MON-FRI
MON-FRI
は、ハイフン文字で範囲を指定しています。/
(スラッシュ) を使用してインクリメントを指定することもできます。たとえば、5 分ごとにイベントが発生するように指定するには、以下の cron 式を使用します。
0 0/5 * * * ?
cron 式構文の完全な説明は、Wikipedia の CRON 式 に関する記事を参照してください。
タスクのスケジューリング
cron 定期実行ルートポリシーを使用して、以下のスケジューリングタスクのいずれかを定義することができます。
ルートの開始
次の表は、ルートの開始を 1 回以上スケジューリングするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | 1 つ以上のルート開始イベントをトリガーする cron 式を指定します。 |
ルートの停止
次の表は、ルートの停止を 1 回以上スケジューリングするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | 1 つ以上のルート停止イベントをトリガーする cron 式を指定します。 |
|
|
| ルートを強制停止する前に、現在のエクスチェンジの処理が終了するまで待つ時間 (猶予期間) を指定します。猶予期間が無限の場合は 0 に設定します。 |
|
|
| 猶予期間の時間単位を指定します。 |
ルートの一時停止
次の表は、ルートの一時停止を 1 回以上スケジュールするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | 1 つ以上のルート一時停止イベントをトリガーする cron 式を指定します。 |
ルートの再開
次の表は、ルートの再開を 1 回以上スケジュールするためのパラメーターを示しています。
パラメーター | 型 | デフォルト | 説明 |
---|---|---|---|
|
| なし | 1 つ以上のルート再開イベントをトリガーする cron 式を指定します。 |
2.10.4. ルートポリシーファクトリー
ルートポリシーファクトリーの使用
Camel 2.14 から利用可能
すべてのルートにルートポリシーを使用する場合は、org.apache.camel.spi.RoutePolicyFactory
をファクトリーとして使用して、個々のルートに RoutePolicy
インスタンスを作成することができます。これは、すべてのルートに同じ種類のルートポリシーを使用する場合に使用することができます。そうすれば、ファクトリーの設定は 1 度だけで済み、作成されたすべてのルートにポリシーが割り当てられます。
CamelContext には、以下のようなファクトリーを追加するための API が用意されています。
context.addRoutePolicyFactory(new MyRoutePolicyFactory());
XML DSL では、ファクトリーで <bean>
を定義するだけです。
<bean id="myRoutePolicyFactory" class="com.foo.MyRoutePolicyFactory"/>
ファクトリーには、ルートポリシーを作成するための createRoutePolicy メソッドが含まれます。
/** * Creates a new {@link org.apache.camel.spi.RoutePolicy} which will be assigned to the given route. * * @param camelContext the camel context * @param routeId the route id * @param route the route definition * @return the created {@link org.apache.camel.spi.RoutePolicy}, or <tt>null</tt> to not use a policy for this route */ RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, RouteDefinition route);
ルートポリシーファクトリーはいくつでも持つことができます。addRoutePolicyFactory
を再度呼び出すか、他のファクトリーを XML で <bean>
と宣言します。
2.11. Camel ルートのリロード
Apache Camel 2.19 以降では、エディターから XML ファイルを保存したときに、Camel の XML ルートのライブリロードを有効にすることができます。この機能は、以下の実行方法で使用できます。
- Camel スタンドアロン (Camel Main クラスで実行)
- Camel Spring Boot (Spring Boot で実行)
- camel:run (maven プラグインで実行)
これ以外に、CamelContext
に ReloadStrategy
を設定したり、独自のカスタム戦略を設定したりすることで、手動で有効にすることもできます。
2.12. Camel Maven プラグイン
Camel Maven プラグインは以下のゴールをサポートします。
- camel:run - Camel アプリケーションを実行します。
- camel:validate - ソースコードを検証し、無効な Camel エンドポイント URI を検査します。
- camel:route-coverage - ユニットテストの実行後、Camel ルートのカバレッジを報告します。
2.12.1. camel:run
Camel Maven プラグインのゴール camel:run
は、Maven からフォークされた JVM で Camel Spring 設定を実行するために使用されます。初めて使用する場合、アプリケーションサンプルとして Spring サンプルを使用するとよいでしょう。
cd examples/camel-example-spring mvn camel:run
これにより、main(...) メソッドを書かなくても、ルーティングルールを起動してテストすることが非常に容易になります。また、複数の jar を作成して、さまざまなルーティングルールのセットをホストし、それらを簡単に個別にテストすることもできます。Camel Maven プラグインは maven プロジェクトのソースコードをコンパイルし、META-INF/spring/*.xml
のクラスパスの XML 設定ファイルを使用して Spring ApplicationContext を起動します。Camel のルートをもう少し速く起動する場合は、代わりに camel:embedded
を試してみてください。
2.12.1.1. オプション
Camel Maven プラグインの run ゴールは、以下のオプションをサポートします。これらのオプションは、コマンドラインから設定するか (-D
構文を使用)、<configuration>
タグの pom.xml
ファイルで定義します。
パラメーター | デフォルト値 | 説明 |
duration | -1 | アプリケーションが終了する前に実行される期間 (秒単位) を設定します。0 以下の値を指定すると、永久に実行されます。 |
durationIdle | -1 | アプリケーションが終了する前にアイドル状態でいられる期間 (秒単位) を設定します。0 以下の値を指定すると、永久に実行されます。 |
durationMaxMessages | -1 | アプリケーションが終了する前にアプリケーションが処理するメッセージ最大数の期間を設定します。 |
logClasspath | false | 起動時にクラスパスをログに記録するかどうか。 |
2.12.1.2. OSGi Blueprint の実行
camel:run
プラグインは、Blueprint アプリケーションの実行もサポートします。デフォルトでは、OSGI-INF/blueprint/*.xml
の OSGi Blueprint ファイルがスキャンされます。以下のように useBlueprint を true に設定して、camel:run プラグインが Blueprint を使用するように設定する必要があります。
<plugin> <groupId>org.jboss.redhat-fuse</groupId> <artifactId>camel-maven-plugin</artifactId> <configuration> <useBlueprint>true</useBlueprint> </configuration> </plugin>
これにより、Camel 関連だけでなく、他の Blueprint サービスも起動することができます。camel:run
ゴールは、camel-blueeprint がクラスパス上にあるか、またはプロジェクト内に blueeprint XML ファイルがある場合は、自動検出することができるので、useBlueprint
オプションを設定する必要がありません。
2.12.1.3. 制限された Blueprint コンテナーの使用
Blueprint のコンテナーとして Felix Connector プロジェクトを使用しています。Felix は完全な Blueprint コンテナーではありません。完全な Blueprint コンテナーで実行する場合は、Apache Karaf または Apache ServiceMix を使用できます。applicationContextUri
設定を使用して、明示的な Blueprint XML ファイルを指定できます。例を以下に示します。
<plugin> <groupId>org.jboss.redhat-fuse</groupId> <artifactId>camel-maven-plugin</artifactId> <configuration> <useBlueprint>true</useBlueprint> <applicationContextUri>myBlueprint.xml</applicationContextUri> <!-- ConfigAdmin options which have been added since Camel 2.12.0 --> <configAdminPid>test</configAdminPid> <configAdminFileName>/user/test/etc/test.cfg</configAdminFileName> </configuration> </plugin>
applicationContextUri
はクラスパスからファイルをロードするので、上の例では myBlueprint.xml
ファイルはクラスパスのルートになければなりません。configAdminPid
は pid 名で、persistence プロパティーファイルを読み込む際に、設定管理サービスの pid 名として使用されます。configAdminFileName
は、設定管理サービスのプロパティーファイルを読み込むために使用されるファイル名です。
2.12.1.4. CDI の実行
camel:run
プラグインは、CDI アプリケーションの実行もサポートします。これにより、Camel 関連だけでなく、すべての CDI 対応サービスを起動できます。下記の例のように、CDI コンテナー (Weld や OpenWebBeans など) を camel-maven-plugin の依存関係に追加する必要があります。Camel のソースからは、以下のように CDI のサンプルを実行できます。
cd examples/camel-example-cdi mvn compile camel:run
2.12.1.5. クラスパスのロギング
camel:run
の実行時に、クラスパスをログに記録するかどうかを設定できます。以下のコマンドを使用して、この設定を有効できます。
<plugin> <groupId>org.jboss.redhat-fuse</groupId> <artifactId>camel-maven-plugin</artifactId> <configuration> <logClasspath>true</logClasspath> </configuration> </plugin>
2.12.1.6. XML ファイルのライブリロードの使用
XML ファイルの変更をスキャンし、それらの XML ファイルに含まれる Camel ルートのリロードをトリガーするように、プラグインを設定できます。
<plugin> <groupId>org.jboss.redhat-fuse</groupId> <artifactId>camel-maven-plugin</artifactId> <configuration> <fileWatcherDirectory>src/main/resources/META-INF/spring</fileWatcherDirectory> </configuration> </plugin>
設定後、プラグインはこのディレクトリーの監視を開始します。エディターからソースコードを編集して保存すると、変更後の内容が実行中の Camel アプリケーションに適用されます。<routes>
や <route>
などの Camel ルートへの変更のみがサポートされることに注意してください。Spring や OSGi Blueprint の <bean>
要素を変更することはできません。
2.12.2. camel:validate
以下の Camel 機能のソースコード検証の場合
- エンドポイント URI
- Simple 式または述語
- ルート ID の重複
次に、コマンドラインまたは、IDEA や Eclipse などの Java エディターから、camel:validate
ゴールを実行できます。
mvn camel:validate
また、プラグインを有効にしてビルドの一部として自動的に実行し、エラーを検出することも可能です。
<plugin> <groupId>org.jboss.redhat-fuse</groupId> <artifactId>camel-maven-plugin</artifactId> <executions> <execution> <phase>process-classes</phase> <goals> <goal>validate</goal> </goals> </execution> </executions> </plugin>
フェーズは、プラグインがいつ実行されるかを決定します。上記の例では、メインのソースコードのコンパイル後に実行される process-classes
がフェーズになります。この maven プラグインは、テストソースコードを検証するように設定することもできます。以下に示すように、フェーズを process-test-classes
に合わせて変更してください。
<plugin> <groupId>org.jboss.redhat-fuse</groupId> <artifactId>camel-maven-plugin</artifactId> <executions> <execution> <configuration> <includeTest>true</includeTest> </configuration> <phase>process-test-classes</phase> <goals> <goal>validate</goal> </goals> </execution> </executions> </plugin>
2.12.2.1. 任意の Maven プロジェクトでのゴール実行
プラグインを pom.xml
ファイルに追加せずに Maven プロジェクトで validate ゴールを実行することもできます。この場合、完全修飾名を使用してプラグインを指定する必要があります。たとえば、Apache Camel から camel-example-cdi
でゴールを実行するには、次のように実行します。
$cd camel-example-cdi $mvn org.apache.camel:camel-maven-plugin:2.20.0:validate
このコマンドを実行すると以下が出力されます。
[INFO] ------------------------------------------------------------------------ [INFO] Building Camel :: Example :: CDI 2.20.0 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- camel-maven-plugin:2.20.0:validate (default-cli) @ camel-example-cdi --- [INFO] Endpoint validation success: (4 = passed, 0 = invalid, 0 = incapable, 0 = unknown components) [INFO] Simple validation success: (0 = passed, 0 = invalid) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
validate は成功し、4 つのエンドポイントが検証されます。ここで、ソースコードの Camel エンドポイント URI の 1 つに、以下のようなタイプミスがあったとします。
@Uri("timer:foo?period=5000")
period
オプションを以下のように変更し、タイプミスが含まれるようにします。
@Uri("timer:foo?perid=5000")
validate ゴールを再度実行すると、以下が報告されます。
[INFO] ------------------------------------------------------------------------ [INFO] Building Camel :: Example :: CDI 2.20.0 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- camel-maven-plugin:2.20.0:validate (default-cli) @ camel-example-cdi --- [WARNING] Endpoint validation error at: org.apache.camel.example.cdi.MyRoutes(MyRoutes.java:32) timer:foo?perid=5000 perid Unknown option. Did you mean: [period] [WARNING] Endpoint validation error: (3 = passed, 1 = invalid, 0 = incapable, 0 = unknown components) [INFO] Simple validation success: (0 = passed, 0 = invalid) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
2.12.2.2. オプション
Camel Maven プラグインの validate ゴールは、以下のオプションをサポートします。これらのオプションは、コマンドラインから設定するか (-D
構文を使用)、<configuration>
タグの pom.xml
ファイルで定義します。
パラメーター | デフォルト値 | 説明 |
downloadVersion | true | インターネットからの Camel カタログバージョンのダウンロードを許可するかどうか。プロジェクトが使用する Camel バージョンと、このプラグインがデフォルトで使用する Camel バージョンが異なる場合にダウンロードが必要です。 |
failOnError | false | 無効な Camel エンドポイントが見つかった場合に失敗するかどうか。デフォルトでは、WARN レベルでエラーがプラグインログに記録されます。 |
logUnparseable | false | 解析不可のため検証できないエンドポイント URI をログに記録するかどうか。 |
includeJava | true | 無効な Camel エンドポイントの検証対象となる Java ファイルを含めるかどうか。 |
includeXml | true | 無効な Camel エンドポイントの検証対象となる XML ファイルを含めるかどうか。 |
includeTest | false | テストソースコードを含めるかどうか。 |
includes | Java および xml ファイルの名前を絞り込み、指定されたパターンのリスト (ワイルドカードおよび正規表現) と一致するファイルのみが含まれるようにします。複数の値はコンマで区切ることができます。 | |
excludes | Java および xml ファイルの名前を絞り込み、指定されたパターンのリスト (ワイルドカードおよび正規表現) と一致するファイルが除外されるようにします。複数の値はコンマで区切ることができます。 | |
ignoreUnknownComponent | true | 不明なコンポーネントを無視するかどうか。 |
ignoreIncapable | true | 解析不可なエンドポイント URI や、Simple 式を無視するかどうか。 |
ignoreLenientProperties | true | lenient プロパティーを使用するコンポーネントを無視するかどうか。true の場合、URI の検証はより厳密になりますが、lenient プロパティーを使用するため、URI にあってもコンポーネントの一部でないプロパティーでは検証に失敗することがあります。たとえば、HTTP コンポーネントを使用して、エンドポイント URI でクエリーパラメーターを提供する場合がこれに該当します。 |
ignoreDeprecated | true | Camel 2.23 の場合: エンドポイント URI で使用される非推奨のオプションを無視するかどうか。 |
duplicateRouteId | true | Camel 2.20 の場合: ルート ID の重複を検証するかどうか。ルート ID は一意である必要があります。重複がある場合、Camel は起動に失敗します。 |
directOrSedaPairCheck | true | Camel 2.23 の場合: direct/seda エンドポイントが未定義コンシューマーに送信しているかを検証するかどうか。 |
showAll | false | エンドポイントと Simple 式 (無効と有効の両方) をすべて表示するかどうか。 |
たとえば、コマンドラインから ignoreDeprecated オプションを無効するには、以下を実行します。
$mvn camel:validate -Dcamel.ignoreDeprecated=false
オプション名として、-D
コマンド引数の前に camel.
(例: camel.ignoreDeprecated
) を付ける必要があります。
2.12.2.3. include テストを使用したエンドポイントの検証
Maven プロジェクトの場合、プラグインを実行してユニットテストのソースコードで使用されるエンドポイントを検証することもできます。以下のように -D
スタイルを使用してオプションを渡すことができます。
$cd myproject $mvn org.apache.camel:camel-maven-plugin:2.20.0:validate -DincludeTest=true
2.12.3. camel:route-coverage
ユニットテストから Camel ルートのカバレッジのレポートを生成するために使用します。これを使用することによって、Camel ルートのどの部分が使用されたかを把握することができます。
2.12.3.1. route-coverage の有効化
以下のいずれかの方法で、ユニットテスト実行時に route-coverage を有効化できます。
- グローバル JVM システムプロパティーを設定してすべてのテストクラスで有効。
-
camel-test-spring
モジュールを使用する場合、テストクラスごとの@EnableRouteCoverage
アノテーションの使用 -
camel-test
モジュールを使用する場合、テストクラスごとのisDumpRouteCoverage
メソッドの上書き
2.12.3.2. JVM システムプロパティーを使用した route-coverage の有効化
JVM システムプロパティー CamelTestRouteCoverage
をオンにして、すべてのテストケースの route-coverage を有効にできます。これは、maven-surefire-plugin
設定のいずれかで実行できます。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <systemPropertyVariables> <CamelTestRouteCoverage>true</CamelTestRouteCoverage> </systemPropertyVariables> </configuration> </plugin>
テストの実行中にコマンドラインから設定する場合は次のとおりです。
mvn clean test -DCamelTestRouteCoverage=true
2.12.3.3. @EnableRouteCoverage アノテーションでの route-coverage の有効化
camel-test-spring
を使用してテストする場合は、@EnableRouteCoverage
アノテーションをテストクラスに追加することで、ユニットテストクラスで route-coverage を有効にすることができます。
@RunWith(CamelSpringBootRunner.class) @SpringBootTest(classes = SampleCamelApplication.class) @EnableRouteCoverage public class FooApplicationTest {
2.12.3.4. isDumpRouteCoverage メソッドでの route-coverage の有効化
camel-test
を使用していて、ユニットテストが CamelTestSupport
を拡張している場合は、以下に示すように route-coverage を有効にすることができます。
@Override public boolean isDumpRouteCoverage() { return true; }
RouteCoverage メソッドで対象指定できるルートには、固有の ID が割り当てられている必要があります。つまり、匿名ルートは使用できません。Java DSL で routeId
を使用して行います。
from("jms:queue:cheese").routeId("cheesy") .to("log:foo") ...
また、XML DSL で id 属性を介してルート ID を付与します。
<route id="cheesy"> <from uri="jms:queue:cheese"/> <to uri="log:foo"/> ... </route>
2.12.3.5. route-coverage レポートの生成
route-coverage レポートを生成するには、以下のようにユニットテストを実行します。
mvn test
そして、Maven ゴールを実行して、以下のように route-coverage レポートを生成できます。
mvn camel:route-coverage
生成されるレポートでは、ソースコードの行番号でどのルートのルートのカバレッジがないかを確認できます。
[INFO] --- camel-maven-plugin:2.21.0:route-coverage (default-cli) @ camel-example-spring-boot-xml --- [INFO] Discovered 1 routes [INFO] Route coverage summary: File: src/main/resources/my-camel.xml RouteId: hello Line # Count Route ------ ----- ----- 28 1 from 29 1 transform 32 1 filter 34 0 to 36 1 to Coverage: 4 out of 5 (80.0%)
この例では、to
のある最後から 2 番目の行のカウント列が 0
であるため、カバレッジがないことが分かります。また、これはソースコードファイル (XML ファイル my-camel.xml
) の 34 行目であることも分かります。
2.12.3.6. オプション
Camel Maven プラグインの coverage ゴールは、以下のオプションをサポートします。これらのオプションは、コマンドラインから設定するか (-D
構文を使用)、<configuration>
タグの pom.xml
ファイルで定義します。
パラメーター | デフォルト値 | 説明 |
failOnError | false | いずれかのルートのカバレッジが 100% でない場合に失敗するかどうか。 |
includeTest | false | テストソースコードを含めるかどうか。 |
includes | Java および xml ファイルの名前を絞り込み、指定されたパターンのリスト (ワイルドカードおよび正規表現) と一致するファイルのみが含まれるようにします。複数の値はコンマで区切ることができます。 | |
excludes | Java および xml ファイルの名前を絞り込み、指定されたパターンのリスト (ワイルドカードおよび正規表現) と一致するファイルが除外されるようにします。複数の値はコンマで区切ることができます。 | |
anonymousRoutes | false | 匿名ルート (ルート ID が割り当てられていないルート) を許可するかどうか。ルート ID を使用することで、正確にルートカバレッジのデータをルートのソースコードとマッチングさせることができます。匿名ルートは、テストされたルートがソースコードのどのルートに対応しているかを正確に知ることが難しくなるため、ルートカバレッジの結果の精度が低くなります。 |
2.13. Apache Camel スタンドアロンの実行
スタンドアロンアプリケーションとして camel を実行する場合、アプリケーションを実行して JVM が終了するまで実行を継続するために使用できる Main クラスを提供します。MainListener
クラスは、org.apache.camel.main
Java パッケージ内にあります。
以下は、Main クラスのコンポーネントです。
-
org.apache.camel.Main
クラスのcamel-core
JAR -
org.apache.camel.spring.Main
クラスのcamel-spring
JAR
以下の例は、Camel から Main クラスを作成して使用する方法を示しています。
public class MainExample { private Main main; public static void main(String[] args) throws Exception { MainExample example = new MainExample(); example.boot(); } public void boot() throws Exception { // create a Main instance main = new Main(); // bind MyBean into the registry main.bind("foo", new MyBean()); // add routes main.addRouteBuilder(new MyRouteBuilder()); // add event listener main.addMainListener(new Events()); // set the properties from a file main.setPropertyPlaceholderLocations("example.properties"); // run until you terminate the JVM System.out.println("Starting Camel. Use ctrl + c to terminate the JVM.\n"); main.run(); } private static class MyRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { from("timer:foo?delay={{millisecs}}") .process(new Processor() { public void process(Exchange exchange) throws Exception { System.out.println("Invoked timer at " + new Date()); } }) .bean("foo"); } } public static class MyBean { public void callMe() { System.out.println("MyBean.callMe method has been called"); } } public static class Events extends MainListenerSupport { @Override public void afterStart(MainSupport main) { System.out.println("MainExample with Camel is now started!"); } @Override public void beforeStop(MainSupport main) { System.out.println("MainExample with Camel is now being stopped!"); } } }
2.14. OnCompletion
概要
OnCompletion DSL 名は、Unit of Work
の完了時に行うアクションを定義するために使用できます。Unit of Work
は、エクスチェンジ全体に対応する Camel の概念です。「エクスチェンジ」 を参照してください。onCompletion
コマンドの機能は以下のとおりです。
-
OnCompletion
コマンドのスコープは、グローバルまたはルートごとに指定できます。ルートスコープはグローバルスコープよりも優先されます。 -
OnCompletion
は、失敗時または成功時にトリガーされるように設定することができます。 -
onWhen
述語は、特定の状況でonCompletion
のみをトリガーするために使用できます。 - スレッドプールを使用するかどうかを設定できますが、デフォルトではスレッドプールは使用しません。
onCompletion のルート専用スコープ
エクスチェンジにて onCompletion
DSL が指定されると、Camel は新しいスレッドを生成します。これにより、onCompletion
タスクに干渉されることなく、元のスレッドを継続することができます。1 つのルートは 1 つの onCompletion
のみをサポートします。以下の例では、エクスチェンジが成功または失敗で完了したかに関係なく、onCompletion
がトリガーされます。これがデフォルト動作です。
from("direct:start") .onCompletion() // This route is invoked when the original route is complete. // This is similar to a completion callback. .to("log:sync") .to("mock:sync") // Must use end to denote the end of the onCompletion route. .end() // here the original route contiues .process(new MyProcessor()) .to("mock:result");
XML の場合、以下のようになります。
<route> <from uri="direct:start"/> <!-- This onCompletion block is executed when the exchange is done being routed. --> <!-- This callback is always triggered even if the exchange fails. --> <onCompletion> <!-- This is similar to an after completion callback. --> <to uri="log:sync"/> <to uri="mock:sync"/> </onCompletion> <process ref="myProcessor"/> <to uri="mock:result"/> </route>
失敗時に onCompletion
をトリガーするには、onFailureOnly
パラメーターを使用することができます。同様に、成功時に onCompletion
をトリガーするには、onCompleteOnly
パラメーターを使用します。
from("direct:start") // Here onCompletion is qualified to invoke only when the exchange fails (exception or FAULT body). .onCompletion().onFailureOnly() .to("log:sync") .to("mock:sync") // Must use end to denote the end of the onCompletion route. .end() // here the original route continues .process(new MyProcessor()) .to("mock:result");
XML の場合、onFailureOnly
および onCompleteOnly
は、onCompletion
タグのブール値として表現されます。
<route> <from uri="direct:start"/> <!-- this onCompletion block will only be executed when the exchange is done being routed --> <!-- this callback is only triggered when the exchange failed, as we have onFailure=true --> <onCompletion onFailureOnly="true"> <to uri="log:sync"/> <to uri="mock:sync"/> </onCompletion> <process ref="myProcessor"/> <to uri="mock:result"/> </route>
グローバルスコープの onCompletion
onCompletion
を複数のルートに対して定義するには、以下を実行します。
// define a global on completion that is invoked when the exchange is complete onCompletion().to("log:global").to("mock:sync"); from("direct:start") .process(new MyProcessor()) .to("mock:result");
onWhen の使用
特定条件で onCompletion
を起動するには、onWhen
述語を使用します。次の例では、メッセージのボディーに Hello
という単語が含まれている場合に、onCompletion
がトリガーされます。
/from("direct:start") .onCompletion().onWhen(body().contains("Hello")) // this route is only invoked when the original route is complete as a kind // of completion callback. And also only if the onWhen predicate is true .to("log:sync") .to("mock:sync") // must use end to denote the end of the onCompletion route .end() // here the original route contiues .to("log:original") .to("mock:result");
onCompletion でのスレッドプール
Camel 2.14 以降、onCompletion
はデフォルトでスレッドプールを使用しません。スレッドプールを強制的に使用するには、executorService
を設定するか、parallelProcessing
を true に設定します。たとえば、Java DSL では以下の形式を使用します。
onCompletion().parallelProcessing() .to("mock:before") .delay(1000) .setBody(simple("OnComplete:${body}"));
XML の場合は以下のようになります。
<onCompletion parallelProcessing="true"> <to uri="before"/> <delay><constant>1000</constant></delay> <setBody><simple>OnComplete:${body}<simple></setBody> </onCompletion>
特定のスレッドプールを参照するには executorServiceRef
オプションを使用します。
<onCompletion executorServiceRef="myThreadPool" <to uri="before"/> <delay><constant>1000</constant></delay> <setBody><simple>OnComplete:${body}</simple></setBody> </onCompletion>>
コンシューマーの応答送信前に onCompletion を実行
onCompletion
は 2 つのモードで実行できます。
- AfterConsumer: コンシューマーが終了した後に実行されるデフォルトのモードです。
-
BeforeConsumer: コンシューマーが呼び出し元に応答を返信する前に実行されます。これにより、
onCompletion
は、特別なヘッダーの追加などエクスチェンジを変更したり、応答ロガーとしてエクスチェンジをログに記録したりすることができます。
たとえば、created by
ヘッダーを応答に追加するには、次に示すように modeBeforeConsumer()
を使用します。
.onCompletion().modeBeforeConsumer() .setHeader("createdBy", constant("Someone")) .end()
XML の場合、mode 属性を BeforeConsumer
に設定します。
<onCompletion mode="BeforeConsumer"> <setHeader headerName="createdBy"> <constant>Someone</constant> </setHeader> </onCompletion>
2.15. メトリクス
概要
Camel 2.14 から利用可能
Camel は既に多くのメトリクスを提供し、Codahale メトリクスとの統合が Camel ルートに追加されています。これにより、エンドユーザーは、Codahale メトリクスを使用して収集されたメトリクスデータに、Camel のルーティング情報を追加できます。
Codahale メトリクスを使用するには、以下が必要です。
- camel-metrics コンポーネントの追加
- XML または Java コードでのルートメトリクスの有効化
パフォーマンスメトリクスは、それらを表示する方法がある場合にのみ使用可能であることに注意してください。メトリクスは JMX 上で利用できるため、JMX と統合できる監視ツールをすべて使用できます。さらに、実際のデータは 100% Codehale JSON です。
メトリクスルートポリシー
単一ルートの Codahale メトリクスを取得するには、ルートごとに MetricsRoutePolicy
を定義します。
Java DSL の場合、ルートのポリシーとして割り当てるための MetricsRoutePolicy
のインスタンスを作成します。以下に例を示します。
from("file:src/data?noop=true").routePolicy(new MetricsRoutePolicy()).to("jms:incomingOrders");
XML DSL の場合、ルートのポリシーとして指定された <bean>
を定義します。以下に例を示します。
<bean id="policy" class="org.apache.camel.component.metrics.routepolicy.MetricsRoutePolicy"/> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route routePolicyRef="policy"> <from uri="file:src/data?noop=true"/> [...]
メトリクスルートポリシーファクトリー
このファクトリーでは、Codahale メトリクスを使用してルート使用状況の統計を公開するルートごとに、RoutePolicy
を追加することができます。このファクトリーは、以下の例のように Java および XML で使用できます。
Java DSL の場合は、以下のようにファクトリーを CamelContext
に追加します。
context.addRoutePolicyFactory(new MetricsRoutePolicyFactory());
XML DSL の場合は、<bean>
を以下のように定義します。
<!-- use camel-metrics route policy to gather metrics for all routes --> <bean id="metricsRoutePolicyFactory" class="org.apache.camel.component.metrics.routepolicy.MetricsRoutePolicyFactory"/>
以下に示すように、Java コードからは、org.apache.camel.component.metrics.routepolicy.MetricsRegistryService
から com.codahale.metrics.MetricRegistry
を取得できます。
MetricRegistryService registryService = context.hasService(MetricsRegistryService.class); if (registryService != null) { MetricsRegistry registry = registryService.getMetricsRegistry(); ... }
オプション
MetricsRoutePolicyFactory
および MetricsRoutePolicy
は、以下のオプションをサポートします。
名前 | デフォルト | 説明 |
|
| メトリクスレポーターまたは統計を json 出力するときの期間に使用する単位。 |
|
| JXM ドメイン名。 |
|
共有 | |
|
| 統計情報を json 形式で出力する際に pretty print を使用するかどうか。 |
|
| メトリクスレポーターまたは統計を json 出力するときのレートに使用する単位。 |
|
|
CamelContext で JMX が有効になっている場合、JMX ツリーのサービスタイプの下に |
2.16. JMX の命名
概要
Apache Camel では、management name pattern を定義することで、JMX で表示される CamelContext
Bean の名前をカスタマイズすることができます。たとえば、以下のように、XML CamelContext
インスタンスの名前パターンをカスタマイズすることができます。
<camelContext id="myCamel" managementNamePattern="#name#">
...
</camelContext>
CamelContext
Bean の名前パターンを明示的に設定しないと、Apache Camel はデフォルトの命名ストラテジーに戻ります。
デフォルトの命名ストラテジー
デフォルトでは、OSGi バンドルにデプロイされた CamelContext
Bean の JMX 名は、バンドルの OSGi シンボリック名 と同じです。たとえば、OSGi のシンボリック名が MyCamelBundle
の場合、JMX の名前は MyCamelBundle
となります。バンドル内に複数の CamelContext
が存在する場合、接尾辞としてカウンター値を追加することにより、JMX 名が明確になります。たとえば、MyCamelBundle
バンドルに複数の Camel コンテキストがある場合、対応する JMX MBeans の名前は以下のようになります。
MyCamelBundle-1 MyCamelBundle-2 MyCamelBundle-3 ...
JMX 命名ストラテジーのカスタマイズ
デフォルトの命名ストラテジーの欠点の 1 つは、特定の CamelContext
Bean が実行間で同じ JMX 名を持つことを保証できないことです。実行間の一貫性を高める場合は、CamelContext
インスタンスの JMX 名パターン を定義することで、JMX 名をより正確に制御することができます。
Java DSL での名前パターンの指定
Java の CamelContext
で名前パターンを指定するには、以下のように setNamePattern
メソッドを呼び出します。
// Java context.getManagementNameStrategy().setNamePattern("#name#");
XML での名前パターンの指定
XML の CamelContext
で名前パターンを指定するには、以下のように camelContext
要素の managementNamePattern
属性を設定します。
<camelContext id="myCamel" managementNamePattern="#name#">
名前パターントークン
以下のいずれかのトークンにリテラルテキストを追加することで、JMX 名前パターンを設定することができます。
トークン | 説明 |
---|---|
|
|
|
|
|
インクリメントカウンター ( |
| デプロイされたバンドルの OSGi バンドル ID OSGi のみ) |
| OSGi シンボリック名 (OSGi のみ) |
| OSGi バンドルバージョン (OSGi のみ) |
例
以下は、サポートされるトークンを使用して定義できる JMX 名前パターンの例です。
<camelContext id="fooContext" managementNamePattern="FooApplication-#name#"> ... </camelContext> <camelContext id="myCamel" managementNamePattern="#bundleID#-#symbolicName#-#name#"> ... </camelContext>
あいまいな名前
カスタマイズされた命名パターンはデフォルトの命名ストラテジーを上書きするため、このアプローチを使用してあいまいな JMX MBean 名を定義することができます。以下に例を示します。
<camelContext id="foo" managementNamePattern="SameOldSameOld"> ... </camelContext> ... <camelContext id="bar" managementNamePattern="SameOldSameOld"> ... </camelContext>
この場合、Apache Camel は起動に失敗し、MBean が既に存在することを示す例外が出力されます。そのため、あいまいな名前のパターンを定義しないように細心の注意を払う必要があります。
2.17. パフォーマンスと最適化
メッセージのコピー
allowUseOriginalMessage
オプションのデフォルト設定は false
です。これは、必要がない場合に、元のメッセージのコピー作成を削減します。allowUseOriginalMessage
オプションを有効にするには、次のコマンドを使用します。
-
エラーハンドラーまたは
onException
要素にuseOriginalMessage=true
を設定します。 -
Java アプリケーションコードで
AllowUseOriginalMessage=true
を設定してから、getOriginalMessage
メソッドを使用します。
2.18 より前の Camel バージョンでは、allowUseOriginalMessage
のデフォルト設定は true です。
第3章 エンタープライズ統合パターンの導入
概要
Apache Camel の エンタープライズ統合パターン は、Gregor Hohpe および Bobby Woolf 両氏の著書である同名の書籍 Enterprise Integration Patterns の影響を受けています。両著者が説明するパターンは、エンタープライズ統合プロジェクトを開発するための優れたツールボックスを提供します。統合アーキテクチャーを説明するための共通の言語を提供する他に、Apache Camel のプログラミングインターフェイスと XML 設定を使用して、多くのパターンを直接実装することができます。
3.1. パターンの概要
書籍 Enterprise Integration Patterns
Apache Camel は、Gregor Hohpe および Bobby Woolf 両氏の著書である Enterprise Integration Patterns に記載されているほとんどのパータンをサポートします。
メッセージングシステム
表3.1「メッセージングシステム」 に記載されているメッセージングシステムパターンは、メッセージングシステムを構成する基本的な概念やコンポーネントを紹介します。
アイコン | 名前 | ユースケース |
---|---|---|
| メッセージチャネルによって接続された 2 つのアプリケーションはどのように情報を交換するか。 | |
| メッセージングを使用して単一のアプリケーションが別のアプリケーションと通信する方法。 | |
| アプリケーションがメッセージングチャネルに接続してメッセージを送受信する方法。 | |
| 独立性と柔軟性を維持しながら、メッセージで複雑な処理を行う方法。 | |
| 定義された条件のセットに応じてメッセージを異なるフィルターに渡すために、個々の処理ステップを切り離す方法。 | |
| メッセージングを使用して、異なるデータフォーマットを使用するシステムの間で通信を行う方法。 |
メッセージングチャネル
メッセージングチャネルは、メッセージングシステムで参加者の接続に使用される基本的なコンポーネントです。表3.2「メッセージングチャネル」 のパターンは、使用できる異なる種類のメッセージングチャネルを説明しています。
アイコン | 名前 | ユースケース |
---|---|---|
| 1 つの受信側のみがドキュメントの受信や呼び出しを実行するように、呼び出し側が確認する方法。 | |
| 送信側が対象のすべての受信側にブロードキャストする方法。 | |
| メッセージングシステムが配信できないメッセージの処理方法。 | |
| メッセージングシステムに障害が発生しても、送信側がメッセージを確実に配信する方法。 | |
| 独立し、分離したアプリケーションを連携でき、他のアプリケーションに影響を与えることなく 1 つ以上のアプリケーションを追加または削除できるアーキテクチャーとは。 |
メッセージの構築
表3.3「メッセージの構築」 のメッセージ構築パターンは、システムを通過するメッセージのさまざまな形式と関数を表しています。
アイコン | 名前 | ユースケース |
---|---|---|
| 受信した応答を生成したリクエストを、要求側が識別する方法。 | |
| 応答側が応答の送信先を認識する方法。 |
メッセージルーティング
表3.4「メッセージのルーティング」 のメッセージルーティングパターンは、メッセージチャネルをリンクするさまざまな方法を表しています。これには、メッセージのボディーを変更せずにメッセージストリームに適用できるさまざまなアルゴリズムが含まれます。
アイコン | 名前 | ユースケース |
---|---|---|
| 単一の論理関数 (在庫確認など) の実装が複数の物理システムに分散されている場合の処理方法。 | |
| コンポーネントが不必要なメッセージを受信しないようにする方法。 | |
| 動的に指定された受信者のリストにメッセージをルーティングする方法。 | |
| 各要素を異なる方法で処理しなければならない可能性がある、複数の要素が含まれるメッセージの処理方法。 | |
| 個別かつ関連するメッセージの結果を組み合わせ、全体として処理できるようにする方法。 | |
| 順序どおりでない関連するメッセージのストリームを正しい順序に戻す方法。 | |
| 要素ごとに異なる処理が必要となる可能性がある複数の要素で設定されるメッセージを処理する場合に、メッセージフロー全体を維持する方法。 | |
複数の受信者にメッセージを送信する必要があり、その各受信者が応答を送信する可能性がある場合に、メッセージフロー全体を維持する方法。 | ||
| 設計時にステップの順序が分からず、ステップの順序がメッセージごとに異なる可能性がある場合に、一連の処理ステップを通じてメッセージを継続的にルーティングする方法。 | |
メッセージのスロットリングによって、特定のエンドポイントがオーバーロードされないようにする方法、または外部サービスと合意した SLA を越えないようにする方法。 | ||
メッセージの送信を遅らせる方法。 | ||
複数のエンドポイント間で負荷を分散する方法。 | ||
外部サービスの呼び出し時に、Hystrix サーキットブレーカーを使用する方法。Camel 2.18 の新機能。 | ||
レジストリーでサービスを検索して、分散システムでリモートサービスを呼び出す方法。Camel 2.18 の新機能。 | ||
メッセージを同時に複数のエンドポイントにルーティングする方法。 | ||
メッセージをループで繰り返し処理する方法。 | ||
ダウンストリームルートをオーバーロードを防ぐために一定の期間で複数のメッセージから 1 つのメッセージをサンプリングする方法。 |
メッセージの変換
表3.5「Message Transformation」 のメッセージ変換パターンは、さまざまな目的のためにメッセージの内容を変更する方法を表しています。
アイコン | 名前 | ユースケース |
---|---|---|
| メッセージの送信元に必要なデータ項目がすべてない場合に他のシステムと通信する方法。 | |
| 数個のデータ項目のみが必要な場合に大きなメッセージの処理を簡単にする方法。 | |
| 情報の内容を減らさずにシステム全体で送信されるメッセージのデータ量を減らす方法。 | |
| 意味的には同等で、受け取った形式が異なるメッセージの処理方法。 | |
メッセージのボディーのソート方法。 |
メッセージングエンドポイント
メッセージングエンドポイントは、メッセージングチャネルとアプリケーション間の接点を示します。表3.6「Messaging Endpoint」 のメッセージングエンドポイントパターンは、エンドポイントに設定できるサービスのさまざまな機能と特性を表しています。
アイコン | 名前 | ユースケース |
---|---|---|
ドメインオブジェクトとメッセージングインフラストラクチャーの間でデータを移動し、お互いに独立した状態を維持する方法。 | ||
| メッセージが利用できるようになったときにアプリケーションが自動的にメッセージを消費する方法。 | |
| アプリケーションの準備ができたときに、アプリケーションがメッセージを消費する方法。 | |
| メッセージングクライアントが複数のメッセージを同時に処理する方法。 | |
| 1 つのチャネルで複数のコンシューマーがメッセージ処理を調整する方法。 | |
| メッセージコンシューマーが受信するメッセージを選択する方法 | |
| サブスクライバーがメッセージをリッスンしていないときにメッセージの欠落を防ぐ方法。 | |
メッセージの受信側が重複メッセージを処理する方法。 | ||
| クライアントがメッセージングシステムでトランザクションを制御する方法。 | |
| 残りのアプリケーションからメッセージングシステムへのアクセスをカプセル化する方法。 | |
| サービスがさまざまなメッセージング技術や、メッセージング以外の技術によって呼び出されるように、アプリケーションで設計する方法。 |
システム管理
表3.7「システム管理」 のシステム管理パターンは、メッセージングシステムを監視、テスト、および管理する方法を表しています。
アイコン | 名前 | ユースケース |
---|---|---|
| ポイントツーポイントチャネルで送信されるメッセージを検査する方法。 |
第4章 REST サービスの定義
概要
Apache Camel は、REST サービスを定義するために複数のアプローチをサポートします。特に、Apache Camel は REST DSL (Domain Specific Language) を提供します。これは、REST コンポーネントを抽象化でき、OpenAPI とも統合できるシンプルながらも強力な Fluent API です。
4.1. Camel における REST サービスの概要
概要
Apache Camel は、Camel アプリケーションで REST サービスを定義するためのさまざまなアプローチやコンポーネントを提供します。本セクションでは、これらのアプローチとコンポーネントの概要を紹介し、要件に最適な実装と API を判断できるようにします。
REST とは
Representational State Transfer (REST) は、4 つの基本的な HTTP 動詞 (GET
、POST
、PUT
、および DELETE
) のみを使用して、HTTP 通信でデータ送信を中心とする分散アプリケーションのアーキテクチャーです。
REST アーキテクチャーは HTTP を直接活用します。これは、SOAP のような HTTP を単なるトランスポートプロトコルとして扱うプロトコルとは対象的です。重要なポイントは、HTTP プロトコル 自体 が、既にいくつかの規約によって拡張され、分散アプリケーションのフレームワークとして機能するのに適していることです。
REST 呼び出しのサンプル
REST アーキテクチャーは標準の HTTP メソッドを中心に設定されているため、多くの場合、通常のブラウザーを REST クライアントとして使用することができます。たとえば、ホストとポート localhost:9091
で実行されている単純な Hello World の REST サービスを呼び出すには、ブラウザーで以下の URL にアクセスします。
http://localhost:9091/say/hello/Garp
仮に、Hello World REST サービスが、以下のようなレスポンスを返すとします。
Hello Garp
このレスポンスは、ブラウザーのウィンドウに表示されます。通常のブラウザー (または curl
コマンドラインユーティリティー) だけを使用して、REST サービスを呼び出せる気軽さは、REST プロトコルが急速に人気を集めている多くの理由の 1 つです。
REST ラッパーレイヤー
REST ラッパーレイヤーは、REST サービスを定義するための簡潔な構文を提供し、異なる REST 実装の上に重ねることができます。
- REST DSL
REST DSL (
camel-core
の一部) は、REST サービスを定義するためのシンプルなビルダー API を提供するファサードまたはラッパーレイヤーです。REST DSL 自体は REST 実装を提供しているわけではありません。REST DSL は、ベースとなる REST 実装と組み合わせる必要があります。たとえば、以下の Java コードは、REST DSL を使用してシンプルな Hello World の REST サービスを定義する方法を示しています。rest("/say") .get("/hello/{name}").route().transform().simple("Hello ${header.name}");
詳細は、「REST DSL を使用した REST サービスの定義」 を参照してください。
- REST コンポーネント
REST コンポーネント (
camel-core
の一部) は、URI 構文を使用して REST サービスの定義を可能にするラッパーレイヤーです。REST DSL と同様に、REST コンポーネント自体は REST 実装を提供しているわけではありません。ベースとなる REST 実装と組み合わせる必要があります。HTTP コンポーネントを明示的に指定しない場合、REST DSL はクラスパス上の利用可能なコンポーネントをチェックすることで、どの HTTP コンポーネントを使用するかを自動検出します。REST DSL は、HTTP コンポーネントのデフォルト名を探し、最初に見つかったものを使用します。クラスパス上に HTTP コンポーネントがなく、かつ HTTP トランスポートが明示的に設定されていない場合は、デフォルトの HTTP コンポーネントは
camel-http
になります。注記HTTP コンポーネントの自動検出機能が Camel 2.18 で追加されました。Camel 2.17 では利用できません。
以下の Java DSL は、camel-rest コンポーネントを使用して Hello World のサービスを定義する方法を示しています。
from("rest:get:say:/hello/{name}").transform().simple("Hello ${header.name}");
REST 実装
Apache Camel は、以下のコンポーネントを通じて、複数の REST 実装を提供します。
- Spark-Rest コンポーネント
Spark-Rest コンポーネント (
camel-spark-rest
) は、URI 構文を使用して REST サービスの定義を可能にする REST 実装です。Spark フレームワーク自体は Java の API で、Sinatra フレームワーク (Python の API) を大まかにベースにしています。たとえば、以下の Java コードでは、Spark-Rest コンポーネントを使用して Hello World のサービスを定義する方法を示しています。from("spark-rest:get:/say/hello/:name").transform().simple("Hello ${header.name}");
Rest コンポーネントとは対照的に、URI の変数の構文は
{name}
ではなく、:name
であることに注意してください。注記Spark-Rest コンポーネントには Java 8 が必要です。
- Restlet コンポーネント
Restlet コンポーネント (
camel-restlet
の一部) は、原則として、異なるトランスポートプロトコルの上に重ねることができる REST 実装です (ただし、このコンポーネントは HTTP プロトコルに対してのみテストされます)。このコンポーネントは、Java で REST サービスを開発する商用フレームワークである Restlet Framework との統合も提供します。たとえば、以下の Java コードは Restlet コンポーネントを使用して Hello World のサービスを定義する方法を示しています。from("restlet:http://0.0.0.0:9091/say/hello/{name}?restletMethod=get") .transform().simple("Hello ${header.name}");
詳細は、Apache Camel Component Reference Guide の Restlet を参照してください。
- Servlet コンポーネント
Servlet コンポーネント (
camel-servlet
内) は、Java サーブレットを Camel ルートにバインドするコンポーネントです。言い換えれば、Servlet コンポーネントを使用すると、標準の Java サーブレットのように Camel ルートをパッケージ化してデプロイすることができます。したがって、Servlet コンポーネントは、 サーブレットコンテナー内部に Camel ルートをデプロイする必要がある場合 (たとえば、Apache Tomcat HTTP サーバーや JBoss Enterprise Appication Platform コンテナーなど) に Camel ルートをデプロイする場合に特に便利です。ただし、Servlet コンポーネントだけは、REST サービスの定義に便利な REST API を提供しません。そのため、Servlet コンポーネントを使用する最も簡単な方法は、REST DSL と組み合わせることです。これにより、ユーザーフレンドリーな API で REST サービスを定義できます。
詳細は、Apache Camel Component Reference Guide の Servlet を参照してください。
JAX-RS REST 実装
JAX-RS (Java API for RESTful Web Services) は、REST リクエストを Java オブジェクトにバインドするためのフレームワークです。バインドを定義するには、この Java クラスに JAX-RS のアノテーションを記述する必要があります。JAX-RS フレームワークは比較的成熟しており、REST サービスを開発するための洗練されたフレームワークも提供します。しかし、プログラムするのもやや複雑です。
Apache Camel と JAX-RS の統合は、Apache CXF の上に重ねた CXFRS コンポーネントによって実装されます。簡単にいうと、JAX-RS は以下のアノテーションを使用して REST リクエストを Java クラスにバインドします (以下は多くのアノテーションの一部のみとなります)。
- @Path
- コンテキストパスを Java クラスにマッピングしたり、サブパスを特定の Java メソッドにマッピングしたりできるアノテーション。
- @GET、@POST、@PUT、@DELETE
- HTTP メソッドを Java メソッドにマッピングするアノテーション。
- @PathParam
- URI パラメーターを Java メソッド引数にマッピングするか、URI パラメーターをフィールドに注入するアノテーション。
- @QueryParam
- クエリーパラメーターを Java メソッド引数にマッピングするか、クエリーパラメーターをフィールドに注入するアノテーション。
REST リクエストまたは REST レスポンスのボディーは、通常、JAXB (XML) データフォーマットであることが期待されます。しかし、Apache CXF は JSON 形式から JAXB 形式への変換もサポートしているため、JSON メッセージも解析することができます。
詳細は、Apache Camel Component Reference Guide および Apache CXF Development Guide の CXFRS を参照してください。
CXFRS コンポーネントは REST DSL と統合されていません。
4.2. REST DSL を使用した REST サービスの定義
REST DSL はファサード
REST DSL は、Java DSL または XML DSL (Domain Specific Language) で REST サービスを定義するための簡略化された構文を提供するファサードです。REST DSL は実際の REST 実装を提供しているわけではなく、既存 の REST 実装 (Apache Camel に複数ある) のラッパーにすぎません。
REST DSL の利点
REST DSL ラッパーレイヤーには、以下の利点があります。
- REST サービスを定義するためのモダンで使いやすい構文である。
- 複数の Apache Camel コンポーネントと互換性がある。
-
OpenAPI インテグレーション (
camel-openapi-java
コンポーネント経由)。
REST DSL と統合可能なコンポーネント
REST DSL は実際の REST 実装ではないため、最初に、ベースとなる実装を提供する Camel コンポーネントを選択する必要があります。現在、以下の Camel コンポーネントが REST DSL に統合されています。
-
Servlet コンポーネント (
camel-servlet
) -
Spark REST コンポーネント (
camel-spark-rest
) -
Netty4 HTTP コンポーネント (
camel-netty4-http
) -
Jetty コンポーネント (
camel-jetty
) -
Restlet コンポーネント (
camel-restlet
) -
Undertow コンポーネント (
camel-undertow
)
REST コンポーネント (camel-core
の一部) は REST 実装ではありません。REST DSL と同様に、REST コンポーネントはファサードであり、URI 構文を使用して REST サービスを定義するための簡潔な構文を提供します。REST コンポーネントには、ベースとなる REST 実装も必要です。
REST 実装を使用するように REST DSL を設定
REST 実装を指定するには、restConfiguration()
ビルダー (Java DSL の場合) または restConfiguration
要素 (XML DSL の場合) を使用します。たとえば、Spark-Rest コンポーネントを使用するように REST DSL を設定するには、Java DSL で以下のようなビルダー式を使用します。
restConfiguration().component("spark-rest").port(9091);
そして、XML DSL では、以下のような要素 (camelContext
の子として) を使用します。
<restConfiguration component="spark-rest" port="9091"/>
構文
REST サービスを定義するための Java DS L 構文は以下のとおりです。
rest("BasePath").Option(). .Verb("Path").Option().[to() | route().CamelRoute.endRest()] .Verb("Path").Option().[to() | route().CamelRoute.endRest()] ... .Verb("Path").Option().[to() | route().CamelRoute];
ここの CamelRoute
は、オプションの組み込み Camel ルートです (標準の Java DSL 構文を使用して定義されています)。
REST サービスの定義は rest()
キーワードで始まり、その後に特定の URL パスセグメントを処理する 1 つ以上の Verb 句が続きます。HTTP 動詞は、get()
、head()
、put()
、post()
、delete()
、patch()
または verb()
のいずれかになります。Verb 句は以下の構文のいずれかを使用できます。
to()
キーワードで終わる Verb 句。以下に例を示します。get("...").Option()+.to("...")
キーワード
route()
で終わる Verb 句 (Camel ルートの埋め込みの場合)。以下に例を示します。get("...").Option()+.route("...").CamelRoute.endRest()
Java による REST DSL
Java で REST DSL でサービスを定義するには、通常の Apache Camel ルート定義と同様に、REST 定義を RouteBuilder.configure()
メソッドのボディーに配置します。たとえば、REST DSL と Spark-Rest コンポーネントの組み合わせを使用して Hello World のサービスを定義するには、以下の Java コードを定義します。
restConfiguration().component("spark-rest").port(9091); rest("/say") .get("/hello").to("direct:hello") .get("/bye").to("direct:bye"); from("direct:hello") .transform().constant("Hello World"); from("direct:bye") .transform().constant("Bye World");
前述の例では、3 種類のビルダーがあります。
restConfiguration()
- 特定の REST 実装 (Spark-Rest) を使用するように REST DSL を設定します。
rest()
-
REST DSL を使用してサービスを定義します。各 Verb 句はキーワード
to()
で終了となります。このキーワードは、受信メッセージをエンドポイントdirect
に転送します (direct
コンポーネントは、同じアプリケーション内でルートをつなぎ合わせます)。 from()
- 通常の Camel ルートを定義します。
XML を使用した REST DSL
XML で XML DSL でサービスを定義するには、rest
要素を camelContext
の子要素として定義します。たとえば、Spark-Rest コンポーネントで REST DSL を使用して単純な Hello World サービスを定義するには、以下の XML コード (Blueprint) を定義します。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> <restConfiguration component="spark-rest" port="9091"/> <rest path="/say"> <get uri="/hello"> <to uri="direct:hello"/> </get> <get uri="/bye"> <to uri="direct:bye"/> </get> </rest> <route> <from uri="direct:hello"/> <transform> <constant>Hello World</constant> </transform> </route> <route> <from uri="direct:bye"/> <transform> <constant>Bye World</constant> </transform> </route> </camelContext>
ベースパスの指定
rest()
キーワード (Java DSL) または rest
要素の path
属性 (XML DSL) を使用してベースパスを定義できます。このパスはすべての Verb 句のパスに接頭辞として付けられます。たとえば、以下は Java DSL のスニペットを示します。
rest("/say")
.get("/hello").to("direct:hello")
.get("/bye").to("direct:bye");
または、以下は XML DSL のスニペットを示します。
<rest path="/say">
<get uri="/hello">
<to uri="direct:hello"/>
</get>
<get uri="/bye" consumes="application/json">
<to uri="direct:bye"/>
</get>
</rest>
REST DSL ビルダーは、以下の URL マッピングで公開します。
/say/hello /say/bye
ベースパスはオプションです。必要であれば、各 Verb 句にフルパスを指定することもできます。
rest() .get("/say/hello").to("direct:hello") .get("/say/bye").to("direct:bye");
Dynamic To の使用
REST DSL では、toD
動的な to パラメーターをサポートしています。動的な to パラメーターを使用して動的な転送先 URI を指定できます。
たとえば、動的エンドポイント URI を使用して動的な JMS キューへ送信するには以下のように定義できます。
public void configure() throws Exception { rest("/say") .get("/hello/{language}").toD("jms:queue:hello-${header.language}"); }
XML DSL では、以下のようになります。
<rest uri="/say"> <get uri="/hello//{language}"> <toD uri="jms:queue:hello-${header.language}"/> </get> <rest>
toD
パラメーターの詳細は、「Dynamic To」 を参照してください。
URI テンプレート
Verb 句で利用する引数では、URI テンプレートで指定できます。これにより、特定のパスセグメントを名前付きプロパティーとして取り込むことができます (これらは Camel メッセージヘッダーにマッピングされます)。たとえば、Hello World アプリケーションをパーソナライズして、発信者の名前で挨拶するようにする場合は、以下のような REST サービスを定義することができます。
rest("/say") .get("/hello/{name}").to("direct:hello") .get("/bye/{name}").to("direct:bye"); from("direct:hello") .transform().simple("Hello ${header.name}"); from("direct:bye") .transform().simple("Bye ${header.name}");
URI テンプレートは {name}
パスセグメントのテキストを取得し、このキャプチャーされたテキストを name
メッセージヘッダーにコピーします。URL が /say/hello/Joe
で終わる GET HTTP リクエストを送信してサービスを呼び出す場合、HTTP レスポンスは Hello Joe
になります。
組み込みルートの構文
to()
キーワード (Java DSL) または to
要素 (XML DSL) で Verb 句を終わらせる代わりに、route()
キーワード (Java DSL) または route
要素 (XML DSL) を使用して Apache Camel ルートを直接 REST DSL に埋め込むことも可能です。route()
キーワードを使用すると、以下の構文でルートを Verb 句に埋め込みできます。
RESTVerbClause.route("...").CamelRoute.endRest()
endRest()
キーワード (Java DSL のみ) は、(rest()
ビルダーに複数の Verb 句がある場合に) Verb 句を区切ることができる必須の句読点です。
たとえば、Hello World の例を Java DSL のように組み込み Camel ルートを使用するようにリファクタリングできます。
rest("/say") .get("/hello").route().transform().constant("Hello World").endRest() .get("/bye").route().transform().constant("Bye World");
XML DSL では以下のようになります。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> ... <rest path="/say"> <get uri="/hello"> <route> <transform> <constant>Hello World</constant> </transform> </route> </get> <get uri="/bye"> <route> <transform> <constant>Bye World</constant> </transform> </route> </get> </rest> </camelContext>
現在の CamelContext
内で、例外句 (onException()
) やインターセプター (intercept()
) を定義した場合、これらの例外句とインターセプターは組み込みルートでもアクティブになります。
REST DSL と HTTP トランスポートコンポーネント
HTTP コンポーネントを明示的に指定しない場合、REST DSL はクラスパス上の利用可能なコンポーネントをチェックすることで、どの HTTP コンポーネントを使用するかを自動検出します。REST DSL は、HTTP コンポーネントのデフォルト名を探し、最初に見つかったものを使用します。クラスパス上に HTTP コンポーネントがなく、かつ HTTP トランスポートが明示的に設定されていない場合は、デフォルトの HTTP コンポーネントは camel-http
になります。
リクエストとレスポンスのコンテンツタイプの指定
Java の consumes()
と produces()
オプション、または XML の consumes
と produces
属性を使用して、HTTP リクエストとレスポンスのコンテンツタイプをフィルタリングすることができます。たとえば、いくつかの一般的なコンテンツタイプ (正式にはインターネットメディアタイプと呼ばれます) は以下のとおりです。
-
text/plain
-
text/html
-
text/xml
-
application/json
-
application/xml
コンテンツタイプは REST DSL の Verb 句のオプションとして指定されます。たとえば、Verb 句を制限して text/plain
の HTTP リクエストのみを受け付け、text/html
の HTTP レスポンスのみを送信するようにするには、以下のような Java コードを使用します。
rest("/email") .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo");
XML では、以下のように consumes
および produces
属性を設定できます。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> ... <rest path="/email"> <post uri="/to/{recipient}" consumes="text/plain" produces="text/html"> <to "direct:foo"/> </get> </rest> </camelContext>
また、consumes()
または produces()
の引数をコンマ区切りのリストとして指定することもできます。たとえば、consumes("text/plain, application/json")
などです。
追加の HTTP メソッド
HTTP サーバーの実装によっては、REST DSL の標準動詞セット (get()
、head()
、put()
、post()
、delete()
、patch()
) では提供されない追加の HTTP メソッドをサポートしているものもあります。追加の HTTP メソッドにアクセスするには、Java DSL の場合は汎用キーワード verb()
、XML DSL の場合は汎用要素 verb
を使用できます。
たとえば、Java DSL の場合、HTTP メソッド TRACE は以下のように実装します。
rest("/say") .verb("TRACE", "/hello").route().transform();
ここで、transform()
は IN メッセージのボディーを OUT メッセージのボディーにコピーし、HTTP リクエストに返答させています。
XML DSL の場合、HTTP メソッド TRACE は以下のように実装します。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> ... <rest path="/say"> <verb uri="/hello" method="TRACE"> <route> <transform/> </route> </get> </camelContext>
カスタム HTTP エラーメッセージの定義
REST サービスがエラーメッセージを返答する必要がある場合、以下のようにカスタム HTTP エラーメッセージを定義できます。
-
Exchange.HTTP_RESPONSE_CODE
ヘッダーにエラーコードの値を設定して、HTTP エラーコードを指定します (例:400
、404
など)。この設定は、正常時のレスポンスではなく、エラーメッセージをレスポンスする REST DSL を示します。 - メッセージのボディーにカスタムエラーメッセージを設定します。
-
必要に応じて
Content-Type
ヘッダーを設定します。 REST サービスが Java オブジェクトとの間でマーシャリングするように設定されている場合 (
bindingMode
が有効になっている場合)、skipBindingOnErrorCode
オプションが有効になっていることを確認する必要があります (デフォルトは有効)。これは、REST DSL がレスポンスを送信する際にメッセージボディーをアンマーシャリングしないようにするためです。オブジェクトバインディングの詳細については、「Java オブジェクトとの間のマーシャリング」 を参照してください。
以下の Java DSL の例は、カスタムエラーメッセージを定義する方法を示しています。
// Java // Configure the REST DSL, with JSON binding mode restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.json); // Define the service with REST DSL rest("/users/") .post("lives").type(UserPojo.class).outType(CountryPojo.class) .route() .choice() .when().simple("${body.id} < 100") .bean(new UserErrorService(), "idTooLowError") .otherwise() .bean(new UserService(), "livesWhere");
この例では、入力 ID が 100 未満の数値の場合、以下のように実装された UserErrorService
Bean を使用してカスタムエラーメッセージを返します。
// Java public class UserErrorService { public void idTooLowError(Exchange exchange) { exchange.getIn().setBody("id value is too low"); exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "text/plain"); exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400); } }
UserErrorService
Bean では、カスタムエラーメッセージを定義し、HTTP エラーコードを 400
に設定します。
パラメーターのデフォルト値
受信する Camel メッセージのヘッダーにデフォルト値を指定することができます。
たとえば、クエリーパラメーターの verbose
などのキーワードを使用して、デフォルト値を指定することができます。以下のコードではデフォルト値は false
となります。これは、verbose
キーを持つヘッダーに他の値が提供されていない場合、デフォルトが false
となります。
rest("/customers/") .get("/{id}").to("direct:customerDetail") .get("/{id}/orders") .param() .name("verbose") .type(RestParamType.query) .defaultValue("false") .description("Verbose order details") .endParam() .to("direct:customerOrders") .post("/neworder").to("direct:customerNewOrder");
カスタム HTTP エラーメッセージでの JsonParserException のラッピング
カスタムのエラーメッセージを返する場合によくあるのは、JsonParserException
例外をラッピングすることです。例として、以下のように、Camel の例外処理メカニズムを利用して、HTTP エラーコード 400 のカスタム HTTP エラーメッセージを作成することができます。
// Java onException(JsonParseException.class) .handled(true) .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400)) .setHeader(Exchange.CONTENT_TYPE, constant("text/plain")) .setBody().constant("Invalid json data");
REST DSL の他のオプション
一般的に、REST DSL のオプションは、以下のようにサービス定義のベース部分 (rest()
の直後) に直接適用することができます。
rest("/email").consumes("text/plain").produces("text/html")
.post("/to/{recipient}").to("direct:foo")
.get("/for/{username}").to("direct:bar");
この場合、指定したオプションは下位のすべての Verb 句に適用されます。または、以下のように、個々の Verb 句にオプションを適用することもできます。
rest("/email") .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo") .get("/for/{username}").consumes("text/plain").produces("text/html").to("direct:bar");
この場合、指定したオプションは関連する Verb 句にのみ適用され、ベース部分の設定は上書きされます。
表4.1「REST DSL のオプション」 は、REST DSL でサポートされているオプションをまとめたものです。
Java DSL | XML DSL | 説明 |
---|---|---|
|
|
バインディングモードを指定します。これを使用して、受信メッセージを Java オブジェクトにマーシャリングすることができます (オプションで、Java オブジェクトを送信メッセージにアンマーシャリングすることもできます)。値は |
|
|
HTTP リクエストで指定されたインターネットメディアタイプ (MIME タイプ) のみを受け入れるように Verb 句を制限します。代表的な値は、 |
|
| JMX Management のカスタム ID を指定します。 |
|
| REST サービスまたは Verb 句の説明文を記載します。JMX Management やツールを使う場合に便利です。 |
|
|
|
|
| RES T サービスの一意の ID を指定します。これは、JMX Management や他のツールを使用する際に便利です。 |
|
|
この Verb 句で処理する HTTP メソッドを指定します。通常は一般的な |
|
|
オブジェクトバインディングが有効な場合 ( |
|
|
HTTP レスポンスで指定されたインターネットメディアタイプ (MIME タイプ) のみを生成するように Verb 句を制限します。代表的な値は、 |
|
|
オブジェクトバインディングが有効な場合 ( |
|
|
Verb 句の引数としてパスセグメントまたは URI テンプレートを指定します。たとえば、 |
|
|
|
4.3. Java オブジェクトとの間のマーシャリング
HTTP で送信するための Java オブジェクトのマーシャリング
REST プロトコルを使用する最も一般的な方法の 1 つは、Java Bean の内容をメッセージボディーで送信することです。これを実現させるには、Java オブジェクトを適切なデータフォーマットとの間でマーシャリングするメカニズムが必要です。Java オブジェクトのエンコードに適した以下のデータフォーマットが REST DSL でサポートされています。
- JSON
JSON (JavaScript Object Notation) は、Java オブジェクトとの間で簡単にマッピングできる軽量なデータフォーマットです。JSON 構文はコンパクトで、緩く型指定され、人間が読み書きがしやすい構文です。これらの理由から、JSON は REST サービスのメッセージ形式として人気があります。
たとえば、以下の JSON コードは、
id
とname
の 2 つのプロパティーフィールドを持つUser
Bean を表現できます。{ "id" : 1234, "name" : "Jane Doe" }
- JAXB
JAXB(Java Architecture for XML Binding) は、Java オブジェクトと XML の間で簡単にマッピングできるデータフォーマットです。XML を Java オブジェクトにマーシャリングするには、使用する Java クラスにアノテーションを付ける必要があります。
たとえば、以下の JAXB コードは、
id
とname
の 2 つのプロパティーフィールドを持つUser
Bean を表現できます。<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <User> <Id>1234</Id> <Name>Jane Doe</Name> </User>
注記Camel 2.17.0 以降、JAXB データフォーマットと型コンバーターは、
XmlRootElement
の代わりにObjectFactory
を使用するクラスの XML から POJO への変換をサポートします。また、Camel コンテキストでは、値が true のCamelJaxbObjectFactory
プロパティーを含める必要があります。ただし、後方互換のためにデフォルトは false になっています。
REST DSL による JSON と JAXB の統合
メッセージボディーを Java オブジェクトへ変換するために必要なコードを自分で書くこともできます。しかし、REST DSL は、この変換を自動的に実行する利便性を提供します。特に、JSON と JAXB を REST DSL と統合すると、以下のようなメリットがあります。
- Java オブジェクトとの間のマーシャリングは自動的に実行されます (適切な設定がある場合)。
- REST DSL は、データフォーマット (JSON または JAXB のいずれか) を自動的に検出し、適切な変換を行うことができます。
- REST DSL は 抽象化レイヤー を提供するので、開発するコードは JSON または JAXB 実装に固有のものではありません。そのため、アプリケーションコードへの影響を最小限に抑えながら、後から実装を切り替えることができます。
サポートされるデータフォーマットコンポーネント
Apache Camel は JSON と JAXB データフォーマットの多くの実装を提供しています。現在、REST DSL では以下のデータフォーマットがサポートされています。
JSON
-
Jackson データフォーマット (
camel-jackson
) (デフォルト) -
gson データフォーマット (
camel-gson
) -
XStream データフォーマット (
camel-xstream
)
-
Jackson データフォーマット (
JAXB
-
JAXB データフォーマット (
camel-jaxb
)
-
JAXB データフォーマット (
オブジェクトマーシャリングを有効にする方法
REST DSL でオブジェクトのマーシャリングを有効にする場合は、以下の点に注意してください。
-
bindingMode
オプションを設定してバインディングモードを有効にします (バインディングモードを設定できるレベルはいくつかあり、詳細は 「バインディングモードの設定」 を参照してください)。 -
受信メッセージでは
type
オプション (必須)、送信メッセージではoutType
オプション (任意) を使用して、変換先 (または変換元) の Java 型を指定します。 - Java オブジェクトを JAXB データフォーマットとの間で変換する場合、Java クラスに適切な JAXB アノテーションを付ける必要があります。
-
jsonDataFormat
オプションやxmlDataFormat
オプション (restConfiguration
ビルダーで指定可能) を使用して、ベースとなるデータフォーマットの実装を指定します。 ルートが JAXB 形式の戻り値を提供する場合、通常、エクスチェンジボディーの Out メッセージを JAXB アノテーションを持つクラスのインスタンス (JAXB 要素) に設定することが期待されます。ただし、JAXB の戻り値を XML 形式で直接提供する場合は、キー
xml.out.mustBeJAXBElement
でdataFormatProperty
をfalse
に設定します (restConfiguration
ビルダーで指定可能)。たとえば、XML DSL の構文では以下になります。<restConfiguration ...> <dataFormatProperty key="xml.out.mustBeJAXBElement" value="false"/> ... </restConfiguration>
必要な依存関係をプロジェクトのビルドファイルに追加します。たとえば、Maven ビルドシステムを使用し、Jackson データフォーマットを使用している場合、次の依存関係を Maven POM ファイルに追加します。
<?xml version="1.0" encoding="UTF-8"?> <project ...> ... <dependencies> ... <!-- use for json binding --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jackson</artifactId> </dependency> ... </dependencies> </project>
アプリケーションを OSGi コンテナーにデプロイする際には、必ず選択したデータフォーマットに必要な機能をインストールしてください。たとえば、Jackson データフォーマット (デフォルト) を使用している場合、以下の Karaf コンソールコマンドを入力して
camel-jackson
機能をインストールします。JBossFuse:karaf@root> features:install camel-jackson
また、Fabric 環境にデプロイする場合は、Fabric プロファイルにその機能を追加します。たとえば、プロファイル
MyRestProfile
を使用している場合は、次のコンソールコマンドを入力して機能を追加できます。JBossFuse:karaf@root> fabric:profile-edit --features camel-jackson MyRestProfile
バインディングモードの設定
bindingMode
オプションはデフォルトで off
になっているため、Java オブジェクトのマーシャリングを有効にするには、明示的に設定する必要があります。表は、サポートされているバインディングモードの一覧を示しています。
Camel 2.16.3 以降では、POJO から JSon/JAXB へのバインディングは、content-type ヘッダーに json または xml が含まれている場合にのみ発生します。これにより、カスタムのコンテンツタイプを指定することで、メッセージボディーがバインディングを使用してマーシャリングしなくなります。これは、メッセージボディーがカスタムバイナリーのペイロードである場合などに便利です。
バインディングモード | 説明 |
---|---|
| バインディングはオフになります (デフォルト)。 |
| バインディングは JSON または XML に対して有効になります。このモードでは、Camel は受信メッセージのフォーマットに基づいて JSON または XML (JAXB) を自動選択します。必ずしも両方のデータフォーマットの実装を有効にする必要はありません。JSON の実装と XML の実装のどちらかまたは両方をクラスパスで提供します。 |
|
バインディングは JSON でのみ有効になります。クラスパスに JSON の実装を用意 しなければなりません (デフォルトでは、Camel は |
|
バインディングは XML でのみ有効です。クラスパスに XML の実装を用意 しなければなりません (デフォルトでは、Camel は |
| バインディングは JSON と XML の両方に対して有効になります。このモードでは、Camel は受信メッセージのフォーマットに基づいて JSON または XML (JAXB) を自動選択します。クラスパスに 両方 のデータフォーマットの実装を用意する必要があります。 |
Java では、これらのバインディングモード値は、以下の enum
型のインスタンスとして表されます。
org.apache.camel.model.rest.RestBindingMode
bindingMode
が設定できるレベルは、以下のように複数あります。
- REST DSL の設定
以下のように、
restConfiguration
ビルダーからbindingMode
オプションを設定できます。restConfiguration().component("servlet").port(8181).bindingMode(RestBindingMode.json);
- サービス定義のベースパート
rest()
キーワードの直後 (Verb 句の前) に、以下のようにbindingMode
オプションを設定することができます。rest("/user").bindingMode(RestBindingMode.json).get("/{id}").VerbClause
- Verb 句
Verb 句で
bindingMode
オプションを設定する場合は、以下のようになります。rest("/user") .get("/{id}").bindingMode(RestBindingMode.json).to("...");
例
Servlet コンポーネントを REST 実装として使用して、REST DSL を使用する方法を示す完全なコード例は、Apache Camel の camel-example-servlet-rest-blueprint
の例を参照してください。この例は、スタンドアロンの Apache Camel ディストリビューション apache-camel-2.23.2.fuse-7_10_0-00018-redhat-00001.zip
をインストールして見ることができます。これは、Fuse インストールの extras/
サブディレクトリーにあります。
スタンドアロン Apache Camel ディストリビューションのインストール後に、以下のディレクトリーでサンプルコードを確認できます。
ApacheCamelInstallDir/examples/camel-example-servlet-rest-blueprint
REST 実装として Servlet コンポーネントを設定
camel-example-servlet-rest-blueprint
の例では、REST DSL のベースとなる実装は Servlet コンポーネントによって提供されます。Servlet コンポーネントは、例4.1「REST DSL の Servlet コンポーネントの設定」 に示されているように、Blueprint XML ファイルで設定されます。
例4.1 REST DSL の Servlet コンポーネントの設定
<?xml version="1.0" encoding="UTF-8"?> <blueprint ...> <!-- to setup camel servlet with OSGi HttpService --> <reference id="httpService" interface="org.osgi.service.http.HttpService"/> <bean class="org.apache.camel.component.servlet.osgi.OsgiServletRegisterer" init-method="register" destroy-method="unregister"> <property name="alias" value="/camel-example-servlet-rest-blueprint/rest"/> <property name="httpService" ref="httpService"/> <property name="servlet" ref="camelServlet"/> </bean> <bean id="camelServlet" class="org.apache.camel.component.servlet.CamelHttpTransportServlet"/> ... <camelContext xmlns="http://camel.apache.org/schema/blueprint"> <restConfiguration component="servlet" bindingMode="json" contextPath="/camel-example-servlet-rest-blueprint/rest" port="8181"> <dataFormatProperty key="prettyPrint" value="true"/> </restConfiguration> ... </camelContext> </blueprint>
Servlet コンポーネントを REST DSL で設定するには、以下の 3 つのレイヤーを設定する必要があります。
- REST DSL レイヤー
-
REST DSL レイヤーは
restConfiguration
要素によって設定され、component
属性をservlet
という値に設定することで Servlet コンポーネントと統合されます。 - Servlet コンポーネントレイヤー
-
Servlet コンポーネントレイヤーは、クラス
CamelHttpTransportServlet
のインスタンスとして実装され、このサンプルインスタンスには Bean IDcamelServlet
があります。 - HTTP コンテナーレイヤー
Servlet コンポーネントは HTTP コンテナーにデプロイする必要があります。Karaf コンテナーには通常、ポート 8181 の HTTP リクエストをリッスンするデフォルトの HTTP コンテナー (Jetty HTTP コンテナー) が提供されています。Servlet コンポーネントをデフォルトの Jetty HTTP コンテナーにデプロイするには、以下を行います。
-
OSGi サービス (
org.osgi.service.http.HttpService
) への OSGi 参照を取得します。このサービスは、OSGi のデフォルト HTTP サーバーへのアクセスを提供する標準化された OSGi インターフェイスです。 -
ユーティリティークラスのインスタンス (
OsgiServletRegisterer
) を作成して、HTTP コンテナーに Servlet コンポーネントを登録します。OsgiServletRegisterer
クラスは、Servlet コンポーネントのライフサイクル管理を簡素化するユーティリティーです。このクラスのインスタンスが作成されると、HttpService
OSGi サービス上のregisterServlet
メソッドが自動的に呼び出され、インスタンスが破棄されると、unregister
メソッドが自動的に呼び出されます。
-
OSGi サービス (
必要な依存関係
この例には、REST DSL にとって重要な以下の 2 つの依存関係があります。
- Servlet コンポーネント
REST DSL のベースとなる実装を提供します。以下のように Maven POM ファイルで指定します。
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-servlet</artifactId> <version>${camel-version}</version> </dependency>
また、OSGi コンテナーにデプロイする場合、以下のように Servlet コンポーネント機能をインストールする必要があります。
JBossFuse:karaf@root> features:install camel-servlet
- Jackson データフォーマット
JSON データフォーマットの実装を提供します。以下のように Maven POM ファイルで指定します。
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jackson</artifactId> <version>${camel-version}</version> </dependency>
また、OSGi コンテナーにデプロイする場合、以下のように Jackson データフォーマット機能をインストールする必要があります。
JBossFuse:karaf@root> features:install camel-jackson
レスポンス用 の Java 型
サンプルアプリケーションは、HTTP リクエストメッセージとレスポンスメッセージで User
型のオブジェクトを渡し合います。Java クラス User
は、例4.2「JSON レスポンス用ユーザークラス」 で示すように定義されます。
例4.2 JSON レスポンス用ユーザークラス
// Java package org.apache.camel.example.rest; public class User { private int id; private String name; public User() { } public User(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
クラス User
は、JSON データ形式で比較的シンプルに表現されます。たとえば、JSON 形式で表現されたこのクラスのインスタンスは次のようになります。
{ "id" : 1234, "name" : "Jane Doe" }
JSON バインディングを使用した REST DSL の例
この例の REST DSL 設定と REST サービス定義を 例4.3「JSON バインディングでの REST DSL の例」 に示します。
例4.3 JSON バインディングでの REST DSL の例
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ...> ... <!-- a bean for user services --> <bean id="userService" class="org.apache.camel.example.rest.UserService"/> <camelContext xmlns="http://camel.apache.org/schema/blueprint"> <restConfiguration component="servlet" bindingMode="json" contextPath="/camel-example-servlet-rest-blueprint/rest" port="8181"> <dataFormatProperty key="prettyPrint" value="true"/> </restConfiguration> <!-- defines the REST services using the base path, /user --> <rest path="/user" consumes="application/json" produces="application/json"> <description>User rest service</description> <!-- this is a rest GET to view a user with the given id --> <get uri="/{id}" outType="org.apache.camel.example.rest.User"> <description>Find user by id</description> <to uri="bean:userService?method=getUser(${header.id})"/> </get> <!-- this is a rest PUT to create/update a user --> <put type="org.apache.camel.example.rest.User"> <description>Updates or create a user</description> <to uri="bean:userService?method=updateUser"/> </put> <!-- this is a rest GET to find all users --> <get uri="/findAll" outType="org.apache.camel.example.rest.User[]"> <description>Find all users</description> <to uri="bean:userService?method=listUsers"/> </get> </rest> </camelContext> </blueprint>
REST オペレーション
例4.3「JSON バインディングでの REST DSL の例」 からの REST サービスは、以下の REST オペレーションを定義します。
GET /camel-example-servlet-rest-blueprint/rest/user/{id}
-
{id}
で識別されたユーザーの詳細を取得します。HTTP レスポンスは JSON 形式で返されます。 PUT /camel-example-servlet-rest-blueprint/rest/user
-
新しいユーザーを作成します。ユーザーの詳細は PUT メッセージのボディーに含まれ、JSON 形式でエンコードされます (
User
オブジェクトタイプと一致するように)。 GET /camel-example-servlet-rest-blueprint/rest/user/findAll
- すべてのユーザーの詳細を取得します。HTTP レスポンスはユーザーの配列で、JSON 形式で返されます。
REST サービスを呼び出すための URL
例4.3「JSON バインディングでの REST DSL の例」 から REST DSL の定義を調べることで、各 REST オペレーションを呼び出すのに必要な URL をまとめることができます。たとえば、指定した ID を持つユーザーの詳細を返す最初の REST オペレーションを呼び出す場合、URL は以下になります。
http://localhost:8181
-
restConfiguration
では、プロトコルのデフォルトはhttp
で、ポートは明示的に8181
に設定されます。 /camel-example-servlet-rest-blueprint/rest
-
restConfiguration
要素のcontextPath
属性によって指定されます。 /user
-
rest
要素のpath
属性によって指定されます。 /{id}
-
verb 要素
get
のuri
属性によって指定されます。
したがって、コマンドラインで以下のコマンドを入力することで、curl
ユーティリティーでこの REST 操作を呼び出すことができます。
curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/123
同様に、残りの REST オペレーションは、以下のサンプルコマンドを入力することにより、curl
で呼び出すことができます。
curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/findAll curl -X PUT -d "{ \"id\": 666, \"name\": \"The devil\"}" -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user
4.4. REST DSL の設定
Java DSL を使用した設定
Java では、restConfiguration()
builder API を使用して REST DSL を設定することができます。たとえば、以下は Servlet コンポーネントをベースの実装として使用するように REST DSL を設定する場合になります。
restConfiguration().component("servlet").bindingMode("json").port("8181") .contextPath("/camel-example-servlet-rest-blueprint/rest");
XML DSL を使用した設定
XML では、restConfiguration
要素を使用して REST DSL を設定できます。たとえば、以下は Servlet コンポーネントをベースの実装として使用するように REST DSL を設定する場合になります。
<?xml version="1.0" encoding="UTF-8"?> <blueprint ...> ... <camelContext xmlns="http://camel.apache.org/schema/blueprint"> ... <restConfiguration component="servlet" bindingMode="json" contextPath="/camel-example-servlet-rest-blueprint/rest" port="8181"> <dataFormatProperty key="prettyPrint" value="true"/> </restConfiguration> ... </camelContext> </blueprint>
設定オプション
表4.3「REST DSL の設定オプション」 は、restConfiguration()
ビルダー (Java DSL) または restConfiguration
要素 (XML DSL) を使用して、REST DSL を設定するオプションを示しています。
Java DSL | XML DSL | 説明 |
---|---|---|
|
|
REST トランスポートとして使用する Camel コンポーネントを指定します (例: |
|
|
REST サービスの公開に使用するプロトコル。ベースとなる REST の実装に依存しますが、通常は |
|
| REST サービスの公開に使用するホスト名。 |
|
| REST サービスの公開に使用するポート番号。 注意: この設定は Servlet コンポーネントによって無視され、代わりにコンテナーの標準 HTTP ポートが使用されます。Apache Karaf OSGi コンテナーの場合、標準の HTTP ポートは通常 8181 になります。JMX などのツールのためにポート値を明記するとよいでしょう。 |
|
|
REST サービスのリーディングコンテキストパスを指定します。これは Servlet などのコンポーネントで使用することができます。これらのコンポーネントは、 |
|
|
ホスト名が明示的に設定されていない場合、このリゾルバーによって REST サービスのホストが決定されます。使用できる値は、ホスト名の形式に解決される
Camel 2.16 までのデフォルトは |
|
|
JSON または XML 形式のメッセージのバインディングモードを有効にします。使用できる値は |
|
|
HTTP エラーがある場合、出力のバインディングをスキップするかどうかを指定します。これにより、JSON や XML にバインドせず、カスタムエラーメッセージを作成することができます。デフォルトは |
|
|
|
|
|
Camel が JSON デ ータフォーマットを変換するために使用するコンポーネントを指定します。使用できる値は、 |
|
|
Camel が XML データフォーマットを変換するために使用するコンポーネントを指定します。使用できる値は |
|
| ベースにある REST 実装に対し、コンポーネントレベル の任意のプロパティーを設定できるようにします。 |
|
| ベースにある REST 実装に対し、エンドポイントレベル の任意のプロパティーを設定できるようにします。 |
|
| ベースにある REST 実装に対し、コンシューマーエンドポイント の任意のプロパティーを設定できます。 |
|
| ベースにあるデータフォーマットコンポーネント (Jackson や JAXB など) に対し、任意のプロパティーを設定できます。Camel 2.14.1 以降では、以下の接頭辞をプロパティーキーに割り当てることができます。
これによって、プロパティー設定を特定の形式 (JSON または XML) および特定のメッセージ方向 (IN または OUT) だけに適用することができます。 |
|
| カスタム CORS ヘッダをキー/値のペアで指定できるようにします。 |
デフォルトの CORS ヘッダー
CORS (オリジン間リソース共有) を有効にすると、デフォルトで以下のヘッダーが設定されます。corsHeaderProperty
DSL コマンド を呼び出すことで、デフォルト設定を任意で上書きできます。
ヘッダーのキー | ヘッダーの値 |
---|---|
|
|
|
|
|
|
|
|
Jackson JSON 機能の有効化または無効化
dataFormatProperty
オプションに以下のキーを設定すると、特定の Jackson JSON 機能を有効または無効にできます。
-
json.in.disableFeatures
-
json.in.enableFeatures
たとえば、Jackson の FAIL_ON_UNKNOWN_PROPERTIES
機能を無効にする場合は、以下のとおりです (JSON 入力に Java オブジェクトにマップできないプロパティーがある場合に Jackson は失敗します)。
restConfiguration().component("jetty") .host("localhost").port(getPort()) .bindingMode(RestBindingMode.json) .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES");
コンマ区切りにすると、複数 の機能を無効にできます。以下に例を示します。
.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE");
以下の例は、Java DSL で Jackson JSON 機能を有効および無効する方法を示しています。
restConfiguration().component("jetty") .host("localhost").port(getPort()) .bindingMode(RestBindingMode.json) .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE") .dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS");
以下の例は、XML DSL で Jackson JSON 機能を有効および無効する方法を示しています。
<restConfiguration component="jetty" host="localhost" port="9090" bindingMode="json"> <dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE"/> <dataFormatProperty key="json.in.enableFeatures" value="FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS"/> </restConfiguration>
有効化または無効化できる Jackson 機能は、以下の Jackson クラスの enum
ID に対応しています。
4.5. OpenAPI インテグレーション
概要
OpenAPI サービスを使用して、CamelContext 内の REST 定義されたルートやエンドポイントの API ドキュメントを生成することができます。これを行うには、camel-openapi-java
モジュール (純粋な Java ベースのモジュール) で Camel REST DSL を使用します。camel-openapi-java
モジュールは CamelContext と統合したサーブレットを作成し、各 REST エンドポイントから情報を取得して JSON または YAML 形式の API ドキュメントを生成します。
Maven を使用する場合は、pom.xml
ファイルを編集して camel-openapi-java
コンポーネントに依存関係を追加します。
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-openapi-java</artifactId> <version>x.x.x</version> <!-- Specify the version of your camel-core module. --> </dependency>
CamelContext での OpenAPI の有効化
Camel REST DSL で OpenAPI を使用できるようにするには、apiContextPath()
を呼び出して、OpenAPI で生成された API ドキュメントのコンテキストパスを設定します。以下に例を示します。
public class UserRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { // Configure the Camel REST DSL to use the netty4-http component: restConfiguration().component("netty4-http").bindingMode(RestBindingMode.json) // Generate pretty print output: .dataFormatProperty("prettyPrint", "true") // Set the context path and port number that netty will use: .contextPath("/").port(8080) // Add the context path for the OpenAPI-generated API documentation: .apiContextPath("/api-doc") .apiProperty("api.title", "User API").apiProperty("api.version", "1.2.3") // Enable CORS: .apiProperty("cors", "true"); // This user REST service handles only JSON files: rest("/user").description("User rest service") .consumes("application/json").produces("application/json") .get("/{id}").description("Find user by id").outType(User.class) .param().name("id").type(path).description("The id of the user to get").dataType("int").endParam() .to("bean:userService?method=getUser(${header.id})") .put().description("Updates or create a user").type(User.class) .param().name("body").type(body).description("The user to update or create").endParam() .to("bean:userService?method=updateUser") .get("/findAll").description("Find all users").outTypeList(User.class) .to("bean:userService?method=listUsers"); } }
OpenAPI モジュールの設定オプション
下表のオプションを使用して、OpenAPI モジュールを設定することができます。以下のようにオプションを設定します。
-
camel-openapi-java
モジュールをサーブレットとして使用している場合は、web.xml
ファイルを編集し、設定する設定オプションごとにinit-param
要素を指定してオプションを設定します。 -
Camel REST コンポーネントから
camel-openapi-java
モジュールを使用している場合は、enableCORS()
、host()
、またはcontextPath()
などの適切なRestConfigurationDefinition
メソッドを呼び出してオプションを設定します。api.xxx
オプションをRestConfigurationDefinition.apiProperty()
メソッドで設定します。
オプション | 型 | 説明 |
---|---|---|
| String | API 関連の連絡先に使用するメールアドレス。 |
| String | API 関連の連絡先に使用する個人または組織の名前。 |
| String | API 関連の問い合わせ先の Web サイトへの URL。 |
| ブール値 |
アプリケーションに複数の |
| String | コンテキストリストに表示される CamelContext ID のフィルターパターン。正規表現を指定し、ワイルドカードとして * を使用することができます。これは、Camel Intercept 機能で使用されるのと同じパターンマッチング機能です。 |
| String | API に適用されるライセンス名。 |
| String | API に適用されるライセンスの URL。 |
| String |
ドキュメントを生成する REST API が使用可能なパスを設定します (例: |
| String | API の利用規約への URL。 |
| String | アプリケーションの名前。 |
| String | API のバージョン。デフォルトは 0.0.0 です。 |
| String |
必須。REST サービスが利用できるパスを設定します。相対パスで指定します。たとえば、 |
| ブール値 |
CORS (オリジン間リソース共有) を有効にするかどうか。これにより、CORS は REST API ドキュメントを閲覧する場合のみ有効になり、REST サービスにアクセスする場合には有効になりません。デフォルトは false です。この表の後で説明するように、代わりに |
| String |
OpenAPI サービスが実行されているホストの名前を指定します。デフォルトでは、 |
| String |
使用するプロトコルスキーム。複数の値はコンマで区切ります (例: |
| String | OpenAPI 仕様のバージョン。デフォルトは 3.0 です。 |
JSON または YAML 形式の出力
Camel 3.1 以降、camel-openapi-java
モジュールは JSON と YAML 形式の両方の出力をサポートしています。必要な出力を指定するには、リクエスト URL に /openapi.json
または /openapi.yaml
を追加します。リクエスト URL に形式が指定されていない場合、camel-openapi-java
モジュールは HTTP Accept ヘッダーをチェックして JSON または YAML を許可するかどうかを検出します。両方が受け入れられるか、両方が受け入れられないと検知された場合は、デフォルトの JSON 形式が出力されます。
例
Apache Camel 3.x ディストリビューションで、camel-example-openapi-cdi
および camel-example-openapi-java
は camel-openapi-java
モジュールの使用方法を実証します。
Apache Camel 2.x ディストリビューションで、camel-example-swagger-cdi
および camel-example-swagger-java
は camel-swagger-java
モジュールの使用方法を実証します。
OpenAPI で生成されたドキュメントの充実
Camel 3.1 以降では、名前、説明、データ型、パラメーター型などのパラメーター詳細を定義することで、OpenAPI で生成されたドキュメントを充実することができます。XML DSL を使用している場合は、param
要素を指定してこれらの情報を追加します。以下の XML DSL の例は、ID パスパラメーターに関する情報を補足する方法を示しています。
<!-- This is a REST GET request to view information for the user with the given ID: --> <get uri="/{id}" outType="org.apache.camel.example.rest.User"> <description>Find user by ID.</description> <param name="id" type="path" description="The ID of the user to get information about." dataType="int"/> <to uri="bean:userService?method=getUser(${header.id})"/> </get>
以下は Java DSL の例です。
.get("/{id}").description("Find user by ID.").outType(User.class) .param().name("id").type(path).description("The ID of the user to get information about.").dataType("int").endParam() .to("bean:userService?method=getUser(${header.id})")
名前が body
のパラメーターを定義する場合は、そのパラメーターの型として body
も指定する必要があります。以下に例を示します。
<!-- This is a REST PUT request to create/update information about a user. --> <put type="org.apache.camel.example.rest.User"> <description>Updates or creates a user.</description> <param name="body" type="body" description="The user to update or create."/> <to uri="bean:userService?method=updateUser"/> </put>
以下は Java DSL の例です。
.put().description("Updates or create a user").type(User.class) .param().name("body").type(body).description("The user to update or create.").endParam() .to("bean:userService?method=updateUser")
Apache Camel ディストリビューションの examples/camel-example-servlet-rest-tomcat
も参照してください。
第5章 メッセージングシステム
概要
本章では、エンドポイント、メッセージングチャネル、メッセージルーターなどの、メッセージングシステムの基本的な設定要素について紹介します。
5.1. メッセージ
概要
メッセージ とは、メッセージングシステムでデータを送信するための最小単位です (以下の図では灰色の丸で表されています)。複数のパーツが含まれるメッセージなどの場合、メッセージ自体に内部構造がある場合があります。これは、図5.1「Message パターン」 では灰色の丸とつながる図形で表されています。
図5.1 Message パターン
メッセージのタイプ
Apache Camel は以下のメッセージタイプを定義します。
- In メッセージ: コンシューマーエンドポイントからプロデューサーエンドポイントへのルートを通過するメッセージ (通常はメッセージのエクスチェンジを開始します)。
- out メッセージ: プロデューサーエンドポイントからコンシューマーエンドポイントに戻るルートを通過するメッセージ (通常は In メッセージの応答になります)。
これらのメッセージタイプはすべて、org.apache.camel.Message
インターフェイスによって内部的に表されます。
メッセージの構造
デフォルトでは、Apache Camel は以下の構造をすべてのメッセージタイプに適用します。
- ヘッダー: メッセージから抽出されたメタデータまたはヘッダーデータが含まれます。
- ボディー: 通常、メッセージ全体が元の形式で含まれます。
- アタッチメント: メッセージの添付 (JBI などの特定のメッセージングシステムとの統合に必要です)。
ヘッダー、ボディー、およびアタッチメントに分割することはメッセージの抽象モデルであることを覚えておくことが重要です。Apache Camel は、さまざまなメッセージ形式を生成するさまざまなコンポーネントをサポートします。最終的には、メッセージのヘッダーおよびボディーに何を配置するかを決定する基盤のコンポーネント実装です。
メッセージの関連付け
Apache Camel は内部的に、個別のメッセージの関連付けに使用されるメッセージ ID を記憶します。ただし、Apache Camel がメッセージを照合する最も重要な方法は エクスチェンジ オブジェクトを介して行われます。
エクスチェンジオブジェクト
エクスチェンジオブジェクトは、関連するメッセージのコレクションをカプセル化するエンティティーで、関連するメッセージのコレクションは メッセージエクスチェンジ と呼ばれ、メッセージのシーケンスを制御するルールは 交換パターン と呼ばれます。たとえば、一般的な交換パターンには、一方向イベントメッセージ (In メッセージ 1 つで設定) と、リクエスト-リプライエクスチェンジ (In メッセージ 1 つと、それに続く Out メッセージで設定) の 2 つがあります。
メッセージへのアクセス
Java DSL でルーティングルールを定義する場合、以下の DSL ビルダーメソッドを使用してメッセージのヘッダーとボディーにアクセスできます。
-
header(String name)
、body()
- 現行の In メッセージの名前付きヘッダーとボディーを返します。 -
outBody()
: 現在の Out メッセージのボディーを返します。
たとえば、In メッセージの username
ヘッダーを設定するには、以下の Java DSL ルートを使用できます。
from(SourceURL).setHeader("username", "John.Doe").to(TargetURL);
5.2. メッセージチャネル
概要
メッセージチャネル は、メッセージングシステムの論理チャネルです。つまり、異なるメッセージチャネルにメッセージを送信することで、メッセージを異なるメッセージタイプに分類する初歩的な方法を提供します。メッセージチャネルの例として、メッセージキューとメッセージトピックが挙げられます。論理チャネルは物理チャネルと同じでは ない ことに注意してください。論理チャネルを物理的に認識する方法はいくつかあります。
Apache Camel では、メッセージチャネルは 図5.2「Message Channel パターン」 のとおり、メッセージ指向コンポーネントのエンドポイント URI によって表されます。
図5.2 Message Channel パターン
メッセージ指向コンポーネント
Apache Camel の以下のメッセージ指向コンポーネントによって、メッセージチャネルの概念がサポートされます。
ActiveMQ
ActiveMQ では、メッセージチャネルは キュー または トピック によって表されます。特定のキューのエンドポイント URI である QueueName の形式は次のとおりです。
activemq:QueueName
特定のトピックのエンドポイント URI である TopicName の形式は次のとおりです。
activemq:topic:TopicName
たとえば、Foo.Bar
キューにメッセージを送信するには、以下のエンドポイント URI を使用します。
activemq:Foo.Bar
ActiveMQ コンポーネントの設定に関する詳細や手順については、Apache Camel Component Reference Guide の ActiveMQ を参照してください。
JMS
Java Messaging Service (JMS) は、さまざまな種類のメッセージシステムにアクセスするために使用される汎用ラッパー層です (たとえば、ActiveMQ、MQSeries、Tibco、BEA、Sonic などをラップするために使用できます)。JMS では、メッセージチャネルはキューまたはトピックによって表されます。特定のキューのエンドポイント URI である QueueName の形式は次のとおりです。
jms:QueueName
特定のトピックのエンドポイント URI である TopicName の形式は次のとおりです。
jms:topic:TopicName
JMS コンポーネントの設定に関する詳細や手順は、Apache Camel Component Reference Guide の Jms を参照してください。
AMQP
AMQP では、メッセージチャネルはキューまたはトピックで表されます。特定のキューのエンドポイント URI である QueueName の形式は次のとおりです。
amqp:QueueName
特定のトピックのエンドポイント URI である TopicName の形式は次のとおりです。
amqp:topic:TopicName
AMQP コンポーネントの設定に関する詳細や手順は、Apache Camel Component Reference Guide の Amqp を参照してください。
5.3. メッセージエンドポイント
概要
メッセージエンドポイント は、アプリケーションとメッセージングシステム間のインターフェイスです。図5.3「Message Endpoint パターン」 のように、送信者のエンドポイントがあります。これは、プロキシーまたはサービスコンシューマーとも呼ばれ、In メッセージの送信を担当します。また、受信者のエンドポイントもあります。これはエンドポイントまたはサービスとも呼ばれ、In メッセージの受信を担当します。
図5.3 Message Endpoint パターン
エンドポイントのタイプ
Apache Camel は、2 つの基本タイプのエンドポイントを定義します。
- コンシューマーエンドポイント: Apache Camel ルートの最初にあり、受信チャネルから In messages を読み取ります (受信者エンドポイントと同等です)。
- プロデューサーエンドポイント: Apache Camel ルートの最後にあり、 In メッセージを送信チャネルに書き込みます (送信者 エンドポイントと同等です)。複数のプロデューサーエンドポイントでルートを定義できます。
エンドポイント URI
Apache Camel では、エンドポイントはエンドポイント URI で表され、通常は以下のようなデータをカプセル化します。
- コンシューマーエンドポイントのエンドポイント URI: 特定の場所をアドバタイズします (たとえば、送信者が接続できるサービスを公開する場合など)。または、URI でメッセージキューなどのメッセージソースを指定できます。エンドポイント URI には、エンドポイントの設定を含めることができます。
- プロデューサーエンドポイントのエンドポイント URI: メッセージの送信先の詳細と、エンドポイントの設定が含まれます。URI はリモートレシーバーエンドポイントの場所を指定する場合があります。それ以外の場合には、宛先にはキュー名などの抽象的な形式を含むことができます。
Apache Camel のエンドポイント URI の一般的な形式は次のとおりです。
ComponentPrefix:ComponentSpecificURI
ComponentPrefix は、特定の Apache Camel コンポーネントを識別する URI 接頭辞に置き換えます (サポートされるすべてのコンポーネントの詳細は Apache Camel Component Reference を参照してください)。URI である ComponentSpecificURI の残りの部分には特定のコンポーネントによって定義された構文があります。たとえば、JMS キュー Foo.Bar
に接続するには、以下のようにエンドポイント URI を定義できます。
jms:Foo.Bar
コンシューマーエンドポイント file://local/router/messages/foo
を直接プロデューサーエンドポイント jms:Foo.Bar
に接続するルートを定義するには、以下の Java DSL フラグメントを使用できます。
from("file://local/router/messages/foo").to("jms:Foo.Bar");
または、以下のように XML で同じルートを定義することもできます。
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file://local/router/messages/foo"/>
<to uri="jms:Foo.Bar"/>
</route>
</camelContext>
Dynamic To
<toD>
パラメーターにより、連結された 1 つ以上の式を使用して、動的に計算されたエンドポイントにメッセージを送信することができます。
デフォルトでは、Simple 言語はエンドポイントの計算に使用されます。以下の例では、ヘッダーによって定義されたエンドポイントにメッセージを送信します。
<route> <from uri="direct:start"/> <toD uri="${header.foo}"/> </route>
Java DSL では、同じコマンドの形式は以下のようになります。
from("direct:start") .toD("${header.foo}");
以下の例のように、URI の前にリテラルを付けることもできます。
<route> <from uri="direct:start"/> <toD uri="mock:${header.foo}"/> </route>
Java DSL では、同じコマンドの形式は以下のようになります。
from("direct:start") .toD("mock:${header.foo}");
上記の例では、header.foo の値が orange の場合、URI は mock:orange
として解決されます。
Simple 以外の言語を使用するには、language: パラメーターを定義する必要があります。パートII「ルーティング式と述語言語」を参照してください。
異なる言語を使用する場合の形式では、URI で language:languagename:
を使用します。たとえば、Xpath を使用する場合は、以下の形式を使用します。
<route> <from uri="direct:start"/> <toD uri="language:xpath:/order/@uri/"> </route>
Java DSL では同じ例が以下のようになります。
from("direct:start") .toD("language:xpath:/order/@uri");
language:
を指定しない場合、エンドポイントはコンポーネント名になります。場合によっては、コンポーネントと言語の名前は xquery のように同じになります。
+
記号を使用して複数の言語を連結できます。以下の例では、URI は Simple 言語と Xpath 言語の組み合わせです。Simple がデフォルトであるため、言語を定義する必要はありません。+
記号の後は、language:xpath
で示される Xpath 命令があります。
<route> <from uri="direct:start"/> <toD uri="jms:${header.base}+language:xpath:/order/@id"/> </route>
Java DSL では形式は以下のようになります。
from("direct:start") .toD("jms:${header.base}+language:xpath:/order/@id");
多くの言語を一度に連結できます。それぞれの言語を +
で区切り、各言語を language:languagename
で指定します。
toD
で以下のオプションを設定することができます。
名前 | デフォルト値 | 説明 |
| 必須のオプション: 使用する URI。 | |
| エンドポイントに送信する際に使用する特定の交換パターンを設定します。元の MEP は後で復元されます。 | |
|
再利用のためにプロデューサーをキャッシュする、 | |
|
| 解決できないエンドポイント URI を無視するかどうかを指定します。無効にすると、Camel は無効なエンドポイント URI を特定する例外を出力します。 |
5.4. パイプとフィルター
概要
図5.4「Pipes and Filters パターン」 に記載されている Pipes and Filters パターンは、フィルターチェーンを作成してルートを構築する方法を表しています。フィルターの出力は、パイプラインの次のフィルターの入力に取り込まれます (UNIX の pipe
コマンドに似ています)。パイプラインアプローチの利点は、サービス (Apache Camel アプリケーションの外部にあるものもあります) を作成して、より複雑な形式のメッセージ処理を作成できることです。
図5.4 Pipes and Filters パターン
InOut 交換パターンのパイプライン
通常、パイプラインのすべてのエンドポイントには、入力 (In メッセージ) および出力 (Out メッセージ) があります。これは、InOut メッセージ交換パターンと互換性があることを意味しています。InOut パイプラインを経由する通常のメッセージフローを 図5.5「InOut エクスチェンジのパイプライン」 に示します。
図5.5 InOut エクスチェンジのパイプライン
パイプラインは、各エンドポイントの出力を次のエンドポイントの入力に接続します。最終的なエンドポイントからの Out メッセージは、元の呼び出し元に返されます。以下のように、このパイプラインのルートを定義できます。
from("jms:RawOrders").pipeline("cxf:bean:decrypt", "cxf:bean:authenticate", "cxf:bean:dedup", "jms:CleanOrders");
以下のように XML で同じルートを設定できます。
<camelContext id="buildPipeline" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="jms:RawOrders"/> <to uri="cxf:bean:decrypt"/> <to uri="cxf:bean:authenticate"/> <to uri="cxf:bean:dedup"/> <to uri="jms:CleanOrders"/> </route> </camelContext>
XML には専用の pipeline 要素がありません。前述の from
と to
要素の組み合わせは、意味的にはパイプラインと同等です。「pipeline() および to() DSL コマンドの比較」 を参照してください。
InOnly および RobustInOnly 交換パターンのパイプライン
パイプラインのエンドポイントから利用可能な Out メッセージがない場合 (InOnly
および RobustInOnly
交換パターンの場合)、パイプラインは通常の方法で接続できません。この場合、パイプラインは 図5.6「InOnly エクスチェンジのパイプライン」 に示すように、元の In メッセージのコピーをパイプラインの各エンドポイントに渡して構築されます。このタイプのパイプラインは、固定の宛先を持つ受信者リストと同等です (「受信者リスト」 を参照)。
図5.6 InOnly エクスチェンジのパイプライン
このパイプラインのルートは、InOut パイプラインと同じ構文を使用して定義されます (Java DSL または XML)。
pipeline() および to() DSL コマンドの比較
Java DSL では、以下の構文のいずれかを使用してパイプラインルートを定義できます。
pipeline () プロセッサーコマンドの使用: 以下のように、パイプラインプロセッサーを使用してパイプラインルートを構築します。
from(SourceURI).pipeline(FilterA, FilterB, TargetURI);
Using the to() command: 以下のように、
to()
コマンドを使用してパイプラインルートを構築します。from(SourceURI).to(FilterA, FilterB, TargetURI);
または、同等の構文を使用することもできます。
from(SourceURI).to(FilterA).to(FilterB).to(TargetURI);
to()
コマンド構文を使用する場合は、パイプラインプロセッサーと常に同等では ない ため注意が必要です。Java DSL では、ルートの直前のコマンドで to()
の意味を変更できます。たとえば、to()
コマンドの前に multicast()
コマンドがある場合は、上記のエンドポイントをパイプラインパターンではなく Multicast パターンにバインドします (「Multicast」 を参照)。
5.5. メッセージルーター
概要
図5.7「Message Router パターン」 に示されている メッセージルーター は、単一のコンシューマーエンドポイントからメッセージを消費し、特定の決定基準に基づいて適切なターゲットエンドポイントにリダイレクトするフィルターのタイプです。メッセージルーターはメッセージのリダイレクトのみに関与し、メッセージの内容は変更しません。
しかし、デフォルトでは、Camel がメッセージエクスチェンジを受信者のエンドポイントにルーティングするたびに、元のエクスチェンジオブジェクトのシャローコピーを送信します。シャローコピーでは、メッセージボディー、ヘッダー、アタッチメントなどの元のエクスチェンジの要素は参照のみでコピーされます。リソースを再利用するシャローコピーを送信することで、Camel はパフォーマンスを最適化します。ただし、これらのシャローコピーはすべてリンクされるため、Camel が複数のエンドポイントにメッセージをルーティングする場合は、異なる受信者にルーティングされるコピーにカスタムロジックを適用できないことがトレードオフになります。Camel を有効にして一意なバージョンのメッセージを異なるエンドポイントにルーティングする方法は、送信メッセージへのカスタム処理の適用 を参照してください。
図5.7 Message Router パターン
メッセージルーターは choice()
プロセッサーを使用して Apache Camel に簡単に実装できます。このプロセッサーでは、when()
を使用して、代替のターゲットエンドポイントをそれぞれ選択することができます (choice プロセッサーの詳細は、「プロセッサー」 を参照してください)。
Java DSL の例
以下の Java DSL の例は、foo
ヘッダーの内容に応じて、3 つの代替の宛先 (seda:a
、seda:b
、または seda:c
) にメッセージをルーティングする方法を示しています。
from("seda:a").choice() .when(header("foo").isEqualTo("bar")).to("seda:b") .when(header("foo").isEqualTo("cheese")).to("seda:c") .otherwise().to("seda:d");
XML 設定の例
以下の例は、XML で同じルートを設定する方法を示しています。
<camelContext id="buildSimpleRouteWithChoice" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="seda:a"/> <choice> <when> <xpath>$foo = 'bar'</xpath> <to uri="seda:b"/> </when> <when> <xpath>$foo = 'cheese'</xpath> <to uri="seda:c"/> </when> <otherwise> <to uri="seda:d"/> </otherwise> </choice> </route> </camelContext>
otherwise を使用しない choice
choice()
を otherwise()
句なしで使用すると、一致しないエクスチェンジはすべてデフォルトでドロップされます。
5.6. メッセージトランスレーター
概要
図5.8「Message Translator パターン」 で示されている Message Translatorパターンは、メッセージの内容を変更し、異なる形式に変換するコンポーネントを記述します。Apache Camel の Bean インテグレーション機能を使用して、メッセージの変換を実行できます。
図5.8 Message Translator パターン
Bean インテグレーション
登録された Bean でメソッドを呼び出し可能にする Bean インテグレーションを使用して、メッセージを変換できます。たとえば、ID が myTransformerBean
の Bean でメソッド myMethodName()
を呼び出すには、以下を実行します。
from("activemq:SomeQueue") .beanRef("myTransformerBean", "myMethodName") .to("mqseries:AnotherQueue");
myTransformerBean
Bean は Spring XML ファイルまたは JNDI で定義されます。beanRef()
で method name パラメーターを省略すると、Bean インテグレーションはメッセージエクスチェンジを確認して、呼び出すメソッド名を推測しようとします。
また、独自の明示的なプロセッサー Processor
を追加して、以下のように変換を実行することもできます。
from("direct:start").process(new Processor() { public void process(Exchange exchange) { Message in = exchange.getIn(); in.setBody(in.getBody(String.class) + " World!"); } }).to("mock:result");
または、DSL を使用して以下のように変換を明示的に設定できます。
from("direct:start").setBody(body().append(" World!")).to("mock:result");
また、テンプレート を使用して、ある宛先からのメッセージを消費し、Velocity や XQuery などのメッセージに変換してから、別の宛先に送信することもできます。InOnly 交換パターン (一方向メッセージング ) を使用する例は次のとおりです。
from("activemq:My.Queue"). to("velocity:com/acme/MyResponse.vm"). to("activemq:Another.Queue");
InOut (request-reply) セマンティクスを使用して、テンプレート生成の応答で ActiveMQ の My.Queue
キューでリクエストを処理する場合、以下のようなルートを使用して応答を JMSReplyTo
宛先に送り返すことができます。
from("activemq:My.Queue"). to("velocity:com/acme/MyResponse.vm");
5.7. メッセージ履歴
概要
Message History パターンは、粗結合されたシステムで、メッセージのフローの分析およびデバッグを可能にします。メッセージ履歴をメッセージに添付すると、メッセージが送信時以降に通過したすべてのアプリケーションの一覧が表示されます。
Apache Camel では、getTracedRouteNodes
メソッドを使用すると、Tracer を使用してメッセージフローを追跡するか、UnitOfWork からの Java API を使用して情報にアクセスできます。
ログでの文字長の制限
ロギングメカニズムを使用して Apache Camel を実行すると、メッセージとその内容を随時ログに記録できます。
メッセージによっては、非常に大きなペイロードが含まれる場合があります。デフォルトでは、Apache Camel はログメッセージの最初の 1000 文字のみを表示します。たとえば、以下のログが表示されます。
[DEBUG ProducerCache - >>>> Endpoint[direct:start] Exchange[Message: 01234567890123456789... [Body clipped after 20 characters, total length is 1000]
Apache Camel がログのボディーを切り取る際の制限をカスタマイズできます。また、ゼロや -1 などの負の値を設定すると、メッセージボディーはログに記録されません。
- Java DSL を使用した制限のカスタマイズ
Java DSL を使用して、Camel プロパティーに制限を設定できます。以下に例を示します。
context.getProperties().put(Exchange.LOG_DEBUG_BODY_MAX_CHARS, "500");
- Spring DSL を使用した制限のカスタマイズ
Spring DSL を使用して、Camel プロパティーに制限を設定できます。以下に例を示します。
<camelContext> <properties> <property key="CamelLogDebugBodyMaxChars" value="500"/> </properties> </camelContext>
第6章 メッセージングチャネル
概要
メッセージングチャネルは、メッセージングアプリケーションの組み込みを提供します。本章では、メッセージングシステムで利用可能なメッセージングチャネルの種類と、それらのチャネルのロールについて説明します。
6.1. Point-to-Point Channel
概要
図6.1「Point to Point Channel パターン」 に示されている Point-to-Point Channel は、1 つの受信側のみが指定のメッセージを消費することを保証するメッセージチャネル です。これは、複数の受信側が同じメッセージを消費できる Publish-Subscribe Channel とは対照的です。特に、Publish-Subscribe Channel では、複数の受信側が同じチャネルにサブスクライブすることが可能です。複数の受信側がメッセージの消費で競合する場合、1 つの受信側のみがメッセージを消費するようにするのはメッセージチャネルのロールです。
図6.1 Point to Point Channel パターン
Point to Point Channel をサポートするコンポーネント
以下の Apache Camel コンポーネントは、Point to Point Channel パターンをサポートします。
JMS
JMS では、Point to Point Channel は キュー で表されます。たとえば、Foo.Bar
という JMS キューのエンドポイント URI を指定できます。
jms:queue:Foo.Bar
JMS コンポーネントはデフォルトでキューエンドポイントを作成するため、修飾子 queue:
は任意です。そのため、以下の同等のエンドポイント URI を指定することもできます。
jms:Foo.Bar
詳細は、Apache Camel Component Reference Guide の Jms を参照してください。
ActiveMQ
ActiveMQ では、Point to Point Channel はキューで表されます。たとえば、以下のように Foo.Bar
という ActiveMQ キューのエンドポイント URI を指定できます。
activemq:queue:Foo.Bar
詳細は、Apache Camel Component Reference Guide の ActiveMQ を参照してください。
SEDA
Apache Camel Staged Event-Driven Architecture (SEDA) コンポーネントは、ブロッキングキューを使用して実装されます。Apache Camel アプリケーションの 内部 にある軽量のポイントツーポイントチャネルを作成する場合は、SEDA コンポーネントを使用します。たとえば、以下のように SedaQueue
という SEDA キューのエンドポイント URI を指定できます。
seda:SedaQueue
JPA
Java Persistence API (JPA) コンポーネントは、エンティティー Bean をデータベースに書き出すために使用される EJB 3 永続化の規格です。詳細は、Apache Camel Component Reference Guide の JPA を参照してください。
XMPP
XMPP (Jabber) コンポーネントは、通信でパーソンツーパーソン (Person-to-Person) モードが使用される場合に、Point to Point Channel パターンをサポートします。詳細は、Apache Camel Component Reference Guide の XMPP を参照してください。
6.2. Publish-Subscribe Channel
概要
図6.2「Publish Subscribe Channel パターン」 に示されているPublish-Subscribe Channel は、複数のサブスクライバーが任意のメッセージを消費できるようにする 「メッセージチャネル」 です。これは、「Point-to-Point Channel」 とは対照的です。Publish-Subscribe Channel は、複数のサブスクライバーにイベントや通知をブロードキャストする方法として頻繁に使用されます。
図6.2 Publish Subscribe Channel パターン
Publish-Subscribe Channel をサポートするコンポーネント
以下の Apache Camel コンポーネントは、Publish Subscribe Channel パターンをサポートします。
JMS
JMS では、パブリッシュサブスクライブチャネルは トピック で表されます。たとえば、StockQuotes
という JMS トピックのエンドポイント URI を指定できます。
jms:topic:StockQuotes
詳細は、Apache Camel Component Reference Guide の Jms を参照してください。
ActiveMQ
ActiveMQ では、Publish-Subscribe Channel はトピックで表されます。たとえば、以下のように StockQuotes
という ActiveMQ トピックのエンドポイント URI を指定できます。
activemq:topic:StockQuotes
詳細は、Apache Camel Component Reference Guide の ActiveMQ を参照してください。
XMPP
XMPP (Jabber) コンポーネントは、グループ通信モードで使用される場合に Publish Subscribe Channel パターンをサポートします。詳細は、Apache Camel Component Reference Guide の Xmpp を参照してください。
静的サブスクリプションリスト
必要に応じて、Apache Camel アプリケーション内にパブリッシュサブスクライブロジックを実装することもできます。簡単な方法として、ルートの最後にターゲットのエンドポイントがすべて明示的にリストされる 静的サブスクリプションリスト を定義する方法があります。ただし、この方法は JMS または ActiveMQ トピックほど柔軟ではありません。
Java DSL の例
以下の Java DSL 例は、Publish-Subscribe Channel を単一のパブリッシャー seda:a
と 3 つのサブスクライバー seda:b
、seda:c
、および seda:d
でシミュレートする方法を示しています。
from("seda:a").to("seda:b", "seda:c", "seda:d");
これは InOnly メッセージ交換パターンでのみ機能します。
XML 設定の例
以下の例は、XML で同じルートを設定する方法を示しています。
<camelContext id="buildStaticRecipientList" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="seda:a"/> <to uri="seda:b"/> <to uri="seda:c"/> <to uri="seda:d"/> </route> </camelContext>
6.3. Dead Letter Channel
概要
図6.3「Dead Letter Channel パターン」 で示されている Dead Letter Channel パターンは、メッセージングシステムが目的の受信者にメッセージを配信できない場合に実行するアクションを記述します。これには、配信を再試行する機能などが含まれ、最終的に配信に失敗した場合には、メッセージが Dead Letter Channel に送信され、未達のメッセージをアーカイブします。
図6.3 Dead Letter Channel パターン
Java DSL でのデッドレターチャネルの作成
以下の例は、Java DSL を使用してデッドレターチャネルを作成する方法を示しています。
errorHandler(deadLetterChannel("seda:errors")); from("seda:a").to("seda:b");
errorHandler()
メソッドは Java DSL インターセプターで、現在のルートビルダーで定義された すべて のルートがこの設定の影響を受けることを意味します。deadLetterChannel()
メソッドは、指定の宛先エンドポイント seda:errors
で新しいデッドレターチャネルを作成する Java DSL コマンドです。
errorHandler()
インターセプターは、すべて のエラータイプを処理するためのキャッチオールメカニズムを提供します。例外処理により粒度の細かい方法を適用する場合は、代わりに onException
句を使用できます (「onException 句」 を参照)。
XML DSL の例
以下のように、XML DSL でデッドレターチャネルを定義できます。
<route errorHandlerRef="myDeadLetterErrorHandler"> ... </route> <bean id="myDeadLetterErrorHandler" class="org.apache.camel.builder.DeadLetterChannelBuilder"> <property name="deadLetterUri" value="jms:queue:dead"/> <property name="redeliveryPolicy" ref="myRedeliveryPolicyConfig"/> </bean> <bean id="myRedeliveryPolicyConfig" class="org.apache.camel.processor.RedeliveryPolicy"> <property name="maximumRedeliveries" value="3"/> <property name="redeliveryDelay" value="5000"/> </bean>
再配信ポリシー
通常、配信に失敗した場合、デッドレターチャネルに直接メッセージを送信することはありません。代わりに、最大限度まで再送信を試み、再配信の試行がすべて失敗した場合は、メッセージをデッドレターチャネルに送信します。メッセージの再配信をカスタマイズするには、デッドレターチャネルを設定して 再配信ポリシー を取得します。たとえば、再配信の最大試行回数を 2 回に指定し、配信試行間の遅延に指数バックオフアルゴリズムを適用するには、以下のようにデッドレターチャネルを設定できます。
errorHandler(deadLetterChannel("seda:errors").maximumRedeliveries(2).useExponentialBackOff()); from("seda:a").to("seda:b");
ここでは、チェーンの関連メソッドを呼び出して、デッドレターチャネルに再配信オプションを設定します (チェーンの各メソッドは現在の RedeliveryPolicy
オブジェクトの参照を返します)。表6.1「再配信ポリシーの設定」 には、再配信ポリシーの設定に使用できるメソッドがまとめられています。
メソッドの署名 | デフォルト | 説明 |
---|---|---|
|
| 正常なシャットダウン中またはルートが停止している間、再配信を試行するかどうかを制御します。すでに進行中の配信は停止時に中断され ません。 |
|
|
指数バックオフが有効な場合は、 d, m*d, m*m*d, m*m*m*d, ... |
|
|
競合の回避が有効になっている場合は、 |
|
|
Camel 2.15: デッドレターチャネルでメッセージの処理中に発生する例外を処理するかどうかを指定します。 |
| なし | Apache Camel 2.0: 「Redeliver Delay パターン」 を参照してください。 |
|
|
Apache Camel 2.0: 再配信機能を無効にします。再配信を有効にするには、 |
|
|
Apache Camel 2.0: |
|
| 最初の再配信を試みるまでの遅延 (ミリ秒単位) を指定します。 |
|
| デッドレターチャネルで例外が発生した場合に WARN レベルでログに記録するかどうかを指定します。 |
|
|
Apache Camel 2.0: |
|
| Apache Camel 2.0: 配信の最大試行回数。 |
|
|
Apache Camel 2.0: 指数バックオフストラテジーを使用する場合 ( |
| なし | Apache Camel 2.0: 再配信を試みる前に呼び出されるプロセッサーを設定します。 |
|
| Apache Camel 2.0: 再配信の試行間の遅延 (ミリ秒単位) を指定します。Apache Camel 2.16.0: デフォルトの再配信遅延は 1 秒です。 |
|
|
Apache Camel 2.0: 配信の失敗をログに記録するログレベルを指定します ( |
|
|
Apache Camel 2.0: 再配信の試行に対するログレベルを指定します ( |
|
| 競合の回避を有効にします。これにより、一定のランダム化をバックオフのタイミングに追加して競合の可能性を低減します。 |
|
|
Apache Camel 2.0: この機能が有効な場合、デッドレターチャネルに送信されたメッセージは、ルートの開始時に存在した ( |
|
| 指数バックオフを有効にします。 |
再配信ヘッダー
Apache Camel がメッセージの再配信を試みると、表6.2「デッドレター再配信ヘッダー」 に記載されているヘッダーを In メッセージに自動設定します。
ヘッダー名 | 型 | 説明 |
---|---|---|
|
|
Apache Camel 2.0: 配信に失敗した回数を返します。この値は、 |
|
|
Apache Camel 2.0: 再配信が 1 回以上試行された場合は true です。この値は |
|
|
Apache Camel 2.6: 再配信の最大設定を保持します ( |
再配信エクスチェンジプロパティー
Apache Camel がメッセージの再配信を試みると、表6.3「再配信エクスチェンジプロパティー」 に記載されているエクスチェンジプロパティーを自動設定します。
エクスチェンジプロパティー名 | 型 | 説明 |
---|---|---|
|
|
失敗したルートのルート ID を提供します。このプロパティーのリテラル名は |
元のメッセージの使用
Apache Camel 2.0 で利用可能: エクスチェンジオブジェクトはルートを通過する際に変更される可能性があります。そのため、例外が発生したときに現行であるエクスチェンジがデッドレターチャネルの保存に適したコピーであるとは限りません。多くの場合、ルートによる変換の対象となる前に、ルート開始時に到達したメッセージをログに記録することが推奨されます。たとえば、以下のルートを見てみましょう。
from("jms:queue:order:input") .to("bean:validateOrder"); .to("bean:transformOrder") .to("bean:handleOrder");
上記のルートは受信 JMS メッセージをリッスンした後、validateOrder
、transformOrder
、および handleOrder
の Bean のシーケンスを使用してメッセージを処理します。ただし、エラーが発生した場合にメッセージがどの状態であるかは分かりません。transformOrder
Bean の前または後にエラーが発生しましたか ?以下のように useOriginalMessage
オプションを有効にすると、jms:queue:order:input
からの元のメッセージのログを確実に Dead Letter Chanel に記録することができます。
// will use original body errorHandler(deadLetterChannel("jms:queue:dead") .useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000);
Redeliver Delay パターン
Apache Camel 2.0 で利用可能: delayPattern
オプションは、再配信回数の特定範囲に遅延を指定するために使用されます。遅延パターンの構文: limit1:delay1;limit2:delay2;limit3:delay3;…
。各 delayN は範囲 limitN ⇐ redeliveryCount < limitN+1
で再配信するように適用されます
たとえば、パターン 5:1000;10:5000;20:20000
について考えてみましょう。このパターンでは、3 つのグループが定義され、以下の再配信の遅延が発生します。
- 1 から 4 の試行 = 0 ミリ秒 (最初のグループは 5 で始まるため) 。
- 5 から 9 の試行 = 1000 ミリ秒 (最初のグループ)。
- 10 から 19 の試行 = 5000 ミリ秒 (2 番目のグループ)。
- 20 以上の試行 = 20000 ミリ秒 (最後のグループ)。
制限 1 を加えてグループを開始し、開始遅延を定義できます。たとえば、1:1000;5:5000
では以下の再配信の遅延が発生します。
- 1 から 4 の試行 = 1000 ミリ (最初のグループ)。
- 5 以上の試行 = 5000 ミリ (最後のグループ)。
次の遅延を前の遅延よりも長くする必要はありません。あらゆる遅延値を使用できます。たとえば、Delay パターン 1:5000;3:1000
は 5 秒の遅延で始まり、遅延を 1 秒に減らします。
失敗したエンドポイント
Apache Camel ルートメッセージ時に、エクスチェンジが送信された 最後 のエンドポイントが含まれるエクスチェンジプロパティーを更新します。したがって、以下のコードを使用して、現在のエクスチェンジが最後に送信された宛先の URI を取得できます。
// Java String lastEndpointUri = exchange.getProperty(Exchange.TO_ENDPOINT, String.class);
Exchange.TO_ENDPOINT
は CamelToEndpoint
と同等の文字列の定数になります。このプロパティーは、Camel がメッセージを 任意 のエンドポイントに送信するたびに更新されます。
ルーティング中にエラーが発生し、エクスチェンジがデッドレターキューに移動された場合、Apache Camel は CamelFailureEndpoint
という名前のプロパティーを追加で設定します。これは、エラーが発生する前にエクスチェンジが最後に送信された宛先を特定します。したがって、以下のコードを使用すると、デッドレターキュー内から失敗したエンドポイントにアクセスできます。
// Java String failedEndpointUri = exchange.getProperty(Exchange.FAILURE_ENDPOINT, String.class);
Exchange.FAILURE_ENDPOINT
は、CamelFailureEndpoint
と同等の文字列定数です。
これらのプロパティーは、指定の宛先エンドポイントの処理が完了した後に障害が発生した場合でも、現在のエクスチェンジで設定された状態を維持します。たとえば、以下のルートを見てみましょう。
from("activemq:queue:foo") .to("http://someserver/somepath") .beanRef("foo");
foo
Bean で障害が発生したと仮定します。この場合、Exchange.TO_ENDPOINT
プロパティーと Exchange.FAILURE_ENDPOINT
プロパティーに値が含まれ続けます。
onRedelivery プロセッサー
Dead Letter Channel が再配信を実行する場合、再配信を試みる 直前 に実行される Processor
を設定できます。これは、メッセージを再配信する前に変更する必要がある場合に使用できます。
たとえば、以下の Dead Letter Channel は、エクスチェンジの再配信前に MyRedeliverProcessor
を呼び出すように設定されます。
// we configure our Dead Letter Channel to invoke // MyRedeliveryProcessor before a redelivery is // attempted. This allows us to alter the message before errorHandler(deadLetterChannel("mock:error").maximumRedeliveries(5) .onRedelivery(new MyRedeliverProcessor()) // setting delay to zero is just to make unit teting faster .redeliveryDelay(0L));
ここで MyRedeliveryProcessor
プロセスは以下のように実装されます。
// This is our processor that is executed before every redelivery attempt
// here we can do what we want in the java code, such as altering the message
public class MyRedeliverProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
// the message is being redelivered so we can alter it
// we just append the redelivery counter to the body
// you can of course do all kind of stuff instead
String body = exchange.getIn().getBody(String.class);
int count = exchange.getIn().getHeader(Exchange.REDELIVERY_COUNTER, Integer.class);
exchange.getIn().setBody(body + count);
// the maximum redelivery was set to 5
int max = exchange.getIn().getHeader(Exchange.REDELIVERY_MAX_COUNTER, Integer.class);
assertEquals(5, max);
}
}
シャットダウンまたは停止中の再配信の制御
ルートを停止したり、正常なシャットダウンを開始する場合、再配信の試行を継続するのがエラー処理のデフォルトの挙動になります。通常、これは望ましい動作ではないため、以下の例のように、allowRedeliveryWhileStopping
オプションを false
に設定すると、シャットダウンまたは停止中に再配信を無効にすることができます。
errorHandler(deadLetterChannel("jms:queue:dead")
.allowRedeliveryWhileStopping(false)
.maximumRedeliveries(20)
.redeliveryDelay(1000)
.retryAttemptedLogLevel(LoggingLevel.INFO));
後方互換性の理由から、allowRedelivery whileStopping
オプションはデフォルトで true
になります。ただし、強行なシャットダウン中は、このオプションの設定に関係なく、再配信が常に抑制されます (たとえば、正常なシャットダウンがタイムアウトした場合など)。
onExceptionOccurred プロセッサーの使用
Dead Letter Channel は、例外発生後にメッセージのカスタム処理を可能にする onExceptionOccurred プロセッサーをサポートします。これは、カスタムロギングにも使用できます。onExceptionOccurred プロセッサーから出力される新しい例外は WARN としてログに記録され、無視されます。既存の例外を上書きすることはありません。
onRedelivery プロセッサーと onExceptionOccurred プロセッサーの違いは、onRedelivery プロセッサーは再配信の試行直前に処理できることです。ただし、例外の発生直後には処理できません。たとえば、再配信を試行する間隔で 5 秒の遅延が発生するようにエラーハンドラーを設定すると、再配信プロセスは例外発生から 5 秒後に呼び出されます。
以下の例は、例外発生時にカスタムロギングを実行する方法を示しています。onExceptionOccurred がカスタムプロセッサーを使用するように設定する必要があります。
errorHandler(defaultErrorHandler().maximumRedeliveries(3).redeliveryDelay(5000).onExceptionOccurred(myProcessor));
onException 句
ルートビルダーで errorHandler()
インターセプターを使用する代わりに、さまざまな例外タイプに異なる再配信ポリシーとデッドレターチャネルを定義する、一連の onException()
句を定義できます。たとえば、NullPointerException
、IOException
、Exception
タイプごとに異なる動作を定義するには、Java DSL を使用してルートビルダーに以下のルールを定義できます。
onException(NullPointerException.class) .maximumRedeliveries(1) .setHeader("messageInfo", "Oh dear! An NPE.") .to("mock:npe_error"); onException(IOException.class) .initialRedeliveryDelay(5000L) .maximumRedeliveries(3) .backOffMultiplier(1.0) .useExponentialBackOff() .setHeader("messageInfo", "Oh dear! Some kind of I/O exception.") .to("mock:io_error"); onException(Exception.class) .initialRedeliveryDelay(1000L) .maximumRedeliveries(2) .setHeader("messageInfo", "Oh dear! An exception.") .to("mock:error"); from("seda:a").to("seda:b");
再配信オプションは、再配信ポリシーメソッドをチェーンして指定されます (表6.1「再配信ポリシーの設定」 のように)。また、to()
DSL コマンドを使用して Dea