Apache Camel 開発者ガイド

Red Hat Fuse 7.8

Apache Camel を使用したアプリケーションの開発

Red Hat Fuse Documentation Team

概要

本ガイドでは、Apache Camel で Red Hat Fuse アプリケーションを開発する方法を説明します。ここでは、基本的なビルディングブロック、エンタープライズ統合パターン、ルーティング式および述語言語の基本的な構文、Apache CXF コンポーネントを使った Web サービスの作成、Apache Camel API の使用、Java API をラップする Camel コンポーネントの作成方法について説明します。

パート I. エンタープライズ統合パターンの実装

ここでは、Apache Camel を使用してルートを構築する方法を説明します。基本的なビルディングブロックおよび EIP コンポーネントをカバーします。

第1章 ルート定義のためのビルディングブロック

概要

Apache Camel はルートを定義するために、Java DSL と Spring XML DSL の 2 つの ドメイン固有言語 (DSL) をサポートします。ルートを定義するための基本的なビルディングブロックは エンドポイント および プロセッサー で、プロセッサーの動作は通常 または論理 述語 によって変更されます。Apache Camel は、さまざまな言語を使用して式や述語を定義できます。

1.1. RouteBuilder クラスの実装

概要

ドメイン固有言語 (DSL) を使用するには、RouteBuilder クラスを拡張し、configure() メソッド (ここにルーティングルールを定義します) を上書きします。

必要に応じて RouteBuilder クラスをいくつでも定義できます。各クラスは 1 度インスタンス化され、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 で RouteBuilder クラスをフィルターするために contextScan を使う場合、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 ローカルルーティングルール

Local routing rules

ローカルルールは、常に from("EndpointURL") メソッドで開始します。ここで、ルーティングルールで使われるメッセージのソース (コンシューマーエンドポイント) を指定します。その後、ルールに任意の長いプロセッサーを追加できます (例: filter())。通常、to("EndpointURL") メソッドでルールを終了します。ここで、ルールを通過したメッセージのターゲット (プロデューサーエンドポイント) を指定します。ただし、いつも必ず 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_EXCHANGEExchangeオブジェクトを格納した 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 ファイルのルートディレクトリーからスキーマのコピーを自動的に取得します。これにより、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&amp;
           recursive=true&amp;ftpClient.dataTimeout=30000&amp;
           ftpClientConfig.serverLanguageCode=fr"/>
  <to uri="bean:doSomething"/>
</route>
注記

&amp; で区切ると、各行に 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 オプションは、INFOWARNERROR などの 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 である ifmyvelocityscriptbeanmysimplescriptbean は、以下のように参照することで、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 プロセッサー」 に示されるように、さまざまなプロセッサーを提供します。

表1.1 Apache Camel プロセッサー

Java DSLXML DSL説明

aggregate()

aggregate

「Aggregator」: 複数の受信エクスチェンジを単一のエクスチェンジに組み合わせるアグリゲーターを作成します。

aop()

aop

アスペクト指向プログラミング (AOP) を使用して、指定されたサブルートの前後で作業を行います。

bean(), beanRef()

bean

Java オブジェクト (または Bean) でメソッドを呼び出して、現在のエクスチェンジを処理します。「Bean インテグレーション」 を参照してください。

choice()

choice

「Content-Based Router」: whenotherwise 句を使い、エクスチェンジの内容に基づいて特定のサブルートを選択します。

convertBodyTo()

convertBodyTo

In メッセージボディーを、指定された型に変換します。

delay()

delay

「Delayer」: ルートの後続へエクスチェンジを伝搬するのを遅延します。

doTry()

doTry

doCatchdoFinallyend 句を使い、例外を処理するための try/catch ブロックを作成します。

end()

該当なし

現在のコマンドブロックを終了します。

enrich(), enrichRef()

enrich

「Content Enricher」: 現在のエクスチェンジと、指定された プロデューサー エンドポイント URI からリクエストされたデータを統合します。

filter()

filter

「Message Filter」: 述語式を使用して受信エクスチェンジをフィルタリングします。

idempotentConsumer()

idempotentConsumer

「Idempotent Consumer」: 重複メッセージを抑制するストラテジーを実装します。

inheritErrorHandler()

@inheritErrorHandler

特定のルートノードで継承されたエラーハンドラーを無効にするために使用できるブール値オプション (Java DSL でサブ句として定義され、XML DSL の属性として定義) 。

inOnly()

inOnly

現在のエクスチェンジの MEP を InOnly (引数がない場合) に設定するか、指定されたエンドポイントへエクスチェンジを InOnly として送信します。

inOut()

inOut

現在のエクスチェンジの MEP を InOut (引数がない場合) に設定するか、指定されたエンドポイントへエクスチェンジを InOut として送信します。

loadBalance()

loadBalance

「Load Balancer」: エンドポイントのコレクションに対する負荷分散を実装します。

log()

log

コンソールにメッセージを記録します。

loop()

loop

「Loop」: 各エクスチェンジをルートの後続に繰り返し再送信します。

markRollbackOnly()

@markRollbackOnly

(トランザクション) 現在のトランザクションをロールバックオンリーにマークします (例外は発生しません)。XML DSL では、このオプションは rollback 要素にブール値属性として設定されます。『Apache Karaf Transaction Guide』を参照してください。

markRollbackOnlyLast()

@markRollbackOnlyLast

(トランザクション) 1 つ以上のトランザクションがこのスレッドに関連付けられてから一時停止されている場合、このコマンドは最新のトランザクションをロールバックオンリーにマークします (例外は発生しません)。XML DSL では、このオプションは rollback 要素にブール値属性として設定されます。『Apache Karaf Transaction Guide』を参照してください。

marshal()

marshal

指定されたデータフォーマットを使用して低レベルまたはバイナリーフォーマットに変換し、特定のトランスポートプロトコルで送信できるようにします。

multicast()

multicast

「Multicast」: 現在のエクスチェンジを複数の宛先にマルチキャストし、各宛先がエクスチェンジの独自のコピーを取得します。

onCompletion()

onCompletion

メインルートの完了後に実行されるサブルート (Java DSL end() で終了) を定義します。「OnCompletion」も参照してください。

onException()

onException

指定された例外が発生するたびに実行されるサブルート (Java DSL end() で終了) を定義します。通常、ルート内ではなく、専用の行で定義されます。

pipeline()

pipeline

「パイプとフィルター」: あるエンドポイントの出力が次のエンドポイントの入力となるように、一連のエンドポイントにエクスチェンジを送ります。「パイプライン処理」も参照してください。

policy()

policy

現在のルートにポリシーを適用します (現時点ではトランザクションポリシーにのみ使用されます)。『Apache Karaf Transaction Guide』を参照してください。

pollEnrich(),pollEnrichRef()

pollEnrich

「Content Enricher」: 現在のエクスチェンジと、指定された コンシューマー エンドポイント URI からポーリングされたデータを統合します。

process(),processRef

process

現在のエクスチェンジでカスタムプロセッサーを実行します。「カスタムプロセッサー」パートIII「高度な Camel プログラミング」 を参照してください。

recipientList()

recipientList

「Recipient List」: エクスチェンジを、実行時に算出される (たとえば、ヘッダーの内容に基づいて) 受信者のリストに送信します。

removeHeader()

removeHeader

指定したヘッダーをエクスチェンジの In メッセージから削除します。

removeHeaders()

removeHeaders

指定したパターンに一致するヘッダーをエクスチェンジの In メッセージから削除します。パターンにはフォームを持たせることができます。prefix\*  を使用した場合、接頭辞で始まるすべての名前と一致します。それ以外の場合、正規表現として解釈されます。

removeProperty()

removeProperty

エクスチェンジから、指定したエクスチェンジプロパティーを削除します。

removeProperties()

removeProperties

指定したパターンに一致するプロパティーをエクスチェンジから削除します。カンマで区切られた 1 つ以上の文字列のリストを引数として取ります。最初の文字列はパターンです (上記の removeHeaders() を参照) 。後続の文字列は例外を指定します。これらのプロパティーは削除されません。

resequence()

resequence

「Resequencer」: 指定されたコンパレータの動作に基づき、受信エクスチェンジの順序を変更します。バッチ モードと ストリーム モードをサポートします。

rollback()

rollback

(トランザクション) 現在のトランザクションをロールバックオンリーにマークします (デフォルトでは例外も発生)。『Apache Karaf Transaction Guide』を参照してください。

routingSlip()

routingSlip

「Routing Slip」: 任意のヘッダーから抽出したエンドポイント URI のリストに基づいて動的に構築されたパイプラインで、エクスチェンジをルーティングします。

sample()

sample

サンプリングスロットラーを作成し、ルート上のトラフィックからエクスチェンジのサンプルを抽出できるようにします。

setBody()

setBody

エクスチェンジの In メッセージのメッセージボディーを設定します。

setExchangePattern()

setExchangePattern

現在のエクスチェンジの MEP を指定された値に設定します。「メッセージ交換パターン」 を参照してください。

setHeader()

setHeader

エクスチェンジの In メッセージに指定したヘッダーを設定します。

setOutHeader()

setOutHeader

エクスチェンジの Out メッセージに指定したヘッダーを設定します。

setProperty()

setProperty()

指定したエクスチェンジプロパティーを設定します。

sort()

sort

In メッセージボディーの内容を並べ替えます (カスタムコンパレーターをオプションで指定できます) 。

split()

split

「Splitter」: 現在のエクスチェンジを一連のエクスチェンジに分割します。分割された各エクスチェンジには元のメッセージボディーの断片が含まれます。

stop()

stop

現在のエクスチェンジのルーティングを停止し、完了したとマークします。

threads()

threads

ルートの後続部分を並列に処理するためにスレッドプールを作成します。

throttle()

throttle

「Throttler」: フローレートを指定レベルに制限します (1 秒あたりのエクスチェンジ数) 。

throwException()

throwException

指定された Java 例外をスローします。

to()

to

エクスチェンジを 1 つ以上のエンドポイントに送信します。「パイプライン処理」 を参照してください。

toF()

該当なし

文字列フォーマットを使用して、エクスチェンジをエンドポイントに送信します。つまり、エンドポイント URI 文字列は C 言語の printf() 関数の形式に置換し、埋め込むことができます。

transacted()

transacted

ルートの後続部分を囲む Spring トランザクションスコープを作成します。『Apache Karaf Transaction Guide』を参照してください。

transform()

transform

「メッセージトランスレーター」: In メッセージヘッダーを Out メッセージヘッダーにコピーし、Out メッセージボディーを指定された値に設定します。

unmarshal()

unmarshal

指定したデータフォーマットを使用して、In メッセージボディーを低レベルまたはバイナリー形式から高レベル形式に変換します。

validate()

validate

述語式を取り、現在のメッセージが有効かどうかを検証します。述語が false を返す場合、PredicateValidationException 例外をスローします。

wireTap()

wireTap

「Wire Tap」: ExchangePattern.InOnly MEP を使用して、現在のエクスチェンジのコピーを指定された Wire tap URI に送信します。

サンプルプロセッサー

ルートでプロセッサーを使用する方法をある程度理解するには、以下の例を参照してください。

Choice

choice() プロセッサーは、受信メッセージを別のプロデューサーエンドポイントにルーティングするために使用される条件文です。代替となる各プロデューサーエンドポイントの前には、述語の引数を取る when() メソッドがあります。述語が true の場合、後続のターゲットが選択されます。そうでない場合、ルール内の次の when() メソッドに処理が進みます。たとえば、以下の choice() プロセッサーは Predicate1 および Predicate2 の値に応じて、受信メッセージを Target1Target2、または 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

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 メッセージを変更するプロセッサー

Processor modifying an in message

以下のルートは、BillingSystem ヘッダーを追加 (または変更) して現在の In メッセージを修正する setHeader() コマンドを示しています。

from("activemq:orderQueue")
    .setHeader("BillingSystem", xpath("/order/billingSystem"))
    .to("activemq:billingQueue");

2 つ目のアプローチは、図2.2「Out メッセージを作成するプロセッサー」 にあるように、プロセッサーが処理の結果として Out メッセージを作成する方法です。

図2.2 Out メッセージを作成するプロセッサー

Processor creating an out message

以下のルートは、文字列 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 エクスチェンジのサンプルパイプライン

sample pipeline for InOnly exchanges

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 エクスチェンジのサンプルパイプライン

Sample pipeline for InOut exchanges

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:addAccountDetailscxf:bean:getCreditRatingcxf: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 エクスチェンジのサンプルパイプライン」 のパイプラインと基本的に同じです。InOutInOptionalOut の相違点は、 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");

これらの両方の例で、各入力エンドポイント URI1URI2 、および URI3 からのエクスチェンジは、相互に独立に、別個のスレッドで処理されます。実際、上記のルートは以下の 3 つに分かれたルートと同等であると考えることができます。

from("URI1").to("DestinationUri");
from("URI2").to("DestinationUri");
from("URI3").to("DestinationUri");

セグメント化されたルート

たとえば、2 つの異なるメッセージングシステムからの受信メッセージをマージし、同じルートを使用して処理したい場合があります。ほとんどの場合、 図2.5「セグメント化されたルートによる複数入力の処理」 に示されるように、ルートをセグメントに分割して複数の入力に対応できます。

図2.5 セグメント化されたルートによる複数入力の処理

Processing multiple inputs with segmented routes

ルートの最初のセグメントは、たとえば activemq:Nyseactivemq:Nasdaq といった外部キューから入力を取得し、その受信エクスチェンジを内部エンドポイント InternalUrl に送信します。2 つ目のルートセグメントは、それらを内部エンドポイントから取得し、宛先キュー activemq:USTxn に送信することで、受信エクスチェンジをマージします。InternalUrl は、ルーターのアプリケーション でのみ使用することが意図されたエンドポイントの URL です。以下のタイプのエンドポイントが内部使用に適しています。

これらのエンドポイントの主な目的は、ルートの異なるセグメントをまとめることにあります。これらはすべて、複数の入力を単一のルートにマージする効果的な方法を提供します。

Direct エンドポイント

direct コンポーネントは、複数のルートを繋ぎ合わせる最も簡単なメカニズムを提供します。direct コンポーネントのイベントモデルは 同期型 であり、ルートの後続のセグメントは最初のセグメントと同じスレッドで実行されます。direct URL の一般的な形式は direct:EndpointID です。エンドポイント ID である EndpointID は、エンドポイントのインスタンスを識別する一意な英数字の文字列です。

たとえば、2 つのメッセージキュー activemq:Nyseactivemq:Nasdaq から入力を受け取り、それらを単一のメッセージキュー activemq:USTxn にマージする場合、以下のルートセットを定義することで実行できます。

from("activemq:Nyse").to("direct:mergeTxns");
from("activemq:Nasdaq").to("direct:mergeTxns");

from("direct:mergeTxns").to("activemq:USTxn");

最初の 2 つのルートはメッセージキュー NyseNasdaq から入力を受け取り、それらをエンドポイント 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 パッケージにある標準の集約ストラテジーの1つで、新規のエクスチェンジを java.util.List インスタンスに追加し、その結果のリストを Exchange.GROUPED_EXCHANGE エクスチェンジプロパティーに保存します。この場合、リストには元のエクスチェンジ ( creditRequests JMS キューからの) と Enricher エクスチェンジ ( file エンドポイントからの) の 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 はいくつかの異なるメカニズムを提供しており、異なるレベルの粒度で例外を処理することができます。まず、doTrydoCatchdoFinally を使用してルート内で例外を処理できます。また、 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>

複数の例外のトラップ

RouteBuilder のスコープ内で複数の onException 句を定義して例外をトラップすることができます。これにより、例外に応じて異なるアクションを実行できます。たとえば、以下の Java DSL で定義された一連の onException 句は ValidationExceptionIOException、および 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 句が試行され、一致するものが見つかるまで続きます。各々の一致するかどうかの試行は、以下のアルゴリズムで制御されます。

  1. 発生する例外が チェーン例外 (例外がキャッチされて別の例外としてスローされたもの) である場合、最もネストされた例外型が最初に一致の基準となります。この例外は、以下のようにテストされます。

    1. テスト対象例外が正確に onException 句で指定された型を持っている場合 (instanceof によってテストされる) は、一致が起こります。
    2. テスト対象例外がonException 句で指定された型の部分型である場合、一致が起こります。
  2. 最もネストされた例外が一致しなかった場合、チェーンの次の例外 (ラップしている例外) がテストされます。このテストは、一致が起こるかチェーンの最後に到達するまで継続します。
注記

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 スコープ - 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「エラーハンドラーの種類」 では、定義可能なさまざまな種類のエラーハンドラーの概要を説明します。

表2.1 エラーハンドラーの種類

Java DSL ビルダーXML DSL Type 属性説明

defaultErrorHandler()

DefaultErrorHandler

例外を呼び出し元に戻し、再配信ポリシーをサポートしますが、デッドレターキューはサポートされません。

deadLetterChannel()

DeadLetterChannel

デフォルトのエラーハンドラーと同じ機能をサポートし、さらにデッドレターキューもサポートします。

loggingErrorChannel()

LoggingErrorChannel

例外が発生するたびに例外テキストをログに記録します。

noErrorHandler()

NoErrorHandler

エラーハンドラーを無効にするために使用できるダミーのハンドラー実装。

 

TransactionErrorHandler

トランザクションが有効化されたルートのエラーハンドラー。トランザクションが有効化されたルートでは、デフォルトのトランザクションエラーハンドラーインスタンスが自動的に使用されます。

2.3.3. doTry、doCatch、および doFinally

概要

ルート内で例外を処理するには、Java の trycatch、および finally ブロックと同様の方法で例外を処理する、doTrydoCatch、および doFinally 句の組み合わせを使用できます。

doCatch と Java における catch の類似点

通常、ルート定義内の doCatch() 句は、Java コードの catch() 文と同様の動作をします。具体的には、以下の機能が doCatch() 句でサポートされています。

  • 複数の doCatch 句 -  1 つの doTry ブロック内に複数の doCatch 句を持たせることができます。この doCatch 句は、Java の catch() 文と同様に、登場する順序でテストされます。Apache Camel は、発生した例外に一致した最初の doCatch 句を実行します。

    注記

    このアルゴリズムは、 onException 句で使用される例外一致アルゴリズムとは異なります。詳細は 「onException 句」 を参照してください。

  • 例外の再スロー  - handled サブ句を使用すると、 doCatch 句から現在の例外を再スローできます (「doCatch での例外の再スロー」 を参照)。

doCatch の特別機能

ただし、 doCatch() 句には Java の catch() 文に類似するものがない特別な機能があります。以下の機能は、doCatch() に固有のものです。

以下の例は、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() 句の中で handled() サブ句の引数を false に設定して呼び出すと、例外を再スローできます。

from("direct:start")
    .doTry()
        .process(new ProcessorFail())
        .to("mock:result")
    .doCatch(IOException.class)
        // mark this as NOT handled, eg the caller will also get the exception
        .handled(false)
        .to("mock:io")
    .doCatch(Exception.class)
        // and catch all other exceptions
        .to("mock:error")
    .end();

上記の例では、IOExceptiondoCatch() にキャッチされると、現在のエクスチェンジが mock:io エンドポイントに送信され、その後に IOException が再スローされます。これにより、ルートの先頭 (from() コマンド) にあるコンシューマーエンドポイントにも例外を処理する機会が与えられます。

以下の例では、Spring XML で同じルートを定義する方法を示しています。

<route>
    <from uri="direct:start"/>
    <doTry>
        <process ref="processorFail"/>
        <to uri="mock:result"/>
        <doCatch>
            <exception>java.io.IOException</exception>
            <!-- mark this as NOT handled, eg the caller will also get the exception -->
            <handled>
                <constant>false</constant>
            </handled>
            <to uri="mock:io"/>
        </doCatch>
        <doCatch>
            <!-- and catch all other exceptions they are handled by default (ie handled = true) -->
            <exception>java.lang.Exception</exception>
            <to uri="mock:error"/>
        </doCatch>
    </doTry>
</route>

onWhen による条件付き例外キャッチ

Apache Camel の doCatch() 句の特別な機能として、実行時に評価される式に基づいて例外のキャッチを条件付けすることができます。つまり、doCatch(ExceptionList).doWhen(Expression) の形式の句を使用して例外をキャッチした場合、述語の式 Expression が実行時に true に評価された場合にのみ例外がキャッチされます。

たとえば、以下の doTry ブロックは、例外メッセージが単語 Severe を含む場合にのみ例外 IOExceptionIllegalStateException をキャッチします。

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 メッセージを送受信できます。You can easily define Apache Camel endpoints in XML, which can then be referenced in a route using the endpoint’s bean ID.詳細は、『Apache Camel Component Reference Guide』の「CXF」を参照してください。

スタックトレース情報を伝播させる方法

Java の例外がサーバー側で発生したときに、例外のスタックトレースがフォールトメッセージにマーシャリングされてクライアントに返されるように、CXF エンドポイントを設定することができます。この機能を有効にするには、以下のように dataFormatPAYLOAD に設定し、 cxfEndpoint 要素の 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 レジストリーと統合させることが可能です。

表2.2 レジストリープラグイン

レジストリー実装レジストリープラグインのある Camel コンポーネント

Spring Bean レジストリー

camel-spring

Guice Bean レジストリー

camel-guice

Blueprint Bean レジストリー

camel-blueprint

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 オブジェクトまたは 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 クラスにオーバーロードされたメソッドが processBody(String)processBody(String,String) の 2 つある場合、以下のようにオーバーロードされたメソッドを呼び出すことができます。

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
  • 数字: 1237 など
  • 文字列: '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 が myBeanIdbean 要素がある場合は、以下のように 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 が myBeanIdbean 要素がある場合は、以下のように 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 の場合、通常、正しいシャットダウンの順序は次のようになります。

  1. camelContext インスタンスをシャットダウンします。
  2. 使用された 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 パッケージのアノテーションを示しています。

表2.3 基本の Bean アノテーション

アノテーション意味パラメーター

@Attachments

アタッチメントのリストにバインドします。

 

@Body

インバウンドメッセージのボディーにバインドします。

 

@Header

インバウンドメッセージのヘッダーにバインドします。

ヘッダーの文字列名。

@Headers

インバウンドメッセージヘッダー全体の java.util.Map にバインドします。

 

@OutHeaders

アウトバウンドメッセージヘッダー全体の java.util.Map にバインドします。

 

@Property

名前のあるエクスチェンジプロパティーにバインドします。

プロパティーの文字列名。

@Properties

エクスチェンジプロパティー全体の java.util.Map にバインドします。

 

たとえば、以下のクラスは基本アノテーションを使用してメッセージデータを 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 パッケージ (およびコア以外のアノテーションのサブパッケージ) のアノテーションを示しています。

表2.4 式言語アノテーション

アノテーション説明

@Bean

Bean 式を注入します。

@Constant

Constant 式を注入します。

@EL

EL 式を注入します。

@Groovy

Groovy 式を注入します。

@Header

ヘッダー式を注入します。

@JavaScript

JavaScript 式を注入します。

@OGNL

OGNL 式を注入します。

@PHP

PHP 式を注入します。

@Python

Python 式を注入します。

@Ruby

Ruby 式を注入します。

@Simple

Simple 式を注入します。

@XPath

XPath 式を注入します。

@XQuery

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 インターフェースを実装したクラスが protectedprivate または package-only スコープであることがあります。このように制限された実装クラスのメソッドを呼び出す場合、Bean バインディングはフォールバックして、公開アクセス可能な対応するインターフェースメソッドを呼び出します。

たとえば、以下の public な 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 のメソッドを示しています。

表2.5 ProcessorDefinition クラスの変換メソッド

メソッド説明

Type convertBodyTo(Class type)

IN メッセージのボディーを指定の型に変換します。

Type removeFaultHeader(String name)

FAULT メッセージのヘッダーを削除するプロセッサーを追加します。

Type removeHeader(String name)

IN メッセージ上のヘッダーを削除するプロセッサーを追加します。

Type removeProperty(String name)

エクスチェンジプロパティーを削除するプロセッサーを追加します。

ExpressionClause<ProcessorDefinition<Type>> setBody()

IN メッセージのボディーをセットするプロセッサーを追加します。

Type setFaultBody(Expression expression)

FAULT メッセージのボディーをセットするプロセッサーを追加します。

Type setFaultHeader(String name, Expression expression)

FAULT メッセージにヘッダーをセットするプロセッサーを追加します。

ExpressionClause<ProcessorDefinition<Type>> setHeader(String name)

IN メッセージにヘッダーをセットするプロセッサーを追加します。

Type setHeader(String name, Expression expression)

IN メッセージにヘッダーをセットするプロセッサーを追加します。

ExpressionClause<ProcessorDefinition<Type>> setOutHeader(String name)

OUT メッセージにヘッダーをセットするプロセッサーを追加します。

Type setOutHeader(String name, Expression expression)

OUT メッセージにヘッダーをセットするプロセッサーを追加します。

ExpressionClause<ProcessorDefinition<Type>> setProperty(String name)

エクスチェンジプロパティーをセットするプロセッサーを追加します。

Type setProperty(String name, Expression expression)

エクスチェンジプロパティーをセットするプロセッサーを追加します。

ExpressionClause<ProcessorDefinition<Type>> transform()

OUT メッセージのボディーをセットするプロセッサーを追加します。

Type transform(Expression expression)

OUT メッセージのボディーをセットするプロセッサーを追加します。

Builder クラス

org.apache.camel.builder.Builder クラスは式または述語が想定される文脈でのメッセージコンテンツへのアクセスを提供します。つまり、Builder のメソッドは通常 DSL コマンドの 引数 の中で呼び出されます (例: 例2.1「受信メッセージのシンプルな変換」body() コマンド)。表2.6「Builder クラスのメソッド」 では、Builder クラスで利用可能な static メソッドをまとめています。

表2.6 Builder クラスのメソッド

メソッド説明

static <E extends Exchange> ValueBuilder<E> body()

エクスチェンジのインバウンドボディーに対する述語および値ビルダーを返します。

static <E extends Exchange,T> ValueBuilder<E> bodyAs(Class<T> type)

インバウンドメッセージのボディーを特定の型として、それに対する述語および値ビルダーを返します。

static <E extends Exchange> ValueBuilder<E> constant(Object value)

定数式を返します。

static <E extends Exchange> ValueBuilder<E> faultBody()

エクスチェンジのフォールトボディーに対する述語および値ビルダーを返します。

static <E extends Exchange,T> ValueBuilder<E> faultBodyAs(Class<T> type)

フォールトメッセージのボディーを特定の型として、それに対する述語および値ビルダーを返します。

static <E extends Exchange> ValueBuilder<E> header(String name)

エクスチェンジのヘッダーに対する述語および値ビルダーを返します。

static <E extends Exchange> ValueBuilder<E> outBody()

エクスチェンジのアウトバウンドボディーに対する述語および値ビルダーを返します。

static <E extends Exchange> ValueBuilder<E> outBodyAs(Class<T> type)

アウトバウンドメッセージのボディーを特定の型として、それに対する述語および値ビルダーを返します。

static ValueBuilder property(String name)

エクスチェンジのプロパティーに対する述語および値ビルダーを返します。

static ValueBuilder regexReplaceAll(Expression content, String regex, Expression replacement)

正規表現のすべての出現箇所を、指定した置換文字列で置き換える式を返します。

static ValueBuilder regexReplaceAll(Expression content, String regex, String replacement)

正規表現のすべての出現箇所を、指定した置換文字列で置き換える式を返します。

static ValueBuilder sendTo(String uri)

指定したエンドポイント URI にエクスチェンジを送信する式を返します。

static <E extends Exchange> ValueBuilder<E> systemProperty(String name)

指定のシステムプロパティーの式を返します。

static <E extends Exchange> ValueBuilder<E> systemProperty(String name, String defaultValue)

指定のシステムプロパティーの式を返します。

ValueBuilder クラス

org.apache.camel.builder.ValueBuilder クラスでは、 Builder のメソッドによって返された値を変更できます。つまり、ValueBuilder のメソッドは、メッセージコンテンツを変更するシンプルな方法を提供します。表2.7「ValueBuilder クラスの変更メソッド」 では、ValueBuilder クラスで利用可能なメソッドをまとめています。この表では、呼び出された値を変更するために使用されるメソッドのみが示されています (詳細は API Reference ドキュメントを参照してください) 。

表2.7 ValueBuilder クラスの変更メソッド

メソッド説明

ValueBuilder<E> append(Object value)

指定された値をこの式の文字列評価に追加します。

Predicate contains(Object value)

左辺の式に右辺の式の値が含まれた述語を作成します。

ValueBuilder<E> convertTo(Class type)

登録された型コンバーターを使用して、現在の値を指定の型に変換します。

ValueBuilder<E> convertToString()

登録された型コンバーターを使用して、現在の値を String に変換します。

Predicate endsWith(Object value)

 

<T> T evaluate(Exchange exchange, Class<T> type)

 

Predicate in(Object… values)

 

Predicate in(Predicate… predicates)

 

Predicate isEqualTo(Object value)

現在の値が指定の value 引数と等しい場合、 true を返します。

Predicate isGreaterThan(Object value)

現在の値が指定の value 引数よりも大きい場合、 true を返します。

Predicate isGreaterThanOrEqualTo(Object value)

現在の値が指定の value 引数以上である場合、 true を返します。

Predicate isInstanceOf(Class type)

現在の値が指定の型のインスタンスである場合、true を返します。

Predicate isLessThan(Object value)

現在の値が指定の value 引数未満の場合、true を返します。

Predicate isLessThanOrEqualTo(Object value)

現在の値が指定の value 引数以下である場合、true を返します。

Predicate isNotEqualTo(Object value)

現在の値が指定の value 引数と等しくない場合、true を返します。

Predicate isNotNull()

現在の値が null でない場合、true を返します。

Predicate isNull()

現在の値が null の場合、true を返します。

Predicate matches(Expression expression)

 

Predicate not(Predicate predicate)

述語の引数を否定にします。

ValueBuilder prepend(Object value)

この式の文字列評価を指定された値に追加します。

Predicate regex(String regex)

 

ValueBuilder<E> regexReplaceAll(String regex, Expression<E> replacement)

正規表現のすべての出現箇所を、指定した置換文字列で置き換えます。

ValueBuilder<E> regexReplaceAll(String regex, String replacement)

正規表現のすべての出現箇所を、指定した置換文字列で置き換えます。

ValueBuilder<E> regexTokenize(String regex)

指定の正規表現を使用してこの式の文字列変換をトークン化します。

ValueBuilder sort(Comparator comparator)

指定されたコンパレーターを使用して現在の値をソートします。

Predicate startsWith(Object value)

現在の値が value 引数の文字列値と一致する場合、true を返します。

ValueBuilder<E> tokenize()

カンマのトークン区切り文字を使用してこの式の文字列変換をトークン化します。

ValueBuilder<E> tokenize(String token)

指定のトークン区切り文字を使用してこの式の文字列変換をトークン化します。

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 に加えて、独自のカスタムバインディングを実装することができます。カスタムバインディングを以下のように定義します。

  1. org.apache.camel.Processor クラスを実装して、コンシューマーエンドポイント (from 要素に登場) で受信するメッセージに対して変換を行います。
  2. 補完関係となる org.apache.camel.Processor クラスを実装して、プロデューサーエンドポイント (to 要素に登場) から送信されるメッセージに対して逆変換を行います。
  3. 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 サーバーに送信します。ホストとポートはプレースホルダーの {{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 文字セットによる実装になります。

Apache Camel の PropertyPlaceholders では以下をサポートします。

  • 検索するキーと共にデフォルト値を指定する。
  • すべてのプレースホルダーキーがデフォルト値で構成されていて、それらが使用される場合、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 ファイルを無視させる場合は、ignoreMissingLocation オプションを true に設定します (通常、.properties ファイルが見つからない場合はエラーが発生します)。

また、Java システムプロパティーまたは O/S 環境変数を使用して指定されたロケーションが見つからないときに Properties コンポーネントが無視するようにする場合にも、ignoreMissingLocation オプションを true に設定します。

プレースホルダー構文

Properties コンポーネントは、設定後は (適切なコンテキストで) プレースホルダーを自動的に置き換えます。プレースホルダーの構文は、以下のようにコンテキストによって異なります。

  • エンドポイント URI および Spring XML ファイル: プレースホルダーは {{Key}} のように指定します。
  • XML DSL の属性設定時: xs:string 属性は以下の構文で設定します。

    AttributeName="{{Key}}"

    その他の属性型 (例: xs:intxs:boolean) は、以下の構文で設定する必要があります。

    prop:AttributeName="Key"

    prophttp://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:intxs:boolean) については、特別な構文 prop:AttributeName="Key" を使用する必要があります。

たとえば、プロパティーファイルに値 true を持つ stop.flag プロパティーが定義されているとすると、このプロパティーを使用して以下のように 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 オプションの設定ができます。

たとえば、プロパティーファイルに値 true を持つ stop.flag プロパティーが定義されているとすると、このプロパティーを使用して以下のように 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 プロパティープレースホルダーのメカニズムを検索することで解決されます。

たとえば、 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 プロパティープレースホルダーのメカニズムと統合できます。

Spring XML ファイルにある Apache Camel の propertyPlaceholder 要素と Spring の ctx:property-placeholder 要素の両方を置き換えるには、BridgePropertyPlaceholderConfigurer を定義します。その後、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>
注記

別の方法として、BridgePropertyPlaceholderConfigurerlocation 属性を設定して Spring プロパティーファイルを指定することもできます。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 に組み込まれているスレッド対応のプロセッサーでスレッドプールを制御および設定するためのさまざまなオプションをまとめています。

表2.8 プロセッサーのマルチスレッドオプション

プロセッサーJava DSLXML DSL

aggregate

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

multicast

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

recipientList

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

split

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

threads

executorService()
executorServiceRef()
poolSize()
maxPoolSize()
keepAliveTime()
timeUnit()
maxQueueSize()
rejectedPolicy()
@executorServiceRef
@poolSize
@maxPoolSize
@keepAliveTime
@timeUnit
@maxQueueSize
@rejectedPolicy

wireTap

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「デフォルトスレッドプールプロファイルの設定」 に示す通りです (これらの設定はアプリケーションコードによって変更されていないことを前提とします)。

表2.9 デフォルトスレッドプールプロファイルの設定

スレッドオプションデフォルト値

maxQueueSize

1000

poolSize

10

maxPoolSize

20

keepAliveTime

60 (秒)

rejectedPolicy

CallerRuns

デフォルトスレッドプールプロファイルの変更

デフォルトスレッドプールプロファイルの設定を変更することで、後続のすべてのデフォルトスレッドプールをカスタムの設定で作成することができます。プロファイルは 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 クラスがサポートするオプションを示します。これらのオプションは、新しいカスタムスレッドプールを定義する際に設定できます。

表2.10 スレッドプールビルダーのオプション

Builder オプション説明

maxQueueSize()

このスレッドプールが受信タスクキューに保持できる保留中の最大タスク数を設定します。-1 の値は上限なしキューを指定します。デフォルト値はデフォルトスレッドプールプロファイルから取得されます。

poolSize()

プールの最小スレッド数を設定します (これは初期プールサイズにもなります)。デフォルト値はデフォルトスレッドプールプロファイルから取得されます。

maxPoolSize()

プールで使用できる最大スレッド数を設定します。デフォルト値はデフォルトスレッドプールプロファイルから取得されます。

keepAliveTime()

スレッドがこの期間 (秒単位で指定) よりも長い間アイドル状態になっている場合、スレッドを終了させる。これにより、負荷が軽くなるとスレッドプールが縮小されます。デフォルト値はデフォルトスレッドプールプロファイルから取得されます。

rejectedPolicy()

受信タスクキューが満杯の場合に実行すべきアクションを指定する。以下の 4 つの値から指定できます。

CallerRuns
(デフォルト値) 呼び出し元のスレッドを使用して、最後に受信したタスクを実行します。しかし、このオプションでは最後に受信したタスクの処理が完了するまで、呼び出し元のスレッドがそれ以上のタスク受信をブロックします。
Abort
例外を発生させて、最後に受信したタスクを中断します。
Discard
例外を発生させずに、最後に受信したタスクを破棄します。
DiscardOldest
最も古い未処理のタスクを破棄して、最後に受信したタスクをタスクキューに入れようと試みます。

build()

カスタムスレッドプールの構築を終了し、build() に引数として指定された ID の下に新しいスレッドプールを登録します。

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」は、コンポーネントの ExecutorService オブジェクトの指定に使用できる scheduledExecutorService プロパティーを公開します。

スレッド名のカスタマイズ

アプリケーションのログをより読みやすくするために、スレッド名 (ログ内でスレッドを識別するために使用されるもの) をカスタマイズすることが推奨されます。スレッド名をカスタマイズするには、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 でルートが自動的に起動するかどうかを制御するには、autoStartup コマンドを boolean 引数 (true または false) または String 引数 (true または false) のいずれかで実行します。たとえば、以下のように 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 インスタンス contextstartRoute() メソッドを呼び出します。

// Java
context.startRoute("nonAuto");

ルート ID nonAuto を持つルートを停止するには、以下のように CamelContext インスタンス contextstopRoute() メソッドを呼び出します。

// 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 はプラグ可能な シャットダウンストラテジー を使用してシャットダウンシーケンスを制御します。デフォルトのシャットダウンストラテジーは、以下のシャットダウンシーケンスを実装します。

  1. ルートが起動順序の 逆順 でシャットダウンされる。
  2. 通常、シャットダウンストラテジーは、現在アクティブなエクスチェンジの処理が終了するまで待機する。ただし、実行中タスクの取り扱いは設定可能。
  3. 全体的なシャットダウンシーケンスには、タイムアウトの上限がある (デフォルトは 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 秒後と設定されています。また、routeStartRepeatCount を 1 に設定し、routeStartRepeatInterval を 3000 ミリ秒に設定することで、このポリシーは、初期起動時刻の 3 秒後に、2 回目のルートを開始するように設定されています。

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 回以上スケジューリングするためのパラメーターを示しています。

パラメーターデフォルト説明

routeStartDate

java.util.Date

なし

ルートの初回起動日時を指定します。

routeStartRepeatCount

int

0

0 以外の値に設定すると、ルートの開始回数が指定されます。

routeStartRepeatInterval

long

0

開始の間隔 (ミリ秒単位) を指定します。

ルートの停止

次の表は、ルートの停止を 1 回以上スケジューリングするためのパラメーターを示しています。

パラメーターデフォルト説明

routeStopDate

java.util.Date

なし

ルートの初回停止日時を指定します。

routeStopRepeatCount

int

0

0 以外の値に設定すると、ルートの停止回数が指定されます。

routeStopRepeatInterval

long

0

停止の間隔 (ミリ秒単位) を指定します。

routeStopGracePeriod

int

10000

ルートを強制停止する前に、現在のエクスチェンジの処理が終了するまで待つ時間 (猶予期間) を指定します。猶予期間が無限の場合は 0 に設定します。

routeStopTimeUnit

long

TimeUnit.MILLISECONDS

猶予期間の時間単位を指定します。

ルートの一時停止

次の表は、ルートの一時停止を 1 回以上スケジュールするためのパラメーターを示しています。

パラメーターデフォルト説明

routeSuspendDate

java.util.Date

なし

ルートが初めて一時停止される日時を指定します。

routeSuspendRepeatCount

int

0

0 以外の値に設定すると、ルートが一時停止される回数が指定されます。

routeSuspendRepeatInterval

long

0

一時停止の間隔 (ミリ秒単位) を指定します。

ルートの再開

次の表は、ルートの再開を 1 回以上スケジュールするためのパラメーターを示しています。

パラメーターデフォルト説明

routeResumeDate

java.util.Date

なし

ルートの初回再開日時を指定します。

routeResumeRepeatCount

int

0

0 以外の値に設定すると、ルートの再開回数が指定されます。

routeResumeRepeatInterval

long

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 システム上でバックグラウンドで実行するジョブをスケジュールする cron ユーティリティーに由来しています。cron 式は、日付と時刻にワイルドカードを使用することで、単一のイベントまたは定期的に繰り返される複数のイベントを効果的に指定することができる構文です。

cron 式は、以下の順序の 6 または 7 個のフィールドで構成されます。

Seconds Minutes Hours DayOfMonth Month DayOfWeek [Year]

Year フィールドは任意のフィールドで、一度だけ発生するイベントを定義する場合を除き、通常は省略できます。各フィールドは、リテラルと特殊文字の組み合わせで構成されます。たとえば、以下の cron 式は、毎日 1 回夜 12 時に発生するイベントを指定します。

0 0 24 * * ?

* 文字は、フィールドのすべての値にマッチするワイルドカードです。したがって、上記の式は毎月の毎日を意味します。? の文字は、フィールドの無視を意味するダミーのプレースホルダーです。DayOfMonthDayOfWeek フィールドの両方を同時に指定するのは論理的ではないので、常に DayOfMonth フィールドか DayOfWeek フィールドのどちらかにこの文字を指定します。たとえば、1 日 1 回発生するイベントを、月曜日から金曜日までのみスケジュールしたい場合は、以下の cron 式を使用します。

0 0 24 ? * MON-FRI

MON-FRI は、ハイフン文字で範囲を指定しています。/ (スラッシュ) を使用してインクリメントを指定することもできます。たとえば、5 分ごとにイベントが発生するように指定するには、以下の cron 式を使用します。

0 0/5 * * * ?

cron 式構文の完全な説明は、Wikipedia の CRON 式 に関する記事を参照してください。

タスクのスケジューリング

cron 定期実行ルートポリシーを使用して、以下のスケジューリングタスクのいずれかを定義することができます。

ルートの開始

次の表は、ルートの開始を 1 回以上スケジューリングするためのパラメーターを示しています。

パラメーターデフォルト説明

routeStartString

String

なし

1 つ以上のルート開始イベントをトリガーする cron 式を指定します。

ルートの停止

次の表は、ルートの停止を 1 回以上スケジューリングするためのパラメーターを示しています。

パラメーターデフォルト説明

routeStopTime

String

なし

1 つ以上のルート停止イベントをトリガーする cron 式を指定します。

routeStopGracePeriod

int

10000

ルートを強制停止する前に、現在のエクスチェンジの処理が終了するまで待つ時間 (猶予期間) を指定します。猶予期間が無限の場合は 0 に設定します。

routeStopTimeUnit

long

TimeUnit.MILLISECONDS

猶予期間の時間単位を指定します。

ルートの一時停止

次の表は、ルートの一時停止を 1 回以上スケジュールするためのパラメーターを示しています。

パラメーターデフォルト説明

routeSuspendTime

String

なし

1 つ以上のルート一時停止イベントをトリガーする cron 式を指定します。

ルートの再開

次の表は、ルートの再開を 1 回以上スケジュールするためのパラメーターを示しています。

パラメーターデフォルト説明

routeResumeTime

String

なし

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 プラグインで実行)

これ以外に、CamelContextReloadStrategy を設定したり、独自のカスタム戦略を設定したりすることで、手動で有効にすることもできます。

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 プロジェクトのソースコードをコンパイルし、クラスパスの XML 設定ファイル (META-INF/spring/*.xml) を使用して Spring ApplicationContext を起動します。Camel のルートをより速く起動したい場合は、代わりに camel:embedded を試してみてください。

2.12.1.1. オプション

Camel Maven プラグインの run ゴールは以下のオプションをサポートしします。これらのオプションはコマンドラインから設定するか (-D 構文を使用)、pom.xml ファイルの <configuration> タグで定義します。

パラメーター

デフォルト値

説明

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 構文を使用)、pom.xml ファイルの <configuration> タグで定義します。

パラメーター

デフォルト値

説明

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

camel.ignoreDeprecated のように、-D コマンド引数を camel. の前に付ける必要があることに注意してください。

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 構文を使用)、pom.xml ファイルの <configuration> タグで定義します。

パラメーター

デフォルト値

説明

failOnError

false

いずれかのルートのカバレッジが 100% でない場合に失敗するかどうか。

includeTest

false

テストソースコードを含めるかどうか。

includes

 

Java および xml ファイルの名前を絞り込み、指定されたパターンのリスト (ワイルドカードおよび正規表現) と一致するファイルのみが含まれるようにします。複数の値はコンマで区切ることができます。

excludes

 

Java および xml ファイルの名前を絞り込み、指定されたパターンのリスト (ワイルドカードおよび正規表現) と一致するファイルが除外されるようにします。複数の値はコンマで区切ることができます。

anonymousRoutes

false

匿名ルート (ルート ID が割り当てられていないルート) を許可するかどうか。ルート ID を使用することで、正確にルートカバレッジのデータをルートのソースコードとマッチングさせることができます。匿名ルートは、テストされたルートがソースコードのどのルートに対応しているかを正確に知ることが難しくなるため、ルートカバレッジの結果の精度が低くなります。

2.13. Apache Camel スタンドアロンの実行

スタンドアロンアプリケーションとして camel を実行する場合、アプリケーションを実行して JVM が終了するまで実行を継続するために使用できる Main クラスを提供します。Java パッケージ org.apache.camel.main の中に MainListener クラスがあります。

以下は、Main クラスのコンポーネントです。

  • camel-core JAR 内の org.apache.camel.Main クラス
  • camel-spring JAR 内の org.apache.camel.spring.Main クラス

以下の例は、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 用の新しいスレッドを生成します。これにより、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 の場合、onFailureOnlyonCompleteOnlyonCompletion タグにブール値で設定します。

<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 メトリクスを使用するには、以下が必要です。

  1. camel-metrics コンポーネントの追加
  2. 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();
  ...
}

オプション

MetricsRoutePolicyFactoryMetricsRoutePolicy は、以下のオプションをサポートしています。

名前

デフォルト

説明

durationUnit

TimeUnit.MILLISECONDS

メトリクスレポーターまたは統計を json 出力するときの期間に使用する単位。

jmxDomain

org.apache.camel.metrics

JXM ドメイン名。

metricsRegistry

 

共有の com.codahale.metrics.MetricRegistry を使用することを許可します。指定しない場合は、Camel はこの CamelContext によって使用される共有インスタンスを作成します。

prettyPrint

false

統計情報を json 形式で出力する際に pretty print を使用するかどうか。

rateUnit

TimeUnit.SECONDS

メトリクスレポーターまたは統計を json 出力するときのレートに使用する単位。

useJmx

false

com.codahale.metrics.JmxReporter を使って詳細な統計情報を JMX に報告するかどうか。

CamelContext で JMX が有効になっている場合、JMX ツリーのサービスタイプの下に MetricsRegistryService mbean が登録されていることに注意してください。この mbean には、統計を JSON 出力する 1 つのオペレーションがあります。useJmx を true に設定する必要があるのは、統計タイプごとに細かい mbeans を生成する場合のみです。

2.16. JMX の命名

概要

Apache Camel では、Management Name パターン を定義することで、JMX で表示される CamelContext Bean の名前をカスタマイズすることができます。たとえば、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 名がいつも同じであることを保証できないことです。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 名前パターンを構成することができます。

表2.11 JMX 名のパターントークン

トークン説明

#camelId#

CamelContext Bean の id 属性の値

#name#

#camelId# と同じ

#counter#

インクリメントカウンター (1 で始まる)

#bundleId#

デプロイされたバンドルの OSGi バンドル ID OSGi のみ)

#symbolicName#

OSGi シンボリック名 (OSGi のみ)

#version#

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「メッセージングシステム」 に記載されているメッセージングシステムパターンは、メッセージングシステムを構成する基本的な概念やコンポーネントを紹介します。

表3.1 メッセージングシステム

アイコン名前ユースケース

Message icon

図5.1「Message パターン」

メッセージチャネルによって接続された 2 つのアプリケーションはどのように情報を交換するか。

Message channel icon

図5.2「Message Channel パターン」

メッセージングを使用して単一のアプリケーションが別のアプリケーションと通信する方法。

Message endpoint icon

図5.3「Message Endpoint パターン」

アプリケーションがメッセージングチャネルに接続してメッセージを送受信する方法。

Pipes and filters icon

図5.4「Pipes and Filters パターン」

独立性と柔軟性を維持しながら、メッセージで複雑な処理を行う方法。

Message router icons

図5.7「Message Router パターン」

定義された条件のセットに応じてメッセージを異なるフィルターに渡すために、個々の処理ステップを切り離す方法。

Message translator icon

図5.8「Message Translator パターン」

メッセージングを使用して、異なるデータフォーマットを使用するシステムの間で通信を行う方法。

メッセージングチャネル

メッセージングチャネルは、メッセージングシステムで参加者の接続に使用される基本的なコンポーネントです。表3.2「メッセージングチャネル」 のパターンは、使用できる異なる種類のメッセージングチャネルを説明しています。

表3.2 メッセージングチャネル

アイコン名前ユースケース

Point to point icon

図6.1「Point to Point Channel パターン」

1 つの受信側のみがドキュメントの受信や呼び出しを実行するように、呼び出し側が確認する方法。

Publish subscribe icon

図6.2「Publish Subscribe Channel パターン」

送信側が対象のすべての受信側にブロードキャストする方法。

Dead letter icon

図6.3「Dead Letter Channel パターン」

メッセージングシステムが配信できないメッセージの処理方法。

Guaranteed delivery icon

図6.4「Guaranteed Delivery パターン」

メッセージングシステムに障害が発生しても、送信側がメッセージを確実に配信する方法。

Message bus icon

図6.5「Message Bus パターン」

独立し、分離したアプリケーションを連携でき、他のアプリケーションに影響を与えることなく 1 つ以上のアプリケーションを追加または削除できるアーキテクチャーとは。

メッセージの構築

表3.3「メッセージの構築」 のメッセージ構築パターンは、システムを通過するメッセージのさまざまな形式と関数を表しています。

表3.3 メッセージの構築

アイコン名前ユースケース

Correlation identifier icon

「概要」

受信した応答を生成したリクエストを、要求側が識別する方法。

Return address icon

「返信先アドレス」

応答側が応答の送信先を認識する方法。

メッセージルーティング

表3.4「メッセージのルーティング」 のメッセージルーティングパターンは、メッセージチャネルをリンクするさまざまな方法を表しています。これには、メッセージのボディーを変更せずにメッセージストリームに適用できるさまざまなアルゴリズムが含まれます。

表3.4 メッセージのルーティング

アイコン名前ユースケース

Content based router icon

「Content-Based Router」

単一の論理関数 (在庫確認など) の実装が複数の物理システムに分散されている場合の処理方法。

Message filter icon

「Message Filter」

コンポーネントが不必要なメッセージを受信しないようにする方法。

Recipient List icon

「Recipient List」

動的に指定された受信者のリストにメッセージをルーティングする方法。

Splitter icon

「Splitter」

各要素を異なる方法で処理しなければならない可能性がある、複数の要素が含まれるメッセージの処理方法。

Aggregator icon

「Aggregator」

個別かつ関連するメッセージの結果を組み合わせ、全体として処理できるようにする方法。

Resequencer icon

「Resequencer」

順序どおりでない関連するメッセージのストリームを正しい順序に戻す方法。

distribution aggregate icon

「Composed Message Processor」

要素ごとに異なる処理が必要となる可能性がある複数の要素で構成されるメッセージを処理する場合に、メッセージフロー全体を維持する方法。

 

「Scatter-Gather」

複数の受信者にメッセージを送信する必要があり、その各受信者が応答を送信する可能性がある場合に、メッセージフロー全体を維持する方法。

Routing slip icon

「Routing Slip」

設計時にステップの順序が分からず、ステップの順序がメッセージごとに異なる可能性がある場合に、一連の処理ステップを通じてメッセージを継続的にルーティングする方法。

 

「Throttler」

メッセージのスロットリングによって、特定のエンドポイントがオーバーロードされないようにする方法、または外部サービスと合意した SLA を越えないようにする方法。

 

「Delayer」

メッセージの送信を遅らせる方法。

 

「Load Balancer」

複数のエンドポイント間で負荷を分散する方法。

 

「Hystrix」

外部サービスの呼び出し時に、Hystrix サーキットブレーカーを使用する方法。Camel 2.18 の新機能。

 

「Service Call」

レジストリーでサービスを検索して、分散システムでリモートサービスを呼び出す方法。Camel 2.18 の新機能。

 

「Multicast」

メッセージを同時に複数のエンドポイントにルーティングする方法。

 

「Loop」

メッセージをループで繰り返し処理する方法。

 

「Sampling」

ダウンストリームルートをオーバーロードを防ぐために一定の期間で複数のメッセージから 1 つのメッセージをサンプリングする方法。

メッセージの変換

表3.5「メッセージの変換」 のメッセージ変換パターンは、さまざまな目的のためにメッセージの内容を変更する方法を表しています。

表3.5 メッセージの変換

アイコン名前ユースケース

Content enricher icon

「Content Enricher」

メッセージの送信元に必要なデータ項目がすべてない場合に他のシステムと通信する方法。

Content filter icon

「Content Filter」

数個のデータ項目のみが必要な場合に大きなメッセージの処理を簡単にする方法。

store in library icon

「Claim Check EIP」

情報の内容を減らさずにシステム全体で送信されるメッセージのデータ量を減らす方法。

Normalizer icon

「ノーマライザー」

意味的には同等で、受け取った形式が異なるメッセージの処理方法。

 

「Sort」

メッセージのボディーのソート方法。

メッセージングエンドポイント

メッセージングエンドポイントは、メッセージングチャネルとアプリケーション間の接点を示します。表3.6「メッセージングエンドポイント」 のメッセージングエンドポイントパターンは、エンドポイントに設定できるサービスのさまざまな機能と特性を表しています。

表3.6 メッセージングエンドポイント

アイコン名前ユースケース
 

「Messaging Mapper」

ドメインオブジェクトとメッセージングインフラストラクチャーの間でデータを移動し、お互いに独立した状態を維持する方法。

Event driven icon

「Event Driven Consumer」

メッセージが利用できるようになったときにアプリケーションが自動的にメッセージを消費する方法。

Polling consumer icon

「Polling Consumer」

アプリケーションの準備ができたときに、アプリケーションがメッセージを消費する方法。

Competing consumers icon

「Competing Consumers」

メッセージングクライアントが複数のメッセージを同時に処理する方法。

Message dispatcher icon

「Message Dispatcher」

1 つのチャネルで複数のコンシューマーがメッセージ処理を調整する方法。

Selective consumer icon

「Selective Consumer」

メッセージコンシューマーが受信するメッセージを選択する方法

Durable subscriber icon

「Durable Subscriber」

サブスクライバーがメッセージをリッスンしていないときにメッセージの欠落を防ぐ方法。

 

「Idempotent Consumer」

メッセージの受信側が重複メッセージを処理する方法。

Transactional client icon

「Transactional Client」

クライアントがメッセージングシステムでトランザクションを制御する方法。

Messaging gateway icon

「Messaging Gateway」

残りのアプリケーションからメッセージングシステムへのアクセスをカプセル化する方法。

Service activator icon

「Service Activator」

サービスがさまざまなメッセージング技術や、メッセージング以外の技術によって呼び出されるように、アプリケーションで設計する方法。

システム管理

表3.7「システム管理」 のシステム管理パターンは、メッセージングシステムを監視、テスト、および管理する方法を表しています。

表3.7 システム管理

アイコン名前ユースケース

Wire tap icon

12章システム管理

ポイントツーポイントチャネルで送信されるメッセージを検査する方法。

第4章 REST サービスの定義

概要

Apache Camel は、REST サービスを定義するために複数のアプローチをサポートします。特に、Apache Camel は REST DSL (Domain Specific Language) を提供します。これは、REST コンポーネントを抽象化でき、Swagger とも統合できるシンプルながらも強力な Fluent API です。

4.1. Camel における REST サービスの概要

概要

Apache Camel は、Camel アプリケーションで REST サービスを定義するためのさまざまなアプローチやコンポーネントを提供します。本セクションでは、これらのアプローチとコンポーネントの概要を紹介し、要件に最適な実装と API を判断できるようにします。

REST とは

Representational State Transfer (REST) は、4 つの基本的な HTTP メソッド (GETPOSTPUT および 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』の「CXFRS」および『Apache CXF Development Guide』を参照してください。

注記

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 コンポーネントと互換性がある。
  • Swagger と統合 ( camel-swagger コンポーネント経由) している。

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 メソッドに相当) となります。Verb 句は 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() で終了となります。キーワード 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 では、動的な to の toD パラメーターをサポートしています。動的な 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 句を区切るためのマークです。

たとえば、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 DSL の consumes()produces() オプション、または XML DSL のconsumesproduces 属性を使用して、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 エラーメッセージを定義できます。

  1. Exchange.HTTP_RESPONSE_CODE ヘッダーにエラーコードの値を設定して、HTTP エラーコードを指定します (例: 400404 など)。この設定は、正常時のレスポンスではなく、エラーメッセージをレスポンスする REST DSL を示します。
  2. メッセージのボディーにカスタムエラーメッセージを設定します。
  3. 必要に応じて Content-Type ヘッダーを設定します。
  4. 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 でサポートされているオプションをまとめたものです。

表4.1 REST DSL のオプション

Java DSLXML DSL説明

bindingMode()

@bindingMode

バインディングモードを指定します。これを使用して、受信メッセージを Java オブジェクトにマーシャリングすることができます (オプションで、Java オブジェクトを送信メッセージにアンマーシャリングすることもできます)。設定可能な値: off (デフォルト)、autojsonxmljson_xml

consumes()

@consumes

HTTP リクエストで指定されたインターネットメディアタイプ (MIME タイプ) のみを受け入れるように Verb 句を制限します。代表的な値: text/plaintext/httptext/xmlapplication/jsonapplication/xml

customId()

@customId

JMX Management のカスタム ID を指定します。

description()

description

REST サービスまたは Verb 句の説明文を記載します。JMX Management やツールを使う場合に便利です。

enableCORS()

@enableCORS

true の場合、HTTP レスポンスで CORS (オリジン間リソース共有) ヘッダーを有効にします。デフォルトは false です。

id()

@id

RES Tサービスの一意の ID を指定します。これは、JMX Management や他のツールを使用する際に便利です。

method()

@method

この Verb 句で処理する HTTP メソッドを指定します。通常は一般的な verb() キーワードと組み合わせて使用します。

outType()

@outType

オブジェクトバインディングが有効な場合 (bindingMode オプションが有効な場合)、このオプションは HTTP レスポンスメッセージを表す Java 型を指定します。

produces()

produces

HTTP レスポンスで指定されたインターネットメディアタイプ (MIME タイプ) のみを生成するように Verb 句を制限します。代表的な値: text/plaintext/httptext/xmlapplication/jsonapplication/xml

type()

@type

オブジェクトバインディングが有効の場合 (bindingMode オプションが有効な場合)、このオプションは HTTP リクエスト メッセージを表す Java 型を指定します。

VerbURIArgument

@uri

Verb 句の引数としてパスセグメントまたは URI テンプレートを指定します。例: get(VerbURIArgument)

BasePathArgument

@path

rest() キーワード (Java DSL) または rest 要素 (XML DSL) でベースパスを指定します。

4.3. Java オブジェクトとの間のマーシャリング

HTTP で送信するための Java オブジェクトのマーシャリング

REST プロトコルを使用する最も一般的な方法の 1 つは、Java Bean の内容をメッセージボディーで送信することです。これを実現させるには、Java オブジェクトを適切なデータフォーマットとの間でマーシャリングするメカニズムが必要です。Java オブジェクトのエンコードに適した以下のデータフォーマットが REST DSL でサポートされています。

JSON

JSON (JavaScript Object Notation) は、Javaオブジェクトとの間で簡単にマッピングできる軽量なデータフォーマットです。JSON 構文はコンパクトで、緩く型指定され、人間が読み書きがしやすい構文です。これらの理由から、JSON は REST サービスのメッセージ形式として人気があります。

たとえば、以下の JSON コードは、idname の 2 つのプロパティーフィールドを持つ User Bean を表現できます。

{
    "id" : 1234,
    "name" : "Jane Doe"
}
JAXB

JAXB(Java Architecture for XML Binding) は、Java オブジェクトと XML の間で簡単にマッピングできるデータフォーマットです。XML を Java オブジェクトにマーシャリングするには、使用する Java クラスにアノテーションを付ける必要があります。

たとえば、以下の JSON コードは、idname の 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 コンテキストでは CamelJaxbObjectFactory プロパティーを true にする必要があります。ただし、後方互換のためにデフォルトは 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)
  • JAXB

    • JAXB データフォーマット (camel-jaxb)

オブジェクトマーシャリングを有効にする方法

REST DSL でオブジェクトのマーシャリングを有効にする場合は、以下の点に注意してください。

  1. bindingMode オプションを設定してバインディングモードを有効にします (バインディングモードを設定できるレベルはいくつかあり、詳細は 「バインディングモードの設定」 を参照してください)。
  2. 受信メッセージでは type オプション (必須)、送信メッセージでは outType オプション (オプション) を使用して、変換先 (または変換元) の Java 型を指定します。
  3. Java オブジェクトを JAXB データフォーマットとの間で変換する場合、Java クラスに適切な JAXB アノテーションを付ける必要があります。
  4. jsonDataFormat オプションや xmlDataFormat オプション (restConfiguration ビルダーで指定可能) を使用して、ベースとなるデータフォーマットの実装を指定します。
  5. ルートが JAXB 形式の戻り値を提供する場合、通常、エクスチェンジボディーの Out メッセージを JAXB アノテーションを持つクラスのインスタンス (JAXB 要素) に設定することが期待されます。ただし、JAXB の返り値を XML 形式で直接提供したい場合は、キーが xml.out.mustBeJAXBElementdataFormatPropertyfalse に設定します (restConfiguration ビルダーで指定可能)。たとえば、XML DSL の構文では以下になります。

    <restConfiguration ...>
      <dataFormatProperty key="xml.out.mustBeJAXBElement"
                          value="false"/>
      ...
    </restConfiguration>
  6. 必要な依存関係をプロジェクトのビルドファイルに追加します。たとえば、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>
  7. アプリケーションを 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 が含まれている場合にのみ発生します。これにより、カスタムのコンテンツタイプを指定することで、メッセージボディーがバインディングを使用してマーシャリングしなくなります。これは、メッセージボディーがカスタムバイナリーのペイロードである場合などに便利です。

表4.2 REST DSL のバインディングモード

バインディングモード説明

off

バインディングはオフになります (デフォルト)

auto

バインディングは JSON または XML に対して有効になります。このモードでは、Camel は受信メッセージのフォーマットに基づいて JSON または XML (JAXB) を自動選択します。必ずしも両方のデータフォーマットの実装を有効にする必要はありません。JSON の実装と XML の実装のどちらかまたは両方をクラスパスで提供します。

json

バインディングは JSON でのみ有効になります。クラスパスに JSON の実装を用意しなければなりません (デフォルトでは、Camel はcamel-jackson の実装を有効にします)。

xml

バインディングは XML でのみ有効です。クラスパスに XML の実装を用意しなければなりません (デフォルトでは、Camel は camel-jaxb の実装を有効にします)。

json_xml

バインディングは 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-780036-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 ID camelServlet があります。
HTTP コンテナーレイヤー

Servlet コンポーネントは HTTP コンテナーにデプロイする必要があります。Karaf コンテナーには通常、ポート 8181 の HTTP リクエストをリッスンするデフォルトの HTTP コンテナー (Jetty HTTP コンテナー) が提供されています。Servlet コンポーネントをデフォルトの Jetty HTTP コンテナーにデプロイするには、以下を行います。

  1. OSGi サービス (org.osgi.service.http.HttpService ) への参照を取得します。このサービスは、OSGi のデフォルト HTTP サーバーへのアクセスを提供する標準化された OSGi インターフェイスです。
  2. ユーティリティクラスのインスタンス (OsgiServletRegisterer) を作成して、HTTP コンテナーに Servlet コンポーネントを登録します。OsgiServletRegisterer クラスは、Servlet コンポーネントのライフサイクル管理を簡素化するユーティリティーです。このクラスのインスタンスが作成されると、HttpService OSGi サービス上の registerServlet メソッドが自動的に呼び出され、インスタンスが破棄されると、unregister メソッドが自動的に呼び出されます。

必要な依存関係

この例には、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 要素 geturi 属性によって指定されます。

したがって、コマンドラインで以下のコマンドを入力することで、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 DSL では、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>

SSL 設定オプション

表4.3「REST DSL の設定オプション」 は、restConfiguration() ビルダー (Java DSL) または restConfiguration 要素 (XML DSL) を使用して REST DSL を設定するオプションを示しています。

表4.3 REST DSL の設定オプション

Java DSLXML DSL説明

component()

@component

REST トランスポートとして使用する Camel コンポーネントを指定します (例: servletrestletspark-rest)。この値は、標準コンポーネント名またはカスタムインスタンスの Bean ID のいずれかになります。このオプションが指定されていない場合、Camel はクラスパス上または Bean レジストリーで RestConsumerFactory のインスタンスを探します。

scheme()

@scheme

REST サービスの公開に使用するプロトコル。ベースとなる REST の実装に依存しますが、通常はhttphttps がサポートされます。デフォルトは http です。

host()

@host

REST サービスの公開に使用するホスト名。

port()

@port

REST サービスの公開に使用するポート番号。

注意: この設定は Servlet コンポーネントによって無視され、代わりにコンテナーの標準 HTTP ポートが使用されます。Apache Karaf OSGi コンテナーの場合、標準の HTTP ポートは通常 8181 になります。JMX などのツールのためにポート値を明記するとよいでしょう。

contextPath()

@contextPath

REST サービスのリーディングコンテキストパスを指定します。これは Servlet などのコンポーネントで使用することができます。これらのコンポーネントは、context-path の設定を使用してアプリケーションをデプロイします。

hostNameResolver()

@hostNameResolver

ホスト名が明示的に設定されていない場合、このリゾルバーによって REST サービスのホストが決定されます。以下の値を使用できます: 1. RestHostNameResolver.localHostName (Java DSL) または localHostName (XML DSL) で、ホスト名が解決されます。 2. RestHostNameResolver.localIp (Java DSL) または localIp (XML DSL) で、ドット付き 10 進 IP アドレス形式に解決されます。3. Camel 2.17 以降では、RestHostNameResolver.allLocalIp を使用して、すべてのローカル IP アドレスに解決することができます。

Camel 2.16 までのデフォルトは localHostName です。Camel 2.17 以降のデフォルトは allLocalIp です。

bindingMode()

@bindingMode

JSON または XML 形式のメッセージのバインディングモードを有効にします。使用できる値は offautojsonxml、または json_xml です。デフォルトは off です。

skipBindingOnErrorCode()

@skipBindingOnErrorCode

HTTP エラーがある場合、出力のバインディングをスキップするかどうかを指定します。これにより、JSON や XML にバインドせず、カスタムエラーメッセージを作成することができます。デフォルトは true です。

enableCORS()

@enableCORS

true の場合、HTTP レスポンスで CORS (オリジン間リソース共有) ヘッダーを有効にします。デフォルトは false です。

jsonDataFormat()

@jsonDataFormat

Camel が JSONデ ータフォーマットを変換するために使用するコンポーネントを指定します。使用できる値は json-jacksonjson-gson、および json-xstream です。デフォルトは json-jackson です。

xmlDataFormat()

@xmlDataFormat

Camel が XML データフォーマットを変換するために使用するコンポーネントを指定します。使用できる値は jaxb です。デフォルトは jaxb です。

componentProperty()

componentProperty

ベースにある REST 実装に対し、コンポーネントレベル の任意のプロパティーを設定できるようにします。

endpointProperty()

endpointProperty

ベースにある REST 実装に対し、エンドポイントレベル の任意のプロパティーを設定できるようにします。

consumerProperty()

consumerProperty

ベースにある REST 実装に対し、コンシューマーエンドポイント の任意のプロパティーを設定できます。

dataFormatProperty()

dataFormatProperty

ベースにあるデータフォーマットコンポーネント (Jackson や JAXB など) に対し、任意のプロパティーを設定できます。Camel 2.14.1 以降では、以下のプレフィックスをプロパティーキーに割り当てることができます。

  • json.in
  • json.out
  • xml.in
  • xml.out

これによって、プロパティー設定を特定の形式 (JSON または XML) および特定のメッセージ方向 (IN または OUT) だけに適用することができます。

corsHeaderProperty()

corsHeaders

カスタム CORS ヘッダをキー/値のペアで指定できるようにします。

デフォルトの CORS ヘッダー

CORS (オリジン間リソース共有) を有効にすると、デフォルトで以下のヘッダーが設定されます。DSL コマンド corsHeaderProperty を呼び出すことでデフォルト設定を上書きできます。

表4.4 デフォルトの CORS ヘッダー

ヘッダーのキーヘッダーの値

Access-Control-Allow-Origin

\*

Access-Control-Allow-Methods

GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH

Access-Control-Allow-Headers

Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers

Access-Control-Max-Age

3600

Jackson JSON 機能の有効化または無効化

オプション dataFormatProperty に以下のキーを設定すると、特定の Jackson JSON 機能を有効または無効にできます。

  • json.in.disableFeatures
  • json.in.enableFeatures

たとえば、Jackson の FAIL_ON_UNKNOWN_PROPERTIES 機能を無効にする場合は次のとおりです (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. Swagger との統合

概要

Swagger サービスを使用して、CamelContext 内の REST 定義されたルートやエンドポイントの API ドキュメントを生成することができます。これを行うには、camel-swagger-java モジュール (純粋な Java ベースのモジュール) を Camel REST DSL で使用します。camel-swagger-java モジュールは CamelContext と統合したサーブレットを作成し、各 REST エンドポイントから情報を取得して JSON または YAML 形式の API ドキュメントを生成します。

Maven 使用の場合は、pom.xml ファイルを編集して camel-swagger-java コンポーネントに依存関係を追加します。

<dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-swagger-java</artifactId>
   <version>x.x.x</version>
   <!-- Specify the version of your camel-core module. -->
</dependency>

CamelContext での Swagger の有効化

REST DSLで Swagger API を使用できるようにするには、apiContextPath() を呼び出して、Swagger で生成された 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 Swagger-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");
    }
}

Swagger モジュールの設定オプション

下表のオプションを使用して、Swagger モジュールを設定することができます。以下のようにオプションを設定します。

  • camel-swagger-java モジュールをサーブレットとして使用している場合は、web.xml ファイルを編集し、設定する設定オプションごとに init-param 要素を指定してオプションを設定します。
  • REST コンポーネントの camel-swagger-java モジュールを使用している場合は、enableCORS()host()contextPath() などの適切な RestConfigurationDefinition メソッドを呼び出して、オプションを設定します。たとえば、api.xxx オプションを RestConfigurationDefinition.apiProperty() メソッドで設定します。
オプション説明

api.contact.email

String

API 関連の連絡先に使用するメールアドレス。

api.contact.name

String

API 関連の連絡先に使用する個人または組織の名前。

api.contact.url

String

API 関連の問い合わせ先の Web サイトへの URL。

apiContextIdListing

Boolean

アプリケーションに複数の CamelContext オブジェクトを使用している場合、デフォルトは現在の CamelContext となります。REST サービスを実行している JVM で、実行されている個々の CamelContext の REST エンドポイントのリストが必要な場合は、このオプションを true に設定します。apiContextIdListing が true の場合、Swagger はCamelContext のルートパス (たとえば /api-docs) で ID 一覧を JSON 形式のリストとして公開します。Swagger で生成されたドキュメントにアクセスするには、REST コンテキストパスに CamelContext IDを追加します (たとえば api-docs/myCamel)。apiContextIdPattern オプションを使用すると、公開する一覧の名前をフィルタリングできます。

apiContextIdPattern

String

コンテキストリストに表示される CamelContext ID のフィルターパターン。正規表現を指定し、ワイルドカードとして * を使用することができます。これは、Camel Intercept 機能で使用されるのと同じパターンマッチング機能です。

api.license.name

String

API に適用されるライセンス名。

api.license.url

String

API に適用されるライセンスの URL。

api.path

String

API ドキュメントを公開するパス (たとえば /api-docs) を設定します。相対パスで指定します。http または https を指定しないでください。camel-swagger-java モジュールは、実行時に絶対パスを protocol://host:port/context-path/api-path で構成します。

api.termsOfService

String

API の利用規約への URL。

api.title

String

アプリケーションの名前。

api.version

String

API のバージョン。デフォルトは 0.0.0 です。

base.path

String

必須。REST サービスが利用できるパスを設定します。相対パスで指定します。http または https は指定しないでください。camel-swagger-java モジュールは、実行時に絶対パスを protocol://host:port/context-path/base.path で構成します。

cors

Boolean

CORS (オリジン間リソース共有) を有効にするかどうか。これにより、CORS は REST API ドキュメントを閲覧する場合のみ有効になり、REST サービスにアクセスする場合には有効になりません。デフォルトは false です。このオプションの代わりに、後述の CorsFilter オプションを使用することが推奨されます。

host

String

Swagger サービスが実行されているホストの名前を指定します。デフォルトでは、localhost に基づいてホスト名が決定されます。

schemes

String

使用するプロトコルスキーム。複数の値はカンマで区切ります (例: "http,https".)。デフォルトは http です。

swagger.version

String

Swagger 仕様のバージョン。デフォルトは 2.0 です。

CORS フィルターの使用した CORS サポートの有効化

Swagger ユーザーインターフェースを使用して REST API ドキュメントを表示する場合は、CORS (オリジン間リソース共有) のサポートを有効にする必要があるでしょう。このサポートは、Swagger ユーザーインターフェースがホストされていて、REST API が実行されているホスト名/ポートとは異なる場合に必要です。

CORS のサポートを有効にするには、RestSwaggerCorsFilterweb.xml ファイルに追加します。CORS フィルターには、CORS を有効にする HTTP ヘッダーを追加します。以下に例を示します。

<!-- Enable CORS filter to allow use of Swagger UI for browsing and testing APIs. -->
<filter>
     <filter-name>RestSwaggerCorsFilter</filter-name>
     <filter-class>org.apache.camel.swagger.rest.RestSwaggerCorsFilter</filter-class>
</filter>
<filter-mapping>
     <filter-name>RestSwaggerCorsFilter</filter-name>
     <url-pattern>/api-docs/*</url-pattern>
     <url-pattern>/rest/*</url-pattern>
</filter-mapping>

RestSwaggerCorsFilter は、すべてのリクエストに対して以下のヘッダーを設定します。

  • Access-Control-Allow-Origin= *
  • access-Control-Allow-Methods = GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH
  • Access-Control-Max-Age = 3600'
  • Access-Control-Allow-Headers = Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers

RestSwaggerCorsFilter は単純なフィルターです。特定のクライアントをブロックしたり、特定のクライアントに対して異なるヘッダー値を設定したりする必要がある場合は、複雑なフィルターが必要になる可能性があります。

JSON または YAML 形式の出力

Camel 2.17 以降、camel-swagger-java モジュールは JSON と YAML 形式の両方の出力をサポートしています。出力したい形式を指定するには、リクエスト URL に /swagger.json または /swagger.yaml を追加します。リクエスト URL に形式が指定されていない場合、camel-swagger-java モジュールは HTTP Accept ヘッダーをチェックして JSON または YAML を許可するかどうかを検出します。両方が受け入れられるか、両方が受け入れられないと検知された場合は、デフォルトの JSON 形式が出力されます。

Apache Camel のディストリビューションには、camel-swagger-java モジュールの使用方法を実証する camel-example-swagger-cdicamel-example-swagger-java サンプルが含まれています。

Swagger で生成されたドキュメントの充実

Camel 2.16 以降では、名前、説明、データ型、パラメーター型などのパラメーター詳細を定義することで、Swagger で生成されたドキュメントを充実することができます。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 パターン

Message pattern

メッセージのタイプ

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 パターン

Message channel pattern

メッセージ指向コンポーネント

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 パターン

Message endpoint pattern

エンドポイントのタイプ

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

 

必須のオプション: 使用する URI。

pattern

 

エンドポイントに送信する際に使用する特定の交換パターンを設定します。元の MEP は後で復元されます。

cacheSize

 

再利用のためにプロデューサーをキャッシュする、ProducerCache のキャッシュサイズを設定します。デフォルトのキャッシュサイズは 1000 で、他の値が指定されていない場合に使用されます。値を -1 に設定すると、キャッシュを完全にオフにします。

ignoreInvalidEndpoint

false

解決できないエンドポイント URI を無視するかどうかを指定します。無効にすると、Camel は無効なエンドポイント URI を特定する例外をスローします。

5.4. パイプとフィルター

概要

図5.4「Pipes and Filters パターン」 に記載されている Pipes and Filters パターンは、フィルターチェーンを作成してルートを構築する方法を表しています。フィルターの出力は、パイプラインの次のフィルターの入力に取り込まれます (UNIX の pipe コマンドに似ています)。パイプラインアプローチの利点は、サービス (Apache Camel アプリケーションの外部にあるものもあります) を作成して、より複雑な形式のメッセージ処理を作成できることです。

図5.4 Pipes and Filters パターン

Pipes and filters pattern

InOut 交換パターンのパイプライン

通常、パイプラインのすべてのエンドポイントには、入力 (In メッセージ) および出力 (Out メッセージ) があります。これは、InOut メッセージ交換パターンと互換性があることを意味しています。InOut パイプラインを経由する通常のメッセージフローを 図5.5「InOut エクスチェンジのパイプライン」 に示します。

図5.5 InOut エクスチェンジのパイプライン

Pipeline for InOut exchanges

パイプラインは、各エンドポイントの出力を次のエンドポイントの入力に接続します。最終的なエンドポイントからの 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 要素がありません。前述のfromto 要素の組み合わせは、意味的にはパイプラインと同等です。「pipeline() および to() DSL コマンドの比較」 を参照してください。

InOnly および RobustInOnly 交換パターンのパイプライン

パイプラインのエンドポイントから利用可能な Out メッセージがない場合 (InOnly および RobustInOnly 交換パターンの場合)、パイプラインは通常の方法で接続できません。この場合、パイプラインは 図5.6「InOnly エクスチェンジのパイプライン」 に示すように、元の In メッセージのコピーをパイプラインの各エンドポイントに渡して構築されます。このタイプのパイプラインは、固定の宛先を持つ受信者リストと同等です (「Recipient List」 を参照)。

図5.6 InOnly エクスチェンジのパイプライン

Pipeline for InOnly exchanges

このパイプラインのルートは、InOut パイプラインと同じ構文を使用して定義されます (Java DSL または XML)。

pipeline() および to() DSL コマンドの比較

Java DSL では、以下の構文のいずれかを使用してパイプラインルートを定義できます。

  • pipeline () プロセッサーコマンドの使用: 以下のように、パイプラインプロセッサーを使用してパイプラインルートを構築します。

    from(SourceURI).pipeline(FilterA, FilterB, TargetURI);
  • to () コマンドの使用: 以下のように、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 パターン

Message router pattern

メッセージルーターは choice() プロセッサーを使用して Apache Camel に簡単に実装できます。このプロセッサーでは、when() を使用して、代替のターゲットエンドポイントをそれぞれ選択することができます (choice プロセッサーの詳細は、「プロセッサー」 を参照してください)。

Java DSL の例

以下の Java DSL の例は、foo ヘッダーの内容に応じて、3 つの代替の宛先 (seda:aseda: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 パターン

Message translator pattern

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 pattern

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 pattern

Publish-Subscribe Channel をサポートするコンポーネント

以下の Apache Camel コンポーネントは、Publish Subscribe Channel パターンをサポートします。

  • JMS
  • ActiveMQ
  • XMPP
  • SEDA (pub-sub で機能する同じ CamelContext で SEDA を使用し、複数のコンシューマーを許可する場合)
  • SEDA を同じ JVM 内で使用 (『Apache Camel Component Reference Guide』の「VM」を参照)

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:bseda: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 パターン

Dead letter channel pattern

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「再配信ポリシーの設定」 には、再配信ポリシーの設定に使用できるメソッドがまとめられています。

表6.1 再配信ポリシーの設定

メソッドの署名デフォルト説明

allowRedeliveryWhileStopping()

true

正常なシャットダウン中またはルートが停止している間、再配信を試行するかどうかを制御します。すでに進行中の配信は停止時に中断され ません

backOffMultiplier(double multiplier)

2

指数バックオフが有効な場合は、m をバックオフ定数とし、d を最初の遅延とします。その後、再配信試行シーケンスは以下のようになります。

d, m*d, m*m*d, m*m*m*d, ...

collisionAvoidancePercent(double collisionAvoidancePercent)

15

競合の回避が有効になっている場合は、p を競合回避の割合 (パーセント) にします。競合回避ポリシーは、現在の値にその p% を足し引きした値を最大値および最小値とするランダムな値で、次の遅延を調整します。

deadLetterHandleNewException

true

Camel 2.15: デッドレターチャネルでメッセージの処理中に発生する例外を処理するかどうかを指定します。true の場合は、例外が処理され、WARN レベルでログに記録されます (そのため、デッドレターチャネルの完了が保証されます)。false の場合は例外が処理されないため、デッドレターチャネルは失敗し、新しい例外が伝播されます。

delayPattern(String delayPattern)

なし

Apache Camel 2.0: 「Redeliver Delay パターン」 を参照してください。

disableRedelivery()

true

Apache Camel 2.0: 再配信機能を無効にします。再配信を有効にするには、maximumRedeliveries() を正の整数値に設定します。

handled(boolean handled)

true

Apache Camel 2.0: true の場合は、メッセージがデッドレターチャネルに移動されたときに現在の例外が消去されます。false の場合は、例外はクライアントに伝播されます。

initialRedeliveryDelay(long initialRedeliveryDelay)

1000

最初の再配信を試みるまでの遅延 (ミリ秒単位) を指定します。

logNewException

true

デッドレターチャネルで例外が発生した場合に WARN レベルでログに記録するかどうかを指定します。

logStackTrace(boolean logStackTrace)

false

Apache Camel 2.0: true の場合は、JVM スタックトレースがエラーログに含まれます。

maximumRedeliveries(int maximumRedeliveries)

0

Apache Camel 2.0: 配信の最大試行回数。

maximumRedeliveryDelay(long maxDelay)

60000

Apache Camel 2.0: 指数バックオフストラテジーを使用する場合 ( useExponentialBackOff() を参照)、理論的には再配信の遅延が無制限に増加する可能性があります。このプロパティーは再配信の遅延の上限を指定します (ミリ秒単位)。

onRedelivery(Processor processor)

なし

Apache Camel 2.0: 再配信を試みる前に呼び出されるプロセッサーを設定します。

redeliveryDelay(long int)

0

Apache Camel 2.0: 再配信の試行間の遅延 (ミリ秒単位) を指定します。Apache Camel 2.16.0: デフォルトの再配信遅延は 1 秒です。

retriesExhaustedLogLevel(LoggingLevel logLevel)

LoggingLevel.ERROR

Apache Camel 2.0: 配信の失敗をログに記録するログレベルを指定します (org.apache.camel.LoggingLevel 定数として指定されます)。

retryAttemptedLogLevel(LoggingLevel logLevel)

LoggingLevel.DEBUG

Apache Camel 2.0: 再配信の試行に対するログレベルを指定します (org.apache.camel.LoggingLevel 定数として指定されます)。

useCollisionAvoidance()

false

競合の回避を有効にします。これにより、一定のランダム化をバックオフのタイミングに追加して競合の可能性を低減します。

useOriginalMessage()

false

Apache Camel 2.0: この機能が有効な場合、デッドレターチャネルに送信されたメッセージは、ルートの開始時に存在した (from() ノードで) のメッセージエクスチェンジのコピーになります。

useExponentialBackOff()

false

指数バックオフを有効にします。

再配信ヘッダー

Apache Camel がメッセージの再配信を試みると、表6.2「デッドレター再配信ヘッダー」 に記載されているヘッダーを In メッセージに自動設定します。

表6.2 デッドレター再配信ヘッダー

ヘッダー名説明

CamelRedeliveryCounter

Integer

Apache Camel 2.0: 配信に失敗した回数を返します。この値は、Exchange.REDELIVERY_COUNTER にも設定されます。

CamelRedelivered

Boolean

Apache Camel 2.0: 再配信が 1 回以上試行された場合は true です。この値は、Exchange.REDELIVERED にも設定されます。

CamelRedeliveryMaxCounter

Integer

Apache Camel 2.6: 再配信の最大回数の設定を保持します (Exchange.REDELIVERY_MAX_COUNTER エクスチェンジプロパティーにも設定されます)。retryWhile を使用する場合や、再配信の最大回数が無制限に設定されている場合は、このヘッダーは設定されません。

再配信エクスチェンジプロパティー

Apache Camel がメッセージの再配信を試みると、表6.3「再配信エクスチェンジプロパティー」 に記載されているエクスチェンジプロパティーを自動設定します。

表6.3 再配信エクスチェンジプロパティー

エクスチェンジプロパティー名説明

Exchange.FAILURE_ROUTE_ID

String

失敗したルートのルート ID を提供します。このプロパティーのリテラル名は CamelFailureRouteId です。

元のメッセージの使用

Apache Camel 2.0 で利用可能: エクスチェンジオブジェクトはルートを通過する際に変更される可能性があります。そのため、例外が発生したときに現行であるエクスチェンジがデッドレターチャネルの保存に適したコピーであるとは限りません。多くの場合、ルートによる変換の対象となる前に、ルート開始時に到達したメッセージをログに記録することが推奨されます。たとえば、以下のルートを見てみましょう。

from("jms:queue:order:input")
       .to("bean:validateOrder");
       .to("bean:transformOrder")
       .to("bean:handleOrder");

上記のルートは受信 JMS メッセージをリッスンした後、validateOrdertransformOrder、および 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 オプションは、再配信回数の特定範囲に遅延を指定するために使用されます。Delay パターンの構文は limit1:delay1;limit2:delay2;limit3:delay3;… のようになります。ここで、各 delayN は範囲 limitNredeliveryCount < 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_ENDPOINTCamelToEndpoint と同じ文字列定数になります。このプロパティーは、Camel がメッセージを 任意 のエンドポイントに送信するたびに更新されます。

ルーティング中にエラーが発生し、エクスチェンジがデッドレターキューに移動された場合、Apache Camel は CamelFailureEndpoint という名前のプロパティーを追加で設定します。これは、エラーが発生する前にエクスチェンジが最後に送信された宛先を特定します。したがって、以下のコードを使用すると、デッドレターキュー内から失敗したエンドポイントにアクセスできます。

// Java
String failedEndpointUri = exchange.getProperty(Exchange.FAILURE_ENDPOINT, String.class);

ここで、Exchange.FAILURE_ENDPOINTCamelFailureEndpoint と同じ文字列定数になります。

注記

これらのプロパティーは、指定の宛先エンドポイントの処理が完了したに障害が発生した場合でも、現在のエクスチェンジで設定された状態を維持します。たとえば、以下のルートを見てみましょう。

        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));
注記

後方互換性の理由から、allowRedeliveryWhileStopping オプションはデフォルトで 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() 句を定義することができます。たとえば、NullPointerExceptionIOException、および 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 コマンドを使用して Dead Letter Channel のエンドポイントを指定します。また、onException() 句で他の Java DSL コマンドを呼び出すこともできます。たとえば、前述の例は setHeader() を呼び出して、messageInfo という名前のメッセージヘッダーにエラーの情報を記録します。

この例では、NullPointerException および IOException 例外タイプが設定されています。その他のすべての例外タイプは、汎用 Exception 例外インターセプターによって処理されます。デフォルトでは、Apache Camel はスローされた例外に最も一致する例外インターセプターを適用します。完全に一致するものが見つからない場合は、最も近いベースタイプなどとの一致を試みます。最後に、他のインターセプターと一致しない場合、その Exception タイプのインターセプターは残りの例外すべてと一致します。

OnPrepareFailure

デッドレターキューにエクスチェンジを渡す前に、onPrepare オプションを使用してカスタムプロセッサーがエクスチェンジを準備できるようにすることができます。これにより、エクスチェンジ失敗の原因など、エクスチェンジに関する情報を追加できます。たとえば、以下のプロセッサーは例外メッセージが含まれるヘッダーを追加します。

public class MyPrepareProcessor implements Processor {
    @Override
    public void process(Exchange exchange) throws Exception {
        Exception cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
        exchange.getIn().setHeader("FailedBecause", cause.getMessage());
    }
}

以下のように、プロセッサーを使用するようにエラーハンドラーを設定できます。

errorHandler(deadLetterChannel("jms:dead").onPrepareFailure(new MyPrepareProcessor()));

ただし、onPrepare オプションは、デフォルトのエラーハンドラーで使用することもできます。

<bean id="myPrepare"
class="org.apache.camel.processor.DeadLetterChannelOnPrepareTest.MyPrepareProcessor"/>

<errorHandler id="dlc" type="DeadLetterChannel" deadLetterUri="jms:dead" onPrepareFailureRef="myPrepare"/>

6.4. Guaranteed Delivery

概要

Guaranteed Delivery (保証付き配信) とは、メッセージがメッセージチャネルに配置されると、アプリケーションの一部が失敗してもメッセージが宛先に到達することを保証することです。通常は 図6.4「Guaranteed Delivery パターン」 のように、宛先への配信を試行する前にメッセージを永続ストレージに書き込むことで、メッセージングシステムは Guaranteed Delivery パターンを実装します。

図6.4 Guaranteed Delivery パターン

Guaranteed delivery pattern

Guaranteed Delivery をサポートするコンポーネント

以下の Apache Camel コンポーネントは Guranteed Delivery パターンをサポートします。

JMS

JMS では、deliveryPersistent クエリーオプションはメッセージの永続ストレージが有効であるかどうかを示します。永続的な配信を有効にするのがデフォルトの動作であるため、通常はこのオプションを設定する必要はありません。Guaranteed Delivery の詳細をすべて設定するには、JMS プロバイダーで設定オプションを設定する必要があります。これらの情報は、使用している JMS プロバイダーによって異なります。たとえば、MQSeries、TibCo、BEA、Sonic などがありますが、いずれも Guaranteed Delivery をサポートするためにさまざまサービスを提供しています。

詳細は、『Apache Camel Component Reference Guide』の「Jms」を参照してください。

ActiveMQ

ActiveMQ では、メッセージの永続性はデフォルトで有効になっています。ActiveMQ はバージョン 5 以降、AMQ メッセージストアをデフォルトの永続メカニズムとして使用します。ActiveMQ でメッセージの永続化を有効にする方法は複数あります。

最も簡単なオプション (図6.4「Guaranteed Delivery パターン」 とは異なる) は、中央のブローカーで永続性を有効にし、信頼できるプロトコルを使用してそのブローカーに接続することです。メッセージが中央のブローカーに送信された後、コンシューマーへの配信が保証されます。たとえば、Apache Camel 設定ファイル META-INF/spring/camel-context.xml では、以下のように OpenWire/TCP プロトコルを使用して中央のブローカーに接続するように ActiveMQ コンポーネントを設定できます。

<beans ... >
  ...
  <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="brokerURL" value="tcp://somehost:61616"/>
  </bean>
  ...
</beans>

リモートエンドポイントに送信される前にメッセージがローカルに保存されるアーキテクチャーを実装する場合 (図6.4「Guaranteed Delivery パターン」 のように)、Apache Camel アプリケーションで組み込みブローカーをインスタンス化してこれを行います。これは、ActiveMQ Peer-to-Peerプロトコルを使用すると簡単に実現できます。これにより、暗黙的に埋め込みブローカーが作成され、他のピアエンドポイントと通信します。たとえば、ActiveMQ コンポーネントを camel-context.xml で以下のように設定し、GroupA 内のすべてのピアに接続するようにします。

<beans ... >
  ...
  <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="brokerURL" value="peer://GroupA/broker1"/>
  </bean>
  ...
</beans>

broker1 は、埋め込みブローカーのブローカー名に置き換えます (グループの他のピアは異なるブローカー名を使用する必要があります)。Peer-to-Peer プロトコルの 1 つの制限は、IP マルチキャストに依存してグループ内の他のピアを見つけることです。これにより、ワイドエリアネットワークでの使用には適していません (また、IP マルチキャストが有効になっていない一部のローカルエリアネットワークにも適していません)。

組み込みブローカーインスタンスに接続する ActiveMQ の VM プロトコルを利用すると、ActiveMQ コンポーネントでより柔軟に組み込みブローカーを作成できます。必要な名前のブローカーが存在しない場合は、VM プロトコルによって自動的に作成されます。このメカニズムを使用すると、カスタム設定で組み込みブローカーを作成できます。以下に例を示します。

<beans ... >
  ...
  <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="brokerURL" value="vm://broker1?brokerConfig=xbean:activemq.xml"/>
  </bean>
  ...
</beans>

activemq.xml は、組み込みブローカーインスタンスを設定する ActiveMQ ファイルに置き換えます。ActiveMQ 設定ファイル内で、以下の永続メカニズムのいずれかを有効にすることができます。

  • AMQ 永続化 (デフォルト): ActiveMQ にネイティブな高速で信頼できるメッセージストア。詳細は「amqPersistenceAdapter」および「AMQ Message Store」を参照してください。
  • JDBC 永続化: JDBC を使用して、JDBC 互換データベースにメッセージを格納します。詳細は、「jdbcPersistenceAdapter」および「ActiveMQ Persistence」を参照してください。
  • ジャーナル永続化: メッセージをローリングログファイルに格納する高速の永続化メカニズム。詳細は「journalPersistenceAdapter」および「ActiveMQ Persistence」を参照してください。
  • Kaha 永続性: ActiveMQ に特化して開発された永続メカニズム。詳細は「kahaPersistenceAdapter」および「ActiveMQ Persistence」を参照してください。

詳細は、『Apache Camel Component Reference Guide』の「ActiveMQ」を参照してください。

ActiveMQ ジャーナル

ActiveMQ Journal コンポーネントは、複数の同時実行プロデューサーがメッセージをキューに書き込み、アクティブなコンシューマーが 1 つのみである特殊なユースケースに対して最適化されています。メッセージはローリングログファイルに格納され、効率を向上するために同時書き込みは集約されます。

6.5. Message Bus

概要

Message Bus は、図6.5「Message Bus パターン」 に示されているメッセージングアーキテクチャーを意味します。これにより、多様なコンピューティングプラットフォーム上で実行されている多様なアプリケーションに接続できます。実質的に、Message Bus は Apache Camel とそのコンポーネントによって構成されます。

図6.5 Message Bus パターン

Message bus pattern

Message Bus パターンの以下の機能は Apache Camel に反映されます。

  • 共通通信インフラストラクチャー: ルーター自体が Apache Camel の共通通信インフラストラクチャーの中核を提供します。しかし、一部のメッセージバスアーキテクチャーとは対照的に、Apache Camel は異種インフラストラクチャーを提供します。このインフラストラクチャーでは、多種多様なトランスポートとメッセージ形式を使用してメッセージをバスに送信できます。
  • アダプター: 必要に応じて、Apache Camel は異なるトランスポートを使用してメッセージ形式を変換し、メッセージを伝播できます。実質的に、Apache Camel はアダプターのように動作できるため、外部アプリケーションはメッセージングプロトコルをリファクタリングせずに Message Bus に接続できます。

    場合によっては、アダプターを直接外部アプリケーションに統合することもできます。たとえば、サービスが JAX-WS および JAXB マッピングを使用して実装される Apache CXF を使用してアプリケーションを開発する場合は、多種多様のトランスポートをサービスにバインドできます。これらのトランスポートバインディングはアダプターとして機能します。

第7章 メッセージの構築

概要

メッセージ構築パターンでは、システムを通過するメッセージのさまざまな形式と関数が記述されます。

7.1. 相関識別子

概要

図7.1「相関識別子パターン」相関識別子 (Correlation Identifier) パターンは 、非同期メッセージングシステムを使用してリクエスト−リプライ型プロトコルを実装する場合にリクエストメッセージとリプライメッセージの照合方法を記述します。リクエストメッセージはリクエストメッセージを識別する一意のトークンである リクエスト ID によって生成されます。リプライメッセージには、一致するリクエスト ID が含まれるトークンである 相関 ID が含まれる必要があります。

Apache Camel は、メッセージでヘッダーを取得または設定することにより、EIP パターンからの相関識別子をサポートします。

ActiveMQ または JMS コンポーネントを使用する場合、相関識別子ヘッダーは JMSCorrelationID と呼ばれます。独自の相関識別子をメッセージ交換に追加すると、1 つの会話 (またはビジネスプロセス) で複数のメッセージを一緒に関連付けることができます。通常、相関識別子は Apache Camel メッセージヘッダーに格納されます。

一部の EIP パターンでは、サブメッセージがスピンオフされます。この場合、Apache Camel は、ソースエクスチェンジにリンクする Exchange.CORRELATION_ID キーのあるプロパティーとして相関 ID をエクスチェンジに追加します。たとえば、SplitterMulticastRecipient List、および Wire Tap EIP がこれを行います。

図7.1 相関識別子パターン

Correlation identifier pattern

7.2. イベントメッセージ

イベントメッセージ

Camel は、メッセージ で交換パターンをサポートすることにより、エンタープライズ統合パターン からの イベントメッセージ (Event Message) をサポートします。メッセージInOnly を設定すると、一方向のイベントメッセージであることを示すことができます。その後、Camel の Apache Camel Component Reference は基盤のトランスポートまたはプロトコルを使用して、このパターンを実装します。

event message solution

多くの Apache Camel Component Reference のデフォルト動作は、JMSFile、または SEDA などに対して InOnly です。

明示的な InOnly の指定

デフォルトが InOut であるコンポーネントを使用している場合、pattern プロパティーを使用して、エンドポイントの メッセージ交換パターン を上書きできます。

foo:bar?exchangePattern=InOnly

Camel 2.0 以降では、DSL を使用して メッセージ交換パターン を指定できます。

Fluent Builder (流れるようなビルダー) の使用

from("mq:someQueue").
  inOnly().
  bean(Foo.class);

または、明示的なパターンでエンドポイントを呼び出すこともできます。

from("mq:someQueue").
  inOnly("mq:anotherQueue");

Spring XML エクステンションの使用

<route>
    <from uri="mq:someQueue"/>
    <inOnly uri="bean:foo"/>
</route>
<route>
    <from uri="mq:someQueue"/>
    <inOnly uri="mq:anotherQueue"/>
</route>

7.3. 返信先アドレス

返信先アドレス

Apache Camel は、JMSReplyTo ヘッダーを使用して、エンタープライズ統合パターン からの 返信先アドレス (Return Address) をサポートします。

return address solution

たとえば、InOutJMS を使用する場合、コンポーネントはデフォルトで JMSReplyTo で指定されたアドレスに返されます。

リクエスト側コード

 getMockEndpoint("mock:bar").expectedBodiesReceived("Bye World");
 template.sendBodyAndHeader("direct:start", "World", "JMSReplyTo", "queue:bar");

Fluent Builder (流れるようなビルダー) を使用したルート

 from("direct:start").to("activemq:queue:foo?preserveMessageQos=true");
 from("activemq:queue:foo").transform(body().prepend("Bye "));
 from("activemq:queue:bar?disableReplyTo=true").to("mock:bar");

Spring XML エクステンションを使用したルート

 <route>
   <from uri="direct:start"/>
   <to uri="activemq:queue:foo?preserveMessageQos=true"/>
 </route>

 <route>
   <from uri="activemq:queue:foo"/>
   <transform>
       <simple>Bye ${in.body}</simple>
   </transform>
 </route>

 <route>
   <from uri="activemq:queue:bar?disableReplyTo=true"/>
   <to uri="mock:bar"/>
 </route>

このパターンの完全な例については、JUnit のテストケースを参照してください。

第8章 メッセージのルーティング

概要

メッセージルーティングパターンでは、メッセージチャネルを互いにリンクするさまざまな方法について説明します。これには、メッセージボディーは変更せずに、メッセージストリームに適用できる、さまざまなアルゴリズムが含まれています。

8.1. Content-Based Router

概要

図8.1「Content-Based Router パターン」 に示されている Content-Based Router を使用すると、メッセージの内容に基づいて適切な宛先にメッセージをルーティングすることができます。

図8.1 Content-Based Router パターン

Content-based router pattern

Java DSL の例

以下の例は、入力エンドポイント seda:a からのリクエストを、さまざまな述語式の評価に応じて seda:bqueue:c、または seda:d のいずれかにルーティングする方法を示しています。

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        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>

8.2. Message Filter

概要

Massage Filter は、特定の基準に基づいて不要なメッセージを除外するプロセッサーです。Apache Camel では、図8.2「Message Filter パターン」 に示される Message Filter パターンは、Java DSL コマンド filter() によって実装されています。この filter() コマンドは単一の述語引数を取り、フィルターを制御します。述語が true の場合、受信メッセージの処理は許可され、述語が false の場合、受信メッセージはブロックされます。

図8.2 Message Filter パターン

Message filter pattern

Java DSL の例

以下の例は、エンドポイント seda:a からエンドポイント seda:b へのルートを作成し、foo ヘッダーに bar という値を持つメッセージ以外のすべてのメッセージをブロックする方法を示しています。

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("seda:a").filter(header("foo").isEqualTo("bar")).to("seda:b");
    }
};

より複雑なフィルター述語を評価するため、XPath、XQuery、SQL (パートII「ルーティング式と述語言語」 を参照) などのサポートされているスクリプト言語のいずれかを呼び出すことができます。以下の例は、name 属性が James と等しい person 要素を含むメッセージ以外のすべてのメッセージをブロックするルートを定義しています。

from("direct:start").
        filter().xpath("/person[@name='James']").
        to("mock:result");

XML 設定例

以下の例は、XML で XPath 述語を使用してルートを定義する方法を示しています (パートII「ルーティング式と述語言語」 を参照)。

<camelContext id="simpleFilterRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <filter>
      <xpath>$foo = 'bar'</xpath>
      <to uri="seda:b"/>
    </filter>
  </route>
  </camelContext>
フィルタリングされたエンドポイントは </filter> タグの中に定義

フィルタリングしたいエンドポイント (例: <to uri="seda:b"/>) は、クロージングタグ </filter> の前に定義する必要があります。そうでない場合は、フィルターは反映されません (2.8 以降で省略するとエラーになります)。

Bean を使用したフィルタリング

フィルターの動作を定義するために Bean を使用した例を以下に示します。

from("direct:start")
     .filter().method(MyBean.class, "isGoldCustomer").to("mock:result").end()
     .to("mock:end");

public static class MyBean {
    public boolean isGoldCustomer(@Header("level") String level) {
        return level.equals("gold");
    }
}

stop() の使用

Camel 2.0 で利用可能

stop は、すべて のメッセージをフィルタリングする特別なタイプのフィルターです。stop は、Content-Based Router 内のいずれかの述語で停止する必要がある場合に使用すると便利です。

以下の例では、メッセージボディーに Bye という単語を含むメッセージが、ルート内でこれ以上伝播しないようにします。.stop() を使用して when() 述語で防ぎます。

from("direct:start")
    .choice()
        .when(bodyAs(String.class).contains("Hello")).to("mock:hello")
        .when(bodyAs(String.class).contains("Bye")).to("mock:bye").stop()
        .otherwise().to("mock:other")
    .end()
    .to("mock:result");

エクスチェンジがフィルターされたかどうかの確認

Camel 2.5 で利用可能

メッセージフィルター EIP は、フィルターが適用されたかどうかを示すプロパティーを、エクスチェンジに追加します。

プロパティーキーは Exchange.FILTER_MATCHED で、String 値 CamelFilterMatched を持ちます。この値は、true または false を示すブール値です。値が true の場合、エクスチェンジはフィルターブロック経由でルーティングされました。

8.3. Recipient List

概要

図8.3「Recipient List パターン」 に示されている Recipient List は、各受信メッセージを複数の異なる宛先に送信するルーターの一種です。また、Recipient List は通常、実行時に受信者リスト (Recipient List) を演算する必要があります。

図8.3 Recipient List パターン

Recipient list pattern

宛先が固定された Recipient List

最もシンプルな Recipient List は、宛先リストが固定され、事前に認識され、交換パターンは InOnly であるものです。この場合、宛先リストを to() Java DSL コマンドへハードワイヤーすることができます。

注記

ここで示す例は、宛先が固定された Recepient List のため、InOnly 交換パターン (Pipes and Filters パターンと似ています) で のみ 動作します。Out メッセージを使った交換パターンの Recipient List を作成したい場合は、代わりに Multicast パターン を使用してください。

Java DSL の例

以下の例は、コンシューマーエンドポイント queue:a から、InOnly エクスチェンジを固定された宛先リストにルーティングする方法を示しています。

from("seda:a").to("seda:b", "seda:c", "seda:d");

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>

実行時に演算された Recipient List

Recipient List パターンを使用するほとんどの場合で、宛先リストを実行時に演算する必要があります。これを行うには、recipientList() プロセッサーを使用します。このプロセッサーは、唯一の引数として宛先リストを取ります。Apache Camel はリスト引数に型コンバーターを適用するため、ほとんどの標準的な Java リスト型 (例: コレクション、リスト、配列など) を使用できます。型コンバーターの詳細については 「組み込み型コンバーター」 を参照してください。

受信者は 同じ エクスチェンジインスタンスのコピーを受け取り、Apache Camel はそれらを順次実行します。

Java DSL の例

以下の例は、 recipientListHeader という名前のメッセージヘッダーから宛先リストを抽出する方法を示しています。ヘッダーの値は、コンマ区切りのエンドポイント URI のリストになります。

from("direct:a").recipientList(header("recipientListHeader").tokenize(","));

header の値がリスト型である場合、値を直接 recipientList() の引数に使うことができます。以下に例を示します。

from("seda:a").recipientList(header("recipientListHeader"));

ただし、この例では、基礎となるコンポーネントがこの特定のヘッダーをどのように解析するかに完全に依存しています。コンポーネントがヘッダーを単純な文字列として解析する場合、この例は動作し ません。ヘッダーは、何らかの型の Java リストに変換する必要があります。

XML 設定の例

以下の例では、XML で前述のルートを定義する方法を示しています。ヘッダー値は、コンマ区切りのエンドポイント URI リストです。

<camelContext id="buildDynamicRecipientList" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <recipientList delimiter=",">
      <header>recipientListHeader</header>
    </recipientList>
  </route>
</camelContext>

複数の受信者に並列で送信

Camel 2.2 で利用可能

Recipient List パターン は、parallelProcessing をサポートしています。これは、Splitter パターンの機能に似ています。並列処理機能を使用して、複数の受信者に並列でエクスチェンジを送信します。以下に例を示します:

from("direct:a").recipientList(header("myHeader")).parallelProcessing();

Spring XML では、並列処理機能は recipientList タグの属性として実装されています。以下に例を示します。

<route>
  <from uri="direct:a"/>
  <recipientList parallelProcessing="true">
    <header>myHeader</header>
  </recipientList>
</route>

例外時に停止

Camel 2.2 で利用可能

Recipient List は、stopOnException 機能をサポートします。これを使用すると、受信者が失敗した場合にそれ以降の受信者への送信を停止することができます。

from("direct:a").recipientList(header("myHeader")).stopOnException();

Spring XML では、Recipient List タグの属性です。

Spring XML では、例外時の停止機能は recipientList タグの属性として実装されます。以下に例を示します。

<route>
  <from uri="direct:a"/>
  <recipientList stopOnException="true">
    <header>myHeader</header>
  </recipientList>
</route>
注記

parallelProcessingstopOnException を同じルートで組み合わせることができます。

無効なエンドポイントの無視

Camel 2.3 の時点で利用可能

Recipient List パターンignoreInvalidEndpoints オプションをサポートします。これにより、Recipient List が無効なエンドポイントをスキップできます (Routing Slips パターン も、このオプションをサポートしています)。以下に例を示します。

from("direct:a").recipientList(header("myHeader")).ignoreInvalidEndpoints();

Spring XML では、以下のように recipientList タグに ignoreInvalidEndpoints 属性を設定することで、このオプションを有効にすることができます。

<route>
  <from uri="direct:a"/>
  <recipientList ignoreInvalidEndpoints="true">
    <header>myHeader</header>
  </recipientList>
</route>

myHeader に 2 つのエンドポイント direct:foo,xxx:bar が含まれるケースを見てみましょう。最初のエンドポイントは有効であり、動作します。2 つ目は無効であるため、無視されます。無効なエンドポイントに遭遇する度に、Apache Camel ログが INFO レベルで記録されます。

カスタム AggregationStrategy の使用

Camel 2.2 で利用可能

Recipient List パターン でカスタム AggregationStrategy を使用できます。これは、リスト内の受信者からのリプライを集計する場合に便利です。Apache Camel はデフォルトで UseLatestAggregationStrategy 集約ストラテジーを使用して、最後に受信したリプライのみを保持します。より高度な集約ストラテジーについては、AggregationStrategy インターフェースを独自に実装し定義できます。詳細については 「Aggregator」 を参照してください。たとえば、集約ストラテジー MyOwnAggregationStrategy をリプライメッセージに適用するには、以下のように Java DSL ルートを定義します。

from("direct:a")
    .recipientList(header("myHeader")).aggregationStrategy(new MyOwnAggregationStrategy())
    .to("direct:b");

Spring XML では、以下のようにカスタム集約ストラテジーを recipientList タグの属性として指定できます。

<route>
  <from uri="direct:a"/>
  <recipientList strategyRef="myStrategy">
    <header>myHeader</header>
  </recipientList>
  <to uri="direct:b"/>
</route>

<bean id="myStrategy" class="com.mycompany.MyOwnAggregationStrategy"/>

カスタムスレッドプールの使用

Camel 2.2 で利用可能

これは、parallelProcessing を使用する場合にのみ必要です。デフォルトでは、Camel は 10 個のスレッドを持つスレッドプールを使用します。今後スレッドプールの管理と設定内容をメンテナンスする際に、変更される可能性がありますのでご注意ください (予定どおりであれば Camel 2.2) 。

カスタム集約ストラテジーを使用するのと同じように設定します。

メソッド呼び出しの Recipient List としての使用

受信者を生成するために、Bean を使用できます。以下に例を示します。

from("activemq:queue:test").recipientList().method(MessageRouter.class, "routeTo");

MessageRouter Bean は以下のように定義されます。

public class MessageRouter {

    public String routeTo() {
        String queueName = "activemq:queue:test2";
        return queueName;
    }
}

Recipient List としての Bean

Bean を Recipient List として動作させるには、@RecipientList アノテーションを Recipient List を返すメソッドに付与します。以下に例を示します。

public class MessageRouter {

    @RecipientList
    public String routeTo() {
        String queueList = "activemq:queue:test1,activemq:queue:test2";
        return queueList;
    }
}

この場合、ルートに recipientList DSL コマンドを含め ない でください。以下のようにルートを定義します。

from("activemq:queue:test").bean(MessageRouter.class, "routeTo");

タイムアウトの使用

Camel 2.5 で利用可能

parallelProcessing を使用する場合、timeout の合計値をミリ秒単位で設定できます。Camel はタイムアウトに達するまでメッセージを並行して処理します。これにより、1 つのメッセージが遅い場合でも処理を継続できます。

以下の例では、recipientlist ヘッダーの値が direct:a,direct:b,direct:c であるため、メッセージは 3 人の受信者に送信されます。250 ミリ秒のタイムアウトがあるので、最後の 2 つのメッセージだけが、タイムフレーム内で完了することができます。そのため、集約すると BC という文字列の結果が得られます。

from("direct:start")
    .recipientList(header("recipients"), ",")
    .aggregationStrategy(new AggregationStrategy() {
            public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
                if (oldExchange == null) {
                    return newExchange;
                }

                String body = oldExchange.getIn().getBody(String.class);
                oldExchange.getIn().setBody(body + newExchange.getIn().getBody(String.class));
                return oldExchange;
            }
        })
        .parallelProcessing().timeout(250)
    // use end to indicate end of recipientList clause
    .end()
    .to("mock:result");

from("direct:a").delay(500).to("mock:A").setBody(constant("A"));

from("direct:b").to("mock:B").setBody(constant("B"));

from("direct:c").to("mock:C").setBody(constant("C"));
注記

この timeout 機能は、splitter でもサポートされ、multicast および recipientList の両方でサポートされています。

デフォルトでは、タイムアウトが発生した場合に AggregationStrategy は呼び出されません。ただし、特殊なバージョンを実装することができます。

// Java
public interface TimeoutAwareAggregationStrategy extends AggregationStrategy {

    /**
     * A timeout occurred
     *
     * @param oldExchange  the oldest exchange (is <tt>null</tt> on first aggregation as we only have the new exchange)
     * @param index        the index
     * @param total        the total
     * @param timeout      the timeout value in millis
     */
    void timeout(Exchange oldExchange, int index, int total, long timeout);

これにより、必要であれば、AggregationStrategy でタイムアウトに対応することができます。

timeout は合計値

タイムアウトは合計です。つまり、X 時間後に Camel は期限内に完了したメッセージのみを集約します。残りの分はキャンセルされます。また Camel は、タイムアウトの原因となった最初のインデックスに対して、TimeoutAwareAggregationStrategytimeout メソッドを 1 度だけ呼び出します。

送信メッセージへのカスタム処理の適用

recipientList がメッセージを Recipient List のいずれかに送信する前に、元のメッセージのシャローコピーであるメッセージレプリカを作成します。シャローコピーでは、元のメッセージのヘッダーおよびペイロードは参照によってのみコピーされます。各新規コピーには、それらの要素独自のインスタンスが含まれていません。その結果、メッセージのシャローコピーがリンクされ、異なるエンドポイントにルーティングする際にカスタム処理を適用することはできません。

レプリカがエンドポイントに送信される前に、各メッセージレプリカに対して何らかのカスタム処理を行う場合は、recipientList 句で onPrepare DSL コマンドを呼び出すことができます。この onPrepare コマンドは、メッセージがシャローコピーされた 直後、かつメッセージがエンドポイントにディスパッチされる 直前 に、カスタムプロセッサーを挿入します。たとえば、以下のルートでは、CustomProc プロセッサーが 各 recipient エンドポイント 用のメッセージレプリカで呼び出されます。

from("direct:start")
  .recipientList().onPrepare(new CustomProc());

onPrepare DSL コマンドの一般的なユースケースとして、メッセージの一部またはすべての要素のディープコピーを実行します。これにより、各メッセージのレプリカは他のレプリカとは独立して変更することができます。たとえば、以下の CustomProc プロセッサークラスは、メッセージボディーのディープコピーを実行します。メッセージボディーの型は BodyType であると想定され、ディープコピーは BodyType.deepCopy() メソッドによって実行されます。

// Java
import org.apache.camel.*;
...
public class CustomProc implements Processor {

    public void process(Exchange exchange) throws Exception {
        BodyType body = exchange.getIn().getBody(BodyType.class);

        // Make a _deep_ copy of of the body object
        BodyType clone =  BodyType.deepCopy();
        exchange.getIn().setBody(clone);

        // Headers and attachments have already been
        // shallow-copied. If you need deep copies,
        // add some more code here.
    }
}

オプション

recipientList DSL コマンドは、以下のオプションをサポートします。

名前

デフォルト値

説明

delimiter

,

式が複数のエンドポイントを返した場合に使用される区切り文字。

strategyRef

 

AggregationStrategy を参照し、各受信者からのリプライを集約して 「Recipient List」 からの唯一となる送信メッセージを生成します。デフォルトでは、Camel は最後のリプライを送信メッセージとして使用します。

strategyMethodName

 

POJO を AggregationStrategy として使用している場合には、このオプションで使用するメソッド名を明示的に指定することができます。

strategyMethodAllowNull

false

POJO を AggregationStrategy として使用している場合には、このオプションを使用することができます。false に設定すると、エンリッチするデータがない場合に aggregate メソッドは使用されません。true に設定すると、エンリッチするデータがない場合には、oldExchangenull 値が使用されます。

parallelProcessing

false

Camel 2.2: 有効にすると、受信者へのメッセージ送信が並列処理されます。呼び出し元スレッドは、すべてのメッセージが完全に処理されるまで待機してから続行することに注意してください。受信者への送信と、受信者からのリプライ処理のみが並列で処理されます。

parallelAggregate

false

有効にすると、AggregationStrategyaggregate メソッドを並列に呼び出すことができます。これには、AggregationStrategy の実装がスレッドセーフである必要があることに注意してください。デフォルトでは、このオプションは false となっており、Camel は自動的に aggregate メソッドを同期呼び出しします。ただし、場合によっては、AggregationStrategy をスレッドセーフとして実装し、このオプションを true に設定することで、パフォーマンスを向上させることができます。

executorServiceRef

 

Camel 2.2: 並列処理に使用するカスタムスレッドプールを参照します。このオプションを設定すると並列処理は自動的に適用されるため、parallelAggregate オプションも有効にする必要はありません。

stopOnException

false

Camel 2.2: 例外発生時、すぐに継続処理を停止するかどうか。無効にすると、いずれかの失敗の有無に関わらず、Camel はメッセージをすべての受信者に送信します。AggregationStragegy クラス内で、例外処理を完全に制御することができます。

ignoreInvalidEndpoints

false

Camel 2.3: エンドポイント URI が解決できない場合、無視されます。もしくは、エンドポイント URI が有効ではないことを示す例外が発生します。

streaming

false

Camel 2.5: 有効な場合、Camel は返信を順不同で処理します (例: 戻って来た順) 。無効な場合、Camel は指定された式と同じ順序で返信を処理します。

timeout

 

camel 2.5: 合計タイムアウト値をミリ秒単位で設定します。「Recipient List」 が指定された時間枠内のすべての応答を送信および処理できない場合、タイムアウトが発生し、「Recipient List」 から抜け出し続行します。AggregationStrategy を提供した場合、timeout メソッドが抜け出す前に呼び出されるため、注意してください。

onPrepareRef

 

camel 2.8: カスタムプロセッサーを参照して、各受信者が受け取るエクスチェンジのコピーを準備します。これにより、必要に応じてメッセージのペイロードをディープクローンするなど、カスタムロジックを実行できます。

shareUnitOfWork

false

Camel 2.8: Unit of Work を共有すべきかどうか。詳細は、「Splitter」 で同じオプションを参照してください。

cacheSize

0

Camel 2.13.1/2.12.4: Routing Slip で再使用されるプロデューサーをキャッシュする ProducerCache のキャッシュサイズを設定できます。デフォルトのキャッシュサイズ 0 を使用します。値を -1 に設定すると、キャッシュをすべて無効にすることができます。

Recipient List で交換パターンを使用

デフォルトでは、Recipient List は既存の交換パターンを使用します。稀ではありますが、別の交換パターンを使用して受信者にメッセージを送信するケースがある可能性があります。

たとえば、InOnly として開始するルートがあるとします。Recipient List で InOut 交換パターンを使用する場合、受信者用エンドポイントで直接交換パターンを設定する必要があります。

以下の例は、新規ファイルが InOnly として開始され、Recipient List へルーティングされるルートを示しています。ActiveMQ(JMS) エンドポイントで InOut を使用したい場合、exchangePattern=InOut オプションを指定する必要があります。ただし、JMS リクエストやリプライをルーティングし続けるため、レスポンスは outbox ディレクトリー内にファイルとして保存されます。

from("file:inbox")
  // the exchange pattern is InOnly initially when using a file route
  .recipientList().constant("activemq:queue:inbox?exchangePattern=InOut")
  .to("file:outbox");
注記

InOut 交換パターンは、タイムアウト時にレスポンスを受け取る必要があります。ただし、レスポンスを受信できない場合は失敗します。

8.4. Splitter

概要

Splitter は、受信メッセージを一連の送信メッセージに分割するルーターの種類です。それぞれの送信メッセージには、元のメッセージの一部が含まれています。Apache Camel では、図8.4「Splitter パターン」 に示される Splitter パターンは、split() Java DSL コマンドによって実装されます。

図8.4 Splitter パターン

Splitter pattern

Apache Camel Splitter は、以下の 2 つのパターンをサポートします。

  • Simple Splitter: Splitter パターンを独自に実装します。
  • Splitter/Aggregator - Splitter パターンと Aggrigator パターンを組み合わせることで、メッセージの断片が処理された後に再結合されます。

Splitter はオリジナルのメッセージを分割する前に、オリジナルのメッセージのシャローコピーを作成します。シャローコピーでは、元のメッセージのヘッダーおよびペイロードは参照としてのみコピーされます。Splitter 自体は、結果として得られたメッセージの一部を異るエンドポイントにルーティングすることはありませんが、分割されたメッセージの一部は、セカンダリールーティングの影響を受ける可能性があります。

メッセージ部分はシャローコピーであるため、元のメッセージにリンクされたままになります。そのため、それらを単独で修正することはできません。複数のエンドポイントにルーティングする前に、メッセージの異なるコピーへカスタムロジックを適用する場合、splitter 句の onPrepareRef DSL オプションを使用して、オリジナルのメッセージのディープコピーを作成する必要があります。オプションの使用方法については、「オプション」 を参照してください。

Java DSL の例

以下の例は、seda:a から seda:b へのルートを定義し、受信メッセージの各行を個別の送信メッセージへ変換することでメッセージを分割しています。

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("seda:a")
          .split(bodyAs(String.class).tokenize("\n"))
          .to("seda:b");
    }
};

Splitter は式言語を使用できるため、XPath、XQuery、SQL などのサポートされているスクリプト言語を使用して、メッセージを分割することができます (パートII「ルーティング式と述語言語」 を参照)。以下の例は、受信メッセージから bar 要素を抽出し、それらを別々の送信メッセージに挿入しています。

from("activemq:my.queue")
  .split(xpath("//foo/bar"))
  .to("file://some/directory")

XML 設定例

以下の例は、XPath スクリプト言語を使用して、XML で Splitter ルートを定義する方法を示しています。

<camelContext id="buildSplitter" xmlns="http://camel.apache.org/schema/spring">
    <route>
      <from uri="seda:a"/>
      <split>
        <xpath>//foo/bar</xpath>
        <to uri="seda:b"/>
      </split>
    </route>
</camelContext>

XML DSL の tokenize 式を使用して、トークンを使い、ボディーまたはヘッダーを分割できます。tokenize 式は、tokenize 要素で定義します。以下の例では、メッセージボディーは \n 区切り文字を使用してトークン化されています。正規表現パターンを使用するには、tokenize 要素に regex=true を設定します。

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <split>
            <tokenize token="\n"/>
            <to uri="mock:result"/>
        </split>
    </route>
    </camelContext>

行のグループに分割

大きなファイルを 1000 行のブロックに分割するには、以下のように Splitter ルートを定義します。

from("file:inbox")
    .split().tokenize("\n", 1000).streaming()
       .to("activemq:queue:order");

tokenize への第 2 引数は、1 つのチャンクにグループ化されるべき行数を指定します。streaming() 句は、ファイル全体を同時に読み取りしないよう Splitter に指示します (ファイルが大きい場合のパフォーマンスがはるかに改善されます)。

以下のように XML DSL で同じルートを定義できます。

<route>
  <from uri="file:inbox"/>
  <split streaming="true">
    <tokenize token="\n" group="1000"/>
    <to uri="activemq:queue:order"/>
  </split>
</route>

group オプションを使用した場合、常に java.lang.String 型が出力されます。

最初の項目のスキップ

メッセージの最初の項目をスキップするには、skipFirst オプションを使用します。

Java DSL では、tokenize パラメーターの 3 番目のオプションに true を指定します。

from("direct:start")
 // split by new line and group by 3, and skip the very first element
      .split().tokenize("\n", 3, true).streaming()
         .to("mock:group");

以下のように XML DSL で同じルートを定義できます。

<route>
  <from uri="file:inbox"/>
    <split streaming="true">
    <tokenize token="\n" group="1000" skipFirst="true" />
    <to uri="activemq:queue:order"/>
  </split>
</route>

Splitter のリプライ

Splitter に入るエクスチェンジが InOut メッセージ交換パターンである場合 (つまりリプライが想定される場合)、Splitter は元の入力メッセージのコピーを Out メッセージスロットのリプライメッセージとして返します。独自の 集約ストラテジー を実装して、このデフォルト動作をオーバーライドすることができます。

並列実行

生成されたメッセージを並行に実行したい場合、並列処理オプションを有効にして、生成されたメッセージを処理するためのスレッドプールをインスタンス化します。以下に例を示します。

XPathBuilder xPathBuilder = new XPathBuilder("//foo/bar");
from("activemq:my.queue").split(xPathBuilder).parallelProcessing().to("activemq:my.parts");

並行 Splitter で使用される基盤となる ThreadPoolExecutor をカスタマイズすることができます。たとえば、以下のように Java DSL でカスタムエクゼキューターを指定することができます。

XPathBuilder xPathBuilder = new XPathBuilder("//foo/bar");
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 16, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
from("activemq:my.queue")
  .split(xPathBuilder)
  .parallelProcessing()
  .executorService(threadPoolExecutor)
  .to("activemq:my.parts");

以下のように、XML DSL でカスタムエクゼキューターを指定できます。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:parallel-custom-pool"/>
    <split executorServiceRef="threadPoolExecutor">
      <xpath>/invoice/lineItems</xpath>
      <to uri="mock:result"/>
    </split>
  </route>
</camelContext>

<bean id="threadPoolExecutor" class="java.util.concurrent.ThreadPoolExecutor">
  <constructor-arg index="0" value="8"/>
  <constructor-arg index="1" value="16"/>
  <constructor-arg index="2" value="0"/>
  <constructor-arg index="3" value="MILLISECONDS"/>
  <constructor-arg index="4"><bean class="java.util.concurrent.LinkedBlockingQueue"/></constructor-arg>
</bean>

Bean を使用した分割処理の実行

Splitter は 任意 の式を使用して分割処理ができるので、method() 式を呼び出すことで、Bean を使用して分割処理を実行できます。Bean は java.util.Collectionjava.util.Iterator、または配列などの反復可能な値を返します。

以下のルートは、mySplitterBean Bean インスタンスのメソッドを呼び出す method() 式を定義しています。

from("direct:body")
        // here we use a POJO bean mySplitterBean to do the split of the payload
        .split()
        .method("mySplitterBean", "splitBody")
        .to("mock:result");
from("direct:message")
        // here we use a POJO bean mySplitterBean to do the split of the message
        // with a certain header value
        .split()
        .method("mySplitterBean", "splitMessage")
        .to("mock:result");

mySplitterBeanMySplitterBean クラスのインスタンスで、以下のように定義されます。

public class MySplitterBean {

    /**
     * The split body method returns something that is iteratable such as a java.util.List.
     *
     * @param body the payload of the incoming message
     * @return a list containing each part split
     */
    public List<String> splitBody(String body) {
        // since this is based on an unit test you can of couse
        // use different logic for splitting as {router} have out
        // of the box support for splitting a String based on comma
        // but this is for show and tell, since this is java code
        // you have the full power how you like to split your messages
        List<String> answer = new ArrayList<String>();
        String[] parts = body.split(",");
        for (String part : parts) {
            answer.add(part);
        }
        return answer;
    }

    /**
     * The split message method returns something that is iteratable such as a java.util.List.
     *
     * @param header the header of the incoming message with the name user
     * @param body the payload of the incoming message
     * @return a list containing each part split
     */
    public List<Message> splitMessage(@Header(value = "user") String header, @Body String body) {
        // we can leverage the Parameter Binding Annotations
        // http://camel.apache.org/parameter-binding-annotations.html
        // to access the message header and body at same time,
        // then create the message that we want, splitter will
        // take care rest of them.
        // *NOTE* this feature requires {router} version >= 1.6.1
        List<Message> answer = new ArrayList<Message>();
        String[] parts = header.split(",");
        for (String part : parts) {
            DefaultMessage message = new DefaultMessage();
            message.setHeader("user", part);
            message.setBody(body);
            answer.add(message);
        }
        return answer;
    }
}

Splitter EIP で BeanIOSplitter オブジェクトを使用して、ストリームモードによりコンテンツ全体をメモリーに読み込まないようにしながら、大きなペイロードを分割することができます。以下の例は、クラスパスから読み込まれるマッピングファイルを使用して、 BeanIOSplitter オブジェクトを設定する方法を示しています:

注記

BeanIOSplitter クラスが Camel 2.18 で新たに追加されました。Camel 2.17 では利用できません。

BeanIOSplitter splitter = new BeanIOSplitter();
   splitter.setMapping("org/apache/camel/dataformat/beanio/mappings.xml");
   splitter.setStreamName("employeeFile");

    // Following is a route that uses the beanio data format to format CSV data
    // in Java objects:
    from("direct:unmarshal")
        // Here the message body is split to obtain a message for each row:
         .split(splitter).streaming()
         .to("log:line")
         .to("mock:beanio-unmarshal");

以下の例は、エラーハンドラーを追加しています:

BeanIOSplitter splitter = new BeanIOSplitter();
   splitter.setMapping("org/apache/camel/dataformat/beanio/mappings.xml");
   splitter.setStreamName("employeeFile");
   splitter.setBeanReaderErrorHandlerType(MyErrorHandler.class);
   from("direct:unmarshal")
      .split(splitter).streaming()
      .to("log:line")
      .to("mock:beanio-unmarshal");

エクスチェンジプロパティー

以下のプロパティーは、分割されたエクスチェンジごとに設定されます。

ヘッダー説明

CamelSplitIndex

int

Apache Camel 2.0: 各エクスチェンジが分割されるたびに増加するスプリットカウンター。カウンターは 0 から始まります。

CamelSplitSize

int

Apache Camel 2.0: 分割されたエクスチェンジの合計数。このヘッダーは、ストリームベースの分割には適用されません。

CamelSplitComplete

boolean

Apache Camel 2.4: このエクスチェンジが最後であるかどうか。

Splitter/Aggregator パターン

個々のコンポーネントの処理が完了した後、分割されたメッセージを単一のエクスチェンジに集約する一般的なパターンです。このパターンをサポートするために、split() DSL コマンドでは、第 2 引数として AggregationStrategy オブジェクトを指定することができます。

Java DSL の例

以下の例は、カスタム集約ストラテジーを使用して、すべての分割されたメッセージが処理された後に、メッセージを再結合する方法を示しています。

from("direct:start")
    .split(body().tokenize("@"), new MyOrderStrategy())
        // each split message is then send to this bean where we can process it
        .to("bean:MyOrderService?method=handleOrder")
        // this is important to end the splitter route as we do not want to do more routing
        // on each split message
    .end()
    // after we have split and handled each message we want to send a single combined
    // response back to the original caller, so we let this bean build it for us
    // this bean will receive the result of the aggregate strategy: MyOrderStrategy
    .to("bean:MyOrderService?method=buildCombinedResponse")

AggregationStrategy の実装

上記のルートで使用されるカスタム集約ストラテジー MyOrderStrategy は、以下のように実装されています。

/**
 * This is our own order aggregation strategy where we can control
 * how each split message should be combined. As we do not want to
 * lose any message, we copy from the new to the old to preserve the
 * order lines as long we process them
 */
public static class MyOrderStrategy implements AggregationStrategy {

    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        // put order together in old exchange by adding the order from new exchange

        if (oldExchange == null) {
            // the first time we aggregate we only have the new exchange,
            // so we just return it
            return newExchange;
        }

        String orders = oldExchange.getIn().getBody(String.class);
        String newLine = newExchange.getIn().getBody(String.class);

        LOG.debug("Aggregate old orders: " + orders);
        LOG.debug("Aggregate new order: " + newLine);

        // put orders together separating by semi colon
        orders = orders + ";" + newLine;
        // put combined order back on old to preserve it
        oldExchange.getIn().setBody(orders);

        // return old as this is the one that has all the orders gathered until now
        return oldExchange;
    }
}

ストリームベースの処理

並列処理を有効にすると、後ろの分割されたメッセージが、前の分割されたメッセージよりも先に、集約の準備が整うことが理論的に起こりえます。つまり、分割された個々のメッセージは、異る順序で Aggregator へ到着する可能性があります。デフォルトでは、Splitter 実装は分割されたメッセージを Aggregator へ渡す前に元の順序に再配置するため、これが発生しません。

分割されたメッセージの処理が完了次第集約したい場合は、以下のようにストリーミングオプションを有効にできます (メッセージの順序が乱れる可能性があります)。

from("direct:streaming")
  .split(body().tokenize(","), new MyOrderStrategy())
    .parallelProcessing()
    .streaming()
    .to("activemq:my.parts")
  .end()
  .to("activemq:all.parts");

以下に示すように、ストリーミングで使用するカスタムイテレーターを指定することもできます。

// Java
import static org.apache.camel.builder.ExpressionBuilder.beanExpression;
...
from("direct:streaming")
     .split(beanExpression(new MyCustomIteratorFactory(),  "iterator"))
     .streaming().to("activemq:my.parts")
ストリーミングおよび XPath

ストリーミングモードを XPath と併用することはできません。XPath は、メモリー内に完全な DOM XML ドキュメントを必要とします。

XML を使用したストリームベースの処理

受信メッセージが非常に大きな XML ファイルである場合、ストリーミングモードで tokenizeXML サブコマンドを使用して最も効率的に処理することができます

たとえば、order 要素のシーケンスを含む大きな XML ファイルの場合、以下のようなルートを使用してファイルを order 要素に分割することができます。

from("file:inbox")
  .split().tokenizeXML("order").streaming()
  .to("activemq:queue:order");

以下のようなルートを定義することで、XML でも同じことができます。

<route>
  <from uri="file:inbox"/>
  <split streaming="true">
    <tokenize token="order" xml="true"/>
    <to uri="activemq:queue:order"/>
  </split>
</route>

トークン要素のエンクロージング (ancestor) 要素で定義される namespace へのアクセスが必要になる場合がよくあります。namespace の定義を ancestor 要素のいずれかから token 要素にコピーするには、namespace 定義を継承する要素を指定する必要があります。

Java DSL で、ancestor 要素を tokenizeXML の 第 2 引数として指定します。たとえば、enclosing orders 要素から namespace 定義を継承します:

from("file:inbox")
  .split().tokenizeXML("order", "orders").streaming()
  .to("activemq:queue:order");

XML DSL では、inheritNamespaceTagName 属性を使用して ancestor 要素を指定します。以下に例を示します。

<route>
  <from uri="file:inbox"/>
  <split streaming="true">
    <tokenize token="order"
              xml="true"
              inheritNamespaceTagName="orders"/>
    <to uri="activemq:queue:order"/>
  </split>
</route>

オプション

split DSL コマンドは以下のオプションをサポートします。

名前

デフォルト値

説明

strategyRef

 

AggregationStrategy を参照し、分割されたメッセージのリプライを集約して、 「Splitter」 からの単一となる送信メッセージを生成します。デフォルトで使用されているものについては、「What does the splitter return」というタイトルの項を参照してください。

strategyMethodName

 

POJO を AggregationStrategy として使用している場合には、このオプションで使用するメソッド名を明示的に指定することができます。

strategyMethodAllowNull

false

POJO を AggregationStrategy として使用している場合には、このオプションを使用することができます。false に設定すると、エンリッチするデータがない場合に aggregate メソッドは使用されません。true に設定すると、エンリッチするデータがない場合には、oldExchangenull 値が使用されます。

parallelProcessing

false

有効にすると、分割されたメッセージの処理が並列で行われます。呼び出し元スレッドは、すべての分割されたメッセージが完全に処理されるまで待機してから続行することに注意してください。

parallelAggregate

false

有効にすると、AggregationStrategyaggregate メソッドを並列に呼び出すことができます。これには、AggregationStrategy の実装がスレッドセーフである必要があることに注意してください。デフォルトでは、このオプションは false となっており、Camel は自動的に aggregate メソッドを同期呼び出しします。ただし、場合によっては、AggregationStrategy をスレッドセーフとして実装し、このオプションを true に設定することで、パフォーマンスを向上させることができます。

executorServiceRef

 

並列処理に使用するカスタムスレッドプールを参照します。このオプションを設定すると、並列処理は自動的に適用されるため、 並列処理用オプションも有効にする必要はありません。

stopOnException

false

Camel 2.2: 例外発生時、すぐに継続処理を停止するかどうか。無効にすると、Camel は分割されたメッセージの 1 つが失敗しても、分割処理を継続します。AggregationStragegy クラス内で、例外処理を完全に制御することができます。

streaming

false

有効にすると、Camel はストリーミング方式で input メッセージを分割します。これにより、メモリーのオーバーヘッドが軽減されます。たとえば、大きなメッセージを分割する場合には、streaming オプションを有効にすることが推奨されます。streaming オプションが有効になっていると、分割されたメッセージのリプライは、順不同で集約されます (例: 分割後の処理が終了したメッセージ順)。無効な場合、Camel は分割された順序と同じ順序で、分割されたメッセージを集約します。

timeout

 

camel 2.5: 合計タイムアウト値をミリ秒単位で設定します。「Recipient List」 が指定された時間内に分割してすべてのリプライを処理できない場合、タイムアウト発生して 「Splitter」 から 抜け出し続行します。AggregationStrategy を提供した場合、timeout メソッドが抜け出す前に呼び出されるため、注意してください。

onPrepareRef

 

camel 2.8: カスタムプロセッサーを参照することで、エクスチェンジの分割されたメッセージが処理される前に準備をすることができます。これにより、必要に応じてメッセージのペイロードをディープクローンするなど、カスタムロジックを実行できます。

shareUnitOfWork

false

Camel 2.8: Unit of Work を共有すべきかどうか。詳細につていは以下をご覧ください

8.5. Aggregator

概要

図8.5「Aggreagator パターン」 に示されている Aggregator パターンにより、関連するメッセージのバッチを単一のメッセージにまとめることができます。

図8.5 Aggreagator パターン

Aggregator pattern

Aggregator の動作を制御するため、Apache Camel では以下のように『Enterprise Integration Patterns』で説明されているプロパティーを指定できます。

  • 相関式: 集約するメッセージを決定します。相関式は各受信メッセージに対して評価され、相関キー を生成します。同じ相関キーを持つ受信メッセージは、同じバッチにグループ化されます。たとえば、すべての 受信メッセージを 1 つのメッセージに集約したい場合は、定数式を使用することができあす。
  • 完了条件: メッセージのバッチが完了したかどうかを決定します。これは単純なサイズ制限として指定することもでき、より一般的には、バッチ完了を示すフラグを述語条件として指定することもできます。
  • 集約アルゴリズム: 特定の相関キーを持つメッセージエクスチェンジを単一のメッセージエクスチェンジに統合します。

たとえば、毎秒 30,000 通のメッセージを受信する株式市場のデータシステムについて考えてみましょう。GUI ツールがこのような大規模の更新レートに対応できない場合は、メッセージフローをスロットルダウンした方がよい場合があります。単純に最新の気配値を選択して古い値を破棄することで、入力される株の気配値を集約することができます(一部の履歴をキャプチャーしたい場合は、delta 処理アルゴリズムを適用することができます)。

注記

Aggregator はより多くの情報を含む ManagedAggregateProcessorMBean を使い、JMX へ登録されるようになりました。これにより、集約コントローラーを使い制御できるようになります。

Aggregator の仕組み

図8.6「Aggregator の実装」 は、A、B、C、D などの相関キーを持つエクスチェンジのストリームを使用して、Aggragator がどのように動作するか概要を示しています。

図8.6 Aggregator の実装

message routing 02

図8.6「Aggregator の実装」 に示されているエクスチェンジの受信ストリームは、以下のように処理されます。

  1. Correlator は、相関キーに基づいてエクスチェンジをソートします。各受信メッセージごとに相関式が評価され、相関キーを生成します。たとえば、図8.6「Aggregator の実装」 で示されているエクスチェンジでは、相関キーは A と評価されます。
  2. 集約ストラテジー は、同じ相関キーを持つエクスチェンジをマージします。新しいエクスチェンジ A が到達すると、Aggregator は集約リポジトリーで対応する 集約エクスチェンジ (A') を検索し、新しいエクスチェンジと結合します。

    特定の集約サイクルが完了するまで、受信したエクスチェンジは、対応する集約エクスチェンジへ継続的に集約されます。集約サイクルは、完了メカニズムのいずれかによって終了されるまで継続されます。

    注記

    Camel 2.16 から、新しい XSLT 集約ストラテジー により、2 つのメッセージを XSLT ファイルでマージできるようになりました。ツールボックスから、AggregationStrategies.xslt() ファイルにアクセスできます。

  3. Aggregator に完了述語が指定された場合、集約エクスチェンジをテストし、ルートの次のプロセッサーに送信する準備ができているかどうかを判断します。以下のように処理を続けます。

    • 完了したら、集約エクスチェンジはルートの後半部分で処理されます。2 つの代替えモデルがあります。1 つは、同期 (デフォルト) で、呼び出しスレッドがブロックされます。2 つ目は 非同期 (並列処理が有効になっている場合) で、集約エクスチェンジはエクゼキュータースレッドプールに送信されます (図8.6「Aggregator の実装」 を参照)。
    • 完了していない場合、集約エクスチェンジは集約リポジトリーに戻されます。
  4. 同期的な完了テストの他、completionTimeout オプションまたは completionInterval オプションの いずれか を有効にすることで、非同期的な完了テストを有効にすることができます。これらの完了テストは別のスレッドで実行され、完了テストを満すたびに、対応するエクスチェンジが完了としてマークされ、ルートの後半部分によって処理されます (並列処理が有効かどうかによって、同期または非同期的に処理されます)。
  5. 並列処理が有効な場合、スレッドプールがルートの後半部分でエクスチェンジを処理します。デフォルトでは、このスレッドプールには 10 個のスレッドが含まれますが、プールをカスタマイズすることもできます (「スレッドオプション」)。

Java DSL の例

以下の例は、UseLatestAggregationStrategy 集計ストラテジーを使用して、同じ StockSymbol ヘッダー値を持つエクスチェンジを集約しています。指定された StockSymbol 値について、その相関キーを持つエクスチェンジを最後に受信してから 3 秒以上経過すると、集約されたエクスチェンジは完了とみなされ、mock エンドポイントに送信されます。

from("direct:start")
    .aggregate(header("id"), new UseLatestAggregationStrategy())
        .completionTimeout(3000)
    .to("mock:aggregated");

XML DSL の例

以下の例は、Spring XML で同じルートを定義する方法を示しています。

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <aggregate strategyRef="aggregatorStrategy"
                   completionTimeout="3000">
            <correlationExpression>
                <simple>header.StockSymbol</simple>
            </correlationExpression>
            <to uri="mock:aggregated"/>
        </aggregate>
    </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.aggregate.UseLatestAggregationStrategy"/>

相関式の指定

Java DSL では、相関式は常に第 1 引数として aggregate() DSL コマンドに渡されます。ここでは、Simple 式言語の使用に制限はありません。XPath、XQuery、SQL などの式言語やスクリプト言語を使用して、相関式を指定することができます。

例えば、XPath 式を使用してエクスチェンジを相関させるには、以下の Java DSL ルートを使用します。

from("direct:start")
    .aggregate(xpath("/stockQuote/@symbol"), new UseLatestAggregationStrategy())
        .completionTimeout(3000)
    .to("mock:aggregated");

特定の受信エクスチェンジで相関式を評価することができない場合、Aggregator はデフォルトで CamelExchangeException をスローします。ignoreInvalidCorrelationKeys オプションを設定して、この例外を抑制することができます。たとえば、Java DSL の場合は以下のようになります。

from(...).aggregate(...).ignoreInvalidCorrelationKeys()

XML DSL では、以下のように ignoreInvalidCorrelationKeys オプションを属性として設定できます。

<aggregate strategyRef="aggregatorStrategy"
           ignoreInvalidCorrelationKeys="true"
           ...>
    ...
</aggregate>

集約ストラテジーの指定

Java DSL では、集約ストラテジーを第 2 引数として aggregate() DSL コマンドに渡すか、aggregationStrategy() 句を使用して指定できます。たとえば、以下のように aggregationStrategy() 句を使用できます。

from("direct:start")
    .aggregate(header("id"))
        .aggregationStrategy(new UseLatestAggregationStrategy())
        .completionTimeout(3000)
    .to("mock:aggregated");

Apache Camel は、以下の基本的な集約ストラテジーを提供しています (各クラスは org.apache.camel.processor.aggregate Java パッケージ配下に属します)。

UseLatestAggregationStrategy
指定された相関キーの最後のエクスチェンジを返し、このキーとの以前のエクスチェンジをすべて破棄します。たとえば、このストラテジーは、特定の株式シンボルの最新価格のみを確認したい場合、証券取引所からのフィードをスロットリングするのに役立ちます。
UseOriginalAggregationStrategy
指定された相関キーの最初のエクスチェンジを返し、このキーを持つそれ以降のすべてのエクスチェンジを破棄します。このストラテジーを使用するには、UseOriginalAggregationStrategy.setOriginal() を呼び出して、最初のエクスチェンジを設定する必要があります。
GroupedExchangeAggregationStrategy
指定された相関キーの すべて のエクスチェンジをリストに連結し、Exchange.GROUPED_EXCHANGE エクスチェンジプロパティーに保存します。「グループ化されたエクスチェンジ」 を参照してください。

カスタム集約ストラテジーの実装

別の集計ストラテジーを適用したい場合は、以下の集計ストラテジーのベースとなるインターフェースのいずれかを実装することができます。

org.apache.camel.processor.aggregate.AggregationStrategy
基本的な Aggregation Strategy インターフェース。
org.apache.camel.processor.aggregate.TimeoutAwareAggregationStrategy

集約サイクルのタイムアウト時にお使いの実装で通知を受け取る場合は、このインターフェースを実装します。timeout 通知メソッドには、以下の署名があります。

void timeout(Exchange oldExchange, int index, int total, long timeout)
org.apache.camel.processor.aggregate.CompletionAwareAggregationStrategy

集約サイクルが正常に完了したときにお使いの実装で通知を受け取る場合は、このインターフェースを実装します。通知メソッドには、以下の署名があります。

void onCompletion(Exchange exchange)

たとえば、以下のコードは、StringAggregationStrategy および ArrayListAggregationStrategy の、2 つのカスタム集計ストラテジーを示しています。

 //simply combines Exchange String body values using '' as a delimiter
 class StringAggregationStrategy implements AggregationStrategy {

     public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
         if (oldExchange == null) {
             return newExchange;
         }

         String oldBody = oldExchange.getIn().getBody(String.class);
         String newBody = newExchange.getIn().getBody(String.class);
         oldExchange.getIn().setBody(oldBody + "" + newBody);
         return oldExchange;
     }
 }

 //simply combines Exchange body values into an ArrayList<Object>
 class ArrayListAggregationStrategy implements AggregationStrategy {

     public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
 	    Object newBody = newExchange.getIn().getBody();
     	ArrayList<Object> list = null;
         if (oldExchange == null) {
 		    list = new ArrayList<Object>();
 		    list.add(newBody);
 		    newExchange.getIn().setBody(list);
 		    return newExchange;
         } else {
 	        list = oldExchange.getIn().getBody(ArrayList.class);
 	    	list.add(newBody);
 		    return oldExchange;
 	    }
     }
 }
注記

Apache Camel 2.0 以降、AggregationStrategy.aggregate() コールバックメソッドも最初のエクスチェンジに対して呼び出されます。aggregate メソッドの最初の呼び出しでは、oldExchange パラメーターは null であり、newExchange パラメーターに最初の受信エクスチェンジが含まれます。

カスタムストラテジークラス ArrayListAggregationStrategy を使用してメッセージを集約するには、以下のようなルートを定義します。

from("direct:start")
    .aggregate(header("StockSymbol"), new ArrayListAggregationStrategy())
    .completionTimeout(3000)
    .to("mock:result");

以下のように、XML でカスタム集約ストラテジーを使用してルートを設定することもできます。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <aggregate strategyRef="aggregatorStrategy"
               completionTimeout="3000">
      <correlationExpression>
        <simple>header.StockSymbol</simple>
      </correlationExpression>
      <to uri="mock:aggregated"/>
    </aggregate>
  </route>
</camelContext>

<bean id="aggregatorStrategy" class="com.my_package_name.ArrayListAggregationStrategy"/>

カスタム集約ストラテジーのライフサイクルの管理

カスタム集約ストラテジーを実装し、そのライフサイクルを、それを管理しているエンタープライズインテグレーションパターンのライフサイクルに合わせることができます。これは、集約ストラテジーが正常にシャットダウンできることを保証するのに役立ちます。

ライフサイクルをサポートする集約ストラテジーを実装するには、org.apache.camel.Service インターフェースを実装し (AggregationStrategy インターフェースに加えて)、start() および stop() ライフサイクルメソッドの実装を提供する必要があります。たとえば、以下のコード例は、ライフサイクルをサポートする集約ストラテジーの概要を示しています。

// Java
import org.apache.camel.processor.aggregate.AggregationStrategy;
import org.apache.camel.Service;
import java.lang.Exception;
...
class MyAggStrategyWithLifecycleControl
       implements AggregationStrategy, Service {

    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        // Implementation not shown...
        ...
    }

    public void start() throws Exception {
        // Actions to perform when the enclosing EIP starts up
        ...
    }

    public void stop() throws Exception {
        // Actions to perform when the enclosing EIP is stopping
        ...
    }
}

エクスチェンジプロパティー

以下のプロパティーは、集約された各エクスチェンジ毎に設定されます。

ヘッダー集約されたエクスチェンジ プロパティーに関する説明

Exchange.AGGREGATED_SIZE

int

このエクスチェンジに集約されたエクスチェンジの合計数。

Exchange.AGGREGATED_COMPLETED_BY

String

エクスチェンジの集約を完了するためのメカニズムを示します。次の値のいずれかを使用できます: predicatesizetimeoutinterval、または consumer

以下のプロパティーは、SQL コンポーネント集約リポジトリーによって再配信されるエクスチェンジに設定されます (「永続集計リポジトリー」 を参照)。

ヘッダー再配信されたエクスチェンジプロパティー に関する説明

Exchange.REDELIVERY_COUNTER

int

現在の再配信試行のシーケンス番号 (1 から開始) 。

完了条件の指定

集約されたエクスチェンジが Aggregator から出て、ルート上の次のノードに遷移するタイミングを判断するので、少なくとも 1 つ の完了条件を指定する必要があります。以下の完了条件を指定できます。

completionPredicate
各エクスチェンジが集約された後に述語を評価し、完全性を評価します。true の値は、エクスチェンジの集約が完了したことを示します。このオプションを設定する代わりに、Predicate インターフェースを実装するカスタム AggregationStrategy を定義できます。この場合、AggregationStrategy が完了述語として使用されます) 。
completionSize
指定された数の受信エクスチェンジが集約された後、エクスチェンジの集約を完了します。
completionTimeout

(completionInterval とは互換性がありません) 指定されたタイムアウト値の間に受信エクスチェンジが集約されない場合、エクスチェンジの集約を完了します。

つまり、タイムアウトメカニズムは 相関キー値のタイムアウトを追跡します。特定のキー値を持つ最新のエクスチェンジを受け取ると、クロックがカウントを開始します。指定したタイムアウト値の間に同じキー値を持つ別のエクスチェンジが受信されない 場合、対応するエクスチェンジの集約は完了とマークされ、ルート上の次のノードに遷移します。

completionInterval

(completionTimeout とは互換性がありません) 各時間間隔 (指定された長さ) が経過した後、未処理のエクスチェンジの集約を すべて 完了します。

時間間隔は、各エクスチェンジの集約毎に調整されていません。このメカニズムは、すべての未処理のエクスチェンジに対し、集約の完了を強制します。したがって、場合によっては、このメカニズムは集約開始直後にエクスチェンジの集約を完了できます。

completionFromBatchConsumer
バッチコンシューマーメカニズムをサポートするコンシューマーエンドポイントと組み合わせて使用すると、この完了オプションは、コンシューマーエンドポイントから受信した情報に基づいて、現在のエクスチェンジのバッチが完了したタイミングを自動的に算出します。「バッチコンシューマー」 を参照してください。
forceCompletionOnStop
このオプションを有効にすると、現在のルートコンテキストが停止したときに、未処理のエクスチェンジの集約をすべて強制的に完了させます。

前述の完了条件は、completionTimeoutcompletionInterval のみ同時に有効にできませんが、任意に組み合わせることができます。条件を組み合わせて使用する場合、最初にトリガーされた完了条件が、有効な完了条件になるのが一般的です。

完了述語の指定

エクスチェンジの集約が完了するタイミングを決定する任意の述語式を指定できます。述語式を評価する方法は 2 つあります。

  • 最新の集約されたエクスチェンジ - これがデフォルトの動作です。
  • 最新の受信エクスチェンジ - eagerCheckCompletion オプションを有効にすると、この動作が選択されます。

たとえば、ALERT メッセージを受信するたびに (最新の受信エクスチェンジの MsgType ヘッダーの値で示される)、株式相場のストリームを終了させたい場合は、以下のようなルートを定義できます。

from("direct:start")
    .aggregate(
      header("id"),
      new UseLatestAggregationStrategy()
    )
        .completionPredicate(
          header("MsgType").isEqualTo("ALERT")
         )
        .eagerCheckCompletion()
    .to("mock:result");

以下の例は、XML を使用して同じルートを設定する方法を示しています。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <aggregate strategyRef="aggregatorStrategy"
               eagerCheckCompletion="true">
      <correlationExpression>
          <simple>header.StockSymbol</simple>
      </correlationExpression>
      <completionPredicate>
          <simple>$MsgType = 'ALERT'</simple>
      </completionPredicate>
      <to uri="mock:result"/>
    </aggregate>
  </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.aggregate.UseLatestAggregationStrategy"/>

動的な完了タイムアウトの指定

動的な完了タイムアウトを指定できます。この場合、タイムアウト値が受信エクスチェンジごとに再計算されます。たとえば、受信エクスチェンジで timeout ヘッダーからタイムアウト値を設定するには、以下のようにルートを定義します。

from("direct:start")
    .aggregate(header("StockSymbol"), new UseLatestAggregationStrategy())
        .completionTimeout(header("timeout"))
    .to("mock:aggregated");

XML DSL で以下のように同じルートを設定できます:

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <aggregate strategyRef="aggregatorStrategy">
            <correlationExpression>
                <simple>header.StockSymbol</simple>
            </correlationExpression>
            <completionTimeout>
                <header>timeout</header>
            </completionTimeout>
            <to uri="mock:aggregated"/>
        </aggregate>
    </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.UseLatestAggregationStrategy"/>
注記

固定のタイムアウト値を追加することもでき、動的なタイムアウト値が null0 であった場合、Apache Camel はこの固定値を使用するようにフォールバックします。

動的な完了サイズの指定

動的な完了サイズ を指定することが可能であり、受信エクスチェンジ毎に完了サイズは再計算されます。たとえば、各受信エクスチェンジの mySize ヘッダーから完了サイズを設定するには、以下のようにルートを定義します。

from("direct:start")
    .aggregate(header("StockSymbol"), new UseLatestAggregationStrategy())
        .completionSize(header("mySize"))
    .to("mock:aggregated");

Spring XML を使用した同じ例を以下に示します。

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <aggregate strategyRef="aggregatorStrategy">
            <correlationExpression>
                <simple>header.StockSymbol</simple>
            </correlationExpression>
            <completionSize>
                <header>mySize</header>
            </completionSize>
            <to uri="mock:aggregated"/>
        </aggregate>
    </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.UseLatestAggregationStrategy"/>
注記

固定のサイズ値を追加することもできます。動的な値が null0 であった場合、Apache Camel はこの固定値を使用するようにフォールバックします。

集約ストラテジー内から単一グループを強制完了

カスタム AggregationStrategy クラスを実装した場合、AggregationStrategy.aggregate() メソッドから返されたエクスチェンジの Exchange.AGGREGATION_COMPLETE_CURRENT_GROUP エクスチェンジプロパティーに true を設定することで、現在のメッセージグループを強制的に完了させる機能があります。この機能は現在のグループに のみ 影響します。他のメッセージグループ (異なる相関 ID を持つ) は強制的に完了しません。この機能は、述語、サイズ、タイムアウトなど、その他の完了機能に対して上書きされます。

たとえば、以下のサンプル AggregationStrategy クラスは、メッセージのボディーサイズが 5 より大きい場合に現在のグループを完了します。

// Java
public final class MyCompletionStrategy implements AggregationStrategy {
    @Override
    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        if (oldExchange == null) {
            return newExchange;
        }
        String body = oldExchange.getIn().getBody(String.class) + "+"
            + newExchange.getIn().getBody(String.class);
        oldExchange.getIn().setBody(body);
        if (body.length() >= 5) {
            oldExchange.setProperty(Exchange.AGGREGATION_COMPLETE_CURRENT_GROUP, true);
        }
        return oldExchange;
    }
}

特別なメッセージですべてのグループを強制完了

特別なヘッダーを持つメッセージをルートに送信することで、未処理のメッセージの集約を強制的に完了することができます。強制完了するために使用できる代替ヘッダー設定は 2 つあります。

Exchange.AGGREGATION_COMPLETE_ALL_GROUPS
true に設定して、現在の集約サイクルを強制的に完了させます。このメッセージはシグナルとして純粋に機能し、いかなる集約サイクルにも含まれません。このシグナルメッセージを処理した後、メッセージの内容は破棄されます。
Exchange.AGGREGATION_COMPLETE_ALL_GROUPS_INCLUSIVE
true に設定して、現在の集約サイクルを強制的に完了させます。このメッセージは現在の集計サイクルに 含まれます

AggregateController の使用

org.apache.camel.processor.aggregate.AggregateController により、Java または JMX API を使用して実行時にアグリゲートを制御できます。エクスチェンジのグループを強制的に完了したり、現在のランタイム統計情報をクエリーしたりするために使われます。

カスタムが設定されていない場合、Aggregator はデフォルト実装を提供します。これは、getAggregateController() メソッドを使用してアクセスできます。ただし、aggregateController を使用して、ルート内でコントローラーを簡単に設定することができます。

private AggregateController controller = new DefaultAggregateController();

from("direct:start")
   .aggregate(header("id"), new MyAggregationStrategy()).completionSize(10).id("myAggregator")
      .aggregateController(controller)
      .to("mock:aggregated");

また、AggregateController の API を使用して、強制的に完了することもできます。例えば、キー foo を持つグループを完了するには、次のコマンドを実行します。

int groups = controller.forceCompletionOfGroup("foo");

戻り値は完了したグループの数になります。すべてのグループを完了する API は以下のとおりです。

 int groups = controller.forceCompletionOfAllGroups();

一意な相関キーの強制

集約シナリオによっては、エクスチェンジのバッチごとに相関キーが一意であるという条件を強制したい場合があります。つまり、特定の相関キーを持つエクスチェンジの集約が完了したら、その相関キーを持つエクスチェンジの集約がこれ以上続行されないようにします。たとえば、ルートの後半部分で一意の相関キーの値を持つエクスチェンジを処理することを想定している場合に、この条件を実施することができます。

完了条件の設定方法によっては、特定の相関キーで複数のエクスチェンジの集約が生成されるリスクがある可能性があります。たとえば、特定の相関キーを持つ すべての エクスチェンジを受信するまで待機する補完述語を定義することもできますが、完了タイムアウトも定義しており、そのキーを持つすべてのエクスチェンジが到着する前に発火してしまう可能性もあります。この場合、遅れて到着するエクスチェンジは、同じ相関キーの値を持つ 2 つ目 のエクスチェンジの集約になる可能性があります。

このようなシナリオでは、closeCorrelationKeyOnCompletion オプションを設定することで、以前の相関キー値と重複したエクスチェンジの集約を抑制するように Aggregator を設定することができます。相関キー値の重複を抑制するためには、Aggregator が以前の相関キーの値をキャッシュに記録する必要があります。このキャッシュのサイズ (キャッシュされた相関キーの数) は、closeCorrelationKeyOnCompletion() DSL コマンドの引数として指定されます。無制限サイズのキャッシュを指定するには、ゼロまたは負の整数値を渡します。たとえば、10000 キー値のキャッシュサイズを指定するには、次のコマンドを実行します。

from("direct:start")
    .aggregate(header("UniqueBatchID"), new MyConcatenateStrategy())
        .completionSize(header("mySize"))
        .closeCorrelationKeyOnCompletion(10000)
    .to("mock:aggregated");

相関キー値が重複している状態でエクスチェンジの集約が完了すると、Aggregator は ClosedCorrelationKeyException 例外をスローします。

Simple 式を使用したストリームベースの処理

ストリーミングモードで tokenizeXML サブコマンドを使用して、Simple 言語式をトークンとして使うことができます。Simple 言語式を使用することで、動的トークンのサポートが可能になります。

たとえば、Java を使用してタグ person で区切られた名前のシーケンスを分割するため、tokenizeXML Bean および Simple 言語トークンを使用して、ファイルを name 要素に分割することができます。

public void testTokenizeXMLPairSimple() throws Exception {
        Expression exp = TokenizeLanguage.tokenizeXML("${header.foo}", null);

<person> で区切られた名前の入力文字列を取得し、<person> をトークンに設定します。

        exchange.getIn().setHeader("foo", "<person>");
        exchange.getIn().setBody("<persons><person>James</person><person>Claus</person><person>Jonathan</person><person>Hadrian</person></persons>");

入力から分割された名前をリストします。

        List<?> names = exp.evaluate(exchange, List.class);
        assertEquals(4, names.size());

        assertEquals("<person>James</person>", names.get(0));
        assertEquals("<person>Claus</person>", names.get(1));
        assertEquals("<person>Jonathan</person>", names.get(2));
        assertEquals("<person>Hadrian</person>", names.get(3));
    }

グループ化されたエクスチェンジ

送信バッチ内の集約されたすべてのエクスチェンジを、単一の org.apache.camel.impl.GroupedExchange ホルダークラスに統合できます。グループ化されたエクスチェンジを有効にするには、以下の Java DSL ルートに示されるように groupExchanges() オプションを指定します。

from("direct:start")
    .aggregate(header("StockSymbol"))
        .completionTimeout(3000)
        .groupExchanges()
    .to("mock:result");

mock:result に送信されるグループ化されたエクスチェンジには、メッセージボディーに集約されたエクスチェンジのリストが含まれます。以下のコードは、後続のプロセッサーがリスト形式でグループ化されたエクスチェンジのコンテンツにアクセスする方法を示しています。

// Java
List<Exchange> grouped = ex.getIn().getBody(List.class);
注記

エクスチェンジをグループ化する機能を有効にする場合、集約ストラテジーを設定するべきではありません (エクスチェンジのグループ化機能は、それ自体が集約ストラテジーになります)。

注記

送信エクスチェンジのプロパティーからグループ化されたエクスチェンジにアクセスする従来の方法は現在非推奨となっており、今後のリリースで削除される予定です。

バッチコンシューマー

Aggregator は Batch Consumer パターンと連携して、バッチコンシューマーによって報告されるメッセージの総数を集約できます (バッチコンシューマーエンドポイントは受信エクスチェンジで CamelBatchSizeCamelBatchIndex、および CamelBatchComplete プロパティーを設定します)。たとえば、File コンシューマーエンドポイントで見つかったすべてのファイルを集約するには、以下のようなルートを使用することができます。

from("file://inbox")
    .aggregate(xpath("//order/@customerId"), new AggregateCustomerOrderStrategy())
    .completionFromBatchConsumer()
    .to("bean:processOrder");

現在、バッチコンシューマー機能をサポートしているエンドポイントは File、FTP、Mail、iBatis、および JPA です。

永続集計リポジトリー

デフォルトの Aggregator はインメモリーのみの AggregationRepository を使用します。保留中の集約されたエクスチェンジを永続的に保存する場合は、SQL Component を永続集計リポジトリーとして使用できます。SQL コンポーネントには JdbcAggregationRepository が含まれており、仕掛かり中の集約されたメッセージを永続化し、メッセージを失うことがないようにします。

エクスチェンジが正常に処理された場合、リポジトリーで confirm メソッドが呼び出されると、完了とマークされます。つまり、同じエクスチェンジが再度失敗すると、成功するまで再試行さることを意味します。

camel-sql への依存関係の追加

SQL コンポーネントを使用するには、プロジェクトに camel-sql への依存関係を含める必要があります。たとえば、Maven pom.xml ファイルを使用している場合は、以下を追記します。

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-sql</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>

集約データベーステーブルの作成

永続化のために、集約テーブルと完成テーブルをそれぞれデータベースに作成する必要があります。たとえば、以下のクエリーは my_aggregation_repo という名前のデータベーステーブルを作成します。

CREATE TABLE my_aggregation_repo (
 id varchar(255) NOT NULL,
 exchange blob NOT NULL,
 constraint aggregation_pk PRIMARY KEY (id)
);

CREATE TABLE my_aggregation_repo_completed (
 id varchar(255) NOT NULL,
 exchange blob NOT NULL,
 constraint aggregation_completed_pk PRIMARY KEY (id)
);
}

集約リポジトリーの設定

フレームワーク XML ファイル (Spring または Blueprint など) で集約リポジトリーを設定する必要があります。

<bean id="my_repo"
    class="org.apache.camel.processor.aggregate.jdbc.JdbcAggregationRepository">
    <property name="repositoryName" value="my_aggregation_repo"/>
    <property name="transactionManager" ref="my_tx_manager"/>
    <property name="dataSource" ref="my_data_source"/>
    ...
</bean>

repositoryNametransactionManager、および dataSource プロパティーが必要です。永続集約リポジトリーの設定オプションの詳細は、『Apache Camel Component Reference Guide』の「SQL Component」を参照してください。

スレッドオプション

図8.6「Aggregator の実装」 にあるように、Aggregator はルートの後半部分から切り離されており、ルートの後半部分へ送信されたエクスチェンジは、専用のスレッドプールによって処理されます。デフォルトでは、このプールには 1 つのスレッドのみがあります。複数のスレッドを持つプールを指定する場合は、以下のように parallelProcessing オプションを有効にします。

from("direct:start")
    .aggregate(header("id"), new UseLatestAggregationStrategy())
        .completionTimeout(3000)
        .parallelProcessing()
    .to("mock:aggregated");

デフォルトでは、ワーカースレッドが 10 個あるプールが作成されます。

作成したスレッドプールをより詳細に制御する場合は、executorService オプションを使用してカスタム java.util.concurrent.ExecutorService インスタンスを指定します (この場合は、parallelProcessing オプションを有効化する必要はありません)。

List への集約

一般的な集約シナリオでは、一連の受信メッセージボディーを List オブジェクトに集約します。このシナリオを容易にするため、Apache Camel は AbstractListAggregationStrategy 抽象クラスを提供しています。手早くこのクラスを拡張して、こういったシチュエーションに応じた集約ストラテジーを作成できます。T 型の受信メッセージボディーは、List<T> 型のメッセージボディーを持つ完了済みエクスチェンジへと集約されます。

たとえば、一連の Integer メッセージボディーを List<Integer> オブジェクトに集約するには、以下のように定義された集約ストラテジーを使用することができます。

import org.apache.camel.processor.aggregate.AbstractListAggregationStrategy;
...
/**
 * Strategy to aggregate integers into a List<Integer>.
 */
public final class MyListOfNumbersStrategy extends AbstractListAggregationStrategy<Integer> {
 
    @Override
    public Integer getValue(Exchange exchange) {
        // the message body contains a number, so just return that as-is
        return exchange.getIn().getBody(Integer.class);
    }
}

Aggregator のオプション

Aggregator は以下のオプションをサポートします。

表8.1 Aggregator のオプション

オプションデフォルト説明

correlationExpression

 

集約に使用する相関キーを評価するために必須な式。同じ相関キーを持つエクスチェンジが集約されます。相関キーを評価できない場合、例外が発生します。これを無効にするには、ignoreBadCorrelationKeys オプションを使用します。

aggregationStrategy

 

既存の結合されたエクスチェンジと受信エクスチェンジを 結合 するために使用される AggregationStrategy は必須です。最初の呼び出し時、oldExchange パラメーターは null です。その後の呼び出しでは、oldExchange には結合されたエクスチェンジが含まれ、newExchange は当然新しい受信エクスチェンジとなります。Camel 2.9.2 以降では、ストラテジーを任意で、タイムアウトコールバックをサポートする TimeoutAwareAggregationStrategy 実装にすることができます。Camel 2.16 以降では、ストラテジーをPreCompletionAwareAggregationStrategy 実装にすることもできます。pre-completion モードで、完了チェックを実行します。

strategyRef

 

レジストリーで AggregationStrategy を検索するための参照。

completionSize

 

集約の完了前に、既に集約されたメッセージの数。このオプションは、固定値を設定することも、動的にサイズを評価する式を使用することもできます。式は Integer が結果として使用されます。両方が設定されている場合、Camel は式の結果が null または 0 であれば、固定値を使用するようにフォールバックします。

completionTimeout

 

集約されたエクスチェンジが完了するまで非アクティブになる時間 (ミリ秒単位) 。このオプションは、固定値として設定することも、動的にタイムアウトを評価する式を使用することもできます。式は Long が結果として使用されます。両方が設定されている場合、Camel は式の結果が null または 0 であれば、固定値を使用するようにフォールバックします。このオプションを completionInterval と併用することはできません。使用できるのはどちらか 1 つだけです。

completionInterval

 

Aggregator が現在すべてのエクスチェンジを集約し完了するまでに繰り返される期間 (ミリ秒単位)。Camel には、期間ごとにトリガーされるバックグラウンドタスクがあります。このオプションは completionTimeout と併用できません。使用できるのはどちらか 1 つだけです。

completionPredicate

 

集約されたエクスチェンジの完了時にシグナルを送る述語 (org.apache.camel.Predicate 型) を指定します。このオプションを設定する代わりに、Predicate インターフェースを実装するカスタム AggregationStrategy を定義することができます。この場合、完了述語として AggregationStrategy が使用されます。

completionFromBatchConsumer

false

このオプションは、エクスチェンジがバッチコンシューマーから送信される場合に使用します。有効にすると、「Aggregator」 は、CamelBatchSize メッセージヘッダーの、バッチコンシューマーによって決定されるバッチサイズを使用します。詳細は「バッチコンシューマー」を参照してください。ポーリングの際、File エンドポイントからコンシュームされたすべてのファイルを集約するために使用できます。

eagerCheckCompletion

false

新しい受信エクスチェンジを受け取ったときに、常に完了を確認するかどうか。このオプションは、エクスチェンジが渡される挙動が変わるため、completionPredicate オプションの動作に影響します。false の場合、述語に渡されるエクスチェンジは 集約されたエクスチェンジであり、AggregationStrategy から集約されたエクスチェンジに保存できる任意の情報を述語で利用することができます。true の場合、述語に渡されるエクスチェンジは 受信 エクスチェンジであり、受信エクスチェンジからデータにアクセスできます。

forceCompletionOnStop

false

true の場合、現在のルートコンテキストが停止したときにすべての集約されたエクスチェンジを完了します。

groupExchanges