検索

Apache Camel 開発ガイド

download PDF
Red Hat Fuse 7.10

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

概要

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

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。まずは、マスター (master)、スレーブ (slave)、ブラックリスト (blacklist)、ホワイトリスト (whitelist) の 4 つの用語の置き換えから始めます。この取り組みは膨大な作業を要するため、今後の複数のリリースで段階的に用語の置き換えを実施して参ります。詳細は、CTO である Chris Wright のメッセージ をご覧ください。

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

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

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

概要

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

1.1. RouteBuilder クラスの実装

概要

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

必要な数だけ RouteBuilder クラスを定義できます。各クラスは一度インスタンス化され、CamelContext オブジェクトに登録されます。通常、各 RouteBuilder オブジェクトのライフサイクルは、ルーターをデプロイするコンテナーによって自動的に管理されます。

RouteBuilder クラス

ルート開発者の主なロールは、1 つ以上の RouteBuilder クラスを実装することです。以下の 2 つの代替 RouteBuilder クラスを継承できます。

  • org.apache.camel.builder.RouteBuilder: これは、あらゆる コンテナータイプへのデプロイに適した汎用の RouteBuilder ベースクラスです。camel-core アーティファクトで提供されます。
  • org.apache.camel.spring.SpringRouteBuilder: このベースクラスは、Spring コンテナーに特別に適合されています。特に、以下の Spring 固有の機能に対する追加サポートが提供されます。追加サポートの対象となる機能は、Spring レジストリーでの Bean 検索 (beanRef() Java DSL コマンドを使用) およびトランザクション (詳細は トランザクションガイド を参照) です。camel-spring アーティファクトで提供されます。

RouteBuilder クラスは、ルーティングルールの開始に使用されるメソッドを定義します (例: from()intercept()、および exception())。

RouteBuilder の実装

例1.1「RouteBuilder クラスの実装」 は、最小限の RouteBuilder 実装を示しています。configure() メソッド本文にはルーティングルールが含まれ、各ルールは単一の Java ステートメントです。

例1.1 RouteBuilder クラスの実装

import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {

public void configure() {
  // Define routing rules here:
  from("file:src/data?noop=true").to("file:target/messages");

  // More rules can be included, in you like.
  // ...
}
}

from(URL1).to(URL2) ルールの形式は、src/data ディレクトリーからファイルを読み込み、それを target/messages ディレクトリーに送信するようルーターに指示します。?noop=true オプションは、src/data ディレクトリー内のソースファイルを (削除ではなく) 保持するようにルーターに指示します。

注記

Spring または Blueprint で contextScan を使用して RouteBuilder クラスをフィルターする場合、Apache Camel はデフォルトでシングルトン Bean を検索します。ただし、以前の動作をオンにして、新しいオプション includeNonSingletons でスコープされたプロトタイプを含めることができます。

1.2. 基本的な Java DSL 構文

DSL とは

ドメイン固有言語 (DSL) は、特別な目的のために設計されたミニ言語です。DSL は論理的に完全である必要はありませんが、選択したドメインで適切に問題を記述するのに十分な表現力が必要になります。通常、DSL には専用のパーサー、インタープリター、またはコンパイラーは 必要ありません。DSL コンストラクトがホスト言語 API のコンストラクトに適切にマッピングされていれば、DSL は既存のオブジェクト指向ホスト言語の上に便乗できます。

架空の DSL で以下の一連のコマンドについて考えてみましょう。

command01;
command02;
command03;

これらのコマンドは、以下のような Java メソッド呼び出しにマップできます。

command01().command02().command03()

ブロックを Java メソッド呼び出しにマップすることもできます。以下に例を示します。

command01().startBlock().command02().command03().endBlock()

DSL 構文は、ホスト言語 API のデータ型によって暗黙的に定義されます。たとえば、Java メソッドの戻り値の型によって、次 (DSL の次のコマンドと同等) をアクティブに呼び出すことのできるメソッドが決定します。

ルータールール構文

Apache Camel は、ルーティングルールを定義する ルーター DSL を定義します。この DSL を使用して、RouteBuilder.configure() 実装のボディーにルールを定義できます。図1.1「ローカルルーティングルール」 は、ローカルルーティングルールを定義する基本的な構文の概要を示しています。

図1.1 ローカルルーティングルール

ローカルルーティングルール

ローカルルールは、常に from("EndpointURL") メソッドで開始します。このメソッドは、ルーティングルールのメッセージのソース (コンシューマーエンドポイント) を指定します。その後、ルールに任意の長いプロセッサーチェーンを追加できます (例: filter())。通常、to("EndpointURL") メソッドでルールを終了します。このメソッドは、ルールを通過するメッセージのターゲット (producer endpoint) を指定します。ただし、常に to() でルールを終了する必要はありません。メッセージのターゲットをルールに指定する別の方法もあります。

注記

特別なプロセッサータイプ (intercept()exception()、または errorHandler() など) でルールを開始することで、グローバルルーティングルールを定義することもできます。グローバルルールは本ガイドの対象範囲外です。

コンシューマーおよびプロデューサー

ローカルルールは、常に from("EndpointURL") を使用してコンシューマーエンドポイントを定義して開始します。そして (常にではありませんが) 通常は、to("EndpointURL") を使用してプロデューサーエンドポイントを定義して終了します。エンドポイント URL である EndpointURL は、デプロイ時に設定された任意のコンポーネントを使用できます。たとえば、ファイルエンドポイント file:MyMessageDirectory、Apache CXF エンドポイント cxf:MyServiceName、または Apache ActiveMQ エンドポイント activemq:queue:MyQName を使用できます。全てのコンポーネント型を網羅するリストは、Apache Camel Component Reference を参照してください。

エクスチェンジ

エクスチェンジ オブジェクトは、メタデータで拡張されたメッセージで設定されます。エクスチェンジはメッセージがルーティングルールを介して伝搬される際に使われる標準形式であるため、 Apache Camel において最も重要です。エクスチェンジの主な設定要素は次のとおりです。

  • Inメッセージ - エクスチェンジによってカプセル化された現在のメッセージです。エクスチェンジがルートを通過するにつれて、このメッセージは変更される場合があります。そのため、ルート開始時の In メッセージは、通常ルート終了時の Inメッセージとは 異なりますorg.apache.camel.Message タイプは、以下の部分を含むメッセージの汎用モデルを提供します。

    • ボディー
    • ヘッダー
    • アタッチメント

    これはメッセージの 汎用 モデルであることを理解することが重要です。Apache Camel は、さまざまなプロトコルおよびエンドポイントをサポートします。したがって、メッセージのボディーまたはヘッダーの形式を標準化することは できません。たとえば、JMS メッセージのボディー形式は、HTTP メッセージまたは Web サービスメッセージのボディーとは全く異なります。このため、ボディーとヘッダーは Object タイプと宣言されます。ボディーとヘッダーの元のコンテンツは、エクスチェンジインスタンスを作成するエンドポイントによって決定されます (つまり、from() コマンドに指定されたエンドポイント)。

  • out メッセージ - 返信メッセージまたは変換されたメッセージの一時的な保持領域です。特定の処理ノード (特に to() コマンド) は、In メッセージをリクエストとして処理し、これをプロデューサーエンドポイントに送信し、そのエンドポイントからのリプライを受信することで、現在のメッセージを変更できます。リプライメッセージは、エクスチェンジの Out メッセージスロットに挿入されます。

    通常、現在のノードで Out メッセージが設定された場合、Apache Camel はエクスチェンジを次のように変更してからルートの次のノードに渡します: In メッセージを破棄し、Out メッセージを In メッセージのスロットに移動します。そのため、リプライは最新のメッセージになります。Apache Camel がノード同士をルート内で接続する方法の詳細は、「パイプライン処理」 を参照してください。

    ただし、Out メッセージが異なる方法で扱われる特殊なケースが 1 つあります。ルート開始時のコンシューマーエンドポイントがリプライメッセージを期待している場合、ルートの最終端にある Out メッセージはコンシューマーエンドポイントのリプライメッセージとみなされます (さらに、この場合、最終ノードが Out メッセージを作成 しなければならず、さもなくばコンシューマーエンドポイントはハングしてしまいます)。

  • メッセージ交換パターン (MEP) - 以下のように、ルート内でのエクスチェンジとエンドポイント間のやり取りに影響を与えます。

    • コンシューマーエンドポイント - 元となるエクスチェンジを作成するコンシューマーエンドポイントは、MEP の初期値を設定します。初期値は、コンシューマーエンドポイントがリプライするか (例: InOut MEP)、リプライしないか (例: InOnly MEP) を示します。
    • プロデューサーエンドポイント - MEP は、エクスチェンジがルートで通過するプロデューサーエンドポイントに影響します (例: エクスチェンジが to() ノードを通過する場合)。たとえば、現在の MEP が InOnly の場合、to() ノードはエンドポイントからのリプライを受け取ることを期待しません。場合によっては、エクスチェンジのプロデューサーエンドポイントとのやり取りをカスタマイズするために、現在の MEP を変更する必要があります。詳細は、「エンドポイント」 を参照してください。
  • エクスチェンジプロパティー - 現在のメッセージのメタデータが含まれる名前付きプロパティーのリスト。

メッセージ交換パターン

Exchange オブジェクトを使用すると、メッセージ処理を異なる メッセージ交換パターン に簡単に一般化することができます。たとえば、非同期プロトコルは、コンシューマーエンドポイントからプロデューサーエンドポイントに流れる単一のメッセージで設定される MEP を定義する場合があります (InOnly MEP)。一方、RPC プロトコルは、リクエストメッセージとリプライメッセージで設定される MEP を定義する場合があります (InOut MEP)。現在、Apache Camel は以下の MEP をサポートします。

  • InOnly
  • RobustInOnly
  • InOut
  • InOptionalOut
  • OutOnly
  • RobustOutOnly
  • OutIn
  • OutOptionalIn

これらのメッセージ交換パターンは、列挙型の定数 org.apache.camel.ExchangePattern によって表されます。

グループ化されたエクスチェンジ

複数のエクスチェンジインスタンスをカプセル化した 1 つのエクスチェンジがあると便利な場合もあります。これに対応する為、グループ化されたエクスチェンジ を使用できます。グループ化されたエクスチェンジは、基本的 Exchange.GROUPED_EXCHANGE エクスチェンジプロパティーに格納されている Exchange オブジェクトの java.util.List が含まれるエクスチェンジインスタンスです。グループ化されたエクスチェンジの使用例は、「Aggregator」 を参照してください。

プロセッサー

プロセッサー は、ルートを通過するエクスチェンジのストリームにアクセスして変更することができるルート内のノードです。プロセッサーは、動作を変更する 述語 の引数を取ることができます。たとえば、図1.1「ローカルルーティングルール」 で示されたルールには、xpath() 述語を引数とする filter() プロセッサーが含まれます。

式および述語

式 (文字列またはその他のデータ型への評価) および述語 (true または false への評価) は、組み込みプロセッサー型の引数として頻繁に発生します。たとえば、以下のフィルタールールは、ヘッダー foo の値が bar と等しい場合にのみ In メッセージを伝播します。

from("seda:a").filter(header("foo").isEqualTo("bar")).to("seda:b");

header("foo").isEqualTo("bar") ではフィルターが述語によって修飾されます。メッセージの内容に基づいてより高度な述語や式を作成するために、式言語および述語言語のいずれかを使用できます (パートII「ルーティング式と述語言語」 を参照)。

1.3. Spring XML ファイルのルータースキーマ

名前空間

ルータースキーマは XML DSL を定義し、以下の XML スキーマ名前空間に属します。

http://camel.apache.org/schema/spring

スキーマの場所の指定

ルータースキーマの場所は通常、Apache Web サイトにある最新バージョンのスキーマを参照する http://camel.apache.org/schema/spring/camel-spring.xsd に指定されます。たとえば、Apache Camel Spring ファイルのルート beans 要素は、通常 例1.2「ルータースキーマの場所の指定」 のように設定されます。

例1.2 ルータースキーマの場所の指定

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <!-- Define your routing rules here -->
  </camelContext>
</beans>

ランタイムスキーマの場所

実行時に、Apache Camel は Spring ファイルで指定されたスキーマの場所からルータースキーマをダウンロードしません。代わりに、Apache Camel は camel-spring JAR ファイルの root ディレクトリーからスキーマのコピーを自動的に取得します。これにより、Spring ファイルの解析に使用されるスキーマのバージョンが、常に現在のランタイムバージョンと一致するようになります。これは、Apache Web サイトに公開されているスキーマの最新バージョンが、現在使用しているランタイムのバージョンと一致しない場合があるため、重要になります。

XML エディターの使用

通常、フル機能の XML エディターを使用して Spring ファイルを編集することが推奨されます。XML エディターの自動補完機能により、ルータースキーマに準拠する XML の作成がはるかに容易になり、XML の形式が不適切な場合はエディターが即座に警告します。

通常、XML エディターは、xsi:schemaLocation 属性で指定した場所からスキーマをダウンロードすることに 依存しています。編集中に正しいスキーマバージョンを使用するようにするため、通常は camel-spring.xsd ファイルの特定のバージョンを選択することが推奨されます。たとえば、Apache Camel バージョン 2.3 用の Spring ファイルを編集するには、以下のように beans 要素を修正します。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring-2.3.0.xsd">
...

編集が完了したら、デフォルトの camel-spring.xsd に戻します。現在ダウンロードできるスキーマのバージョンを確認するには、Web ページ http://camel.apache.org/schema/spring に移動します。

1.4. エンドポイント

概要

Apache Camel エンドポイントは、ルートにおけるメッセージの水源 (蛇口) とシンク (流し台) です。エンドポイントは一般的なビルディングブロックと言えます。満たす必要がある唯一の要件は、メッセージの水源 (プロデューサーエンドポイント) またはメッセージのシンク (コンシューマーエンドポイント) のいずれかとして機能することです。そのため、Apache Camel でサポートされるエンドポイント型には、HTTP などのプロトコルをサポートするエンドポイントから、定期的な間隔でダミーメッセージを生成する Quartz などの単純なタイマーエンドポイントまで、非常に多様な種類があります。Apache Camel の大きな強みの 1 つは、新しいエンドポイント型を実装するカスタムコンポーネントを、比較的簡単に追加できることです。

エンドポイント URI

エンドポイントは、以下の一般的な形式を持つ エンドポイント URI で識別されます。

scheme:contextPath[?queryOptions]

URI スキームは は、http などのプロトコルを識別し、contextPath はプロトコルによって解釈される URI の詳細を提供します。さらに、ほとんどのスキームでは、以下の形式で指定される queryOptions (クエリーオプション) を定義できます。

?option01=value01&option02=value02&...

たとえば、以下の HTTP URI を使用して Google 検索エンジンページに接続できます。

http://www.google.com

以下のファイル URI を使用して、C:\temp\src\data ディレクトリー配下に表示されるすべてのファイルを読み取ることができます。

file://C:/temp/src/data

すべての スキーム がプロトコルを表しているわけではありません。スキーム は、タイマーなどの有用なユーティリティーへのアクセスのみを提供することもあります。たとえば、以下の Timer エンドポイント URI は、1 秒毎 (=1000 ミリ秒) にエクスチェンジを生成します。これを使用して、ルートでアクティビティーをスケジュールできます。

timer://tickTock?period=1000

長いエンドポイント URI の使用

エンドポイント URI は、提供される設定情報がすべて付随することで、かなり長くなることがあります。Red Hat Fuse 6.2 以降では、長い URI での作業をより管理しやすくする方法が 2 つあります。

エンドポイントの個別設定

エンドポイントは個別に設定でき、ルートから短縮 ID を使用してエンドポイントを参照できます。

<camelContext ...>

  <endpoint id="foo" uri="ftp://foo@myserver">
    <property name="password" value="secret"/>
    <property name="recursive" value="true"/>
    <property name="ftpClient.dataTimeout" value="30000"/>
    <property name="ftpClient.serverLanguageCode" value="fr"/>
  </endpoint>

  <route>
    <from uri="ref:foo"/>
    ...
  </route>
</camelContext>

URI 内でオプションをいくつか設定し、property 属性を使用して追加オプションを指定することもできます (または URI からオプションを上書きすることもできます)。

<endpoint id="foo" uri="ftp://foo@myserver?recursive=true">
  <property name="password" value="secret"/>
  <property name="ftpClient.dataTimeout" value="30000"/>
  <property name="ftpClient.serverLanguageCode" value="fr"/>
</endpoint>
複数行にまたがるエンドポイント設定の分割

改行を使用して URI 属性を分割できます。

<route>
  <from uri="ftp://foo@myserver?password=secret&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 である ifmyvelocityscriptbean および mysimplescriptbean は、以下のように参照することで、Bean の内容を使うことができます。

Velocity endpoint:
------------------
from("velocity:ref:myvelocityscriptbean").<rest_of_route>.

Language endpoint (for invoking a scripting language):
-----------------------------------------------------
from("direct:start")
  .to("language:simple:ref:mysimplescriptbean")
 Where Camel implicitly converts the bean to a String.

Apache Camel コンポーネント

各 URI スキーム は本質的にエンドポイントファクトリーとなる Apache Camel コンポーネント にマップされます。つまり、特定のタイプのエンドポイントを使用するには、対応する Apache Camel コンポーネントをランタイムコンテナーにデプロイする必要があります。たとえば、JMS エンドポイントを使用するには、コンテナーに JMS コンポーネントをデプロイします。

Apache Camel は、アプリケーションをさまざまなトランスポートプロトコルおよびサードパーティー製品と統合できる、多種多様なコンポーネントを提供します。たとえば、より一般的に使用されるコンポーネントには、File、JMS、CXF (Web サービス) 、HTTP、Jetty、Direct、および Mock などがあります。サポートされるコンポーネントの完全なリストは、Apache Camel コンポーネントのドキュメント を参照してください。

Apache Camel コンポーネントの大半は、Camel コアとは別にパッケージ化されています。Maven を使用してアプリケーションをビルドする場合、関連するコンポーネントアーティファクトの依存関係を追加するだけで、コンポーネント (およびサードパーティーの依存関係) を簡単にアプリケーションへ追加できます。たとえば、HTTP コンポーネントを含めるには、以下の Maven 依存関係をプロジェクトの POM ファイルに追加します。

<!-- Maven POM File -->
  <properties>
    <camel-version>{camelFullVersion}</camel-version>
    ...
  </properties>

  <dependencies>
    ...
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-http</artifactId>
      <version>${camel-version}</version>
    </dependency>
    ...
  </dependencies>

以下のコンポーネントは Camel コア (camel-core アーティファクト内) に組み込まれているので、いつでも利用できます。

  • Bean
  • Browse
  • Dataset
  • Direct
  • File
  • Log
  • Mock
  • Properties
  • Ref
  • SEDA
  • Timer
  • VM

コンシューマーエンドポイント

コンシューマーエンドポイント は、ルートの 先頭 に (つまり from() DSL コマンドで) 表示されるエンドポイントです。言い換えると、コンシューマーエンドポイントはルート内の処理を開始するロールがあります。新しいエクスチェンジインスタンス (通常は受信または取得したメッセージを基に) を作成し、ルートの残りの部分でエクスチェンジを処理するためのスレッドを提供します。

たとえば、以下の JMS コンシューマーエンドポイントは payments キューからメッセージをプルし、ルートでメッセージを処理します。

from("jms:queue:payments")
  .process(SomeProcessor)
  .to("TargetURI");

または、Spring XML で同等のものを記述するとこのようになります。

<camelContext id="CamelContextID"
              xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="jms:queue:payments"/>
    <process ref="someProcessorId"/>
    <to uri="TargetURI"/>
  </route>
</camelContext>

コンポーネントには、 コンシューマー専用 のものもあります。つまり、それらはコンシューマーエンドポイントの定義のみに使用できます。たとえば、Quartz コンポーネントは、コンシューマーエンドポイントを定義するためにのみ使用されます。以下の Quartz エンドポイントは、1 秒ごと (1000 ミリ秒) にイベントを生成します。

from("quartz://secondTimer?trigger.repeatInterval=1000")
  .process(SomeProcessor)
  .to("TargetURI");

必要に応じて、fromF() Java DSL コマンドを使用して、エンドポイント URI をフォーマットされた文字列として指定できます。たとえば、ユーザー名とパスワードを FTP エンドポイントの URI に指定するには、以下のように Java でルートを記述します。

fromF("ftp:%s@fusesource.com?password=%s", username, password)
  .process(SomeProcessor)
  .to("TargetURI");

最初の %s は文字列 username の値に置き換えられ、2 番目の %s は文字列 password の値に置き換えられます。この文字列のフォーマットメカニズムは String.format() で実装されており、C 言語の printf() 関数によって提供されるフォーマットに似ています。詳細は java.util.Formatter を参照してください。

プロデューサーエンドポイント

プロデューサーエンドポイント は、ルートの 途中 またはルートの 末尾 で表示されるエンドポイントです (例: to() DSL コマンド)。つまり、プロデューサーエンドポイントは既存のエクスチェンジオブジェクトを受け取り、エクスチェンジの内容を指定されたエンドポイントに送信します。

たとえば、以下の JMS プロデューサーエンドポイントは、現在のエクスチェンジの内容を指定の JMS キューにプッシュします。

from("SourceURI")
  .process(SomeProcessor)
  .to("jms:queue:orderForms");

または、Spring XML で同等のものを記述すると、このようになります。

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURI"/>
    <process ref="someProcessorId"/>
    <to uri="jms:queue:orderForms"/>
  </route>
</camelContext>

一部のコンポーネントは プロデューサー専用 で、プロデューサーエンドポイントを定義するためにのみ使用できます。たとえば、HTTP エンドポイントはプロデューサーエンドポイントを定義するためにのみ使用されます。

from("SourceURI")
  .process(SomeProcessor)
  .to("http://www.google.com/search?hl=en&q=camel+router");

必要に応じて、toF() Java DSL コマンドを使用して、エンドポイント URI をフォーマットされた文字列として指定できます。たとえば、カスタム Google クエリーを HTTP URI に置き換えるには、以下のように Java でルートを記述します。

from("SourceURI")
  .process(SomeProcessor)
  .toF("http://www.google.com/search?hl=en&q=%s", myGoogleQuery);

%s と書かれた箇所が、カスタムクエリー文字列 myGoogleQuery に置き換えられます。詳細は java.util.Formatter を参照してください。

1.5. プロセッサー

概要

ルーターが単にコンシューマーエンドポイントをプロデューサーエンドポイントに接続するだけでなく、より複雑なことを実行できるようにするため、プロセッサー をルートに追加することができます。プロセッサーは、ルーティングルールに挿入して、ルールを通過するメッセージの任意処理を実行するコマンドです。Apache Camel は、表1.1「Apache Camel プロセッサー」 に示されるように、さまざまなプロセッサーを提供します。

表1.1 Apache Camel プロセッサー
Java DSLXML DSL説明

aggregate()

aggregate

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

aop()

aop

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

bean(), beanRef()

bean

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

choice()

choice

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

convertBodyTo()

convertBodyTo

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

delay()

delay

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

doTry()

doTry

doCatchdoFinally、および end 句を使い、例外を処理するための 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()

パイプライン

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

policy()

policy

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

pollEnrich(),pollEnrichRef()

pollEnrich

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

process(),processRef

process

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

recipientList()

recipientList

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

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()

上記を以下のように変更します。

エクスチェンジを 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:foomock:bar エンドポイント間でメッセージの負荷分散が行われます。ただし、ロードバランサー句が choice の条件に組み込まれている場合は、以下のように endChoice() コマンドを使用して句を終了する必要があります。

from("direct:start")
    .choice()
        .when(bodyAs(String.class).contains("Camel"))
            .loadBalance().roundRobin().to("mock:foo").to("mock:bar").endChoice()
        .otherwise()
            .to("mock:result");

フィルター

filter() プロセッサーを使用すると、必要のないメッセージがプロデューサーエンドポイントに到達しないようにすることができます。述語の引数を 1 つ取ります。述語が true の場合、メッセージエクスチェンジはプロデューサーに対して許可されます。述語が false の場合、メッセージエクスチェンジはブロックされます。たとえば、以下のフィルターは、受信メッセージに bar の値を持つヘッダー foo が含まれない限り、メッセージエクスチェンジをブロックします。

from("SourceURL").filter(header("foo").isEqualTo("bar")).to("TargetURL");

または、Spring XML で同等のものを記述すると、このようになります。

<camelContext id="filterRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <filter>
      <simple>header.foo = 'bar'</simple>
      <to uri="TargetURL"/>
    </filter>
  </route>
</camelContext>

Throttler

throttle() プロセッサーは、プロデューサーエンドポイントがオーバーロードしないようにします。スロットラーは、1 秒間に通過できるメッセージの数を制限することで機能します。受信メッセージが指定されたレートを超える場合、スロットラーは超過したメッセージをバッファーに蓄積し、ゆっくりとプロデューサーエンドポイントに送信します。たとえば、スループットのレートを毎秒 100 メッセージに制限するには、以下のルールを定義します。

from("SourceURL").throttle(100).to("TargetURL");

または、Spring XML で同等のものを記述すると、このようになります。

<camelContext id="throttleRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <throttle maximumRequestsPerPeriod="100" timePeriodMillis="1000">
      <to uri="TargetURL"/>
    </throttle>
  </route>
</camelContext>

カスタムプロセッサー

ここに記載されている標準プロセッサーがいずれも必要な機能を提供しない場合は、いつでも独自のカスタムプロセッサーを定義できます。カスタムプロセッサーを作成するには、org.apache.camel.Processor インターフェイスを実装し、process() メソッドを上書きするクラスを定義します。以下のカスタムプロセッサー MyProcessor は、受信メッセージから、ヘッダー foo を削除します。

例1.3 カスタムプロセッサークラスの実装

public class MyProcessor implements org.apache.camel.Processor {
public void process(org.apache.camel.Exchange exchange) {
  inMessage = exchange.getIn();
  if (inMessage != null) {
      inMessage.removeHeader("foo");
  }
}
};

カスタムプロセッサーをルータールールに挿入するには、process() メソッドを呼び出します。このメソッドは、ルールにプロセッサーを挿入するための一般的なメカニズムを提供します。たとえば、以下のルールは、例1.3「カスタムプロセッサークラスの実装」 で定義されたプロセッサーを呼び出します。

org.apache.camel.Processor myProc = new MyProcessor();

from("SourceURL").process(myProc).to("TargetURL");

第2章 ルート構築の基本原則

概要

Apache Camel は、ルートの中で繋ぎ合わせられる複数のプロセッサーおよびコンポーネントを提供します。本章では、提供されるビルディングブロックを使用してルートを構築する原則を説明します。

2.1. パイプライン処理

概要

Apache Camel では、パイプライン処理はルート定義でノードを接続するための主要なパラダイムです。パイプラインの概念は、おそらく UNIX オペレーティングシステムのユーザーには最も馴染のあるものでしょう。オペレーティングシステムのコマンドを結合するために使用しているからです。たとえば、ls | more はディレクトリー一覧 ls をページスクロールのユーティリティー more にパイプするコマンドの例です。パイプラインの基本的な概念は、あるコマンドの 出力 が次の 入力 に読み込まれることです。ルートにおいては、あるプロセッサーからの Out メッセージが次のプロセッサーの In メッセージにコピーされることに相当します。

プロセッサーノード

最初のエンドポイントを除き、ルートのすべてのノードは プロセッサー で、org.apache.camel.Processor インターフェイスを継承します。つまり、プロセッサーが DSL ルートの基本ビルディングブロックを設定します。たとえば、filter()delayer()setBody()setHeader()、および to() などの DSL コマンドはすべてプロセッサーを表します。プロセッサーがどのように接続しあってルートを設定するかを考えるときに、2 つの異なる処理アプローチを区別することが重要になります。

最初のアプローチは、図2.1「In メッセージを変更するプロセッサー」 にあるように、プロセッサーが単純にエクスチェンジの In メッセージを変更する方法です 。この場合、エクスチェンジの Out メッセージは null のままになります。

図2.1 In メッセージを変更するプロセッサー

In メッセージを変更するプロセッサー

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

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

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

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

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

以下のルートは、文字列 DummyBody が含まれるメッセージボディーを持った Out メッセージを作成する transform() コマンドを示しています。

from("activemq:orderQueue")
    .transform(constant("DummyBody"))
    .to("activemq:billingQueue");

constant("DummyBody") は定数式を表します。transform() の引数の型は式である必要があるため、文字列 DummyBody を直接渡すことはできません。

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

図2.3「InOnly エクスチェンジのサンプルパイプライン」 は、 InOnly エクスチェンジのプロセッサーパイプラインの例を示しています。プロセッサー A は In メッセージを変更し、プロセッサー B および C は Out メッセージを作成します。ルートビルダーが、図に示されているようにプロセッサー同士を繋ぎ合わせます。特にプロセッサー B と C は パイプライン の形式で繋がれています。つまり、エクスチェンジをプロセッサー C に読み込ませる前にプロセッサー B の Out メッセージが In メッセージに移動され、エクスチェンジをプロデューサーエンドポイントに読み込ませる前にプロセッサー C の Out メッセージが In メッセージに移動されています。したがって、図2.3「InOnly エクスチェンジのサンプルパイプライン」 に示されるようにプロセッサーの出力と入力は連続したパイプラインに結合されています。

図2.3 InOnly エクスチェンジのサンプルパイプライン

InOnly エクスチェンジのパイプラインサンプル

Apache Camel はデフォルトでパイプラインパターンを使用するため、特別な構文を使用してルートにパイプラインを作成する必要はありません。たとえば、以下のルートは userdataQueue キューからメッセージを取り出し、(テキスト形式で顧客アドレスを生成するために) Velocity テンプレートを通してメッセージをパイプ処理し、生成されたテキストアドレスをキュー envelopeAddresses に送信します。

from("activemq:userdataQueue")
    .to(ExchangePattern.InOut, "velocity:file:AdressTemplate.vm")
    .to("activemq:envelopeAddresses");

Velocity エンドポイントである velocity:file:AddressTemplate.vm は、ファイルシステム内の Velocity テンプレートファイルの file:AddressTemplate.vm の場所を指定します。この to() コマンドは、エクスチェンジを Velocity エンドポイントに送信する前に、エクスチェンジパターンをInOut に変更し、その後 InOnly に戻します。Velocity エンドポイントの詳細は、Apache Camel Component Reference GuideVelocity を参照してください。

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

図2.4「InOut エクスチェンジのサンプルパイプライン」 は、通常リモートプロシージャーコール (RPC) のセマンティクスをサポートするときに使用する、 InOut エクスチェンジ用のプロセッサーパイプラインの例を示しています。プロセッサー A、B、および C はパイプライン形式で結合され、各プロセッサーの出力が次の入力に読み込まれます。プロデューサーエンドポイントによって生成された最後の Out メッセージは、コンシューマーエンドポイントまで遡って、元のリクエストへの返信として提供されます。

図2.4 InOut エクスチェンジのサンプルパイプライン

InOut エクスチェンジのサンプルパイプライン

InOut 交換パターンをサポートするには、ルートの最後のノード (プロデューサーエンドポイントかその他の種類のプロセッサーかに限らず) が Out メッセージを作成することが 必須 です。そうでない場合は、コンシューマーエンドポイントに接続するクライアントがハングし、リプライメッセージを無期限に待ち続けることになります。すべてのプロデューサーエンドポイントが Out メッセージを作成するわけではないことに注意してください。

受信 HTTP リクエストを処理し、支払い要求を処理する以下のルートを見てみましょう。

from("jetty:http://localhost:8080/foo")
    .to("cxf:bean:addAccountDetails")
    .to("cxf:bean:getCreditRating")
    .to("cxf:bean:processTransaction");

受信支払い要求は、Web サービスのパイプライン、cxf:bean:addAccountDetailscxf:bean:getCreditRating、および cxf:bean:processTransaction を介して渡すことで処理されます。最後の Web サービス processTransaction が生成する応答 (Out メッセージ) は、Jetty エンドポイント経由で返信されます。

パイプラインがエンドポイントのシーケンスで設定される場合は、以下の代替構文を使用することもできます。

from("jetty:http://localhost:8080/foo")
    .pipeline("cxf:bean:addAccountDetails", "cxf:bean:getCreditRating", "cxf:bean:processTransaction");

InOptionalOut エクスチェンジのパイプライン

InOptionalOut エクスチェンジのパイプラインは、図2.4「InOut エクスチェンジのサンプルパイプライン」 のパイプラインと基本的に同じです。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 セグメント化されたルートによる複数入力の処理

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

ルートの最初のセグメントは、たとえば 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 パッケージの標準集約ストラテジーで、各新しいエクスチェンジを java.util.List インスタンスに追加し、生成されるリストを Exchange.GROUPED_EXCHANGE エクスチェンジプロパティーに保存します。この場合、リストには、(creditRequests JMS キューからの) 元のエクスチェンジと (file エンドポイントからの) Enricher エクスチェンジの 2 つの要素が含まれます。

グループ化されたエクスチェンジにアクセスするには、以下のようなコードを使用します。

public class MergeCreditRequestAndRatings {
    public void merge(Exchange ex) {
        // Obtain the grouped exchange
        List<Exchange> list = ex.getProperty(Exchange.GROUPED_EXCHANGE, List.class);

        // Get the exchanges from the grouped exchange
        Exchange originalEx = list.get(0);
        Exchange ratingsEx  = list.get(1);

        // Merge the exchanges
        ...
    }
}

このアプリケーションへの別のアプローチとしては、データをマージするコードをカスタム集約ストラテジークラスに直接実装することが考えられます。

Content Enricher パターンの詳細は、「Content Enricher」 を参照してください。

2.3. 例外処理

概要

Apache Camel はいくつかの異なるメカニズムを提供しており、異なるレベルの粒度で例外を処理することができます。まず、doTrydoCatch、および doFinally を使用してルート内で例外を処理できます。また、onException を使用して、各例外型に対して実行するアクションを指定し、RouteBuilder 内のすべてのルートにそのルールを適用することもできます。または、errorHandler を使用して、すべての 例外型に対して実行するアクションを指定し、そのルールを RouteBuilder 内のすべてのルートに適用することもできます。

例外処理の詳細は、「Dead Letter Channel」 を参照してください。

2.3.1. onException 句

概要

onException 句は、1 つ以上のルートで発生する例外をトラップするための強力なメカニズムです。これは型固有のもので、異なる例外型を処理するための個別のアクションを定義することができます。基本的にルートと同じ (実際には、若干拡張された) 構文でアクションを定義できるため、例外を処理する方法にかなりの柔軟性が得られます。また、トラップモデルをベースにしていることにより、1 つの onException 句で任意のルート内の任意のノードで発生した例外を処理できます。

onException を使用した例外のトラップ

onException 句は、例外をキャッチするのではなく、トラップ するメカニズムです。つまり、一度 onException 句を定義すると、ルート内の任意の地点で発生する例外がトラップされます。これは、特定のコードフラグメントが try ブロックで 明示的 に囲まれている場合にのみ例外がキャッチされる、Java の try/catch メカニズムとは対照的です。

onException 句を定義すると、Apache Camel ランタイムが各ルートノードを暗黙的に try ブロックで囲んでしまいます。このため、onException 句はルートの任意の地点で例外をトラップすることができます。ただし、このラッピングは自動的に行われ、ルート定義には表示されません。

Java DSL の例

以下の Java DSL の例では、onException 句は RouteBuilder クラスで定義されているすべてのルートに適用されます。いずれかのルート (from("seda:inputA") または from("seda:inputB")) の処理中に ValidationException 例外が発生すると、onException 句はその例外をトラップし、現在のエクスチェンジを validationFailed JMS キュー (デッドレターキューとして機能する) にリダイレクトします。

// Java
public class MyRouteBuilder extends RouteBuilder {

  public void configure() {
    onException(ValidationException.class)
      .to("activemq:validationFailed");

    from("seda:inputA")
      .to("validation:foo/bar.xsd", "activemq:someQueue");

    from("seda:inputB").to("direct:foo")
      .to("rnc:mySchema.rnc", "activemq:anotherQueue");
  }
}
XML DSL の例

上記の例は、exception 句を定義する onException 要素を使用して、以下のように XML DSL で表すこともできます。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:camel="http://camel.apache.org/schema/spring"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <onException>
            <exception>com.mycompany.ValidationException</exception>
            <to uri="activemq:validationFailed"/>
        </onException>
        <route>
            <from uri="seda:inputA"/>
            <to uri="validation:foo/bar.xsd"/>
            <to uri="activemq:someQueue"/>
        </route>
        <route>
            <from uri="seda:inputB"/>
            <to uri="rnc:mySchema.rnc"/>
            <to uri="activemq:anotherQueue"/>
        </route>
    </camelContext>

</beans>
複数の例外のトラップ

複数の onException 句を定義して、RouteBuilder スコープ内で例外をトラップすることができます。これにより、例外に応じて異なるアクションを実行できます。たとえば、以下の Java DSL で定義された一連の onException 句は、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 scope: RouteBuilder.configure() メソッド内で独立した文として定義された onException 句は、その RouteBuilder インスタンスで定義されたすべてのルートに影響します。一方、これらの onException 句は他の RouteBuilder インスタンス内で定義されたルートに対する 影響はありませんonException 句は、ルート定義の前に表示する 必要があります

    この時点までのすべての例は、RouteBuilder スコープを使用して定義されます。

  • Route スコープ - onException 句をルート内に直接埋め込むこともできます。onException 句は、それらが定義されているルートに のみ 影響します。
Route スコープ

ルート定義内のどこにでも onException 句を埋め込むことができますが、end() DSL コマンドを使用して埋め込んだ onException 句を終了する必要があります。

たとえば、以下のように Java DSL で埋め込み onException 句を定義できます。

// Java
from("direct:start")
  .onException(OrderFailedException.class)
    .maximumRedeliveries(1)
    .handled(true)
    .beanRef("orderService", "orderFailed")
    .to("mock:error")
  .end()
  .beanRef("orderService", "handleOrder")
  .to("mock:result");

XML DSL では、埋め込み onException 句を以下のように定義できます。

<route errorHandlerRef="deadLetter">
    <from uri="direct:start"/>
    <onException>
        <exception>com.mycompany.OrderFailedException</exception>
        <redeliveryPolicy maximumRedeliveries="1"/>
        <handled>
            <constant>true</constant>
        </handled>
        <bean ref="orderService" method="orderFailed"/>
        <to uri="mock:error"/>
    </onException>
    <bean ref="orderService" method="handleOrder"/>
    <to uri="mock:result"/>
</route>

2.3.2. エラーハンドラー

概要

errorHandler() 句は、このメカニズムが異なる例外型を識別 できない 点を除いて、onException 句と同様の機能を提供します。errorHandler() 句は、Apache Camel が提供する元々の例外処理メカニズムで、onException 句が実装される前から利用可能でした。

Java DSL の例

errorHandler() 句は RouteBuilder クラスで定義され、その RouteBuilder クラスのすべてのルートに適用されます。これは、該当するルートのいずれかで例外が その種類に関わらず 発生するたびに実行されます。たとえば、失敗したすべてのエクスチェンジを ActiveMQ の deadLetter キューにルーティングするエラーハンドラーを定義するには、以下のように RouteBuilder を定義します。

public class MyRouteBuilder extends RouteBuilder {

    public void configure() {
        errorHandler(deadLetterChannel("activemq:deadLetter"));

        // The preceding error handler applies
        // to all of the following routes:
        from("activemq:orderQueue")
          .to("pop3://fulfillment@acme.com");
        from("file:src/data?noop=true")
          .to("file:target/messages");
        // ...
    }
}

ただし、デッドレターチャネルへのリダイレクトは、再配信の試行が終了するまで発生しません。

XML DSL の例

XML DSL では、errorHandler 要素を使用して、camelContext スコープ内にエラーハンドラーを定義します。たとえば、失敗したすべてのエクスチェンジを ActiveMQ deadLetter キューにルーティングするエラーハンドラーを定義するには、以下のように errorHandler 要素を定義します。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:camel="http://camel.apache.org/schema/spring"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <errorHandler type="DeadLetterChannel"
                      deadLetterUri="activemq:deadLetter"/>
        <route>
            <from uri="activemq:orderQueue"/>
            <to uri="pop3://fulfillment@acme.com"/>
        </route>
        <route>
            <from uri="file:src/data?noop=true"/>
            <to uri="file:target/messages"/>
        </route>
    </camelContext>

</beans>
エラーハンドラーの種類

表2.1「エラーハンドラーの種類」 では、定義可能なさまざまな種類のエラーハンドラーの概要を説明します。

表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 句」 を参照してください。

  • 例外 の再出力 — コンストラクトを使用して 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 () 句で例外を再出力することができます。

from("direct:start")
    .doTry()
        .process(new ProcessorFail())
        .to("mock:result")
    .doCatch(IOException.class)
         .to("mock:io")
        // Rethrow the exception using a construct instead of handled(false) which is deprecated in a doTry/doCatch clause.
        .throwException(new IllegalArgumentException("Forced"))
    .doCatch(Exception.class)
        // Catch all other exceptions.
        .to("mock:error")
    .end();
注記

doTry/doCatch 句で非推奨となった handled (false) の代わりにプロセッサーを使用して例外を再出力することもできます。

.process(exchange -> {throw exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);})

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

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

<route>
    <from uri="direct:start"/>
    <doTry>
        <process ref="processorFail"/>
        <to uri="mock:result"/>
        <doCatch>
            <to uri="mock:io"/>
            <throwException message="Forced" exceptionType="java.lang.IllegalArgumentException"/>
        </doCatch>
        <doCatch>
            <!-- Catch all other exceptions. -->
            <exception>java.lang.Exception</exception>
            <to uri="mock:error"/>
        </doCatch>
    </doTry>
</route>
onWhen による条件付き例外キャッチ

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

たとえば、以下の doTry ブロックは、例外メッセージが単語 Severe を含む場合にのみ、例外 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 メッセージを送受信できます。XML で Apache Camel のエンドポイントを簡単に定義でき、それをエンドポイントの Bean ID を使用してルート内で参照できます。詳細は、Apache Camel Component Reference GuideCXF を参照してください。

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

Java の例外がサーバー側で発生したときに、例外のスタックトレースがフォールトメッセージにマーシャリングされてクライアントに返されるように、CXF エンドポイントを設定することができます。この機能を有効にするには、以下のように cxfEndpoint 要素で、dataFormatPAYLOAD に設定し、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 Object または POJO) を使用してエクスチェンジオブジェクトを処理するには、インバウンドエクスチェンジを Java オブジェクトのメソッドにバインドする bean() プロセッサーを使用します。たとえば、MyBeanProcessor クラスを使用してインバウンドエクスチェンジを処理するには、以下のようにルートを定義します。

from("file:data/inbound")
    .bean(MyBeanProcessor.class, "processBody")
    .to("file:data/outbound");

bean() プロセッサーは MyBeanProcessor 型のインスタンスを作成し、processBody() メソッドを呼び出してインバウンドエクスチェンジを処理します。単一のルートからのみ MyBeanProcessor インスタンスにアクセスする場合には、この方法が適切です。しかし、複数のルートから同じ MyBeanProcessor インスタンスにアクセスする場合は、Object 型を最初の引数として取る bean() のバリアントを使用します。以下に例を示します。

MyBeanProcessor myBean = new MyBeanProcessor();

from("file:data/inbound")
    .bean(myBean, "processBody")
    .to("file:data/outbound");
from("activemq:inboundData")
    .bean(myBean, "processBody")
    .to("activemq:outboundData");

オーバーロードされた Bean メソッドへのアクセス

Bean がオーバーロードされた複数のメソッドを定義する場合、メソッド名とそのパラメーター型を指定して、どのオーバーロードされたメソッドを呼び出すかを選択できます。たとえば、MyBeanBrocessor クラスに 2 つのオーバーロードされたメソッド processBody(String) および processBody(String,String) がある場合、後者のオーバーロードされたメソッドを以下のように呼び出すことができます。

from("file:data/inbound")
  .bean(MyBeanProcessor.class, "processBody(String,String)")
  .to("file:data/outbound");

または、各パラメーターの型を明示的に指定するのではなく、受け取るパラメーターの数でメソッドを特定する場合は、ワイルドカード文字 * を使用できます。たとえば、パラメーターの正確な型に関係なく、2 つのパラメーターを取る名前が processBody のメソッドを呼び出すには、以下のように bean() プロセッサーを呼び出します。

from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBody(*,*)")
.to("file:data/outbound");

メソッドを指定する場合、単純な修飾なしの型名 (例: processBody(Exchange)) または完全修飾型名 (例: processBody(org.apache.camel.Exchange)) のいずれかを使用できます。

注記

現在の実装では、指定された型名はパラメーター型に完全に一致する必要があります。型の継承は考慮されません。

パラメーターの明示的な指定

Bean メソッドを呼び出す際に、パラメーター値を明示的に指定できます。以下の単純な型の値を渡すことができます。

  • ブール値: true または false
  • 数値: 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 が myBeanId と同じ bean 要素がある場合は、以下のように beanRef() プロセッサーを使用して Java DSL ルート内で Bean を参照できます。

from("file:data/inbound").beanRef("myBeanId", "processBody").to("file:data/outbound");

beanRef() プロセッサーは、指定された Bean インスタンスで MyBeanProcessor.processBody() メソッドを呼び出します。

Spring XML ルート内から、Camel スキーマの bean 要素を使用して Bean を呼び出すこともできます。以下に例を示します。

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="file:data/inbound"/>
    <bean ref="myBeanId" method="processBody"/>
    <to uri="file:data/outbound"/>
  </route>
</camelContext>

効率を若干向上させるために、cache オプションを true に設定して、Bean が使用されるたびにレジストリーを検索しないようにすることもできます。たとえば、キャッシュを有効にするには、以下のように bean 要素の cache 属性を設定します。

<bean ref="myBeanId" method="processBody" cache="true"/>

Java からの Spring Bean へのアクセス

Spring bean 要素を使用してオブジェクトインスタンスを作成する場合、Bean の ID (bean 要素の id 属性の値) を使用して Java からオブジェクトインスタンスを参照できます。たとえば、ID が myBeanId と同じ bean 要素がある場合は、以下のように beanRef() プロセッサーを使用して Java DSL ルート内で Bean を参照できます。

from("file:data/inbound").beanRef("myBeanId", "processBody").to("file:data/outbound");

または、以下のように @BeanInject アノテーションを使用して、依存性注入によって Spring Bean を参照することもできます。

// Java
import org.apache.camel.@BeanInject;
...
public class MyRouteBuilder extends RouteBuilder {

   @BeanInject("myBeanId")
   com.acme.MyBeanProcessor bean;

   public void configure() throws Exception {
     ..
   }
}

@BeanInject アノテーションから Bean ID を省略した場合、Camel は型別にレジストリーを検索しますが、これは指定された型の Bean が 1 つだけの場合にのみ機能します。たとえば、com.acme.MyBeanProcessor 型の Bean を検索して依存性注入するには、以下を実行します。

@BeanInject
com.acme.MyBeanProcessor bean;

Spring XML における Bean のシャットダウン順序

Camel コンテキストで使用される Bean の場合、通常、正しいシャットダウンの順序は次のようになります。

  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 バインディングはフォールバックして、公開アクセス可能な対応するインターフェイスメソッドを呼び出します。

たとえば、以下のパブリック 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 サーバーに送信します。この Web サーバーのホストとポートは、プレースホルダーの {{remote.host}}{{remote.port}} に置き換えられます。

from("direct:start").to("http://{{remote.host}}:{{remote.port}}");

プレースホルダーの値は、以下のように Java プロパティーファイルに定義されています。

# Java properties file
remote.host=myserver.com
remote.port=8080
注記

プロパティープレースホルダーはエンコーディングオプションをサポートし、UTF-8 などの特定の文字セットを使用して、.properties ファイルの読み取りを可能にします。ただし、デフォルトでは ISO-8859-1 文字セットによる実装になります。

PropertyPlaceholders を使用した Apache Camel では、以下がサポートされます。

  • 検索するキーと共にデフォルト値を指定する。
  • すべてのプレースホルダーキーがデフォルト値で設定されていて、それらが使用される場合、PropertiesComponent を定義する必要がない。
  • サードパーティー関数を使用してプロパティー値を検索する。これにより、独自のロジックを実装できます。

    注記

    プロパティー値を検索する関数として、標準で OS 環境変数、JVM システムプロパティー、サービス名イディオムの 3 つを提供します。

プロパティーファイル

プロパティー設定は 1 つ以上の Java プロパティーファイルに格納され、標準の Java プロパティーファイル形式に準拠する必要があります。各プロパティー設定は、それぞれ独立した行に Key=Value の形式で表示されます。空白以外の最初の文字が # または ! から始まる行は、コメントとして扱われます。

たとえば、プロパティーファイルは 例2.4「プロパティーファイルの例」 のような内容になります。

例2.4 プロパティーファイルの例

# Property placeholder settings
# (in Java properties file format)
cool.end=mock:result
cool.result=result
cool.concat=mock:{{cool.result}}
cool.start=direct:cool
cool.showid=true

cheese.end=mock:cheese
cheese.quote=Camel rocks
cheese.type=Gouda

bean.foo=foo
bean.bar=bar

プロパティーの解決

Properties コンポーネントは、ルート定義の中で使用を開始する前に、1 つ以上のプロパティーファイルのロケーションを指定して設定しておく必要があります。以下のいずれかのリゾルバーを使用して、プロパティー値を提供する必要があります。

classpath:PathName,PathName,…​
(デフォルト) クラスパス上のロケーションを指定します。PathName は、フォワードスラッシュを使用して区切られたファイルパス名です。
file:PathName,PathName,…​
ファイルシステムのロケーションを指定します。PathName はフォワードスラッシュを使用して区切られたファイルパス名です。
ref:BeanID
レジストリーの java.util.Properties オブジェクトの ID を指定します。
blueprint:BeanID
cm:property-placeholder Bean の ID を指定します。この Bean は、OSGi Blueprint ファイルのコンテキスト内で、OSGi Configuration Admin サービスで定義されたプロパティーにアクセスするために使用されます。詳細は、「OSGi Blueprint プロパティープレースホルダーとの統合」 を参照してください。

たとえば、クラスパス上にある com/fusesource/cheese.properties プロパティーファイルと com/fusesource/bar.properties プロパティーファイルを指定するには、以下のようなロケーション文字列を使用します。

com/fusesource/cheese.properties,com/fusesource/bar.properties
注記

クラスパスリゾルバーはデフォルトで使用されるため、この例では classpath: 接頭辞は省略できます。

システムプロパティーと環境変数を使用したロケーションの指定

ロケーション PathName に Java システムプロパティーおよび O/S 環境変数を埋め込むことができます。

Java システムプロパティーは、${PropertyName} 構文を使用してロケーションリゾルバーに埋め込むことができます。たとえば、Red Hat Fuse のルートディレクトリーが Java システムプロパティー karaf.home に保存されている場合、以下のようにそのディレクトリーの値をファイルロケーションに埋め込むことができます。

file:${karaf.home}/etc/foo.properties

O/S 環境変数は、${env:VarName} 構文を使用してロケーションリゾルバーに埋め込むことができます。たとえば、Fuse のルートディレクトリーが環境変数 SMX_HOME に保存されている場合、以下のようにそのディレクトリーの値をファイルロケーションに埋め込むことができます。

file:${env:SMX_HOME}/etc/foo.properties

Properties コンポーネントの設定

プロパティープレースホルダーの使用を開始する前に、1 つ以上のプロパティーファイルのロケーションを指定して、Properties コンポーネントを設定する必要があります。

Java DSL では、以下のように Properties コンポーネントにプロパティーファイルのロケーションを設定できます。

// Java
import org.apache.camel.component.properties.PropertiesComponent;
...
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("com/fusesource/cheese.properties,com/fusesource/bar.properties");
context.addComponent("properties", pc);

addComponent() の呼び出しに示されているように、Properties コンポーネントの名前を properties に設定する 必要があります

XML DSL では、以下のように専用の propertyPlacholder 要素を使用して Properties コンポーネントを設定できます。

<camelContext ...>
   <propertyPlaceholder
      id="properties"
      location="com/fusesource/cheese.properties,com/fusesource/bar.properties"
   />
</camelContext>

Properties コンポーネントの初期化時に見つからない .properties ファイルを、Properties コンポーネントに無視させる場合は、ignoreMissingLocation オプションを true に設定します (通常、.properties ファイルが見つからない場合はエラーが発生します)。

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

プレースホルダー構文

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

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

    AttributeName="{{Key}}"

    その他の属性タイプ (xs:int または xs:boolean など) は、以下の構文を使用して設定する必要があります。

    prop:AttributeName="Key"

    prop は、http://camel.apache.org/schema/placeholder 名前空間に関連付けられています。

  • Java DSL の EIP オプション設定時: Java DSL でエンタープライズ統合パターン (EIP) コマンドにオプションを設定するには、流れるような DSL に以下の placeholder() 句を追加します。

    .placeholder("OptionName", "Key")
  • Simple 言語式: プレースホルダーは ${properties:Key}. のように指定します。

エンドポイント URI 内での置換

ルートの中でエンドポイント URI 文字列が現れると、そのエンドポイント URI を構文解析する最初のステップは、常にプロパティープレースホルダーパーサーを適用することです。プレースホルダーパーサーは、二重かっこ {{Key}} の間に表示されるプロパティー名を自動的に置換します。たとえば、例2.4「プロパティーファイルの例」 にあるプロパティー設定では、以下のようにルートを定義できます。

from("{{cool.start}}")
    .to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
    .to("mock:{{cool.result}}");

デフォルトでは、プレースホルダーパーサーはレジストリーから properties Bean ID を検索し、Properties コンポーネントを検索します。必要であれば、エンドポイント URI でスキーマを明示的に指定できます。たとえば、各エンドポイント URI に接頭辞 properties: を付けて、以下のように同等のルートを定義できます。

from("properties:{{cool.start}}")
    .to("properties:log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
    .to("properties:mock:{{cool.result}}");

スキーマを明示的に指定する場合、Properties コンポーネントにオプションを指定することもできます。たとえば、プロパティーファイルのロケーションを上書きするために、以下のように location オプションを設定できます。

from("direct:start").to("properties:{{bar.end}}?location=com/mycompany/bar.properties");

Spring XML ファイル内での置換

XML DSL で DSL 要素のさまざまな属性を設定するために、プロパティープレースホルダーを使用することもできます。このコンテキストにおいても、プレースホルダー構文には二重かっこ {{Key}} を使用します。たとえば、以下のようにプロパティープレースホルダーを使用して jmxAgent 要素を定義できます。

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <propertyPlaceholder id="properties" location="org/apache/camel/spring/jmx.properties"/>

    <!-- we can use property placeholders when we define the JMX agent -->
    <jmxAgent id="agent" registryPort="{{myjmx.port}}"
              usePlatformMBeanServer="{{myjmx.usePlatform}}"
              createConnector="true"
              statisticsLevel="RoutesOnly"
            />

    <route>
        <from uri="seda:start"/>
        <to uri="mock:result"/>
    </route>
</camelContext>

XML DSL 属性値の置換

xs:string 型の属性値を指定するには、通常のプレースホルダー構文を使用できます (例: <jmxAgent registryPort="{{myjmx.port}}" …​>)。しかし、他の型の属性 (例: xs:intxs:boolean) については、特別な構文 prop:AttributeName="Key" を使用する必要があります。

たとえば、プロパティーファイルで stop.flag プロパティーの値が true に定義されている場合、このプロパティーを使用して以下のように stopOnException ブール値属性を設定できます。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:prop="http://camel.apache.org/schema/placeholder"
       ... >

    <bean id="illegal" class="java.lang.IllegalArgumentException">
        <constructor-arg index="0" value="Good grief!"/>
    </bean>

    <camelContext xmlns="http://camel.apache.org/schema/spring">

        <propertyPlaceholder id="properties"
                             location="classpath:org/apache/camel/component/properties/myprop.properties"
                             xmlns="http://camel.apache.org/schema/spring"/>

        <route>
            <from uri="direct:start"/>
            <multicast prop:stopOnException="stop.flag">
                <to uri="mock:a"/>
                <throwException ref="damn"/>
                <to uri="mock:b"/>
            </multicast>
        </route>

    </camelContext>

</beans>
重要

prop 接頭辞は、前述の例の beans 要素に示されるように、Spring ファイルの http://camel.apache.org/schema/placeholder 名前空間に明示的に割り当てられている必要があります。

Java DSL における EIP オプションの置換

Java DSL で EIP コマンドを呼び出す場合は、placeholder("OptionName", "Key") の形式のサブ句を追加することで、プロパティープレースホルダーの値を使用した EIP オプションの設定ができます。

たとえば、プロパティーファイルで stop.flag プロパティーの値が true に定義されている場合、このプロパティーを使用して以下のように Multicast EIP の stopOnException オプションを設定できます。

from("direct:start")
    .multicast().placeholder("stopOnException", "stop.flag")
        .to("mock:a").throwException(new IllegalAccessException("Damn")).to("mock:b");

Simple 言語式内での置換

Simple 言語の式の中でプロパティープレースホルダーを置換することもできますが、この場合プレースホルダーの構文は ${properties:Key} になります。たとえば、以下のようにして Simple 式内の cheese.quote プレースホルダーを置換できます。

from("direct:start")
    .transform().simple("Hi ${body} do you think ${properties:cheese.quote}?");

構文 ${properties:Key:DefaultVal} を使用すると、プロパティーのデフォルト値を指定できます。以下に例を示します。

from("direct:start")
    .transform().simple("Hi ${body} do you think ${properties:cheese.quote:cheese is good}?");

構文 ${properties-location:Location:Key} を使用して、プロパティーファイルのロケーションをオーバーライドすることもできます。たとえば、com/mycompany/bar.properties プロパティーファイルの設定を使用して、bar.quote プレースホルダーを置き換えるには、以下のように Simple 式を定義します。

from("direct:start")
    .transform().simple("Hi ${body}. ${properties-location:com/mycompany/bar.properties:bar.quote}.");

XML DSL 内でのプロパティープレースホルダーの使用

以前のリリースでは、 XML DSL のプレースホルダーをサポートするために xs:string 型の属性が使用されていました。たとえば、timeout 属性は xs:int 型になります。したがって、文字列の値をプレースホルダーキーとして設定することはできませんでした。

Apache Camel 2.7 以降、特別なプレースホルダーの名前空間を使用することでそれが可能になりました。以下の例は、その名前空間を使うための prop 接頭辞を示しています。これにより、XML DSL の属性に prop 接頭辞を付けて使用できます。

注記

Multicast において、キー stop を使用してプレースホルダーの値をオプション stopOnException に設定しています。また、プロパティーファイルの中で以下の値を定義しています。

stop=true
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:prop="http://camel.apache.org/schema/placeholder"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
    ">

    <!-- Notice in the declaration above, we have defined the prop prefix as the Camel placeholder namespace -->

    <bean id="damn" class="java.lang.IllegalArgumentException">
        <constructor-arg index="0" value="Damn"/>
    </bean>

    <camelContext xmlns="http://camel.apache.org/schema/spring">

        <propertyPlaceholder id="properties"
                             location="classpath:org/apache/camel/component/properties/myprop.properties"
                             xmlns="http://camel.apache.org/schema/spring"/>

        <route>
            <from uri="direct:start"/>
            <!-- use prop namespace, to define a property placeholder, which maps to
                 option stopOnException={{stop}} -->
            <multicast prop:stopOnException="stop">
                <to uri="mock:a"/>
                <throwException ref="damn"/>
                <to uri="mock:b"/>
            </multicast>
        </route>

    </camelContext>

</beans>

OSGi Blueprint プロパティープレースホルダーとの統合

Red Hat Fuse の OSGi コンテナーにルートをデプロイする場合、Apache Camel プロパティープレースホルダーのメカニズムを Fuse が持つ Blueprint プロパティープレースホルダーのメカニズムと統合できます (実際には、この統合はデフォルトで有効になっています) 。統合のセットアップには、基本的に以下の 2 つの方法があります。

暗黙的な Blueprint の統合

OSGi Blueprint ファイル内で camelContext 要素を定義すると、Apache Camel プロパティープレースホルダーのメカニズムは自動的に Blueprint プロパティープレースホルダーのメカニズムと統合します。つまり、camelContext のスコープ内に現れる Apache Camel 構文に従ったプレースホルダー (例: {{cool.end}}) は、暗黙的に blueprint property placeholder のメカニズムを検索することで解決されます。

たとえば、OSGi Blueprint ファイルで定義された以下のようなルートがあるとします。ルートの最後のエンドポイントは、プロパティープレースホルダー {{result}} で定義されています。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
           xsi:schemaLocation="
           http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

    <!-- OSGI blueprint property placeholder -->
    <cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint">
        <!-- list some properties for this test -->
        <cm:default-properties>
            <cm:property name="result" value="mock:result"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <!-- in the route we can use {{ }} placeholders which will look up in blueprint,
             as Camel will auto detect the OSGi blueprint property placeholder and use it -->
        <route>
            <from uri="direct:start"/>
            <to uri="mock:foo"/>
            <to uri="{{result}}"/>
        </route>
    </camelContext>

</blueprint>

Blueprint プロパティープレースホルダーのメカニズムは、cm:property-placeholder Bean を作成することで初期化されます。上記の例では、cm:property-placeholder Bean は camel.blueprint 永続化 ID に関連付けられています。永続化 ID は、OSGi Configuration Admin サービスから関連するプロパティーのグループを参照する標準的な方法です。つまり、cm:property-placeholder Bean は、camel.blueprint 永続化 ID の下で定義されたすべてのプロパティーへのアクセスを提供します。一部のプロパティーにデフォルト値を指定することもできます (ネストされた cm:property 要素を使用します)。

Blueprint のコンテキストでは、Apache Camel プレースホルダーのメカニズムは Bean レジストリー内の cm:property-placeholder インスタンスを検索します。このインスタンスが見つかると、Apache Camel プレースホルダーのメカニズムと自動的に統合され、{{result}} のようなプレースホルダーは Blueprint プロパティープレースホルダーのメカニズムに対して (この例では myblueprint.placeholder Bean を通して) キーを検索することで解決されます。

注記

デフォルトの Blueprint プレースホルダー構文 (Blueprint プロパティーに直接アクセスする) は ${Key} です。そのため、camelContext 要素の 範囲外 では、使用しなければならないプレースホルダー構文は ${Key} になります。しかし、camelContext 要素の 範囲内 では、使用しなければならないプレースホルダー構文は {{Key}} になります。

明示的な Blueprint の統合

Apache Camel プロパティープレースホルダーのメカニズムがプロパティーを探す場所をさらに制御する場合は、propertyPlaceholder 要素を定義してリゾルバーのロケーションを明示的に指定できます。

たとえば、以下の Blueprint の設定について考えるとします。この例は、明示的に propertyPlaceholder インスタンスを作成している点が前述の例とは異なっています。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
           xsi:schemaLocation="
           http://www.osgi.org/xmlns/blueprint/v1.0.0 ">https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

    <!-- OSGI blueprint property placeholder -->
    <cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint">
        <!-- list some properties for this test -->
        <cm:default-properties>
            <cm:property name="result" value="mock:result"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">

        <!-- using Camel properties component and refer to the blueprint property placeholder by its id -->
        <propertyPlaceholder id="properties" location="blueprint:myblueprint.placeholder"/>

        <!-- in the route we can use {{ }} placeholders which will lookup in blueprint -->
        <route>
            <from uri="direct:start"/>
            <to uri="mock:foo"/>
            <to uri="{{result}}"/>
        </route>

    </camelContext>

</blueprint>

前述の例では、propertyPlaceholder 要素は、場所を blueprint:myblueprint.placeholder に設定することにより、使用する cm:property-placeholder Bean を明示的に指定します。つまり、blueprint: リゾルバーは、cm:property-placeholder Bean の ID myblueprint.placeholder を明示的に参照します。

このスタイルによる設定は、Blueprint ファイルに複数の cm:property-placeholder Bean が定義されていて、どれを使用すべきかを指定する必要がある場合に有効です。また、ロケーションをコンマ区切りのリストで指定することで、複数のロケーションからプロパティーを取得することも可能になります。たとえば、cm:property-placeholder Bean とクラスパス上のプロパティーファイル myproperties.properties の両方からプロパティーを検索する場合、以下のように propertyPlaceholder 要素を定義します。

<propertyPlaceholder id="properties"
  location="blueprint:myblueprint.placeholder,classpath:myproperties.properties"/>

Spring プロパティープレースホルダーとの統合

Spring XML ファイルの XML DSL を使用して Apache Camel アプリケーションを定義している場合、org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer 型の Spring Bean を宣言することで、Apache Camel プロパティープレースホルダーのメカニズムを Spring プロパティープレースホルダーのメカニズムと統合できます。

BridgePropertyPlaceholderConfigurer を定義します。これは、Spring XML ファイルの Apache Camel の propertyPlaceholder 要素と Spring の ctx:property-placeholder 要素の両方を置き換えます。その後、Spring の ${PropName} 構文または Apache Camel の {{PropName}} 構文のいずれかを使用して、設定したプロパティーを参照できます。

たとえば、cheese.properties ファイルからプロパティー設定を読み取るブリッジプロパティープレースホルダーを定義するとします。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ctx="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <!-- Bridge Spring property placeholder with Camel -->
  <!-- Do not use <ctx:property-placeholder ... > at the same time -->
  <bean id="bridgePropertyPlaceholder"
        class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
    <property name="location"
              value="classpath:org/apache/camel/component/properties/cheese.properties"/>
  </bean>

  <!-- A bean that uses Spring property placeholder -->
  <!-- The ${hi} is a spring property placeholder -->
  <bean id="hello" class="org.apache.camel.component.properties.HelloBean">
    <property name="greeting" value="${hi}"/>
  </bean>

  <camelContext xmlns="http://camel.apache.org/schema/spring">
    <!-- Use Camel's property placeholder {{ }} style -->
    <route>
      <from uri="direct:{{cool.bar}}"/>
      <bean ref="hello"/>
      <to uri="{{cool.end}}"/>
    </route>
  </camelContext>

</beans>
注記

または、Spring プロパティーファイルを指すように、BridgePropertyPlaceholderConfigurerlocation 属性を設定することもできます。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 GuideFile2Apache Camel Component Reference GuideFtp2 は、どちらも scheduledExecutorService プロパティーを公開しており、コンポーネントの ExecutorService オブジェクトを指定するために使用できます。

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

アプリケーションのログをより読みやすくするために、スレッド名 (ログ内でスレッドを識別するために使用されるもの) をカスタマイズすることが推奨されます。スレッド名をカスタマイズするには、ExecutorServiceStrategy クラスまたは ExecutorServiceManager クラスの setThreadNamePattern メソッドを呼び出すことで、スレッド名パターン を設定します。または、スレッド名パターンを設定するより簡単な方法として、CamelContext オブジェクトに threadNamePattern プロパティーを設定する方法もあります。

スレッド名パターンでは、以下のプレースホルダーが使用できます。

#camelId#
現在の CamelContext の名前。
#counter#
インクリメントカウンターとして実装された一意のスレッド ID。
#name#
通常の Camel スレッド名。
#longName#
長いスレッド名。エンドポイントパラメーターなどを含めることができる。

以下は、スレッド名パターンの典型的な例です。

Camel (#camelId#) thread #counter# - #name#

以下の例は、XML DSL を使用して Camel コンテキストに threadNamePattern 属性を設定する方法を示しています。

<camelContext xmlns="http://camel.apache.org/schema/spring"
              threadNamePattern="Riding the thread #counter#" >
  <route>
    <from uri="seda:start"/>
    <to uri="log:result"/>
    <to uri="mock:result"/>
  </route>
</camelContext>

2.9. ルートの起動およびシャットダウンの制御

概要

デフォルトでは、Apache Camel アプリケーション (CamelContext インスタンスで表される) の起動時にルートが自動的に起動し、Apache Camel アプリケーションのシャットダウン時にルートは自動的にシャットダウンします。クリティカルでないデプロイメントについては、シャットダウン順序の詳細は通常それほど重要ではありません。しかし、本番環境では、データ損失を回避するために、シャットダウン時に残っているタスクを完了させることが重要になります。また、通常、依存関係の順序違反 (実行中のタスクが完了できなくなる) を防止するために、ルートがシャットダウンする順番を制御したくなることもあります。

このため、Apache Camel はアプリケーションの 正常なシャットダウン をサポートする機能を提供しています。正常なシャットダウンにより、ルートの停止と起動を完全に制御し、ルートのシャットダウン順序を制御し、現在実行中のタスクを完了まで実行できるようになります。

ルート ID の設定

ルート ID を各ルートに割り当てるのが良いプラクティスです。ルート ID を設定すると、ロギングメッセージや管理機能がよりわかりやすくなるだけでなく、ルートの停止と起動の制御がより行いやすくなります。

たとえば、Java DSL では、以下のように routeId() コマンドを実行して、ルート ID myCustomerRouteId をルートに割り当てることができます。

from("SourceURI").routeId("myCustomRouteId").process(...).to(TargetURI);

XML DSL では、以下のように route 要素の id 属性を設定します。

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route id="myCustomRouteId" >
    <from uri="SourceURI"/>
    <process ref="someProcessorId"/>
    <to uri="TargetURI"/>
  </route>
</camelContext>

ルートの自動起動の無効化

デフォルトでは、起動時に CamelContext が認識しているすべてのルートは自動的に起動されます。しかし、特定のルートの起動を手動で制御する場合は、そのルートの自動起動を無効にできます。

Java DSL ルートが自動的に起動するかどうかを制御するには、boolean 引数 (true または false) か、String 引数 (true または false) のいずれかで、autoStartup コマンドを呼び出します。たとえば、以下のように Java DSL でルートの自動起動を無効にできます。

from("SourceURI")
  .routeId("nonAuto")
  .autoStartup(false)
  .to(TargetURI);

XML DSL では、以下のように route 要素の autoStartup 属性を false に設定して、ルートの自動起動を無効にできます。

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route id="nonAuto" autoStartup="false">
    <from uri="SourceURI"/>
    <to uri="TargetURI"/>
  </route>
</camelContext>

手動によるルートの起動および停止

Java において、CamelContext インスタンスの startRoute() および stopRoute() メソッドを呼び出すことにより、ルートを手動でいつでも起動または停止できます。たとえば、ルート ID nonAuto を持つルートを開始するには、以下のように CamelContext インスタンス 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 秒後に設定されています。また、このポリシーは、初期起動時刻の 3 秒後に、2 回目のルートを開始するように設定されています。これは、routeStartRepeatCount を 1 に設定し、routeStartRepeatInterval を 3000 ミリ秒に設定することで設定されます。

Java DSL では、ルート内で routePolicy() DSL コマンドを呼び出して、ルートポリシーをルートにアタッチします。

例2.7 単純な定期実行ルートの Java DSL の例

// Java
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
long startTime = System.currentTimeMillis() + 3000L;
policy.setRouteStartDate(new Date(startTime));
policy.setRouteStartRepeatCount(1);
policy.setRouteStartRepeatInterval(3000);

from("direct:start")
   .routeId("test")
   .routePolicy(policy)
   .to("mock:success");
注記

複数の引数を指定して routePolicy() を呼び出すことで、ルート上に複数のポリシーを指定することができます。

XML DSL の例

例2.8「単純な定期実行ルートの XML DSL の例」 は、XML DSL を使用して起動するルートをスケジュールする方法を示しています。

XML DSL では、route 要素に routePolicyRef 属性を設定して、ルートポリシーをルートにアタッチします。

例2.8 単純な定期実行ルートの XML DSL の例

<bean id="date" class="java.util.Data"/>

<bean id="startPolicy" class="org.apache.camel.routepolicy.quartz.SimpleScheduledRoutePolicy">
    <property name="routeStartDate" ref="date"/>
    <property name="routeStartRepeatCount" value="1"/>
    <property name="routeStartRepeatInterval" value="3000"/>
</bean>

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route id="myroute" routePolicyRef="startPolicy">
        <from uri="direct:start"/>
        <to uri="mock:success"/>
    </route>
</camelContext>
注記

routePolicyRef の値をコンマ区切りの Bean ID リストとして設定することで、ルート上に複数のポリシーを指定できます。

日付と時刻の定義

単純な定期実行ルートポリシーで使用されるトリガーの初動時刻は、java.util.Date タイプを使用して指定します。Date インスタンスを定義する最も柔軟な方法は、java.util.GregorianCalendar クラスを使用することです。GregorianCalendar クラスの便利なコンストラクターとメソッドを使用して日付を定義し、GregorianCalendar.getTime() を呼び出して Date インスタンスを取得します。

たとえば、2011 年 1 月 1 日正午の日時を定義するには、以下のように GregorianCalendar コンストラクターを呼び出します。

// Java
import java.util.GregorianCalendar;
import java.util.Calendar;
...
GregorianCalendar gc = new GregorianCalendar(
    2011,
    Calendar.JANUARY,
    1,
    12,  // hourOfDay
    0,   // minutes
    0    // seconds
);

java.util.Date triggerDate = gc.getTime();

GregorianCalendar クラスは、異なるタイムゾーンの時間の定義もサポートします。デフォルトでは、コンピューターのローカルタイムゾーンを使用します。

正常シャットダウン

単純な定期実行ルートポリシーを設定してルートを停止すると、ルートの停止アルゴリズムが自動的に正常シャットダウンの手順に統合されます (「ルートの起動およびシャットダウンの制御」 を参照)。よって、タスクは現在のエクスチェンジが処理を完了するまで待機してから、ルートをシャットダウンします。ただし、タイムアウトを設定することで、ルートがエクスチェンジの処理を終了したかどうかにかかわらず、指定した時間後にルートを強制的に停止することができます。

タイムアウト時の処理中エクスチェンジのロギング

指定のタイムアウト期間内に正常シャットダウンが適切に行われなかった場合、Apache Camel はより強行なシャットダウンを実行します。ルートやスレッドプールなどを強制的にシャットダウンします。

タイムアウト後、Apache Camel は現在処理中のエクスチェンジの情報をログに記録します。エクスチェンジの元および現在のルートをログに記録します。

たとえば、以下のログは、1 つの処理中のエクスチェンジがあり、その元のルートは route1 で、現在 delay1 ノードの同じ route1 にあることを表しています。

正常なシャットダウン中に、org.apache.camel.impl.DefaultShutdownStrategy で DEBUG ロギングレベルを有効にすると、同じインフライトエクスチェンジ情報がログに記録されます。

2015-01-12 13:23:23,656 [- ShutdownTask] INFO DefaultShutdownStrategy - There are 1 inflight exchanges:
InflightExchange: [exchangeId=ID-davsclaus-air-62213-1421065401253-0-3, fromRouteId=route1, routeId=route1, nodeId=delay1, elapsed=2007, duration=2017]

これらのログを表示したくない場合は、logInflightExchangesOnTimeout オプションを false に設定してこれをオフにできます。

  context.getShutdownStrategegy().setLogInflightExchangesOnTimeout(false);
タスクのスケジューリング

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

ルートの開始

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

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

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

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

Seconds Minutes Hours DayOfMonth Month DayOfWeek [Year]

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

0 0 24 * * ?

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

0 0 24 ? * MON-FRI

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

0 0/5 * * * ?

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

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

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

ルートの開始

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

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

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

2.12.1.1. オプション

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

パラメーター

デフォルト値

説明

duration

-1

アプリケーションが終了する前に実行される期間 (秒単位) を設定します。0 以下の値を指定すると、永久に実行されます。

durationIdle

-1

アプリケーションが終了する前にアイドル状態でいられる期間 (秒単位) を設定します。0 以下の値を指定すると、永久に実行されます。

durationMaxMessages

-1

アプリケーションが終了する前にアプリケーションが処理するメッセージ最大数の期間を設定します。

logClasspath

false

起動時にクラスパスをログに記録するかどうか。

2.12.1.2. OSGi Blueprint の実行

camel:run プラグインは、Blueprint アプリケーションの実行もサポートします。デフォルトでは、OSGI-INF/blueprint/*.xml の OSGi Blueprint ファイルがスキャンされます。以下のように useBlueprint を true に設定して、camel:run プラグインが Blueprint を使用するように設定する必要があります。

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <useBlueprint>true</useBlueprint>
  </configuration>
</plugin>

これにより、Camel 関連だけでなく、他の Blueprint サービスも起動することができます。camel:run ゴールは、camel-blueeprint がクラスパス上にあるか、またはプロジェクト内に blueeprint XML ファイルがある場合は、自動検出することができるので、useBlueprint オプションを設定する必要がありません。

2.12.1.3. 制限された Blueprint コンテナーの使用

Blueprint のコンテナーとして Felix Connector プロジェクトを使用しています。Felix は完全な Blueprint コンテナーではありません。完全な Blueprint コンテナーで実行する場合は、Apache Karaf または Apache ServiceMix を使用できます。applicationContextUri 設定を使用して、明示的な Blueprint XML ファイルを指定できます。例を以下に示します。

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <useBlueprint>true</useBlueprint>
    <applicationContextUri>myBlueprint.xml</applicationContextUri>
    <!-- ConfigAdmin options which have been added since Camel 2.12.0 -->
    <configAdminPid>test</configAdminPid>
    <configAdminFileName>/user/test/etc/test.cfg</configAdminFileName>
  </configuration>
</plugin>

applicationContextUri はクラスパスからファイルをロードするので、上の例では myBlueprint.xml ファイルはクラスパスのルートになければなりません。configAdminPid は pid 名で、persistence プロパティーファイルを読み込む際に、設定管理サービスの pid 名として使用されます。configAdminFileName は、設定管理サービスのプロパティーファイルを読み込むために使用されるファイル名です。

2.12.1.4. CDI の実行

camel:run プラグインは、CDI アプリケーションの実行もサポートします。これにより、Camel 関連だけでなく、すべての CDI 対応サービスを起動できます。下記の例のように、CDI コンテナー (Weld や OpenWebBeans など) を camel-maven-plugin の依存関係に追加する必要があります。Camel のソースからは、以下のように CDI のサンプルを実行できます。

cd examples/camel-example-cdi
mvn compile camel:run
2.12.1.5. クラスパスのロギング

camel:run の実行時に、クラスパスをログに記録するかどうかを設定できます。以下のコマンドを使用して、この設定を有効できます。

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <logClasspath>true</logClasspath>
  </configuration>
</plugin>
2.12.1.6. XML ファイルのライブリロードの使用

XML ファイルの変更をスキャンし、それらの XML ファイルに含まれる Camel ルートのリロードをトリガーするように、プラグインを設定できます。

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <fileWatcherDirectory>src/main/resources/META-INF/spring</fileWatcherDirectory>
  </configuration>
</plugin>

設定後、プラグインはこのディレクトリーの監視を開始します。エディターからソースコードを編集して保存すると、変更後の内容が実行中の Camel アプリケーションに適用されます。<routes><route> などの Camel ルートへの変更のみがサポートされることに注意してください。Spring や OSGi Blueprint の <bean> 要素を変更することはできません。

2.12.2. camel:validate

以下の Camel 機能のソースコード検証の場合

  • エンドポイント URI
  • Simple 式または述語
  • ルート ID の重複

次に、コマンドラインまたは、IDEA や Eclipse などの Java エディターから、camel:validate ゴールを実行できます。

mvn camel:validate

また、プラグインを有効にしてビルドの一部として自動的に実行し、エラーを検出することも可能です。

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <executions>
    <execution>
      <phase>process-classes</phase>
      <goals>
        <goal>validate</goal>
      </goals>
    </execution>
  </executions>
</plugin>

フェーズは、プラグインがいつ実行されるかを決定します。上記の例では、メインのソースコードのコンパイル後に実行される process-classes がフェーズになります。この maven プラグインは、テストソースコードを検証するように設定することもできます。以下に示すように、フェーズを process-test-classes に合わせて変更してください。

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <executions>
    <execution>
      <configuration>
        <includeTest>true</includeTest>
      </configuration>
      <phase>process-test-classes</phase>
      <goals>
        <goal>validate</goal>
      </goals>
    </execution>
  </executions>
</plugin>
2.12.2.1. 任意の Maven プロジェクトでのゴール実行

プラグインを pom.xml ファイルに追加せずに Maven プロジェクトで validate ゴールを実行することもできます。この場合、完全修飾名を使用してプラグインを指定する必要があります。たとえば、Apache Camel から camel-example-cdi でゴールを実行するには、次のように実行します。

$cd camel-example-cdi
$mvn org.apache.camel:camel-maven-plugin:2.20.0:validate

このコマンドを実行すると以下が出力されます。

[INFO] ------------------------------------------------------------------------
[INFO] Building Camel :: Example :: CDI 2.20.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- camel-maven-plugin:2.20.0:validate (default-cli) @ camel-example-cdi ---
[INFO] Endpoint validation success: (4 = passed, 0 = invalid, 0 = incapable, 0 = unknown components)
[INFO] Simple validation success: (0 = passed, 0 = invalid)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

validate は成功し、4 つのエンドポイントが検証されます。ここで、ソースコードの Camel エンドポイント URI の 1 つに、以下のようなタイプミスがあったとします。

@Uri("timer:foo?period=5000")

period オプションを以下のように変更し、タイプミスが含まれるようにします。

@Uri("timer:foo?perid=5000")

validate ゴールを再度実行すると、以下が報告されます。

[INFO] ------------------------------------------------------------------------
[INFO] Building Camel :: Example :: CDI 2.20.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- camel-maven-plugin:2.20.0:validate (default-cli) @ camel-example-cdi ---
[WARNING] Endpoint validation error at: org.apache.camel.example.cdi.MyRoutes(MyRoutes.java:32)

	timer:foo?perid=5000

	                   perid    Unknown option. Did you mean: [period]


[WARNING] Endpoint validation error: (3 = passed, 1 = invalid, 0 = incapable, 0 = unknown components)
[INFO] Simple validation success: (0 = passed, 0 = invalid)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
2.12.2.2. オプション

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

パラメーター

デフォルト値

説明

downloadVersion

true

インターネットからの Camel カタログバージョンのダウンロードを許可するかどうか。プロジェクトが使用する Camel バージョンと、このプラグインがデフォルトで使用する Camel バージョンが異なる場合にダウンロードが必要です。

failOnError

false

無効な Camel エンドポイントが見つかった場合に失敗するかどうか。デフォルトでは、WARN レベルでエラーがプラグインログに記録されます。

logUnparseable

false

解析不可のため検証できないエンドポイント URI をログに記録するかどうか。

includeJava

true

無効な Camel エンドポイントの検証対象となる Java ファイルを含めるかどうか。

includeXml

true

無効な Camel エンドポイントの検証対象となる XML ファイルを含めるかどうか。

includeTest

false

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

includes

 

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

excludes

 

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

ignoreUnknownComponent

true

不明なコンポーネントを無視するかどうか。

ignoreIncapable

true

解析不可なエンドポイント URI や、Simple 式を無視するかどうか。

ignoreLenientProperties

true

lenient プロパティーを使用するコンポーネントを無視するかどうか。true の場合、URI の検証はより厳密になりますが、lenient プロパティーを使用するため、URI にあってもコンポーネントの一部でないプロパティーでは検証に失敗することがあります。たとえば、HTTP コンポーネントを使用して、エンドポイント URI でクエリーパラメーターを提供する場合がこれに該当します。

ignoreDeprecated

true

Camel 2.23 の場合: エンドポイント URI で使用される非推奨のオプションを無視するかどうか。

duplicateRouteId

true

Camel 2.20 の場合: ルート ID の重複を検証するかどうか。ルート ID は一意である必要があります。重複がある場合、Camel は起動に失敗します。

directOrSedaPairCheck

true

Camel 2.23 の場合: direct/seda エンドポイントが未定義コンシューマーに送信しているかを検証するかどうか。

showAll

false

エンドポイントと Simple 式 (無効と有効の両方) をすべて表示するかどうか。

たとえば、コマンドラインから ignoreDeprecated オプションを無効するには、以下を実行します。

$mvn camel:validate -Dcamel.ignoreDeprecated=false

オプション名として、-D コマンド引数の前に camel. (例: camel.ignoreDeprecated) を付ける必要があります。

2.12.2.3. include テストを使用したエンドポイントの検証

Maven プロジェクトの場合、プラグインを実行してユニットテストのソースコードで使用されるエンドポイントを検証することもできます。以下のように -D スタイルを使用してオプションを渡すことができます。

$cd myproject
$mvn org.apache.camel:camel-maven-plugin:2.20.0:validate -DincludeTest=true

2.12.3. camel:route-coverage

ユニットテストから Camel ルートのカバレッジのレポートを生成するために使用します。これを使用することによって、Camel ルートのどの部分が使用されたかを把握することができます。

2.12.3.1. route-coverage の有効化

以下のいずれかの方法で、ユニットテスト実行時に route-coverage を有効化できます。

  • グローバル JVM システムプロパティーを設定してすべてのテストクラスで有効。
  • camel-test-spring モジュールを使用する場合、テストクラスごとの @EnableRouteCoverage アノテーションの使用
  • camel-test モジュールを使用する場合、テストクラスごとの isDumpRouteCoverage メソッドの上書き
2.12.3.2. JVM システムプロパティーを使用した route-coverage の有効化

JVM システムプロパティー CamelTestRouteCoverage をオンにして、すべてのテストケースの route-coverage を有効にできます。これは、maven-surefire-plugin 設定のいずれかで実行できます。

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <systemPropertyVariables>
      <CamelTestRouteCoverage>true</CamelTestRouteCoverage>
    </systemPropertyVariables>
  </configuration>
</plugin>

テストの実行中にコマンドラインから設定する場合は次のとおりです。

mvn clean test -DCamelTestRouteCoverage=true
2.12.3.3. @EnableRouteCoverage アノテーションでの route-coverage の有効化

camel-test-spring を使用してテストする場合は、@EnableRouteCoverage アノテーションをテストクラスに追加することで、ユニットテストクラスで route-coverage を有効にすることができます。

@RunWith(CamelSpringBootRunner.class)
@SpringBootTest(classes = SampleCamelApplication.class)
@EnableRouteCoverage
public class FooApplicationTest {
2.12.3.4. isDumpRouteCoverage メソッドでの route-coverage の有効化

camel-test を使用していて、ユニットテストが CamelTestSupport を拡張している場合は、以下に示すように route-coverage を有効にすることができます。

@Override
public boolean isDumpRouteCoverage() {
    return true;
}

RouteCoverage メソッドで対象指定できるルートには、固有の ID が割り当てられている必要があります。つまり、匿名ルートは使用できません。Java DSL で routeId を使用して行います。

from("jms:queue:cheese").routeId("cheesy")
  .to("log:foo")
  ...

また、XML DSL で id 属性を介してルート ID を付与します。

<route id="cheesy">
  <from uri="jms:queue:cheese"/>
  <to uri="log:foo"/>
  ...
</route>
2.12.3.5. route-coverage レポートの生成

route-coverage レポートを生成するには、以下のようにユニットテストを実行します。

mvn test

そして、Maven ゴールを実行して、以下のように route-coverage レポートを生成できます。

mvn camel:route-coverage

生成されるレポートでは、ソースコードの行番号でどのルートのルートのカバレッジがないかを確認できます。

[INFO] --- camel-maven-plugin:2.21.0:route-coverage (default-cli) @ camel-example-spring-boot-xml ---
[INFO] Discovered 1 routes
[INFO] Route coverage summary:

File:	src/main/resources/my-camel.xml
RouteId:	hello

  Line #      Count   Route
  ------      -----   -----
      28          1   from
      29          1     transform
      32          1     filter
      34          0       to
      36          1     to

Coverage: 4 out of 5 (80.0%)

この例では、to のある最後から 2 番目の行のカウント列が 0 であるため、カバレッジがないことが分かります。また、これはソースコードファイル (XML ファイル my-camel.xml) の 34 行目であることも分かります。

2.12.3.6. オプション

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

パラメーター

デフォルト値

説明

failOnError

false

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

includeTest

false

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

includes

 

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

excludes

 

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

anonymousRoutes

false

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

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

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

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

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

以下の例は、Camel から Main クラスを作成して使用する方法を示しています。

public class MainExample {

    private Main main;

    public static void main(String[] args) throws Exception {
        MainExample example = new MainExample();
        example.boot();
    }

    public void boot() throws Exception {
        // create a Main instance
        main = new Main();
        // bind MyBean into the registry
        main.bind("foo", new MyBean());
        // add routes
        main.addRouteBuilder(new MyRouteBuilder());
        // add event listener
        main.addMainListener(new Events());
        // set the properties from a file
        main.setPropertyPlaceholderLocations("example.properties");
        // run until you terminate the JVM
        System.out.println("Starting Camel. Use ctrl + c to terminate the JVM.\n");
        main.run();
    }

    private static class MyRouteBuilder extends RouteBuilder {
        @Override
        public void configure() throws Exception {
            from("timer:foo?delay={{millisecs}}")
                .process(new Processor() {
                    public void process(Exchange exchange) throws Exception {
                        System.out.println("Invoked timer at " + new Date());
                    }
                })
                .bean("foo");
        }
    }

    public static class MyBean {
        public void callMe() {
            System.out.println("MyBean.callMe method has been called");
        }
    }

    public static class Events extends MainListenerSupport {

        @Override
        public void afterStart(MainSupport main) {
            System.out.println("MainExample with Camel is now started!");
        }

        @Override
        public void beforeStop(MainSupport main) {
            System.out.println("MainExample with Camel is now being stopped!");
        }
    }
}

2.14. OnCompletion

概要

OnCompletion DSL 名は、Unit of Work の完了時に行うアクションを定義するために使用できます。Unit of Work は、エクスチェンジ全体に対応する Camel の概念です。「エクスチェンジ」 を参照してください。onCompletion コマンドの機能は以下のとおりです。

  • OnCompletion コマンドのスコープは、グローバルまたはルートごとに指定できます。ルートスコープはグローバルスコープよりも優先されます。
  • OnCompletion は、失敗時または成功時にトリガーされるように設定することができます。
  • onWhen 述語は、特定の状況で onCompletion のみをトリガーするために使用できます。
  • スレッドプールを使用するかどうかを設定できますが、デフォルトではスレッドプールは使用しません。

onCompletion のルート専用スコープ

エクスチェンジにて onCompletion DSL が指定されると、Camel は新しいスレッドを生成します。これにより、onCompletion タスクに干渉されることなく、元のスレッドを継続することができます。1 つのルートは 1 つの onCompletion のみをサポートします。以下の例では、エクスチェンジが成功または失敗で完了したかに関係なく、onCompletion がトリガーされます。これがデフォルト動作です。

from("direct:start")
     .onCompletion()
         // This route is invoked when the original route is complete.
         // This is similar to a completion callback.
         .to("log:sync")
         .to("mock:sync")
     // Must use end to denote the end of the onCompletion route.
     .end()
     // here the original route contiues
     .process(new MyProcessor())
     .to("mock:result");

XML の場合、以下のようになります。

<route>
    <from uri="direct:start"/>
    <!-- This onCompletion block is executed when the exchange is done being routed. -->
    <!-- This callback is always triggered even if the exchange fails. -->
    <onCompletion>
        <!-- This is similar to an after completion callback. -->
        <to uri="log:sync"/>
        <to uri="mock:sync"/>
    </onCompletion>
    <process ref="myProcessor"/>
    <to uri="mock:result"/>
</route>

失敗時に onCompletion をトリガーするには、onFailureOnly パラメーターを使用することができます。同様に、成功時に onCompletion をトリガーするには、onCompleteOnly パラメーターを使用します。

from("direct:start")
     // Here onCompletion is qualified to invoke only when the exchange fails (exception or FAULT body).
     .onCompletion().onFailureOnly()
         .to("log:sync")
         .to("mock:sync")
     // Must use end to denote the end of the onCompletion route.
     .end()
     // here the original route continues
     .process(new MyProcessor())
     .to("mock:result");

XML の場合、onFailureOnly および onCompleteOnly は、onCompletion タグのブール値として表現されます。

<route>
    <from uri="direct:start"/>
    <!-- this onCompletion block will only be executed when the exchange is done being routed -->
    <!-- this callback is only triggered when the exchange failed, as we have onFailure=true -->
    <onCompletion onFailureOnly="true">
        <to uri="log:sync"/>
        <to uri="mock:sync"/>
    </onCompletion>
    <process ref="myProcessor"/>
    <to uri="mock:result"/>
</route>

グローバルスコープの onCompletion

onCompletion を複数のルートに対して定義するには、以下を実行します。

// define a global on completion that is invoked when the exchange is complete
 onCompletion().to("log:global").to("mock:sync");

 from("direct:start")
     .process(new MyProcessor())
     .to("mock:result");

onWhen の使用

特定条件で onCompletion を起動するには、onWhen 述語を使用します。次の例では、メッセージのボディーに Hello という単語が含まれている場合に、onCompletion がトリガーされます。

/from("direct:start")
     .onCompletion().onWhen(body().contains("Hello"))
         // this route is only invoked when the original route is complete as a kind
         // of completion callback. And also only if the onWhen predicate is true
         .to("log:sync")
         .to("mock:sync")
     // must use end to denote the end of the onCompletion route
     .end()
     // here the original route contiues
     .to("log:original")
     .to("mock:result");

onCompletion でのスレッドプール

Camel 2.14 以降、onCompletion はデフォルトでスレッドプールを使用しません。スレッドプールを強制的に使用するには、executorService を設定するか、parallelProcessing を true に設定します。たとえば、Java DSL では以下の形式を使用します。

onCompletion().parallelProcessing()
     .to("mock:before")
     .delay(1000)
     .setBody(simple("OnComplete:${body}"));

XML の場合は以下のようになります。

<onCompletion parallelProcessing="true">
   <to uri="before"/>
   <delay><constant>1000</constant></delay>
   <setBody><simple>OnComplete:${body}<simple></setBody>
 </onCompletion>

特定のスレッドプールを参照するには executorServiceRef オプションを使用します。

<onCompletion executorServiceRef="myThreadPool"
   <to uri="before"/>
   <delay><constant>1000</constant></delay>
   <setBody><simple>OnComplete:${body}</simple></setBody>
 </onCompletion>>

コンシューマーの応答送信前に onCompletion を実行

onCompletion は 2 つのモードで実行できます。

  • AfterConsumer: コンシューマーが終了した後に実行されるデフォルトのモードです。
  • BeforeConsumer: コンシューマーが呼び出し元に応答を返信する前に実行されます。これにより、onCompletion は、特別なヘッダーの追加などエクスチェンジを変更したり、応答ロガーとしてエクスチェンジをログに記録したりすることができます。

たとえば、created by ヘッダーを応答に追加するには、次に示すように modeBeforeConsumer() を使用します。

.onCompletion().modeBeforeConsumer()
     .setHeader("createdBy", constant("Someone"))
 .end()

XML の場合、mode 属性を BeforeConsumer に設定します。

<onCompletion mode="BeforeConsumer">
   <setHeader headerName="createdBy">
     <constant>Someone</constant>
   </setHeader>
 </onCompletion>

2.15. メトリクス

概要

Camel 2.14 から利用可能

Camel は既に多くのメトリクスを提供し、Codahale メトリクスとの統合が Camel ルートに追加されています。これにより、エンドユーザーは、Codahale メトリクスを使用して収集されたメトリクスデータに、Camel のルーティング情報を追加できます。

Codahale メトリクスを使用するには、以下が必要です。

  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();
  ...
}

オプション

MetricsRoutePolicyFactory および MetricsRoutePolicy は、以下のオプションをサポートします。

名前

デフォルト

説明

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 pattern を定義することで、JMX で表示される CamelContext Bean の名前をカスタマイズすることができます。たとえば、以下のように、XML CamelContext インスタンスの名前パターンをカスタマイズすることができます。

<camelContext id="myCamel" managementNamePattern="#name#">
    ...
</camelContext>

CamelContext Bean の名前パターンを明示的に設定しないと、Apache Camel はデフォルトの命名ストラテジーに戻ります。

デフォルトの命名ストラテジー

デフォルトでは、OSGi バンドルにデプロイされた CamelContext Bean の JMX 名は、バンドルの OSGi シンボリック名 と同じです。たとえば、OSGi のシンボリック名が MyCamelBundle の場合、JMX の名前は MyCamelBundle となります。バンドル内に複数の CamelContext が存在する場合、接尾辞としてカウンター値を追加することにより、JMX 名が明確になります。たとえば、MyCamelBundle バンドルに複数の Camel コンテキストがある場合、対応する JMX MBeans の名前は以下のようになります。

MyCamelBundle-1
MyCamelBundle-2
MyCamelBundle-3
...

JMX 命名ストラテジーのカスタマイズ

デフォルトの命名ストラテジーの欠点の 1 つは、特定の CamelContext Bean が実行間で同じ JMX 名を持つことを保証できないことです。実行間の一貫性を高める場合は、CamelContext インスタンスの JMX 名パターン を定義することで、JMX 名をより正確に制御することができます。

Java DSL での名前パターンの指定

Java の CamelContext で名前パターンを指定するには、以下のように setNamePattern メソッドを呼び出します。

// Java
context.getManagementNameStrategy().setNamePattern("#name#");

XML での名前パターンの指定

XML の CamelContext で名前パターンを指定するには、以下のように camelContext 要素の managementNamePattern 属性を設定します。

<camelContext id="myCamel" managementNamePattern="#name#">

名前パターントークン

以下のいずれかのトークンにリテラルテキストを追加することで、JMX 名前パターンを設定することができます。

表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

「受信者リスト」

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

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「Message Transformation」 のメッセージ変換パターンは、さまざまな目的のためにメッセージの内容を変更する方法を表しています。

表3.5 Message Transformation
アイコン名前ユースケース

Content enricher icon

「Content Enricher」

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

Content filter icon

「Content Filter」

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

store in library icon

「Claim Check EIP」

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

Normalizer icon

「ノーマライザー」

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

 

「並び替え」

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

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

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

表3.6 Messaging Endpoint
アイコン名前ユースケース
 

「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 コンポーネントを抽象化でき、OpenAPI とも統合できるシンプルながらも強力な 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 GuideRestlet を参照してください。

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 GuideServlet を参照してください。

JAX-RS REST 実装

JAX-RS (Java API for RESTful Web Services) は、REST リクエストを Java オブジェクトにバインドするためのフレームワークです。バインドを定義するには、この Java クラスに JAX-RS のアノテーションを記述する必要があります。JAX-RS フレームワークは比較的成熟しており、REST サービスを開発するための洗練されたフレームワークも提供します。しかし、プログラムするのもやや複雑です。

Apache Camel と JAX-RS の統合は、Apache CXF の上に重ねた CXFRS コンポーネントによって実装されます。簡単にいうと、JAX-RS は以下のアノテーションを使用して REST リクエストを Java クラスにバインドします (以下は多くのアノテーションの一部のみとなります)。

@Path
コンテキストパスを Java クラスにマッピングしたり、サブパスを特定の Java メソッドにマッピングしたりできるアノテーション。
@GET、@POST、@PUT、@DELETE
HTTP メソッドを Java メソッドにマッピングするアノテーション。
@PathParam
URI パラメーターを Java メソッド引数にマッピングするか、URI パラメーターをフィールドに注入するアノテーション。
@QueryParam
クエリーパラメーターを Java メソッド引数にマッピングするか、クエリーパラメーターをフィールドに注入するアノテーション。

REST リクエストまたは REST レスポンスのボディーは、通常、JAXB (XML) データフォーマットであることが期待されます。しかし、Apache CXF は JSON 形式から JAXB 形式への変換もサポートしているため、JSON メッセージも解析することができます。

詳細は、Apache Camel Component Reference Guide および Apache CXF Development GuideCXFRS を参照してください。

注記

CXFRS コンポーネントは REST DSL と統合されていません

4.2. REST DSL を使用した REST サービスの定義

REST DSL はファサード

REST DSL は、Java DSL または XML DSL (Domain Specific Language) で REST サービスを定義するための簡略化された構文を提供するファサードです。REST DSL は実際の REST 実装を提供しているわけではなく、既存 の REST 実装 (Apache Camel に複数ある) のラッパーにすぎません。

REST DSL の利点

REST DSL ラッパーレイヤーには、以下の利点があります。

  • REST サービスを定義するためのモダンで使いやすい構文である。
  • 複数の Apache Camel コンポーネントと互換性がある。
  • OpenAPI インテグレーション (camel-openapi-java コンポーネント経由)。

REST DSL と統合可能なコンポーネント

REST DSL は実際の REST 実装ではないため、最初に、ベースとなる実装を提供する Camel コンポーネントを選択する必要があります。現在、以下の Camel コンポーネントが REST DSL に統合されています。

  • Servlet コンポーネント (camel-servlet)
  • Spark REST コンポーネント (camel-spark-rest)
  • Netty4 HTTP コンポーネント (camel-netty4-http)
  • Jetty コンポーネント (camel-jetty)
  • Restlet コンポーネント (camel-restlet)
  • Undertow コンポーネント (camel-undertow)
注記

REST コンポーネント (camel-core の一部) は REST 実装ではありません。REST DSL と同様に、REST コンポーネントはファサードであり、URI 構文を使用して REST サービスを定義するための簡潔な構文を提供します。REST コンポーネントには、ベースとなる REST 実装も必要です。

REST 実装を使用するように REST DSL を設定

REST 実装を指定するには、restConfiguration() ビルダー (Java DSL の場合) または restConfiguration 要素 (XML DSL の場合) を使用します。たとえば、Spark-Rest コンポーネントを使用するように REST DSL を設定するには、Java DSL で以下のようなビルダー式を使用します。

restConfiguration().component("spark-rest").port(9091);

そして、XML DSL では、以下のような要素 (camelContext の子として) を使用します。

<restConfiguration component="spark-rest" port="9091"/>

構文

REST サービスを定義するための Java DS L 構文は以下のとおりです。

rest("BasePath").Option().
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    ...
    .Verb("Path").Option().[to() | route().CamelRoute];

ここの CamelRoute は、オプションの組み込み Camel ルートです (標準の Java DSL 構文を使用して定義されています)。

REST サービスの定義は rest() キーワードで始まり、その後に特定の URL パスセグメントを処理する 1 つ以上の Verb 句が続きます。HTTP 動詞は、get()head()put()post()delete()patch() または verb() のいずれかになります。Verb 句は以下の構文のいずれかを使用できます。

  • to() キーワードで終わる Verb 句。以下に例を示します。

    get("...").Option()+.to("...")
  • キーワード route() で終わる Verb 句 (Camel ルートの埋め込みの場合)。以下に例を示します。

    get("...").Option()+.route("...").CamelRoute.endRest()

Java による REST DSL

Java で REST DSL でサービスを定義するには、通常の Apache Camel ルート定義と同様に、REST 定義を RouteBuilder.configure() メソッドのボディーに配置します。たとえば、REST DSL と Spark-Rest コンポーネントの組み合わせを使用して Hello World のサービスを定義するには、以下の Java コードを定義します。

restConfiguration().component("spark-rest").port(9091);

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

from("direct:hello")
    .transform().constant("Hello World");
from("direct:bye")
    .transform().constant("Bye World");

前述の例では、3 種類のビルダーがあります。

restConfiguration()
特定の REST 実装 (Spark-Rest) を使用するように REST DSL を設定します。
rest()
REST DSL を使用してサービスを定義します。各 Verb 句はキーワード to() で終了となります。このキーワードは、受信メッセージをエンドポイント direct に転送します (direct コンポーネントは、同じアプリケーション内でルートをつなぎ合わせます)。
from()
通常の Camel ルートを定義します。

XML を使用した REST DSL

XML で XML DSL でサービスを定義するには、rest 要素を camelContext の子要素として定義します。たとえば、Spark-Rest コンポーネントで REST DSL を使用して単純な Hello World サービスを定義するには、以下の XML コード (Blueprint) を定義します。

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  <restConfiguration component="spark-rest" port="9091"/>

  <rest path="/say">
    <get uri="/hello">
      <to uri="direct:hello"/>
    </get>
    <get uri="/bye">
      <to uri="direct:bye"/>
    </get>
  </rest>

  <route>
    <from uri="direct:hello"/>
    <transform>
      <constant>Hello World</constant>
    </transform>
  </route>
  <route>
    <from uri="direct:bye"/>
    <transform>
      <constant>Bye World</constant>
    </transform>
  </route>
</camelContext>

ベースパスの指定

rest() キーワード (Java DSL) または rest 要素の path 属性 (XML DSL) を使用してベースパスを定義できます。このパスはすべての Verb 句のパスに接頭辞として付けられます。たとえば、以下は Java DSL のスニペットを示します。

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

または、以下は XML DSL のスニペットを示します。

<rest path="/say">
  <get uri="/hello">
    <to uri="direct:hello"/>
  </get>
  <get uri="/bye" consumes="application/json">
    <to uri="direct:bye"/>
  </get>
</rest>

REST DSL ビルダーは、以下の URL マッピングで公開します。

/say/hello
/say/bye

ベースパスはオプションです。必要であれば、各 Verb 句にフルパスを指定することもできます。

rest()
    .get("/say/hello").to("direct:hello")
    .get("/say/bye").to("direct:bye");

Dynamic To の使用

REST DSL では、toD 動的な to パラメーターをサポートしています。動的な to パラメーターを使用して動的な転送先 URI を指定できます。

たとえば、動的エンドポイント URI を使用して動的な JMS キューへ送信するには以下のように定義できます。

public void configure() throws Exception {
   rest("/say")
     .get("/hello/{language}").toD("jms:queue:hello-${header.language}");
}

XML DSL では、以下のようになります。

<rest uri="/say">
  <get uri="/hello//{language}">
    <toD uri="jms:queue:hello-${header.language}"/>
  </get>
<rest>

toD パラメーターの詳細は、「Dynamic To」 を参照してください。

URI テンプレート

Verb 句で利用する引数では、URI テンプレートで指定できます。これにより、特定のパスセグメントを名前付きプロパティーとして取り込むことができます (これらは Camel メッセージヘッダーにマッピングされます)。たとえば、Hello World アプリケーションをパーソナライズして、発信者の名前で挨拶するようにする場合は、以下のような REST サービスを定義することができます。

rest("/say")
    .get("/hello/{name}").to("direct:hello")
    .get("/bye/{name}").to("direct:bye");

from("direct:hello")
    .transform().simple("Hello ${header.name}");
from("direct:bye")
    .transform().simple("Bye ${header.name}");

URI テンプレートは {name} パスセグメントのテキストを取得し、このキャプチャーされたテキストを name メッセージヘッダーにコピーします。URL が /say/hello/Joe で終わる GET HTTP リクエストを送信してサービスを呼び出す場合、HTTP レスポンスは Hello Joe になります。

組み込みルートの構文

to() キーワード (Java DSL) または to 要素 (XML DSL) で Verb 句を終わらせる代わりに、route() キーワード (Java DSL) または route 要素 (XML DSL) を使用して Apache Camel ルートを直接 REST DSL に埋め込むことも可能です。route() キーワードを使用すると、以下の構文でルートを Verb 句に埋め込みできます。

RESTVerbClause.route("...").CamelRoute.endRest()

endRest() キーワード (Java DSL のみ) は、(rest() ビルダーに複数の Verb 句がある場合に) Verb 句を区切ることができる必須の句読点です。

たとえば、Hello World の例を Java DSL のように組み込み Camel ルートを使用するようにリファクタリングできます。

rest("/say")
    .get("/hello").route().transform().constant("Hello World").endRest()
    .get("/bye").route().transform().constant("Bye World");

XML DSL では以下のようになります。

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/say">
    <get uri="/hello">
      <route>
        <transform>
          <constant>Hello World</constant>
        </transform>
      </route>
    </get>
    <get uri="/bye">
      <route>
        <transform>
          <constant>Bye World</constant>
        </transform>
      </route>
    </get>
  </rest>
</camelContext>
注記

現在の CamelContext 内で、例外句 (onException()) やインターセプター (intercept()) を定義した場合、これらの例外句とインターセプターは組み込みルートでもアクティブになります。

REST DSL と HTTP トランスポートコンポーネント

HTTP コンポーネントを明示的に指定しない場合、REST DSL はクラスパス上の利用可能なコンポーネントをチェックすることで、どの HTTP コンポーネントを使用するかを自動検出します。REST DSL は、HTTP コンポーネントのデフォルト名を探し、最初に見つかったものを使用します。クラスパス上に HTTP コンポーネントがなく、かつ HTTP トランスポートが明示的に設定されていない場合は、デフォルトの HTTP コンポーネントは camel-http になります。

リクエストとレスポンスのコンテンツタイプの指定

Java の consumes()produces() オプション、または XML の 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 クラスにアノテーションを付ける必要があります。

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

REST DSL による JSON と JAXB の統合

メッセージボディーを Java オブジェクトへ変換するために必要なコードを自分で書くこともできます。しかし、REST DSL は、この変換を自動的に実行する利便性を提供します。特に、JSON と JAXB を REST DSL と統合すると、以下のようなメリットがあります。

  • Java オブジェクトとの間のマーシャリングは自動的に実行されます (適切な設定がある場合)。
  • REST DSL は、データフォーマット (JSON または JAXB のいずれか) を自動的に検出し、適切な変換を行うことができます。
  • REST DSL は 抽象化レイヤー を提供するので、開発するコードは JSON または JAXB 実装に固有のものではありません。そのため、アプリケーションコードへの影響を最小限に抑えながら、後から実装を切り替えることができます。

サポートされるデータフォーマットコンポーネント

Apache Camel は JSON と JAXB データフォーマットの多くの実装を提供しています。現在、REST DSL では以下のデータフォーマットがサポートされています。

  • JSON

    • Jackson データフォーマット (camel-jackson) (デフォルト)
    • gson データフォーマット (camel-gson)
    • XStream データフォーマット (camel-xstream)
  • 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-7_10_0-00018-redhat-00001.zip をインストールして見ることができます。これは、Fuse インストールの extras/ サブディレクトリーにあります。

スタンドアロン Apache Camel ディストリビューションのインストール後に、以下のディレクトリーでサンプルコードを確認できます。

ApacheCamelInstallDir/examples/camel-example-servlet-rest-blueprint

REST 実装として Servlet コンポーネントを設定

camel-example-servlet-rest-blueprint の例では、REST DSL のベースとなる実装は Servlet コンポーネントによって提供されます。Servlet コンポーネントは、例4.1「REST DSL の Servlet コンポーネントの設定」 に示されているように、Blueprint XML ファイルで設定されます。

例4.1 REST DSL の Servlet コンポーネントの設定

<?xml version="1.0" encoding="UTF-8"?>
<blueprint ...>

  <!-- to setup camel servlet with OSGi HttpService -->
  <reference id="httpService" interface="org.osgi.service.http.HttpService"/>

  <bean class="org.apache.camel.component.servlet.osgi.OsgiServletRegisterer"
        init-method="register"
        destroy-method="unregister">
    <property name="alias" value="/camel-example-servlet-rest-blueprint/rest"/>
    <property name="httpService" ref="httpService"/>
    <property name="servlet" ref="camelServlet"/>
  </bean>

  <bean id="camelServlet" class="org.apache.camel.component.servlet.CamelHttpTransportServlet"/>
  ...
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>
    ...
  </camelContext>

</blueprint>

Servlet コンポーネントを REST DSL で設定するには、以下の 3 つのレイヤーを設定する必要があります。

REST DSL レイヤー
REST DSL レイヤーは restConfiguration 要素によって設定され、component 属性を servlet という値に設定することで Servlet コンポーネントと統合されます。
Servlet コンポーネントレイヤー
Servlet コンポーネントレイヤーは、クラス CamelHttpTransportServlet のインスタンスとして実装され、このサンプルインスタンスには Bean ID camelServlet があります。
HTTP コンテナーレイヤー

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

  1. OSGi サービス (org.osgi.service.http.HttpService) への OSGi 参照を取得します。このサービスは、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 では、restConfiguration 要素を使用して REST DSL を設定できます。たとえば、以下は Servlet コンポーネントをベースの実装として使用するように REST DSL を設定する場合になります。

<?xml version="1.0" encoding="UTF-8"?>
<blueprint ...>
  ...
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
    ...
    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>
    ...
  </camelContext>

</blueprint>

設定オプション

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

表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 などのコンポーネントで使用することができます。これらのコンポーネントは、curl の設定を使用してアプリケーションをデプロイします。

hostNameResolver()

@hostNameResolver

ホスト名が明示的に設定されていない場合、このリゾルバーによって REST サービスのホストが決定されます。使用できる値は、ホスト名の形式に解決される RestHostNameResolver.localHostName (Java DSL) または localHostName (XML DSL)、およびドットの 10 進数の IP アドレス形式に解決される RestHostNameResolver.localIp (Java DSL) または localIp (XML DSL) になります。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-gsonjson-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 (オリジン間リソース共有) を有効にすると、デフォルトで以下のヘッダーが設定されます。corsHeaderProperty DSL コマンド を呼び出すことで、デフォルト設定を任意で上書きできます。

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

Access-Control-Allow-Origin

\*

Access-Control-Allow-Methods

GETHEADPOSTPUTDELETETRACEOPTIONSCONNECTPATCH

Access-Control-Allow-Headers

OriginAcceptX-Requested-WithContent-TypeAccess-Control-Request-MethodAccess-Control-Request-Headers

Access-Control-Max-Age

3600

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

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

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

たとえば、Jackson の FAIL_ON_UNKNOWN_PROPERTIES 機能を無効にする場合は、以下のとおりです (JSON 入力に Java オブジェクトにマップできないプロパティーがある場合に Jackson は失敗します)。

restConfiguration().component("jetty")
    .host("localhost").port(getPort())
    .bindingMode(RestBindingMode.json)
    .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES");

コンマ区切りにすると、複数 の機能を無効にできます。以下に例を示します。

.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE");

以下の例は、Java DSL で Jackson JSON 機能を有効および無効する方法を示しています。

restConfiguration().component("jetty")
    .host("localhost").port(getPort())
    .bindingMode(RestBindingMode.json)
    .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE")
    .dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS");

以下の例は、XML DSL で Jackson JSON 機能を有効および無効する方法を示しています。

<restConfiguration component="jetty" host="localhost" port="9090" bindingMode="json">
  <dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE"/>
  <dataFormatProperty key="json.in.enableFeatures" value="FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS"/>
</restConfiguration>

有効化または無効化できる Jackson 機能は、以下の Jackson クラスの enum ID に対応しています。

4.5. OpenAPI インテグレーション

概要

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

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

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

CamelContext での OpenAPI の有効化

Camel REST DSL で OpenAPI を使用できるようにするには、apiContextPath() を呼び出して、OpenAPI で生成された API ドキュメントのコンテキストパスを設定します。以下に例を示します。

public class UserRouteBuilder extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        // Configure the Camel REST DSL to use the netty4-http component:
        restConfiguration().component("netty4-http").bindingMode(RestBindingMode.json)
            // Generate pretty print output:
            .dataFormatProperty("prettyPrint", "true")
            // Set the context path and port number that netty will use:
            .contextPath("/").port(8080)
            // Add the context path for the OpenAPI-generated API documentation:
            .apiContextPath("/api-doc")
                .apiProperty("api.title", "User API").apiProperty("api.version", "1.2.3")
                // Enable CORS:
                .apiProperty("cors", "true");

        // This user REST service handles only JSON files:
        rest("/user").description("User rest service")
            .consumes("application/json").produces("application/json")
            .get("/{id}").description("Find user by id").outType(User.class)
                .param().name("id").type(path).description("The id of the user to get").dataType("int").endParam()
                .to("bean:userService?method=getUser(${header.id})")
            .put().description("Updates or create a user").type(User.class)
                .param().name("body").type(body).description("The user to update or create").endParam()
                .to("bean:userService?method=updateUser")
            .get("/findAll").description("Find all users").outTypeList(User.class)
                .to("bean:userService?method=listUsers");
    }
}

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

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

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

api.contact.email

String

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

api.contact.name

String

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

api.contact.url

String

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

apiContextIdListing

ブール値

アプリケーションに複数の CamelContext オブジェクトを使用している場合、デフォルトの動作では、現在の CamelContext のみに REST エンドポイントが一覧表示されます。REST サービスを実行している JVM で、実行されている個々の CamelContext の REST エンドポイントのリストが必要な場合は、このオプションを true に設定します。apiContextIdListing が true の場合、OpenAPI はルートパスの CamelContext ID (たとえば /api-docs) を JSON 形式の名前のリストとして公開します。OpenAPI で生成されたドキュメントにアクセスするには、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

ドキュメントを生成する REST API が使用可能なパスを設定します (例: /api-docs)。相対パスで指定します。たとえば、http または https は指定しないでください。camel-openapi-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-openapi-java モジュールは、ランタイム時に protocol://host:port/context-path/base.path の形式で絶対パスを計算します。

cors

ブール値

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

host

String

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

schemes

String

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

opeapi.version

String

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

JSON または YAML 形式の出力

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

Apache Camel 3.x ディストリビューションで、camel-example-openapi-cdi および camel-example-openapi-javacamel-openapi-java モジュールの使用方法を実証します。

Apache Camel 2.x ディストリビューションで、camel-example-swagger-cdi および camel-example-swagger-javacamel-swagger-java モジュールの使用方法を実証します。

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

Camel 3.1 以降では、名前、説明、データ型、パラメーター型などのパラメーター詳細を定義することで、OpenAPI で生成されたドキュメントを充実することができます。XML DSL を使用している場合は、param 要素を指定してこれらの情報を追加します。以下の XML DSL の例は、ID パスパラメーターに関する情報を補足する方法を示しています。

<!-- This is a REST GET request to view information for the user with the given ID: -->
<get uri="/{id}" outType="org.apache.camel.example.rest.User">
     <description>Find user by ID.</description>
     <param name="id" type="path" description="The ID of the user to get information about." dataType="int"/>
     <to uri="bean:userService?method=getUser(${header.id})"/>
</get>

以下は Java DSL の例です。

.get("/{id}").description("Find user by ID.").outType(User.class)
   .param().name("id").type(path).description("The ID of the user to get information about.").dataType("int").endParam()
   .to("bean:userService?method=getUser(${header.id})")

名前が body のパラメーターを定義する場合は、そのパラメーターの型として body も指定する必要があります。以下に例を示します。

<!-- This is a REST PUT request to create/update information about a user. -->
<put type="org.apache.camel.example.rest.User">
     <description>Updates or creates a user.</description>
     <param name="body" type="body" description="The user to update or create."/>
     <to uri="bean:userService?method=updateUser"/>
</put>

以下は Java DSL の例です。

.put().description("Updates or create a user").type(User.class)
     .param().name("body").type(body).description("The user to update or create.").endParam()
     .to("bean:userService?method=updateUser")

Apache Camel ディストリビューションの examples/camel-example-servlet-rest-tomcat も参照してください。

第5章 メッセージングシステム

概要

本章では、エンドポイント、メッセージングチャネル、メッセージルーターなどの、メッセージングシステムの基本的な設定要素について紹介します。

5.1. メッセージ

概要

メッセージ とは、メッセージングシステムでデータを送信するための最小単位です (以下の図では灰色の丸で表されています)。複数のパーツが含まれるメッセージなどの場合、メッセージ自体に内部構造がある場合があります。これは、図5.1「Message パターン」 では灰色の丸とつながる図形で表されています。

図5.1 Message パターン

Message パターン

メッセージのタイプ

Apache Camel は以下のメッセージタイプを定義します。

  • In メッセージ: コンシューマーエンドポイントからプロデューサーエンドポイントへのルートを通過するメッセージ (通常はメッセージのエクスチェンジを開始します)。
  • out メッセージ: プロデューサーエンドポイントからコンシューマーエンドポイントに戻るルートを通過するメッセージ (通常は In メッセージの応答になります)。

これらのメッセージタイプはすべて、org.apache.camel.Message インターフェイスによって内部的に表されます。

メッセージの構造

デフォルトでは、Apache Camel は以下の構造をすべてのメッセージタイプに適用します。

  • ヘッダー: メッセージから抽出されたメタデータまたはヘッダーデータが含まれます。
  • ボディー: 通常、メッセージ全体が元の形式で含まれます。
  • アタッチメント: メッセージの添付 (JBI などの特定のメッセージングシステムとの統合に必要です)。

ヘッダー、ボディー、およびアタッチメントに分割することはメッセージの抽象モデルであることを覚えておくことが重要です。Apache Camel は、さまざまなメッセージ形式を生成するさまざまなコンポーネントをサポートします。最終的には、メッセージのヘッダーおよびボディーに何を配置するかを決定する基盤のコンポーネント実装です。

メッセージの関連付け

Apache Camel は内部的に、個別のメッセージの関連付けに使用されるメッセージ ID を記憶します。ただし、Apache Camel がメッセージを照合する最も重要な方法は エクスチェンジ オブジェクトを介して行われます。

エクスチェンジオブジェクト

エクスチェンジオブジェクトは、関連するメッセージのコレクションをカプセル化するエンティティーで、関連するメッセージのコレクションは メッセージエクスチェンジ と呼ばれ、メッセージのシーケンスを制御するルールは 交換パターン と呼ばれます。たとえば、一般的な交換パターンには、一方向イベントメッセージ (In メッセージ 1 つで設定) と、リクエスト-リプライエクスチェンジ (In メッセージ 1 つと、それに続く Out メッセージで設定) の 2 つがあります。

メッセージへのアクセス

Java DSL でルーティングルールを定義する場合、以下の DSL ビルダーメソッドを使用してメッセージのヘッダーとボディーにアクセスできます。

  • header(String name)body() - 現行の In メッセージの名前付きヘッダーとボディーを返します。
  • outBody(): 現在の Out メッセージのボディーを返します。

たとえば、In メッセージの username ヘッダーを設定するには、以下の Java DSL ルートを使用できます。

from(SourceURL).setHeader("username", "John.Doe").to(TargetURL);

5.2. メッセージチャネル

概要

メッセージチャネル は、メッセージングシステムの論理チャネルです。つまり、異なるメッセージチャネルにメッセージを送信することで、メッセージを異なるメッセージタイプに分類する初歩的な方法を提供します。メッセージチャネルの例として、メッセージキューとメッセージトピックが挙げられます。論理チャネルは物理チャネルと同じでは ない ことに注意してください。論理チャネルを物理的に認識する方法はいくつかあります。

Apache Camel では、メッセージチャネルは 図5.2「Message Channel パターン」 のとおり、メッセージ指向コンポーネントのエンドポイント URI によって表されます。

図5.2 Message Channel パターン

Message Channel パターン

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

Apache Camel の以下のメッセージ指向コンポーネントによって、メッセージチャネルの概念がサポートされます。

ActiveMQ

ActiveMQ では、メッセージチャネルは キュー または トピック によって表されます。特定のキューのエンドポイント URI である QueueName の形式は次のとおりです。

activemq:QueueName

特定のトピックのエンドポイント URI である TopicName の形式は次のとおりです。

activemq:topic:TopicName

たとえば、Foo.Bar キューにメッセージを送信するには、以下のエンドポイント URI を使用します。

activemq:Foo.Bar

ActiveMQ コンポーネントの設定に関する詳細や手順については、Apache Camel Component Reference GuideActiveMQ を参照してください。

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 GuideJms を参照してください。

AMQP

AMQP では、メッセージチャネルはキューまたはトピックで表されます。特定のキューのエンドポイント URI である QueueName の形式は次のとおりです。

amqp:QueueName

特定のトピックのエンドポイント URI である TopicName の形式は次のとおりです。

amqp:topic:TopicName

AMQP コンポーネントの設定に関する詳細や手順は、Apache Camel Component Reference GuideAmqp を参照してください。

5.3. メッセージエンドポイント

概要

メッセージエンドポイント は、アプリケーションとメッセージングシステム間のインターフェイスです。図5.3「Message Endpoint パターン」 のように、送信者のエンドポイントがあります。これは、プロキシーまたはサービスコンシューマーとも呼ばれ、In メッセージの送信を担当します。また、受信者のエンドポイントもあります。これはエンドポイントまたはサービスとも呼ばれ、In メッセージの受信を担当します。

図5.3 Message Endpoint パターン

Message Endpoint パターン

エンドポイントのタイプ

Apache Camel は、2 つの基本タイプのエンドポイントを定義します。

  • コンシューマーエンドポイント: Apache Camel ルートの最初にあり、受信チャネルから In messages を読み取ります (受信者エンドポイントと同等です)。
  • プロデューサーエンドポイント: Apache Camel ルートの最後にあり、 In メッセージを送信チャネルに書き込みます (送信者 エンドポイントと同等です)。複数のプロデューサーエンドポイントでルートを定義できます。

エンドポイント URI

Apache Camel では、エンドポイントはエンドポイント URI で表され、通常は以下のようなデータをカプセル化します。

  • コンシューマーエンドポイントのエンドポイント URI: 特定の場所をアドバタイズします (たとえば、送信者が接続できるサービスを公開する場合など)。または、URI でメッセージキューなどのメッセージソースを指定できます。エンドポイント URI には、エンドポイントの設定を含めることができます。
  • プロデューサーエンドポイントのエンドポイント URI: メッセージの送信先の詳細と、エンドポイントの設定が含まれます。URI はリモートレシーバーエンドポイントの場所を指定する場合があります。それ以外の場合には、宛先にはキュー名などの抽象的な形式を含むことができます。

Apache Camel のエンドポイント URI の一般的な形式は次のとおりです。

ComponentPrefix:ComponentSpecificURI

ComponentPrefix は、特定の Apache Camel コンポーネントを識別する URI 接頭辞に置き換えます (サポートされるすべてのコンポーネントの詳細は Apache Camel Component Reference を参照してください)。URI である ComponentSpecificURI の残りの部分には特定のコンポーネントによって定義された構文があります。たとえば、JMS キュー Foo.Bar に接続するには、以下のようにエンドポイント URI を定義できます。

jms:Foo.Bar

コンシューマーエンドポイント file://local/router/messages/foo を直接プロデューサーエンドポイント jms:Foo.Bar に接続するルートを定義するには、以下の Java DSL フラグメントを使用できます。

from("file://local/router/messages/foo").to("jms:Foo.Bar");

または、以下のように XML で同じルートを定義することもできます。

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="file://local/router/messages/foo"/>
    <to uri="jms:Foo.Bar"/>
  </route>
</camelContext>

Dynamic To

<toD> パラメーターにより、連結された 1 つ以上の式を使用して、動的に計算されたエンドポイントにメッセージを送信することができます。

デフォルトでは、Simple 言語はエンドポイントの計算に使用されます。以下の例では、ヘッダーによって定義されたエンドポイントにメッセージを送信します。

<route>
  <from uri="direct:start"/>
  <toD uri="${header.foo}"/>
</route>

Java DSL では、同じコマンドの形式は以下のようになります。

from("direct:start")
  .toD("${header.foo}");

以下の例のように、URI の前にリテラルを付けることもできます。

<route>
  <from uri="direct:start"/>
  <toD uri="mock:${header.foo}"/>
</route>

Java DSL では、同じコマンドの形式は以下のようになります。

from("direct:start")
  .toD("mock:${header.foo}");

上記の例では、header.foo の値が orange の場合、URI は mock:orange として解決されます。

Simple 以外の言語を使用するには、language: パラメーターを定義する必要があります。パートII「ルーティング式と述語言語」を参照してください。

異なる言語を使用する場合の形式では、URI で language:languagename: を使用します。たとえば、Xpath を使用する場合は、以下の形式を使用します。

<route>
  <from uri="direct:start"/>
  <toD uri="language:xpath:/order/@uri/">
</route>

Java DSL では同じ例が以下のようになります。

from("direct:start")
  .toD("language:xpath:/order/@uri");

language: を指定しない場合、エンドポイントはコンポーネント名になります。場合によっては、コンポーネントと言語の名前は xquery のように同じになります。

+ 記号を使用して複数の言語を連結できます。以下の例では、URI は Simple 言語と Xpath 言語の組み合わせです。Simple がデフォルトであるため、言語を定義する必要はありません。+ 記号の後は、language:xpath で示される Xpath 命令があります。

<route>
  <from uri="direct:start"/>
  <toD uri="jms:${header.base}+language:xpath:/order/@id"/>
</route>

Java DSL では形式は以下のようになります。

from("direct:start")
  .toD("jms:${header.base}+language:xpath:/order/@id");

多くの言語を一度に連結できます。それぞれの言語を + で区切り、各言語を language:languagename で指定します。

toD で以下のオプションを設定することができます。

名前

デフォルト値

説明

uri

 

必須のオプション: 使用する 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 パターン

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

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

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

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

パイプラインは、各エンドポイントの出力を次のエンドポイントの入力に接続します。最終的なエンドポイントからの Out メッセージは、元の呼び出し元に返されます。以下のように、このパイプラインのルートを定義できます。

from("jms:RawOrders").pipeline("cxf:bean:decrypt", "cxf:bean:authenticate", "cxf:bean:dedup", "jms:CleanOrders");

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

<camelContext id="buildPipeline" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="jms:RawOrders"/>
    <to uri="cxf:bean:decrypt"/>
    <to uri="cxf:bean:authenticate"/>
    <to uri="cxf:bean:dedup"/>
    <to uri="jms:CleanOrders"/>
  </route>
</camelContext>

XML には専用の pipeline 要素がありません。前述の fromto 要素の組み合わせは、意味的にはパイプラインと同等です。「pipeline() および to() DSL コマンドの比較」 を参照してください。

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

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

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

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

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

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

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

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

    from(SourceURI).pipeline(FilterA, FilterB, TargetURI);
  • Using the to() command: 以下のように、to() コマンドを使用してパイプラインルートを構築します。

    from(SourceURI).to(FilterA, FilterB, TargetURI);

    または、同等の構文を使用することもできます。

    from(SourceURI).to(FilterA).to(FilterB).to(TargetURI);

to() コマンド構文を使用する場合は、パイプラインプロセッサーと常に同等では ない ため注意が必要です。Java DSL では、ルートの直前のコマンドで to() の意味を変更できます。たとえば、to() コマンドの前に multicast() コマンドがある場合は、上記のエンドポイントをパイプラインパターンではなく Multicast パターンにバインドします (「Multicast」 を参照)。

5.5. メッセージルーター

概要

図5.7「Message Router パターン」 に示されている メッセージルーター は、単一のコンシューマーエンドポイントからメッセージを消費し、特定の決定基準に基づいて適切なターゲットエンドポイントにリダイレクトするフィルターのタイプです。メッセージルーターはメッセージのリダイレクトのみに関与し、メッセージの内容は変更しません。

しかし、デフォルトでは、Camel がメッセージエクスチェンジを受信者のエンドポイントにルーティングするたびに、元のエクスチェンジオブジェクトのシャローコピーを送信します。シャローコピーでは、メッセージボディー、ヘッダー、アタッチメントなどの元のエクスチェンジの要素は参照のみでコピーされます。リソースを再利用するシャローコピーを送信することで、Camel はパフォーマンスを最適化します。ただし、これらのシャローコピーはすべてリンクされるため、Camel が複数のエンドポイントにメッセージをルーティングする場合は、異なる受信者にルーティングされるコピーにカスタムロジックを適用できないことがトレードオフになります。Camel を有効にして一意なバージョンのメッセージを異なるエンドポイントにルーティングする方法は、送信メッセージへのカスタム処理の適用 を参照してください。

図5.7 Message Router パターン

Message Router パターン

メッセージルーターは 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 パターン

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

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 GuideJms を参照してください。

ActiveMQ

ActiveMQ では、Point to Point Channel はキューで表されます。たとえば、以下のように Foo.Bar という ActiveMQ キューのエンドポイント URI を指定できます。

activemq:queue:Foo.Bar

詳細は、Apache Camel Component Reference GuideActiveMQ を参照してください。

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 GuideJPA を参照してください。

XMPP

XMPP (Jabber) コンポーネントは、通信でパーソンツーパーソン (Person-to-Person) モードが使用される場合に、Point to Point Channel パターンをサポートします。詳細は、Apache Camel Component Reference GuideXMPP を参照してください。

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

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

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

  • JMS
  • ActiveMQ
  • XMPP
  • SEDA (pub-sub で機能する同じ CamelContext で SEDA を使用し、複数のコンシューマーを許可する場合)
  • Apache Camel Component Reference GuideVM を SEDA とし、同じ JVM 内で使用します。

JMS

JMS では、パブリッシュサブスクライブチャネルは トピック で表されます。たとえば、StockQuotes という JMS トピックのエンドポイント URI を指定できます。

jms:topic:StockQuotes

詳細は、Apache Camel Component Reference GuideJms を参照してください。

ActiveMQ

ActiveMQ では、Publish-Subscribe Channel はトピックで表されます。たとえば、以下のように StockQuotes という ActiveMQ トピックのエンドポイント URI を指定できます。

activemq:topic:StockQuotes

詳細は、Apache Camel Component Reference GuideActiveMQ を参照してください。

XMPP

XMPP (Jabber) コンポーネントは、グループ通信モードで使用される場合に Publish Subscribe Channel パターンをサポートします。詳細は、Apache Camel Component Reference GuideXmpp を参照してください。

静的サブスクリプションリスト

必要に応じて、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 パターン

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

ブール値

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 オプションは、再配信回数の特定範囲に遅延を指定するために使用されます。遅延パターンの構文: 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_ENDPOINT は、CamelFailureEndpoint と同等の文字列定数です。

注記

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

        from("activemq:queue:foo")
        .to("http://someserver/somepath")
        .beanRef("foo");

foo Bean で障害が発生したと仮定します。この場合、Exchange.TO_ENDPOINT プロパティーと Exchange.FAILURE_ENDPOINT プロパティーに値が含まれ続けます。

onRedelivery プロセッサー

Dead Letter Channel が再配信を実行する場合、再配信を試みる 直前 に実行される Processor を設定できます。これは、メッセージを再配信する前に変更する必要がある場合に使用できます。

たとえば、以下の Dead Letter Channel は、エクスチェンジの再配信前に MyRedeliverProcessor を呼び出すように設定されます。

// we configure our Dead Letter Channel to invoke
// MyRedeliveryProcessor before a redelivery is
// attempted. This allows us to alter the message before
errorHandler(deadLetterChannel("mock:error").maximumRedeliveries(5)
        .onRedelivery(new MyRedeliverProcessor())
        // setting delay to zero is just to make unit teting faster
        .redeliveryDelay(0L));

ここで MyRedeliveryProcessor プロセスは以下のように実装されます。

// This is our processor that is executed before every redelivery attempt
// here we can do what we want in the java code, such as altering the message
public class MyRedeliverProcessor implements Processor {

    public void process(Exchange exchange) throws Exception {
        // the message is being redelivered so we can alter it

        // we just append the redelivery counter to the body
        // you can of course do all kind of stuff instead
        String body = exchange.getIn().getBody(String.class);
        int count = exchange.getIn().getHeader(Exchange.REDELIVERY_COUNTER, Integer.class);

        exchange.getIn().setBody(body + count);

        // the maximum redelivery was set to 5
        int max = exchange.getIn().getHeader(Exchange.REDELIVERY_MAX_COUNTER, Integer.class);
        assertEquals(5, max);
    }
}

シャットダウンまたは停止中の再配信の制御

ルートを停止したり、正常なシャットダウンを開始する場合、再配信の試行を継続するのがエラー処理のデフォルトの挙動になります。通常、これは望ましい動作ではないため、以下の例のように、allowRedeliveryWhileStopping オプションを false に設定すると、シャットダウンまたは停止中に再配信を無効にすることができます。

errorHandler(deadLetterChannel("jms:queue:dead")
    .allowRedeliveryWhileStopping(false)
    .maximumRedeliveries(20)
    .redeliveryDelay(1000)
    .retryAttemptedLogLevel(LoggingLevel.INFO));
注記

後方互換性の理由から、allowRedelivery whileStopping オプションはデフォルトで true になります。ただし、強行なシャットダウン中は、このオプションの設定に関係なく、再配信が常に抑制されます (たとえば、正常なシャットダウンがタイムアウトした場合など)。

onExceptionOccurred プロセッサーの使用

Dead Letter Channel は、例外発生後にメッセージのカスタム処理を可能にする onExceptionOccurred プロセッサーをサポートします。これは、カスタムロギングにも使用できます。onExceptionOccurred プロセッサーから出力される新しい例外は WARN としてログに記録され、無視されます。既存の例外を上書きすることはありません。

onRedelivery プロセッサーと onExceptionOccurred プロセッサーの違いは、onRedelivery プロセッサーは再配信の試行直前に処理できることです。ただし、例外の発生直後には処理できません。たとえば、再配信を試行する間隔で 5 秒の遅延が発生するようにエラーハンドラーを設定すると、再配信プロセスは例外発生から 5 秒後に呼び出されます。

以下の例は、例外発生時にカスタムロギングを実行する方法を示しています。onExceptionOccurred がカスタムプロセッサーを使用するように設定する必要があります。

errorHandler(defaultErrorHandler().maximumRedeliveries(3).redeliveryDelay(5000).onExceptionOccurred(myProcessor));

onException 句

ルートビルダーで errorHandler() インターセプターを使用する代わりに、さまざまな例外タイプに異なる再配信ポリシーとデッドレターチャネルを定義する、一連の onException() 句を定義できます。たとえば、NullPointerExceptionIOExceptionException タイプごとに異なる動作を定義するには、Java DSL を使用してルートビルダーに以下のルールを定義できます。

onException(NullPointerException.class)
    .maximumRedeliveries(1)
    .setHeader("messageInfo", "Oh dear! An NPE.")
    .to("mock:npe_error");

onException(IOException.class)
    .initialRedeliveryDelay(5000L)
    .maximumRedeliveries(3)
    .backOffMultiplier(1.0)
    .useExponentialBackOff()
    .setHeader("messageInfo", "Oh dear! Some kind of I/O exception.")
    .to("mock:io_error");

onException(Exception.class)
    .initialRedeliveryDelay(1000L)
    .maximumRedeliveries(2)
    .setHeader("messageInfo", "Oh dear! An exception.")
    .to("mock:error");

from("seda:a").to("seda:b");

再配信オプションは、再配信ポリシーメソッドをチェーンして指定されます (表6.1「再配信ポリシーの設定」 のように)。また、to() DSL コマンドを使用して Dea