Apache Karaf トランザクションガイド

Red Hat Fuse 7.9

Apache Karaf コンテナーのトランザクションアプリケーションの作成

概要

Fuse のトランザクション対応アプリケーションの開発

前書き

本ガイドでは、Fuse トランザクションアプリケーションを実装するための情報および手順を提供します。その情報は、以下のように構成されています。

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

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

第1章 トランザクションの概要

本章では、基本的なトランザクションの概念とトランザクションマネージャーで重要なサービス品質に説明し、トランザクションを紹介します。その情報は、以下のように構成されています。

1.1. トランザクションとは

トランザクションのプロトタイプとは、概念的には単一のステップ (口座 A から口座 B への送金など) で構成されるものの、一連のステップとして実装されなければならない操作のことです。このような操作は、障害によって一部の手順が未完了のままになり、システムが一貫性のない状態になる可能性があるため、システム障害に対して脆弱です。たとえば、口座 A から口座 B に送金する操作について考えてみましょう。口座 A から送金金額を差し引いた後、口座 B にその金額を加える前にシステムが失敗したとします。結果として、その金額が消えてしまいます。

このような操作が確実に信頼できるようにするには、トランザクションとして実装します。トランザクションは、アトミックで一貫性があり、分離され、耐久性があるため、信頼性の高い実行を保証します。これらのプロパティーは、トランザクションの ACID プロパティーと呼ばれます。

1.2. トランザクションの ACID プロパティー

トランザクションの ACID プロパティーは以下のように定義されます。

  • Atomic -トランザクションは、全か無の手順です。個々の更新は、トランザクションの完了時に同時にアセンブリされ、コミットまたは中断 (ロールバック) されます。
  • Consistent - トランザクションは、システムをある整合性のある状態から別の整合性のある状態にする作業単位です。
  • Isolated -トランザクションの実行中に、その部分的な結果は他のエンティティーから確認できません。
  • Durable- トランザクションがコミットされた直後にシステムに障害が発生しても、トランザクションの結果は永続的です。

1.3. トランザクションクライアント

トランザクションクライアントは、トランザクションの開始と終了を可能にする API またはオブジェクトです。通常、トランザクションクライアントはトランザクションを 開始コミット、または ロールバック する操作を公開します。

標準の JavaEE アプリケーションでは、javax.transaction.UserTransaction インターフェースはトランザクションクライアント API を公開します。Spring Framework のコンテキストである Spring Boot では、org.springframework.transaction.PlatformTransactionManager インターフェースはトランザクションクライアント API を公開します。

1.4. トランザクション用語の説明

以下の表で、重要なトランザクション用語の一部を定義します。

用語説明

デマケーション (demarcation)

トランザクションのデマケーションとは、トランザクションの開始および終了を意味します。トランザクションを終了すると、トランザクションで実行された作業がコミットまたはロールバックされます。デマケーションは、たとえばトランザクションクライアント API を呼び出すことによって明示的にすることも、トランザクションエンドポイントからメッセージがポーリングされる場合などに暗黙的にすることもできます。詳細は、9章トランザクションを使用する Camel アプリケーションの作成 を参照してください。

リソース

リソース は、永続的または永久な変更に対応できるコンピューターシステムのコンポーネントです。実際には、ほとんどの場合、リソースはデータベースまたはデータベース上に階層化されたサービスです。たとえば、永続性を持つメッセージサービスなどです。ただし、他の種類のリソースも考えられます。たとえば、自動預け払い機 (ATM) は一種のリソースです。顧客が機械から現金を物理的に受け入れた後は、取引を取り消すことはできません。

トランザクションマネージャー

トランザクションマネージャー は、1 つ以上のリソース全体でトランザクションを調整する役割があります。多くの場合、トランザクションマネージャーはリソースに組み込まれます。たとえば、エンタープライズレベルのデータベースには、通常、そのデータベースでコンテンツを変更するトランザクションを管理できるトランザクションマネージャーが含まれます。通常は、複数 のリソースを持つトランザクションには 外部 トランザクションマネージャーが必要です。

トランザクションコンテキスト

トランザクションコンテキスト は、トランザクションを追跡するために必要な情報をカプセル化するオブジェクトです。トランザクションコンテキストの形式は、関連するトランザクションマネージャーの実装に完全に依存します。少なくとも、トランザクションコンテキストには一意のトランザクション識別子が含まれます。

分散トランザクション

分散トランザクションは、トランザクションスコープが複数のネットワークノードにまたがる分散システムのトランザクションを指します。分散トランザクションをサポートする基本的な前提条件は、標準形式でトランザクションコンテキストの送信をサポートするネットワークプロトコルです。分散トランザクションは Apache Camel トランザクションの範囲外です。「分散トランザクションマネージャー」 も参照してください。

X/Open XA 標準

XOpen XA 標準には、リソースをトランザクションマネージャーに統合するためのインターフェースが記述されています。複数のリソースを含むトランザクションを管理するには、参加しているリソースは XA 標準をサポートする必要があります。XA 標準をサポートするリソースは、特別なオブジェクトである XA スイッチ を公開し、トランザクションマネージャー (またはトランザクション処理モニター) がリソースのトランザクションを制御できるようにします。XA 標準は、1 フェーズコミットプロトコルと 2 フェーズコミットプロトコルの両方をサポートします。

1.5. 複数のリソースを変更するトランザクションの管理

1 つ のリソースが関係するトランザクションでは、通常リソースに組み込まれたトランザクションマネージャーを使用できます。複数 のリソースが関係するトランザクションの場合は、外部トランザクションマネージャーまたはトランザクション処理 (TP) モニターを使用する必要があります。この場合、XA スイッチを登録することで、リソースをトランザクションマネージャーと統合する必要があります。

単一リソースシステムで動作するトランザクションのコミットに使用されるプロトコルと、複数のリソースシステムで動作するトランザクションのコミットに使用されるプロトコルには、重要な違いがあります。

  • 1 フェーズコミット - 単一リソースシステム用。このプロトコルは、1 回のステップでトランザクションをコミットします。
  • 2 フェーズコミット - 複数リソースシステム用。このプロトコルは、2 つのステップでトランザクションをコミットします。

トランザクションに複数のリソースを含めると、一部 (すべてではない) のリソースでトランザクションをコミットした後にシステム障害が発生するリスクが高まります。これにより、システムが一貫性のない状態になります。2 フェーズコミットプロトコルは、このリスクを排除するように設計されています。これにより、再起動後に、システムを 常に 一貫性のある状態に復元できます。

1.6. トランザクションとスレッドの関係

トランザクション処理を理解するには、トランザクションとスレッドの基本的な関係を理解することが重要です。これは、トランザクションはスレッド固有であるということです。つまり、トランザクションが開始されると、特定のスレッドにアタッチされます。(技術的には、トランザクションコンテキストオブジェクト が作成され、現在のスレッドに関連付けられます)。この時点からトランザクションが終了するまで、スレッド内のすべてのアクティビティーがこのトランザクションスコープ内で発生します。他のスレッドでのアクティビティーは、このトランザクションのスコープ内ではありません。ただし、他のスレッドでのアクティビティーは、他のトランザクションのスコープ内にある可能性があります。

トランザクションとスレッド間の関係は、以下のようになります。

  • アプリケーションは、各トランザクションが別個のスレッドで作成される限り、複数のトランザクションを同時に処理できます
  • トランザクション内でサブスレッドを作成することに注意してください。トランザクションの途中で、たとえば threads() Camel DSL コマンドを呼び出して、新しいスレッドプールを作成する場合、新しいスレッドは元のトランザクションのスコープに含まれません
  • 前述と同じ理由で、暗黙的に新しいスレッドを作成する処理手順に注意してください
  • トランザクションスコープは通常ルートセグメント全体に拡張されません。つまり、1 つのルートセグメントが to(JoinEndpoint) で終わり、別のルートセグメントが from(JoinEndpoint) で始まる場合、これらのルートセグメントは通常同じトランザクションに属し ません。ただし、例外があります。
注記

高度なトランザクションマネージャーの実装によっては、トランザクションコンテキストを自由にスレッドからデタッチしたり、スレッドに自由にアタッチできます。たとえば、あるスレッドから別のスレッドにトランザクションコンテキストを移動できます。場合によっては、単一のトランザクションコンテキストを複数のスレッドにアタッチすることもできます。

1.7. トランザクションサービスの品質

トランザクションシステムを実装する製品を選択する際には、さまざまなデータベース製品とトランザクションマネージャが利用可能で、無料のものと商用ものがあります。すべてのトランザクション処理には名目上のサポートがありますが、これらの製品でサポートされるサービスの質は高いものから低いものまでさまざまです。このセクションでは、さまざまなトランザクション製品の信頼性と洗練性を比較する際に考慮する必要がある機能の種類について簡単に説明します。

1.7.1. リソースによって提供されるサービスの質

次の機能によって、リソースのサービス品質が決まります。

1.7.1.1. トランザクション分離レベル

ANSI SQL は、以下のように 4 つの トランザクション分離レベル を定義します。

SERIALIZABLE
トランザクションは相互に完全に分離されます。つまり、トランザクションがコミットされるまで、1 つのトランザクションがその他のトランザクションに影響を与えることはありません。この分離レベルは、すべてのトランザクションが次々に実行されるかのように影響するため、シリアライズ可能 呼ばれます (ただし、実際には、リソースはアルゴリズムを最適化できるため、一部のトランザクションを同時に処理できます)。
REPEATABLE_READ
トランザクションがデータベースを読み取りまたは更新するたびに、読み取りまたは書き込みロックが取得され、トランザクションが終了するまで保持されます。これにより、ほぼ完全な分離が得られます。しかし、分離が完全でないケースが 1 つあります。WHERE 句を使用して行の範囲を読み取る SQL の SELECT ステートメントについて考えてみましょう。最初のトランザクションの実行中に別のトランザクションがこの範囲に行を追加する場合、最初のトランザクションは SELECT の呼び出しを繰り返すと (ファントム読み取り)、この新しい行を確認できます。
READ_COMMITTED
書き込みロックは、トランザクションが終了するまで保持されます。読み取りロックは、トランザクションが終了するまで保持されません。そのため、他のトランザクションによってコミットされた更新が継続中のトランザクションで確認できるので、読み取りを繰り返すと結果が異なります。
READ_UNCOMMITTED
読み取りロックも書き込みロックも、トランザクションが終了するまで保持されません。したがって、ダーティーな読み取りが可能です。ダーティーレディー (Dirty ready) とは、他のトランザクションによって行われたコミットされていない変更が進行中のトランザクションで認識される場合です。

データベースは、通常、異なるトランザクション分離レベルをすべてサポートしているわけではありません。たとえば、一部の無料データベースでは READ_UNCOMMITTED のみサポートされます。また、一部のデータベースでは、ANSI 標準とは微妙に異なる方法でトランザクション分離レベルを実装しています。分離は、データベースのパフォーマンスとのトレードオフを伴う複雑な問題です (たとえば、Wikipedia の Isolation を参照)。

1.7.1.2. XA 標準のサポート

リソースが複数のリソースが関係するトランザクションに参加するには、XOpen XA 標準をサポートする必要があります。リソースの XA 標準の実装に特別な制限が適用されているかどうかを必ず確認してください。たとえば、XA 標準の実装は 1 つのデータベース接続に制限されています。つまり、一度に 1 つのスレッドだけがそのリソースが関係するトランザクションを処理できます。

1.7.2. トランザクションマネージャーが提供するサービスの品質

次の機能によって、トランザクションマネージャーのサービス品質が決まります。

1.7.2.1. 一時停止/再開およびアタッチ/デタッチのサポート

トランザクションマネージャーによっては、以下のようにトランザクションコンテキストとアプリケーションスレッド間の関連付けを操作する高度な機能をサポートしているものもあります。

  • 現在のトランザクションの一時停止/再開 - アプリケーションが現在のスレッドでトランザクション以外の作業を行う間、現在のトランザクションコンテキストを一時的に中断できます。
  • トランザクションコンテキストのアタッチ/デタッチ - トランザクションコンテキストをあるスレッドから別のスレッドに移動したり、複数のスレッドを含むようにトランザクションスコープを拡張したりできます。

1.7.2.2. 複数リソースのサポート

トランザクションマネージャーが他と大きく違うことは、複数のリソースをサポートできることです。これは通常、トランザクションマネージャーがリソースの XA スイッチを登録する方法を提供する XA 標準のサポートを伴います。

注記

厳密に言うと、XA 標準は複数のリソースをサポートする唯一の方法ではありませんが、これは最も実用的な方法です。代替手段は、通常、XA スイッチによって提供されるアルゴリズムを実装するための面倒な (および重要な) カスタムコードを記述することです。

1.7.2.3. 分散トランザクション

トランザクションマネージャーによっては、スコープに分散システムの複数のノードが含まれるトランザクションを管理する機能があります。トランザクションコンテキストは、WS-AtomicTransactions や CORBA OTS などの特別なプロトコルを使用してノードからノードに伝播されます。

1.7.2.4. トランザクションの監視

高度なトランザクションマネージャーは、通常、保留中のトランザクションのステータスを監視するためのビジュアルツールを提供します。この種のツールは、システム障害後に特に有用であり、不確実な状態 (ヒューリスティック例外) のままになっているトランザクションを特定して解決するのに役立ちます。

1.7.2.5. 障害からの復旧

システム障害 (クラッシュ) が発生した場合の堅牢性に関して、トランザクションマネージャーによって大きな違いがあります。トランザクションマネージャーが使用する主なストラテジーは、トランザクションの各ステップを実行する前にデータを永続的なログに書き込むことです。障害が発生すると、ログのデータを使用してトランザクションを復旧できます。一部のトランザクションマネージャーは、このストラテジーを他のものよりも慎重に実装します。たとえば、高性能のトランザクションマネージャーは通常永続トランザクションログを複製し、各ログを別々のホストマシンに格納できるようにします。

第2章 Karaf でトランザクションの使用 (OSGi)

ここでは、トランザクションを使用して Artemis JMS ブローカーにアクセスする Camel アプリケーションについて説明します。その情報は、以下のように構成されています。

2.1. 前提条件

この Camel アプリケーションの実装には以下の前提条件があります。

  • 外部 AMQ 7 の JMS メッセージブローカーが実行中である必要があります。

    以下のサンプルコードは、スタンドアロン (Docker 以外) バージョンの amq-broker-7.1.0-bin.zip を実行します。実行により amq7 インスタンスが作成され、実行されます。

    $ pwd
    /data/servers/amq-broker-7.1.0
    
    $ bin/artemis create --user admin --password admin --require-login amq7
    Creating ActiveMQ Artemis instance at: /data/servers/amq-broker-7.1.0/amq7
    
    Auto tuning journal ...
    done! Your system can make 27.78 writes per millisecond, your journal-buffer-timeout will be 36000
    
    You can now start the broker by executing:
    
       "/data/servers/amq-broker-7.1.0/amq7/bin/artemis" run
    
    Or you can run the broker in the background using:
    
       "/data/servers/amq-broker-7.1.0/amq7/bin/artemis-service" start
    
    $ amq7/bin/artemis run
               __  __  ____    ____            _
         /\   |  \/  |/ __ \  |  _ \          | |
        /  \  | \  / | |  | | | |_) |_ __ ___ | | _____ _ __
       / /\ \ | |\/| | |  | | |  _ <| '__/ _ \| |/ / _ \ '__|
      / ____ \| |  | | |__| | | |_) | | | (_) |   <  __/ |
     /_/    \_\_|  |_|\___\_\ |____/|_|  \___/|_|\_\___|_|
    
     Red Hat JBoss AMQ 7.1.0.GA
    
    
    018-05-02 16:37:19,294 INFO  [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server
    ...
  • クライアントライブラリーが必要です。Artemis ライブラリーは、Maven Central または Red Hat リポジトリーで利用できます。たとえば、次を使用できます。

    • mvn:org.apache.activemq/artemis-core-client/2.4.0.amq-710008-redhat-1
    • mvn:org.apache.activemq/artemis-jms-client/2.4.0.amq-710008-redhat-1

    この代わりに、以下のように Artemis/AMQ 7 クライアントライブラリーを Karaf 機能としてインストールできます。

    • karaf@root()> feature:install artemis-jms-client artemis-core-client
  • Karaf シェルコマンドまたは専用の Artemis サポートを提供するサポート機能は次のとおりです。

    karaf@root()> feature:install jms pax-jms-artemis pax-jms-config
  • 必要な Camel 機能は以下のとおりです。

    karaf@root()> feature:install camel-jms camel-blueprint

2.2. camel-jms プロジェクトのビルド

Fuse Software Downloads ページから quickstarts をダウンロードできます。

zip ファイルの内容をローカルフォルダーに展開します (例: quickstarts という名前の新規フォルダー)。

その後、/camel/camel-jms の例を OSGi バンドルとしてビルドし、インストールできます。このバンドルには、AMQ 7 JMS キューにメッセージを送信する Camel ルートの Blueprint XML 定義が含まれます。

以下の例では、$FUSE_HOME が展開した Fuse ディストリビューションの場所になります。このプロジェクトをビルドするには、以下を実行します。

  1. Maven を呼び出してプロジェクトをビルドします。

    $ cd quickstarts
    
    $ mvn clean install -f camel/camel-jms/
  2. javax.jms.ConnectionFactory サービスが OSGi ランタイムに公開されるように、JMS 接続ファクトリー設定を作成します。これを行うには、quickstarts/camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg$FUSE_HOME/etc ディレクトリーにコピーします。この設定は、作業用の接続ファクトリーを作成するために処理されます。以下に例を示します。

    $ cp camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg ../etc/
  3. パブリッシュされた接続ファクトリーを確認します。

    karaf@root()> service:list javax.jms.ConnectionFactory
    [javax.jms.ConnectionFactory]
    -----------------------------
     felix.fileinstall.filename = file:$FUSE_HOME/etc/org.ops4j.connectionfactory-amq7.cfg
     name = artemis
     osgi.jndi.service.name = artemis
     password = admin
     pax.jms.managed = true
     service.bundleid = 251
     service.factoryPid = org.ops4j.connectionfactory
     service.id = 436
     service.pid = org.ops4j.connectionfactory.d6207fcc-3fe6-4dc1-a0d8-0e76ba3b89bf
     service.scope = singleton
     type = artemis
     url = tcp://localhost:61616
     user = admin
    Provided by :
     OPS4J Pax JMS Config (251)
    
    karaf@root()> jms:info -u admin -p admin artemis
    Property │ Value
    ─────────┼──────────────────────────
    product  │ ActiveMQ
    version  │ 2.4.0.amq-711002-redhat-1
    
    karaf@root()> jms:queues -u admin -p admin artemis
    JMS Queues
    ────────────────────────────────────
    df2501d1-aa52-4439-b9e4-c0840c568df1
    DLQ
    ExpiryQueue
  4. バンドルをインストールします。

    karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/7.0.0.redhat-SNAPSHOT
    Bundle ID: 256
  5. 動作していることを確認します。

    karaf@root()> camel:context-list
     Context               Status              Total #       Failed #     Inflight #   Uptime
     -------               ------              -------       --------     ----------   ------
     jms-example-context   Started                   0              0              0   2 minutes
    karaf@root()> camel:route-list
     Context               Route               Status              Total #       Failed #     Inflight #   Uptime
     -------               -----               ------              -------       --------     ----------   ------
     jms-example-context   file-to-jms-route   Started                   0              0              0   2 minutes
     jms-example-context   jms-cbr-route       Started                   0              0              0   2 minutes
  6. Camel ルートが起動すると、即座に work/jms/input ディレクトリーが Fuse インストールに表示されます。このクイックスタートの src/main/data ディレクトリーにあるファイルを新たに作成された work/jms/input ディレクトリーにコピーします。
  7. しばらく待つと、work/jms/output ディレクトリー以下に同じファイルが国別に分類されます。

    • work/jms/output/othersorder1.xmlorder2.xml、および order4.xml
    • work/jms/output/usorder3.xml および order5.xml
    • work/jms/output/frorder6.xml
  8. ビジネスロギングを確認するには、ログを確認します。

    2018-05-02 17:20:47,952 | INFO  | ile://work/jms/input | file-to-jms-route                | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Receiving order order1.xml
    2018-05-02 17:20:48,052 | INFO  | umer[incomingOrders] | jms-cbr-route                    | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Sending order order1.xml to another country
    2018-05-02 17:20:48,053 | INFO  | umer[incomingOrders] | jms-cbr-route                    | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Done processing order1.xml
  9. キューが動的に作成されたことを確認します。

    karaf@root()> jms:queues -u admin -p admin artemis
    JMS Queues
    ────────────────────────────────────
    DLQ
    17767323-937f-4bad-a403-07cd63311f4e
    ExpiryQueue
    incomingOrders
  10. Camel ルートの統計を確認します。

    karaf@root()> camel:route-info jms-example-context file-to-jms-route
    Camel Route file-to-jms-route
    	Camel Context: jms-example-context
    	State: Started
    	State: Started
    
    
    Statistics
    	Exchanges Total: 1
    	Exchanges Completed: 1
    	Exchanges Failed: 0
    	Exchanges Inflight: 0
    	Min Processing Time: 67 ms
    	Max Processing Time: 67 ms
    	Mean Processing Time: 67 ms
    	Total Processing Time: 67 ms
    	Last Processing Time: 67 ms
    	Delta Processing Time: 67 ms
    	Start Statistics Date: 2018-05-02 17:14:17
    	Reset Statistics Date: 2018-05-02 17:14:17
    	First Exchange Date: 2018-05-02 17:20:48
    	Last Exchange Date: 2018-05-02 17:20:48

2.3. camel-jms プロジェクトの説明

Camel ルートは以下のエンドポイント URI を使用しています。

<route id="file-to-jms-route">
...
    <to uri="jms:queue:incomingOrders?transacted=true" />
</route>

<route id="jms-cbr-route">
    <from uri="jms:queue:incomingOrders?transacted=true" />
...
</route>

jms コンポーネントは、以下のスニペットを使用して設定されます。

<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
    <property name="connectionFactory">
        <reference interface="javax.jms.ConnectionFactory" />
    </property>
    <property name="transactionManager" ref="transactionManager"/>
</bean>

transactionManager 参照は以下のとおりです。

<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />

ご覧のとおり、JMS 接続ファクトリーと PlatformTransactionManager の Spring インターフェースの両方が参照のみです。Blueprint XML で 定義 する必要はありません。これらの サービス は Fuse によって公開されます。

etc/org.ops4j.connectionfactory-amq7.cfg を使用して javax.jms.ConnectionFactory が作成されていることはすでに確認しました。

トランザクションマネージャーは次のとおりです。

karaf@root()> service:list org.springframework.transaction.PlatformTransactionManager
[org.springframework.transaction.PlatformTransactionManager]
------------------------------------------------------------
 service.bundleid = 21
 service.id = 527
 service.scope = singleton
Provided by :
 Red Hat Fuse :: Fuse Modules :: Transaction (21)
Used by:
 Red Hat Fuse :: Quickstarts :: camel-jms (256)

実際のトランザクションマネージャーが登録された他のインターフェースを確認します。

karaf@root()> headers 21

Red Hat Fuse :: Fuse Modules :: Transaction (21)
------------------------------------------------
...
Bundle-Name = Red Hat Fuse :: Fuse Modules :: Transaction
Bundle-SymbolicName = fuse-pax-transx-tm-narayana
Bundle-Vendor = Red Hat
...

karaf@root()> bundle:services -p 21

Red Hat Fuse :: Fuse Modules :: Transaction (21) provides:
----------------------------------------------------------
objectClass = [org.osgi.service.cm.ManagedService]
service.bundleid = 21
service.id = 519
service.pid = org.ops4j.pax.transx.tm.narayana
service.scope = singleton
 ----
objectClass = [javax.transaction.TransactionManager]
provider = narayana
service.bundleid = 21
service.id = 520
service.scope = singleton
 ----
objectClass = [javax.transaction.TransactionSynchronizationRegistry]
provider = narayana
service.bundleid = 21
service.id = 523
service.scope = singleton
 ----
objectClass = [javax.transaction.UserTransaction]
provider = narayana
service.bundleid = 21
service.id = 524
service.scope = singleton
 ----
objectClass = [org.jboss.narayana.osgi.jta.ObjStoreBrowserService]
provider = narayana
service.bundleid = 21
service.id = 525
service.scope = singleton
 ----
objectClass = [org.ops4j.pax.transx.tm.TransactionManager]
provider = narayana
service.bundleid = 21
service.id = 526
service.scope = singleton
 ----
objectClass = [org.springframework.transaction.PlatformTransactionManager]
service.bundleid = 21
service.id = 527
service.scope = singleton

トランザクションマネージャーは、以下のインターフェースから利用できます。

  • javax.transaction.TransactionManager
  • javax.transaction.TransactionSynchronizationRegistry
  • javax.transaction.UserTransaction
  • org.jboss.narayana.osgi.jta.ObjStoreBrowserService
  • org.ops4j.pax.transx.tm.TransactionManager
  • org.springframework.transaction.PlatformTransactionManager

これらのいずれも、必要なコンテキストで使用できます。たとえば、camel-jms の場合、org.apache.camel.component.jms.JmsConfiguration.transactionManager フィールドを初期化する必要があります。これが、この例では以下を使用する理由です。

<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />

たとえば、上記を以下の代わりに使用します。

<reference id="transactionManager" interface="javax.transaction.TransactionManager" />

第3章 トランザクションマネージャーを設定および参照するためのインターフェース

JavaEE と Spring Boot は、それぞれ Fuse でトランザクションマネージャーを設定し、デプロイされたアプリケーションでトランザクションマネージャーを使用するために、トランザクションクライアントインターフェースを提供します。管理タスクである設定と、開発タスクである設定とでは明確な区別があります。アプリケーション開発者は、アプリケーションが以前に設定したトランザクションマネージャーを示すようにする必要があります。

3.1. トランザクションマネージャーの動作

トランザクションマネージャーは、1 つ以上のリソース全体でトランザクションを調整するためのアプリケーションの一部です。トランザクションマネージャーの役割は以下のとおりです。

  • デマケーション (demarcation) - begin、commit、rollback メソッドを使用してトランザクションを開始および終了します。
  • トランザクションコンテキストの管理 - トランザクションコンテキストには、トランザクションマネージャーがトランザクションを追跡するのに必要な情報が含まれます。トランザクションマネージャーは、トランザクションコンテキストを作成し、現在のスレッドにアタッチします。
  • 複数のリソースにわたるトランザクションの調整 - エンタープライズレベルのトランザクションマネージャーは、通常、複数のリソースにわたってトランザクションを調整できます。この機能には 2 フェーズコミットプロトコルが必要で、リソースは XA プロトコルを使用して登録および管理する必要があります。「XA 標準のサポート」 を参照してください。

    これは、すべてのトランザクションマネージャーでサポートされていない高度な機能です。

  • 障害からの復旧 - トランザクションマネージャーは、システム障害があり、アプリケーションが失敗した場合に、リソースが一貫性のない状態にならないようにする役割があります。場合によっては、システムを一貫性のある状態に復元するには、手動による介入が必要になる場合があります。

3.2. ローカル、グローバル、および分散トランザクションマネージャー

トランザクションマネージャーは、ローカル、グローバル、または分散型です。

3.2.1. ローカルトランザクションマネージャー

ローカルトランザクションマネージャー は、単一のリソースに対してのみトランザクションを調整できるトランザクションマネージャーです。ローカルトランザクションマネージャーの実装は通常、リソース自体に組み込まれ、アプリケーションによって使用されるトランザクションマネージャーは、この組み込みトランザクションマネージャーのシンラッパーです。

たとえば、Oracle データベースには、デマケーション操作をサポートする組み込みトランザクションマネージャーがあり (SQL BEGINCOMMIT、または ROLLBACK ステートメントを使用するか、ネイティブ Oracle API を使用)、さまざまなレベルのトランザクション分離があります。Oracle トランザクションマネージャーの制御は JDBC を介してエクスポートできます。この JDBC API は、トランザクションを区切るためにアプリケーションによって使用されます。

この場合、このコンテキストでリソースを構成するものを理解することが重要になります。たとえば、JMS 製品を使用している場合、JMS リソースは JMS 製品の単一の実行インスタンスであり、個々のキューおよびトピックではありません。さらに、同じ基礎となるリソースに別々の方法でアクセスする場合に、複数のリソースのように見えるものが実際には単一のリソースである可能性があります。たとえば、アプリケーションは、直接 (JDBC を介して) リレーショナルデータベースにアクセスする場合と、間接的に (Hibernate のようなオブジェクトリレーショナルマッピングツールを介して) アクセスする場合があります。この場合、同じ基盤となるトランザクションマネージャーが関与するため、これらのコードの一部を同じトランザクションに登録できるはずです。

注記

これはすべての場合に機能することを保証できません。これは原則的に可能ですが、Spring Framework やその他のラッパーレイヤーの設計の詳細により、実際には動作しなくなる可能性があります。

アプリケーションでは、多数の異なるローカルトランザクションマネージャを互いに独立して動作させることができます。たとえば、JMS エンドポイントが JMS トランザクションマネージャを参照する JMS キューとトピックを操作する Camel ルートを 1 つ使用できます。別のルートは JDBC 経由でリレーショナルデータベースにアクセスできます。しかし、同じルートで JDBC と JMS アクセスを組み合わせて、両方とも同じトランザクションに参加させることはできません。

3.2.2. グローバルトランザクションマネージャー

グローバルトランザクションマネージャーは、複数のリソースでトランザクションを調整できるトランザクションマネージャーです。これは、リソース自体に組み込まれているトランザクションマネージャに依存できない場合に必要です。外部システムは、トランザクション処理モニター (TPモニター) とも呼ばれ、異なるリソース間でトランザクションを調整できます。

以下は、複数のリソースで動作するトランザクションの前提条件です。

  • グローバルトランザクションマネージャーまたは TP モニター - 複数の XA リソースに対応するために 2 フェーズコミットプロトコルを実装する外部トランザクションシステム。
  • XA 標準をサポートするリソース - 2 フェーズコミットに参加するには、リソースが XA 標準をサポートしている必要があります。「XA 標準のサポート」 を参照してください。実際には、リソースは XA スイッチ オブジェクトをエクスポートできることを意味します。これにより、外部 TP モニターに対してトランザクションの完全な制御が可能になります。
ヒント

Spring Framework 自体は、グローバルトランザクションを管理するための TP モニターを提供するわけではありません。しかし、OSGi が提供する TP モニターまたは JavaEE が提供する TP 監視との統合に対するサポートを提供します (インテグレーションは JtaTransactionManager クラスによって実装されます) 。そのため、完全なトランザクションサポートでアプリケーションを OSGi コンテナーにデプロイする場合、Spring で複数のトランザクションリソースを使用できます。

3.2.3. 分散トランザクションマネージャー

通常、サーバーはトランザクションに関係するリソースに直接接続します。ただし、分散システムでは、Web サービスを介して間接的にのみ公開されるリソースに接続する必要がある場合があります。この場合、分散トランザクションをサポートすることができる TP モニターが必要です。Web サービスの WS-AtomicTransactions 仕様など、分散プロトコルのトランザクションをサポートする方法を記述する複数の標準を使用できます。

3.3. JavaEE トランザクションクライアントの使用

JavaEE を使用する場合、トランザクションマネージャーと対話するための最も基本的で標準的な方法は、Java Transaction API (JTA) インターフェース javax.transaction.UserTransaction です。標準的な使用法は次のとおりです。

InitialContext context = new InitialContext();
UserTransaction ut = (UserTransaction) context.lookup("java:comp/UserTransaction");
ut.begin();

// Access transactional, JTA-aware resources such as database and/or message broker

ut.commit(); // or ut.rollback()

JNDI(Java Naming and Directory Interface)から UserTransaction インスタンスを取得する方法は、トランザクションクライアントを取得する方法の 1 つです。JavaEE 環境では、CDI (コンテキストおよび依存性注入) を使用してトランザクションクライアントにアクセスできます。

以下の図は、JavaEE Camel アプリケーションを示しています。

javaee transaction api

この図は、Camel コードとアプリケーションコードの両方がアクセスできることを示しています。

  • javax.transaction.UserTransaction インスタンスは、アプリケーションから直接トランザクションをデマケーションしたり、Spring の TransactionTemplate クラスを使用してトランザクション対応の Camel コンポーネントを介して内部でデマケーションできます。
  • JDBC API 経由でのデータベースは直接的であるか、たとえば Spring の JdbcTemplate を使用するか、camel-jdbc コンポーネントを使用します。
  • Spring の JmsTemplate クラスを使用するか、camel-jms コンポーネントを使用して直接 JMS API 経由のであるメッセージブローカー。

javax.transaction.UserTransactionオブジェクトを使用する場合、トランザクションクライアントのみを直接操作しているため、使用されている実際のトランザクションマネージャを認識する必要はありません。(「トランザクションクライアント」 を参照してください)。 Spring と Camel は内部的に Spring のトランザクションファシリティーを使用するため、別のアプローチが採用されます。

JavaEE アプリケーション

一般的な JavaEE シナリオでは、アプリケーションは JavaEE アプリケーションサーバー (通常は WAR または EAR アーカイブ) にデプロイされます。JNDI または CDI により、アプリケーションは javax.transaction.UserTransaction サービスのインスタンスにアクセスできます。次に、アプリケーションはこのトランザクションクライアントインスタンスを使用してトランザクションを区切ります。トランザクション内で、アプリケーションは JDBC または JMS アクセスを実行します。

Camel コンポーネントおよびアプリケーションコード

これらは、JMS/JDBC 操作を実行するコードを表します。Camel には、JMS/JDBC リソースにアクセスするための独自の高度なメソッドがあります。アプリケーションコードは、特定の API を直接使用することがあります。

JMS 接続ファクトリー

これは、javax.jms.Connection およびjavax.jms.Session (または JMS 2.0 の javax.jms.JmsContext) のインスタンスを取得するために使用される javax.jms.ConnectionFactory インターフェースです。これは、アプリケーションによって直接使用することも、org.springframework.jms.core.JmsTemplate で内部的に使用する場合もある Camel コンポーネントで間接的に使用することもできます。アプリケーションコードと Camel コンポーネントのいずれでも、この接続ファクトリーの詳細は必要ありません。接続ファクトリーはアプリケーションサーバーで設定されます。この設定は JavaEE サーバーで見ることができます。Fuse などの OSGi サーバーも同様です。システム管理者は、アプリケーションとは独立して接続ファクトリーを設定します。通常、接続ファクトリーはプーリング機能を実装します。

JDBC データソース

これは、java.sql.Connection のインスタンスの取得に使用される javax.sql.DataSource インターフェースです。JMS と同様に、このデータソースは直接的または間接的に使用できます。たとえば、camel-sql コンポーネントは org.springframework.jdbc.core.JdbcTemplate クラスを内部で使用します。JMS と同様に、アプリケーションコードや Camel は、このデータソースの詳細を必要としません。設定は、4章Narayana トランザクションマネージャーの設定 で説明されているメソッドを使用してアプリケーションサーバー内または OSGi サーバー内で実行されます。

3.4. Spring Boot トランザクションクライアントの使用

Spring Framework (および Spring Boot) の主な目標の 1 つは、JavaEE API を使いやすくすることです。すべての主要な JavaEE vanilla API は Spring Framework (Spring Boot) に含まれます。これらは、特定の API の代替または置換ではなく、例外の処理に関して設定オプションを追加したり、より一貫した使用法を追加するラッパーです。

次の表は、特定の JavaEE API とそれに適合する Spring 関連のインターフェースを示しています。

JavaEE APISpring ユーティリティー設定で使用

JDBC

org.springframework.jdbc.core.JdbcTemplate

javax.sql.DataSource

JMS

org.springframework.jms.core.JmsTemplate

javax.jms.ConnectionFactory

JTA

org.springframework.transaction.support.TransactionTemplate

org.springframework.transaction.PlatformTransactionManager

JdbcTemplate およびJmsTemplate は、それぞれ javax.sql.DataSourcejavax.jms.ConnectionFactory を直接使用します。ただし、TransactionTemplatePlatformTransactionManager の Spring インターフェースを使用します。これは、Spring は単に JavaEE を 改善 するだけでなく、JavaEE クライアント API を独自のものに置き換えます。

Spring は、javax.transaction.UserTransaction を実際のシナリオでは単純すぎるインターフェースとして扱いまs.また、javax.transaction.UserTransaction はローカルの単一のリソーストランザクションとグローバルマルチリソーストランザクションを区別せず、org.springframework.transaction.PlatformTransactionManager の実装によって開発者はより自由になります。

以下は、Spring Boot の正規の API の使用法です。

// Create or get from ApplicationContext or injected with @Inject/@Autowired.
JmsTemplate jms = new JmsTemplate(...);
JdbcTemplate jdbc = new JdbcTemplate(...);
TransactionTemplate tx = new TransactionTemplate(...);

tx.execute((status) -> {
    // Perform JMS operations within transaction.
    jms.execute((SessionCallback<Object>)(session) -> {
        // Perform operations on JMS session
        return ...;
    });
    // Perform JDBC operations within transaction.
    jdbc.execute((ConnectionCallback<Object>)(connection) -> {
        // Perform operations on JDBC connection.
        return ...;
    });
    return ...;
});

上記の例では、3 種類の テンプレート はすべて簡単にインスタンス化されますが、Spring の ApplicationContext からも取得したり、@Autowired アノテーションを使用してインジェクトしたりできます。

3.4.1. Spring PlatformTransactionManager インターフェースの使用

前述のように、javax.transaction.UserTransaction は通常 JavaEE アプリケーションの JNDI から取得されます。しかし、Spring は、多くのシナリオにおいてこのインターフェースの明示的な実装を提供します。完全な JTA シナリオは必ずしも必要ではなく、アプリケーションが JDBC などの単一のリソースだけにアクセスする必要がある場合があります。

通常、org.springframework.transaction.PlatformTransactionManager は、従来のトランザクションクライアント操作である begincommit、および rollback を提供する Spring トランザクションクライアント API です。つまり、このインターフェースは、実行時にトランザクションを制御する上で必要な方法を提供します。

注記

トランザクションシステムの他の主要な機能とは、トランザクションリソースを実装するための API です。しかし、トランザクションリソースは通常、基礎となるデータベースによって実装されるため、トランザクションプログラミングのこの側面がアプリケーションプログラマーにとって問題になることはほとんどありません。

3.4.1.1. PlatformTransactionManager インターフェースの定義

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

3.4.1.2. TransactionDefinition インターフェース

TransactionDefinition インターフェースを使用して、新規に作成されたトランザクションの特性を指定します。新しいトランザクションの分離レベルと伝播ポリシーを指定できます。詳細は、「トランザクション伝搬ポリシー」 を参照してください。

3.4.1.3. TransactionStatus インターフェースの定義

TransactionStatus インターフェースを使用して、現在のトランザクション (つまり、現在のスレッドに関連するトランザクション) のステータスを確認し、現在のトランザクションをロールバック用にマーク付けできます。以下はインターフェース定義です。

public interface TransactionStatus extends SavepointManager, Flushable {

    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();
}

3.4.1.4. PlatformTransactionManager インターフェースで定義されるメソッド

PlatformTransactionManager インターフェースは以下のメソッドを定義します。

getTransaction()
新しいトランザクションを作成し、新しいトランザクションの特性を定義する TransactionDefinition オブジェクトを渡すことで、それを現在のスレッドに関連付けます。これは、数多くの他のトランザクションクライアント API の begin() メソッドと似ています。
commit()
現在のトランザクションをコミットし、登録されたリソースに対する保留中のすべての変更が永続的になります。
rollback()
現在のトランザクションをロールバックし、登録されたリソースに対する保留中のすべての変更が元に戻されます。

3.4.2. トランザクションマネージャーの使用手順

通常、PlatformTransactionManager インターフェースは直接使用しません。Apache Camel では、通常以下のようにトランザクションマネージャーを使用します。

  1. トランザクションマネージャーのインスタンスを作成します。Spring には複数の実装を使用できます。詳細は 「Spring Boot トランザクションクライアントの使用」を参照してください。
  2. トランザクションマネージャーインスタンスを Apache Camel コンポーネントまたはルートの transacted() DSL コマンドに渡します。トランザクションコンポーネントまたは transacted() コマンドは、トランザクションを区切る役割を果たします。詳細は、9章トランザクションを使用する Camel アプリケーションの作成を参照してください。

3.4.3. Spring PlatformTransactionManager 実装

ここでは、Spring Framework によって提供されるトランザクションマネージャーの実装の概要を説明します。実装はローカルトランザクションマネージャーとグローバルトランザクションマネージャーの 2 つのカテゴリーに分類されます。

Camel から起動:

  • camel-jms コンポーネントによって使用される org.apache.camel.component.jms.JmsConfiguration オブジェクトには、org.springframework.transaction.PlatformTransactionManager インターフェースのインスタンスが必要です。
  • org.apache.camel.component.sql.SqlComponent は内部的に org.springframework.jdbc.core.JdbcTemplate クラスを使用し、この JDBC テンプレートも org.springframework.transaction.PlatformTransactionManager と統合されます。

ご覧のとおり、このインターフェースのいくつかの実装が必要です。シナリオによっては、必要なプラットフォームトランザクションマネージャーを設定できます。

3.4.3.1. ローカル PlatformTransactionManager 実装

以下のリストは、Spring Framework が提供するローカルトランザクションマネージャーの実装をまとめたものです。これらのトランザクションマネージャーは、1 つのリソースのみをサポートします。

org.springframework.jms.connection.JmsTransactionManager
このトランザクションマネージャーの実装は、1 つの JMS リソースを管理できます。キューまたはトピックをいくつでも接続できますが、それらのキューまたはトピックが同じ基盤の JMS メッセージング製品インスタンスに属している場合に限られます。さらに、トランザクションでは他のタイプのリソースを登録することはできません。
org.springframework.jdbc.datasource.DataSourceTransactionManager
このトランザクションマネージャーの実装は、1 つの JDBC データベースを管理できます。異なるデータベーステーブルをいくつでも更新できますが、そのテーブルが同じ基盤のデータベースインスタンスに属する場合 のみに限られます。
org.springframework.orm.jpa.JpaTransactionManager
このトランザクションマネージャーの実装では、Java Persistence API (JPA) リソースを管理できます。ただし、トランザクションに他の種類のリソースを同時に登録することはできません。
org.springframework.orm.hibernate5.HibernateTransactionManager
このトランザクションマネージャーの実装は、Hibernate リソースを管理できます。ただし、トランザクションに他の種類のリソースを同時に登録することはできません。さらに、JPA API はネイティブ Hibernate API よりも優先されます。

また、使用頻度が低い他の PlatformTransactionManager の実装もあります。

3.4.3.2. グローバル PlatformTransactionManager 実装

Spring Framework は、OSGi ランタイムで使用するグローバルトランザクションマネージャーの実装を 1 つ提供します。org.springframework.transaction.jta.JtaTransactionManager は、トランザクションの複数のリソースに対する操作をサポートします。このトランザクションマネージャーは XA トランザクション API をサポートし、1 つのトランザクションに複数のリソースを登録することができます。このトランザクションマネージャーを使用するには、OSGi コンテナーまたは JavaEE サーバー内にアプリケーションをデプロイする必要があります。

PlatformTransactionManager の単一リソース実装は実際の 実装 ですが、JtaTransactionManager は標準の javax.transaction.TransactionManager の実際の実装に対するラッパーです。

このため、 (JNDI または CDI を使用することで) すでに javax.transaction.TransactionManager のインスタンスが設定され、通常は javax.transaction.UserTransaction のインスタンスにアクセスできる環境で PlatformTransactionManagerJtaTransactionManager 実装を使用することが推奨されます。通常、これらの JTA インターフェースはいずれも単一のオブジェクト/サービスによって実装されます。

JtaTransactionManager の設定/使用例は次のとおりです。

InitialContext context = new InitialContext();
UserTransaction ut = (UserTransaction) context.lookup("java:comp/UserTransaction");
TransactionManager tm = (TransactionManager) context.lookup("java:/TransactionManager");

JtaTransactionManager jta = new JtaTransactionManager();
jta.setUserTransaction(ut);
jta.setTransactionManager(tm);

TransactionTemplate jtaTx = new TransactionTemplate(jta);

jtaTx.execute((status) -> {
    // Perform resource access in the context of global transaction.
    return ...;
});

上記の例では、JTA オブジェクトの実際のインスタンス (UserTransaction および TransactionManager) が JNDI から取得されます。OSGi では、OSGi サービスレジストリーから取得することもできます。

3.5. トランザクションのクライアントとトランザクションマネージャー間の OSGi インターフェース

JavaEE トランザクションクライアント API と Spring Boot トランザクションクライアント API の説明後、Fuse などの OSGi サーバー内で関係を確認すると便利です。OSGi の機能の 1 つに、グローバルサービスレジストリーがあります。このレジストリーは次の目的で使用できます。

  • フィルターまたはインターフェース別にサービスを検索します。
  • 特定のインターフェースおよびプロパティーでサービスを登録します。

Java EE アプリケーションサーバーにデプロイされたアプリケーションが、JNDI (サービスロケータメソッド) を使用して javax.transaction.UserTransaction への参照を取得したり、CDI (依存性注入メソッド) で注入したりするのと同じように、OSGi では、同じ参照 (直接的または間接的) を以下のいずれかの方法で取得できます。

  • org.osgi.framework.BundleContext.getServiceReference()メソッド (サービスロケータ) を呼び出します。
  • Blueprint コンテナーに注入します。
  • Service Component Runtime (SCR) アノテーション (依存関係の注入) を使用します。

以下の図は、OSGi ランタイムにデプロイされる Fuse アプリケーションを示しています。アプリケーションコードや Camel コンポーネントは API を使用してトランザクションマネージャー、データソース、および接続ファクトリーへの参照を取得します。

osgi transaction architecture

アプリケーション (バンドル) は、OSGi レジストリーに登録されているサービスと対話します。アクセスは インターフェース 経由で実行され、これがアプリケーションに関連するものすべてになります。

Fuse では、(直接的または小さなラッパーを介して) トランザクションクライアントインターフェースを実装する基本オブジェクトは org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager です。以下のインターフェースを使用してトランザクションマネージャーにアクセスできます。

  • javax.transaction.TransactionManager
  • javax.transaction.UserTransaction
  • org.springframework.transaction.PlatformTransactionManager
  • org.ops4j.pax.transx.tm.TransactionManager

これらのインターフェースを直接使用することも、Camel などのフレームワークやライブラリーを選択して暗黙的に使用できます。

Fuse で org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager を設定する方法は、4章Narayana トランザクションマネージャーの設定 を参照してください。本ガイドの後半は、章の情報で構成され、JDBC データソースや JMS 接続ファクトリーなどの他のサービスを設定および使用する方法について説明します。

第4章 Narayana トランザクションマネージャーの設定

Fuse では、組み込みグのローバルトランザクションマネージャーは JBoss Narayana Transaction Manager であり、Enterprise Application Platform (EAP) 7 によって使用されるトランザクションマネージャーと同じです。

OSGi ランタイムでは、Fuse for Karaf と同様に、追加のインテグレーションレイヤーは PAX TRANSX プロジェクト によって提供されます。

以下のトピックでは、Narayana 設定について説明します。

4.1. Narayana インストール

Narayana トランザクションマネージャーは、以下のインターフェースと追加のサポートインターフェースで OSGi バンドルを使用するために公開されます。

  • javax.transaction.TransactionManager
  • javax.transaction.UserTransaction
  • org.springframework.transaction.PlatformTransactionManager
  • org.ops4j.pax.transx.tm.TransactionManager

7.9.0.fuse-790071-redhat-00001 ディストリビューションでは、これらのインターフェースを最初から利用できるようになります。

pax-transx-tm-narayana 機能には、Narayana を組み込むオーバーライドされたバンドルが含まれます。

karaf@root()> feature:info pax-transx-tm-narayana
Feature pax-transx-tm-narayana 0.3.0
Feature has no configuration
Feature has no configuration files
Feature depends on:
  pax-transx-tm-api 0.0.0
Feature contains followed bundles:
  mvn:org.jboss.fuse.modules/fuse-pax-transx-tm-narayana/7.0.0.fuse-000191-redhat-1 (overriden from mvn:org.ops4j.pax.transx/pax-transx-tm-narayana/0.3.0)
Feature has no conditionals.

fuse-pax-transx-tm-narayana バンドルが提供するサービスは以下のとおりです。

karaf@root()> bundle:services fuse-pax-transx-tm-narayana

Red Hat Fuse :: Fuse Modules :: Transaction (21) provides:
----------------------------------------------------------
[org.osgi.service.cm.ManagedService]
[javax.transaction.TransactionManager]
[javax.transaction.TransactionSynchronizationRegistry]
[javax.transaction.UserTransaction]
[org.jboss.narayana.osgi.jta.ObjStoreBrowserService]
[org.ops4j.pax.transx.tm.TransactionManager]
[org.springframework.transaction.PlatformTransactionManager]

このバンドルは org.osgi.service.cm.ManagedService を登録するため、CM 設定の変更を追跡し、これに反応します。

karaf@root()> bundle:services -p fuse-pax-transx-tm-narayana

Red Hat Fuse :: Fuse Modules :: Transaction (21) provides:
----------------------------------------------------------
objectClass = [org.osgi.service.cm.ManagedService]
service.bundleid = 21
service.id = 232
service.pid = org.ops4j.pax.transx.tm.narayana
service.scope = singleton
...

デフォルトの org.ops4j.pax.transx.tm.narayana PID は次のとおりです。

karaf@root()> config:list '(service.pid=org.ops4j.pax.transx.tm.narayana)'
----------------------------------------------------------------
Pid:            org.ops4j.pax.transx.tm.narayana
BundleLocation: ?
Properties:
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.localOSRoot = communicationStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreDir = /data/servers/7.9.0.fuse-790071-redhat-00001/data/narayana
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.localOSRoot = defaultStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreDir = /data/servers/7.9.0.fuse-790071-redhat-00001/data/narayana
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.localOSRoot = stateStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreDir = /data/servers/7.9.0.fuse-790071-redhat-00001/data/narayana
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
   com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean.recoveryBackoffPeriod = 10
   felix.fileinstall.filename = file:/data/servers/7.9.0.fuse-790071-redhat-00001/etc/org.ops4j.pax.transx.tm.narayana.cfg
   service.pid = org.ops4j.pax.transx.tm.narayana

概要:

  • Fuse for Karaf には、完全機能のグローバルな Narayana トランザクションマネージャーが含まれています。
  • トランザクションマネージャーは、さまざまなクライアントインターフェース (JTA、Spring-tx、PAX JMS) で正しく公開されます。
  • Narayana は、org.ops4j.pax.transx.tm.narayana で利用可能な標準の OSGi メソッドである Configuration Admin を使用して設定できます。
  • デフォルト設定は $FUSE_HOME/etc/org.ops4j.pax.transx.tm.narayana.cfg にあります。

4.2. サポートされるトランザクションプロトコル

Narayana トランザクションマネージャー は、EAP で使用される JBoss/Red Hat 製品です。Narayana は、幅広い標準ベースのトランザクションプロトコルを使用して開発されたアプリケーションをサポートするトランザクションツールキットです。

  • JTA
  • JTS
  • Web サービストランザクション
  • REST トランザクション
  • STM
  • XATMI/TX

4.3. Narayana 設定

pax-transx-tm-narayana バンドルには jbossts-properties.xml ファイルが含まれており、これはトランザクションマネージャーのさまざまな側面のデフォルト設定を提供します。これらのプロパティーはすべて、$FUSE_HOME/etc/org.ops4j.pax.transx.tm.narayana.cfg ファイルに直接上書きしたり、Configuration Admin API を使用して上書きしたりできます。

Narayana の基本設定は、さまざまな EnvironmentBean オブジェクトによって行われます。そのような Bean はすべて、異なる接頭辞を持つプロパティーを使用して設定できます。以下の表は、使用される設定オブジェクトと接頭辞の概要を示しています。

設定 Beanプロパティープレフィックス

com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean

com.arjuna.ats.arjuna.coordinator

com.arjuna.ats.arjuna.common.CoreEnvironmentBean

com.arjuna.ats.arjuna

com.arjuna.ats.internal.arjuna.objectstore.hornetq.HornetqJournalEnvironmentBean

com.arjuna.ats.arjuna.hornetqjournal

com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean

com.arjuna.ats.arjuna.objectstore

com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean

com.arjuna.ats.arjuna.recovery

com.arjuna.ats.jdbc.common.JDBCEnvironmentBean

com.arjuna.ats.jdbc

com.arjuna.ats.jta.common.JTAEnvironmentBean

com.arjuna.ats.jta

com.arjuna.ats.txoj.common.TxojEnvironmentBean

com.arjuna.ats.txoj.lockstore

プレフィックス を使用すると、設定を簡素化できます。ただし、通常、以下のいずれかの形式を使用する必要があります。

NameEnvironmentBean.propertyName (推奨形式)

fully-qualified-class-name.field-name

たとえば、com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean.commitOnePhase フィールドについて考えてみましょう。これは、com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean.commitOnePhase プロパティーを使用して設定することも、単純な (推奨) フォーム CoordinatorEnvironmentBean.commitOnePhase を使用して設定することが可能です。プロパティーの設定方法や設定方法の詳細については、Narayana の製品ドキュメントを参照してください。

ObjectStoreEnvironmentBean などの一部の Bean は、別の目的で設定を提供する 名前付き インスタンスごとに複数回設定できます。この場合、インスタンスの名前は、プレフィックス (上記のいずれか) と field-name の間で使用されます。たとえば、communicationStore という名前の ObjectStoreEnvironmentBean インスタンスのオブジェクトストアのタイプは、名前付きプロパティーを使用して設定できます。

  • com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreType
  • ObjectStoreEnvironmentBean.communicationStore.objectStoreType

4.4. ログストレージの設定

最も重要な設定は、オブジェクトストレージのタイプおよび場所です。通常、com.arjuna.ats.arjuna.objectstore.ObjectStoreAPI インターフェースの実装は 3 つあります。

com.arjuna.ats.internal.arjuna.objectstore.hornetq.HornetqObjectStoreAdaptor
AMQ 7 の内部からの org.apache.activemq.artemis.core.journal.Journal ストレージを使用します。
com.arjuna.ats.internal.arjuna.objectstore.jdbc.JDBCStore
JDBC を使用して TX ログファイルを維持します。
com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore (および特殊な実装)
カスタムファイルベースのログストレージを使用します。

デフォルトでは、Fuse は FileSystemStore の特殊な実装である com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore を使用します。

トランザクション/オブジェクトログが保持される、Narayana によって使用される ストア が 3 つあります。

  • defaultStore
  • communicationStore
  • stateStore

詳細は、Narayana ドキュメントの「State management」を参照してください。

これらの 3 つの ストア のデフォルト設定は、以下のとおりです。

# default store
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreDir = ${karaf.data}/narayana
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.localOSRoot = defaultStore
# communication store
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreDir = ${karaf.data}/narayana
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.localOSRoot = communicationStore
# state store
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreDir = ${karaf.data}/narayana
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.localOSRoot = stateStore

ShadowNoFileLockStore はベースディレクトリー (objectStoreDir) および特定のストアのディレクトリー (localOSRoot) で設定されます。

多くの設定オプションは、Narayana ドキュメントガイド に含まれています。ただし、Narayana ドキュメントには、設定オプションの正規参照はさまざまな EnvironmentBean クラスの Javadoc であると記載されています。

第5章 Narayana トランザクションマネージャーの使用

本セクションでは、javax.transaction.UserTransaction インターフェース、org.springframework.transaction.PlatformTransactionManager インターフェース、または javax.transaction.Transaction インターフェースを実装することで、Narayana トランザクションマネージャーの使用方法について説明します。使用するインターフェースは、アプリケーションのニーズによって異なります。本章の最後では、XA リソースの登録の問題解決について説明しています。その情報は、以下のように構成されています。

Java トランザクション API の詳細は、Java Transaction API (JTA) 1.2 仕様および Javadoc を参照してください。

5.1. UserTransaction オブジェクトの使用

トランザクションのデマケーション用に javax.transaction.UserTransaction インターフェースを実装します。つまり、トランザクションの開始、コミット、またはロールバックを行います。これは、アプリケーションコードで直接使用する JTA インターフェースです。ただし、UserTransaction インターフェースはトランザクションを区切る方法の 1 つにすぎません。トランザクションを区切ることができるさまざまな方法は、9章トランザクションを使用する Camel アプリケーションの作成 を参照してください。

5.1.1. UserTransaction インターフェースの定義

JTA UserTransaction インターフェースは以下のように定義されます。

public interface javax.transaction.UserTransaction {

    public void begin();

    public void commit();

    public void rollback();

    public void setRollbackOnly();

    public int getStatus();

    public void setTransactionTimeout(int seconds);
}

5.1.2. UserTransaction メソッドの説明

UserTransaction インターフェースは以下のメソッドを定義します。

begin()
新しいトランザクションを開始し、現在のスレッドに関連付けます。XA リソースがこのトランザクションに関連付けられると、トランザクションは暗黙的に XA トランザクションになります。
commit()

現在のトランザクションを正常に完了し、保留中のすべての変更が永続的になります。コミットの後、現在のスレッドに関連付けられたトランザクションはなくなります。

注記

ただし、現在のトランザクションがロールバックのみとしてマークされている場合は、commit() が呼び出されるとトランザクションは実際にはロールバックされます。

rollback()
保留中のすべての変更が破棄されるように、トランザクションを即座に中止します。ロールバックの後、現在のスレッドに関連付けられたトランザクションはなくなります。
setRollbackOnly()
現在のトランザクションの状態を変更して、ロールバックが唯一の結果となるようにしますが、ロールバックはまだ実行されません。
getStatus()

現在のトランザクションのステータスを返します。これは、javax.transaction.Status インターフェースで定義された以下の整数値のいずれかになります。

  • STATUS_ACTIVE
  • STATUS_COMMITTED
  • STATUS_COMMITTING
  • STATUS_MARKED_ROLLBACK
  • STATUS_NO_TRANSACTION
  • STATUS_PREPARED
  • STATUS_PREPARING
  • STATUS_ROLLEDBACK
  • STATUS_ROLLING_BACK
  • STATUS_UNKNOWN
setTransactionTimeout()
現在のトランザクションのタイムアウト (秒単位) をカスタマイズします。トランザクションが指定のタイムアウト内で解決されない場合、トランザクションマネージャーは自動的にこれをロールバックします。

5.2. TransactionManager オブジェクトの使用

javax.transaction.TransactionManager オブジェクトを使用する最も一般的な方法は、フレームワーク API (例: Camel JMS コンポーネント) に渡すことです。これにより、フレームワークはトランザクションのデマケーションに対応できます。TransactionManager オブジェクトを直接使用する必要がある場合があります。これは、suspend()resume() メソッドなどの高度なトランザクション API にアクセスする必要がある場合に役立ちます。

5.2.1. TransactionManager インターフェースの定義

JTA TransactionManager インターフェースの定義は以下のとおりです。

interface javax.transaction.TransactionManager {

    // Same as UserTransaction methods

    public void begin();

    public void commit();

    public void rollback();

    public void setRollbackOnly();

    public int getStatus();

    public void setTransactionTimeout(int seconds);

    // Extra TransactionManager methods

    public Transaction getTransaction();

    public Transaction suspend() ;

    public void resume(Transaction tobj);
}

5.2.2. TransactionManager メソッドの説明

TransactionManager インターフェースは、UserTransaction インターフェースにあるすべてのメソッドをサポートします。トランザクションのデマケーションには TransactionManager オブジェクトを使用できます。さらに、TransactionManager オブジェクトは以下のメソッドをサポートします。

getTransaction()
現在のトランザクションへの参照を取得します。これは、現在のスレッドに関連付けられているトランザクションです。現在のトランザクションがない場合は、このメソッドは null を返します。
suspend()

現在のスレッドから現在のトランザクションをデタッチし、トランザクションへの参照を返します。このメソッドを呼び出した後、現在のスレッドのトランザクションコンテキストがなくなります。この後に行う作業は、トランザクションのコンテキストで行われなくなります。

注記

すべてのトランザクションマネージャーがトランザクションの一時停止をサポートしているわけではありません。ただし、この機能は Narayana でサポートされています。

resume()
一時停止されたトランザクションを現在のスレッドコンテキストに再アタッチします。このメソッドを呼び出した後、トランザクションコンテキストが復元され、この時点以降に行われたすべての作業は、トランザクションのコンテキストで実行されます。

5.3. トランザクションオブジェクトの使用

トランザクションを一時停止または再開する必要がある場合、またはリソースを明示的に登録する必要がある場合、javax.transaction.Transaction オブジェクトを直接使用する必要がある場合があります。「XA エンリストメント問題の解決」 の説明にあるように、フレームワークまたはコンテナーは通常、リソースを自動的に登録します。

5.3.1. トランザクションインターフェースの定義

JTA Transaction インターフェースの定義は以下のとおりです。

interface javax.transaction.Transaction {

    public void commit();

    public void rollback();

    public void setRollbackOnly();

    public int getStatus();

    public boolean enlistResource(XAResource xaRes);

    public boolean delistResource(XAResource xaRes, int flag);

    public void registerSynchronization(Synchronization sync);
}

5.3.2. トランザクションメソッドの説明

commit()rollback()setRollbackOnly()、および getStatus() メソッドの動作は、UserTransaction インターフェースの対応するメソッドと同じです。実際、UserTransaction オブジェクトは現在のトランザクションを取得し、Transaction オブジェクトで対応するメソッドを呼び出す便利なラッパーです。

また、Transaction インターフェースは、UserTransaction インターフェースにカウンターがない以下のメソッドを定義します。

enlistResource()

XA リソースを現在のトランザクションに関連付けます。

注記

このメソッドは、XA トランザクションのコンテキストにおいて重要な意味を持ちます。これは、XA トランザクションを特徴付ける現在のトランザクションに複数の XA リソースを登録する機能です。一方、リソースを明示的に登録することは迷惑であり、通常、フレームワークまたはコンテナーがこれを行うことが想定されます。たとえば、「XA エンリストメント問題の解決」 を参照してください。

delistResource()

指定されたリソースをトランザクションの関連付けから解除します。flag 引数は、javax.transaction.Transaction インターフェースで定義される以下の整数値のいずれかを取ることができます。

  • TMSUCCESS
  • TMFAIL
  • TMSUSPEND
registerSynchronization()
javax.transaction.Synchronization オブジェクトを現在のトランザクションに登録します。Synchronization オブジェクトは、コミットの準備フェーズの直前にコールバックを受信し、トランザクションの完了直後にコールバックを受け取ります。

5.4. XA エンリストメント問題の解決

XA リソースを登録する JTA の標準的な手順は、現在のトランザクションを表す現在の javax.transaction.Transaction オブジェクトに XA リソースを明示的に追加することです。つまり、新しいトランザクションが開始されるたびに XA リソースを明示的に登録する必要があります。

5.4.1. XA リソースの登録方法

XA リソースをトランザクションに登録するには、Transaction インターフェースで enlistResource() メソッドを呼び出す必要があります。たとえば、TransactionManager オブジェクトおよび XAResource オブジェクトがある場合は、以下のように XAResource オブジェクトを登録できます。

// Java
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
...
// Given:
// 'tm' of type TransactionManager
// 'xaResource' of type XAResource

// Start the transaction
tm.begin();

Transaction transaction = tm.getTransaction();
transaction.enlistResource(xaResource);

// Do some work...
...

// End the transaction
tm.commit();

リソースの登録に関する複雑さは、新しいトランザクション ごと にリソースを登録する必要があり、リソースの使用を開始する前にリソースを登録する必要があることです。リソースを明示的に登録すると、エラーが発生しやすいコードに enlistResource() 呼び出しがいくつもある状態になる可能性があります。さらに、適切な場所で enlistResource() を呼び出すことが困難なことがあります。たとえば、トランザクションの詳細の一部を非表示にするフレームワークを使用している場合などです。

5.4.2. 自動登録

XA リソースを明示的に登録する代わりに、XA リソースの自動登録をサポートする機能を使用する方が簡単で安全です。たとえば、JMS および JDBC リソースを使用するコンテキストでは、標準の手法は自動登録をサポートするラッパークラスを使用することです。

JDBC と JMS アクセスの共通パターンは次のとおりです。

  1. アプリケーションコードは、JDBC アクセスの場合は javax.sql.DataSource、JMS の場合は javax.jms.ConnectionFactory が JDBC または JMS 接続を取得することを想定します。
  2. アプリケーション/OSGi サーバー内で、これらのインターフェースのデータベースまたはブローカー固有の実装が登録されます。
  3. アプリケーション/OSGi サーバーは、データベース/ブローカー固有のファクトリーを汎用的な、プーリングおよび登録するファクトリーに ラップ します。

このようにして、アプリケーションコードは引き続き javax.sql.DataSource および javax.jms.ConnectionFactory を使用しますが、内部的にこれらのコードがアクセスされると、通常は以下に関する追加機能があります。

  • 接続プーリング - 毎回データベース/メッセージのブローカーに新しい接続を作成する代わりに、事前に初期化された接続の プール が使用されます。プーリング の別の側面は、接続の定期的な検証などです。
  • JTA 登録 - java.sql.Connection (JDBC) または javax.jms.Connection (JMS) のインスタンスを返す前に、真の XA リソースであれば、実際の接続オブジェクトが登録されます。JTA トランザクション内で登録が行われます (ある場合)。

自動登録では、アプリケーションコードを変更する必要はありません。

JDBC データソースおよび JMS 接続ファクトリーのラッパーのプーリングおよび登録に関する詳細は、6章JDBC データソースの使用 および 7章JMS 接続ファクトリーの使用 を参照してください。

第6章 JDBC データソースの使用

以下のトピックでは、Fuse OSGi ランタイムでの JDBC データソースの使用について説明します。

6.1. Connection インターフェースについて

データ操作の実行に使用する最も重要な オブジェクト は、java.sql.Connection インターフェースの実装です。Fuse 設定の観点からは、Connection オブジェクトの 取得 方法を理解することが重要です。

関連するオブジェクトを含むライブラリーは以下の通りです。

  • PostgreSQL: mvn:org.postgresql/postgresql/42.2.5
  • MySQL: mvn:mysql/mysql-connector-java/5.1.34

既存の実装 (ドライバー JAR に含まれる) は以下を提供します。

  • PostgreSQL: org.postgresql.jdbc.PgConnection
  • MySQL: com.mysql.jdbc.JDBC4Connection (com.mysql.jdbc.Driver のさまざまな connect*() メソッドも参照)

これらの実装には、DML、DDL、および簡単なトランザクション管理を実行するためのデータベース固有のロジックが含まれます。

理論的には、これらの接続オブジェクトを手動で作成することは可能ですが、よりクリーンな API を提供するために、詳細を非表示にする JDBC メソッドが 2 つあります。

  • java.sql.Driver.connect() - このメソッドは、ずっと前にスタンドアロンアプリケーションで使用されていました。
  • javax.sql.DataSource.getConnection() - ファクトリーパターンを使用する場合に推奨される方法です。JMS 接続ファクトリーから JMS 接続を取得する場合と同様の方法が使用されます。

ドライバーマネージャー の方法については、ここで説明しません。このメソッドは、指定の接続オブジェクトのプレーンコンストラクターの上にあるほんの小さな であることだけを述べれば十分です

データベース固有の通信プロトコルを効果的に実装する java.sql.Connection に加えて、次の 2 つの特殊な接続インターフェースがあります。

  • javax.sql.PooledConnection は物理接続を表します。コードは、このプールされた接続ファクトリー接続と直接対話しません。代わりに、getConnection() メソッドから取得した接続が使用されます。この間接化により、アプリケーションサーバーのレベルで接続プールを管理できます。通常、getConnection() を使用して取得した接続はプロキシーです。このようなプロキシー接続が閉じられると、物理接続は切断されず、代わりに管理された接続プールで再び利用できるようになります。
  • javax.sql.XAConnectionjavax.transaction.TransactionManager で使用するために XA 対応の接続に関連する javax.transaction.xa.XAResource オブジェクトを取得できます。javax.sql.XAConnectionjavax.sql.PooledConnection, it also provides the `getConnection() メソッドを拡張しているため、一般的な DML/DQL メソッドを使用した JDBC 接続オブジェクトへのアクセスを提供します。

6.2. JDBC データソースの概要

JDBC 1.4 標準では、java.sql.Connection オブジェクトの ファクトリー として機能する javax.sql.DataSource インターフェースが導入されました。通常、このようなデータソースは JNDI レジストリーにバインドされ、サーブレットや EJB などの Java EE コンポーネント内に配置または注入されます。これらのデータソースは、アプリケーションサーバー 内で 設定 され、デプロイされたアプリケーションで名前で 参照される ことが重要な側面となります。

以下の connection オブジェクトには、独自の データソース があります。

データソースConnection

javax.sql.DataSource

java.sql.Connection

javax.sql.ConnectionPoolDataSource

javax.sql.PooledConnection

javax.sql.XADataSource

javax.sql.XAConnection

上記の各 データソース における最も重要な相違点は以下のとおりです。

  • 最も重要なのは javax.sql.DataSource で、java.sql.Connection インスタンスを取得するための ファクトリーと同様のオブジェクトです。ほとんどの javax.sql.DataSource 実装が通常接続プーリングを実行することが事実であり、画像を変更すべきではありません。これは、アプリケーションコードで使用する必要がある唯一のインターフェースです。次のどれを実装しているかは関係ありません。

    • 直接 JDBC アクセス
    • JPA 永続ユニット設定 (<jta-data-source> または <non-jta-data-source>いずれか)
    • Apache Camel や Spring Framework などの一般的なライブラリー
  • 最も重要なのは、javax.sql.ConnectionPoolDataSource は汎用 (データベース固有でない) 接続プール/データソースとデータベース固有のデータソースとの間の ブリッジ であることです。SPI インターフェースとして扱われる場合があります。通常、アプリケーションコードは、JNDI から取得され、アプリケーションサーバーによって実装される汎用 javax.sql.DataSource オブジェクトに対応します (おそらく commons-dbcp2 などのライブラリーを使用します)。一方、アプリケーションコードは javax.sql.ConnectionPoolDataSource と直接インターフェースしません。アプリケーションサーバーとデータベース固有のドライバーの間で使用されます。次のシーケンス図は、これを示しています。

    Application -> "App Server": javax.sql.DataSource.getConnection()
    "App Server" -[#0000FF]> "Database Driver": javax.sql.ConnectionPoolDataSource.getPooledConnection()
    "Database Driver" -> "App Server": javax.sql.PooledConnection
    "App Server" -> Application: javax.sql.PooledConnection.getConnection()
  • javax.sql.XADataSourcejavax.sql.XAConnection および javax.transaction.xa.XAResource を取得する方法です。javax.sql.ConnectionPoolDataSource と同じで、これはアプリケーションサーバーとデータベース固有のドライバーとの間で使用されます。以下は、JTA トランザクションマネージャーが含まれ、さまざまなアクターが含まれる若干修正された図になります。

    Application -> "App Server": UserTransaction.begin()
    "App Server" -> "Transaction Manager": TransactionManager.getTransaction()
    create Transaction
    "Transaction Manager" -> Transaction: new
    "App Server" <- Transaction: javax.transaction.Transaction
    
    Application -> "App Server": DataSource.getConnection()
    "App Server" -[#0000FF]> "Database Driver": javax.sql.XADataSource.getXAConnection()
    create XAConnection
    "Database Driver" -> XAConnection: new
    XAConnection -> "App Server": javax.sql.XAConnection
    
    "App Server" -> XAConnection: javax.sql.XAConnection.getXAResource()
    XAConnection -> "App Server": javax.transaction.xa.XAResource
    "App Server" -> Transaction: Transaction.enlistResource(XAResource)
    
    "App Server" -> Application: XAConnection.getConnection()

上記の 2 つの図に示すように、アプリケーションサーバーと対話できます。これは一般的なエンティティーで、 javax.sql.DataSource および javax.transaction.UserTransaction インスタンスを設定できます。このようなインスタンスには、JNDI によってアクセスできるか、CDI または他の依存メカニズムを使用したインジェクションによってアクセスすることができます。

重要

重要な点は、アプリケーションが XA トランザクションや接続プーリングを使用する場合でも、アプリケーションは他の 2 つの JDBC データソースインターフェースではなく、javax.sql.DataSource と対話することです。

6.2.1. データベース固有および汎用データソース

JDBC データソース実装は 2 つのカテゴリーに分類されます。

  • 以下のような、汎用 javax.sql.DataSource の実装:

  • javax.sql.DataSourcejavax.sql.XADataSource、および javax.sql.ConnectionPoolDataSource のデータベース固有の実装

汎用 javax.sql.DataSource 実装によって独自のデータベース固有の接続を作成できないことが紛らわしい可能性があります。汎用 データソースが java.sql.Driver.connect() または java.sql.DriverManager.getConnection() を使用できる場合でも、通常はデータベース固有の javax.sql.DataSource 実装でこの 汎用 データソースを設定する方が優れており、クリーンです。

汎用 データソースが JTA と対話する場合は、javax.sql.XADataSource のデータベース固有の実装で設定する 必要 があります。

画像を閉じるには、通常、汎用 データソースは、接続プーリングを実行するために javax.sql.ConnectionPoolDataSource のデータベース固有の実装を必要としません。既存のプールは通常、標準の JDBC インターフェース (javax.sql.ConnectionPoolDataSource および javax.sql.PooledConnection) なしでプーリングを処理し、代わりに独自のカスタム実装を使用します。

6.2.2. 一部の汎用データソース

よく知られた汎用データソースである Apache Commons DBCP(2) のサンプルについて考えてみましょう。

javax.sql.XADataSource 実装

DBCP2 には、javax.sql.XADataSource の実装は含まれていないことが予想されます。

javax.sql.ConnectionPoolDataSource 実装

DBCP2 には javax.sql.ConnectionPoolDataSource の実装 org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS が含まれています。これは java.sql.DriverManager.getConnection() を呼び出すことで javax.sql.PooledConnection オブジェクトを作成します。このプールは直接使用しないでください。以下のようなドライバーの アダプター として扱う必要があります。

  • 独自の javax.sql.ConnectionPoolDataSource 実装は提供しない。
  • 接続プールの JDBC 推奨事項 に従って使用する必要がある。

上記のシーケンス図に示すように、ドライバーは javax.sql.ConnectionPoolDataSource を直接提供するか、org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS アダプター の助けを借りて提供します。一方、DBCP2 は、以下のいずれかで アプリケーションサーバー コントラクトを以下のいずれかで実装します。

  • org.apache.commons.dbcp2.datasources.PerUserPoolDataSource
  • org.apache.commons.dbcp2.datasources.SharedPoolDataSource

これらのプールは、設定段階で javax.sql.ConnectionPoolDataSource のインスタンスを取ります。

これは DBCP2 の最も重要で興味深い部分です。

javax.sql.DataSource 実装

接続プール機能を実装するには、javax.sql.ConnectionPoolDataSourcejavax.sql.PooledConnection SPI を使用するために JDBC の 推奨事項 に従う必要はありません。

以下は DBCP2 の 通常 のデータソースの一覧です。

  • org.apache.commons.dbcp2.BasicDataSource
  • org.apache.commons.dbcp2.managed.BasicManagedDataSource
  • org.apache.commons.dbcp2.PoolingDataSource
  • org.apache.commons.dbcp2.managed.ManagedDataSource

以下の 2 つの があります。

basicpooling

この は、プーリング設定 の側面を決定します。

いずれの種類のデータソースも、java.sql.Connection オブジェクトの プーリング を実行します。唯一 の違いは、以下のことです。

  • basic データソースは、org.apache.commons.pool2.impl.GenericObjectPool の内部インスタンスの設定に使用される maxTotalminIdle などの Bean プロパティーを使用して設定されます。
  • プーリング データソースは、外部作成/設定された org.apache.commons.pool2.ObjectPool で設定されます。

managed非 managed

この は、接続作成 の側面と JTA の動作を決定します。

  • managed でない basic データソースは、内部で java.sql.Driver.connect() を使用して java.sql.Connection インスタンスを作成します。

    managed でない pooling データソースは、渡された org.apache.commons.pool2.ObjectPool オブジェクトを使用して java.sql.Connection インスタンスを作成します。

  • managed pooling データソースは、org.apache.commons.dbcp2.managed.ManagedConnection オブジェクト内で java.sql.Connection インスタンスをラップし、JTA コンテキストで必要になる場合に javax.transaction.Transaction.enlistResource() が呼び出されます。ただし、ラップされる実際の接続は、プールが設定された org.apache.commons.pool2.ObjectPool オブジェクトから取得されます。

    managed basic データソースは、専用の org.apache.commons.pool2.ObjectPool の設定を解放します。代わりに、既存の実際のデータベース固有の javax.sql.XADataSource オブジェクトを設定すれば十分です。Bean プロパティーは、org.apache.commons.pool2.impl.GenericObjectPool の内部インスタンスを作成するために使用され、managed pooling データソース (org.apache.commons.dbcp2.managed.ManagedDataSource) の内部インスタンスに渡されます。

注記

DBCP2 では XA トランザクションリカバリー のみが不可能です。DBCP2 はアクティブな JTA トランザクションに XAResources を正しく登録しますが、リカバリーは実行されません。これは個別に行う必要があり、通常、設定は選択したトランザクションマネージャーの実装に固有です (Narayana など)。

6.2.3. 使用するパターン

推奨されるパターンは以下のとおりです。

  • 接続/XA 接続を作成できるデータベース固有の設定 (URL、クレデンシャルなど) で、データベース固有javax.sql.DataSource または javax.sql.XADataSource インスタンスを作成または取得します。
  • データベース固有でない設定 (接続プーリング、トランザクションマネージャーなど) でデータベース固有でない javax.sql.DataSource インスタンス (上記のデータベース固有のデータソースで内部的設定された) を作成および取得します。
  • javax.sql.DataSource を使用して java.sql.Connection のインスタンスを取得し、JDBC オペレーションを実行します。

以下は 標準的 な例になります。

// Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
PGXADataSource postgresql = new org.postgresql.xa.PGXADataSource();
// Database-specific configuration
postgresql.setUrl("jdbc:postgresql://localhost:5432/reportdb");
postgresql.setUser("fuse");
postgresql.setPassword("fuse");
postgresql.setCurrentSchema("report");
postgresql.setConnectTimeout(5);
// ...

// Non database-specific, pooling, enlisting javax.sql.DataSource
BasicManagedDataSource pool = new org.apache.commons.dbcp2.managed.BasicManagedDataSource();
// Delegate to database-specific XADatasource
pool.setXaDataSourceInstance(postgresql);
// Delegate to JTA transaction manager
pool.setTransactionManager(transactionManager);
// Non database-specific configuration
pool.setMinIdle(3);
pool.setMaxTotal(10);
pool.setValidationQuery("select schema_name, schema_owner from information_schema.schemata");
// ...

// JDBC code:
javax.sql.DataSource applicationDataSource = pool;

try (Connection c = applicationDataSource.getConnection()) {
    try (Statement st = c.createStatement()) {
        try (ResultSet rs = st.executeQuery("select ...")) {
            // ....

Fuse 環境では多くの設定オプションがあり、DBCP2 を使用する必要はありません。

6.3. JDBC データソースの設定

OSGi トランザクションアーキテクチャー」で説明したように、一部のサービスを OSGi サービスレジストリーに登録する必要があります。javax.transaction.UserTransaction インターフェースなどを使用してトランザクションマネージャーインスタンスを 検索 (ルックアップ) できるのと同様に、javax.sql.DataSource インターフェースを使用して JDBC データソースで同じことができます。要件は以下のとおりです。

  • ターゲットデータベースと通信できるデータベース固有のデータソース
  • プーリングと場合によってはトランザクション管理 (XA) を設定できる汎用データソース

Fuse などの OSGi 環境では、データソースが OSGi サービスとして登録されていれば、アプリケーションからアクセスできます。基本的には、以下のように行われます。

org.osgi.framework.BundleContext.registerService(javax.sql.DataSource.class,
                                                 dataSourceObject,
                                                 properties);
org.osgi.framework.BundleContext.registerService(javax.sql.XADataSource.class,
                                                 xaDataSourceObject,
                                                 properties);

このようなサービスを登録する方法は 2 つあります。

  • jdbc:ds-create Karaf コンソールコマンドを使用してデータソースを公開します。これは 設定方法 です。
  • Blueprint、OSGi Declarative Services (SCR)、または BundleContext.registerService() API 呼び出しなどの方法を使用してデータソースを公開します。この方法には、コードやメタデータが含まれる専用の OSGi バンドルが必要です。これは、デプロイメント方法です。

6.4. OSGi JDBC サービスの使用

OSGi Enterprise R6 仕様の第 125 章には、org.osgi.service.jdbc パッケージの単一のインターフェースが定義されています。以下は、OSGi がデータソースをどのように処理するかを示しています。

public interface DataSourceFactory {

    java.sql.Driver createDriver(Properties props);

    javax.sql.DataSource createDataSource(Properties props);

    javax.sql.ConnectionPoolDataSource createConnectionPoolDataSource(Properties props);

    javax.sql.XADataSource createXADataSource(Properties props);
}

前述のように、java.sql.Driver からプレーン java.sql.Connection 接続を直接取得できる可能性があります。

汎用 org.osgi.service.jdbc.DataSourceFactory

org.osgi.service.jdbc.DataSourceFactory の最もシンプルな実装は、mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0 バンドルによって提供される org.ops4j.pax.jdbc.impl.DriverDataSourceFactory です。これはすべて、標準の Java™ ServiceLoader ユーティリティーの /META-INF/services/java.sql.Driver 記述子が含まれる可能性のあるバンドルを追跡するだけです。標準の JDBC ドライバーをインストールする場合、pax-jdbc バンドルは java.sql.Driver.connect() 呼び出しを使用して接続を取得するために (直接ではなく) 使用できる DataSourceFactory を登録します。

karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
Bundle ID: 223
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
Bundle ID: 224
karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 225
karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 226

karaf@root()> bundle:services -p org.postgresql.jdbc42

PostgreSQL JDBC Driver JDBC42 (225) provides:
---------------------------------------------
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = org.postgresql.Driver
osgi.jdbc.driver.name = PostgreSQL JDBC Driver
osgi.jdbc.driver.version = 42.2.5
service.bundleid = 225
service.id = 242
service.scope = singleton

karaf@root()> bundle:services -p com.mysql.jdbc

Oracle Corporation's JDBC Driver for MySQL (226) provides:
----------------------------------------------------------
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = com.mysql.jdbc.Driver
osgi.jdbc.driver.name = com.mysql.jdbc
osgi.jdbc.driver.version = 5.1.34
service.bundleid = 226
service.id = 243
service.scope = singleton
-----
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = com.mysql.fabric.jdbc.FabricMySQLDriver
osgi.jdbc.driver.name = com.mysql.jdbc
osgi.jdbc.driver.version = 5.1.34
service.bundleid = 226
service.id = 244
service.scope = singleton

karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = org.postgresql.Driver
 osgi.jdbc.driver.name = PostgreSQL JDBC Driver
 osgi.jdbc.driver.version = 42.2.5
 service.bundleid = 225
 service.id = 242
 service.scope = singleton
Provided by :
 PostgreSQL JDBC Driver JDBC42 (225)

[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = com.mysql.jdbc.Driver
 osgi.jdbc.driver.name = com.mysql.jdbc
 osgi.jdbc.driver.version = 5.1.34
 service.bundleid = 226
 service.id = 243
 service.scope = singleton
Provided by :
 Oracle Corporation's JDBC Driver for MySQL (226)

[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = com.mysql.fabric.jdbc.FabricMySQLDriver
 osgi.jdbc.driver.name = com.mysql.jdbc
 osgi.jdbc.driver.version = 5.1.34
 service.bundleid = 226
 service.id = 244
 service.scope = singleton
Provided by :
 Oracle Corporation's JDBC Driver for MySQL (226)

上記のコマンドは、登録までの手順の 1 つで、javax.sql.DataSource サービスはまだ登録されません。上記の仲介 org.osgi.service.jdbc.DataSourceFactory サービスを使用して、以下を取得できます。

  • java.sql.Driver
  • javax.sql.DataSource (プロパティー urluser、および passwordcreateDataSource() メソッドに渡して取得)

データベース固有でない pax-jdbc バンドルによって作成された汎用 org.osgi.service.jdbc.DataSourceFactory から、javax.sql.ConnectionPoolDataSource または javax.sql.XADataSource を取得できません。

注記

mvn:org.postgresql/postgresql/42.2.5バンドルは OSGi JDBC 仕様を正しく実装し、XA および ConnectionPool データソースを作成するメソッドを含む、実装されているすべてのメソッドで org.osgi.service.jdbc.DataSourceFactory インスタンスを登録します。

専用のデータベース固有の org.osgi.service.jdbc.DataSourceFactory 実装

以下のような追加のバンドルがあります。

  • mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
  • mvn:org.ops4j.pax.jdbc/pax-jdbc-db2/1.3.0
  • …​

これらのバンドルは、javax.sql.ConnectionPoolDataSourcejavax.sql.XADataSource など、あらゆる種類の ファクトリー を返すことができるデータベース固有の org.osgi.service.jdbc.DataSourceFactory サービスを登録します。以下に例を示します。

karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
Bundle ID: 227

karaf@root()> bundle:services -p org.ops4j.pax.jdbc.mysql

OPS4J Pax JDBC MySQL Driver Adapter (227) provides:
---------------------------------------------------
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = com.mysql.jdbc.Driver
osgi.jdbc.driver.name = mysql
service.bundleid = 227
service.id = 245
service.scope = singleton

karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
...
[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = com.mysql.jdbc.Driver
 osgi.jdbc.driver.name = mysql
 service.bundleid = 227
 service.id = 245
 service.scope = singleton
Provided by :
 OPS4J Pax JDBC MySQL Driver Adapter (227)

6.4.1. PAX-JDBC 設定サービス

pax-jdbc (または pax-jdbc-mysqlpax-jdbc-oracleorg.osgi.service.jdbc.DataSourceFactory )バンドルでは、指定のデータベースのデータソースを取得するために使用可能な 「データベース固有および汎用データソース」サービスを登録することができます(「」を参照)。ただし、実際のデータソースはまだありません。

mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0 バンドルは、以下の 2 つを行うマネージドサービスファクトリーを提供します。

  • org.osgi.service.jdbc.DataSourceFactory OSGi サービスを追跡して、そのメソッドを呼び出します。

    public DataSource createDataSource(Properties props);
    public XADataSource createXADataSource(Properties props);
    public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props);
  • org.ops4j.datasource ファクトリー PID を追跡、上記のメソッドに必要なプロパティーを収集します。たとえば、${karaf.etc}/org.ops4j.datasource-mysql.cfgファイルの作成など、Configuration Admin サービスで使用可能な任意の方法を使用して、ファクトリー設定を作成する場合は、最終的な手順を実行して、実際のデータベース固有のデータソースを公開できます。

ここでは、Fuse の新規インストールを開始するための詳細で標準的なステップバイステップガイドを示します。

注記

必要なバンドルを正確に表示するために、機能の代わりにバンドルを明示的にインストールします。便宜上、PAX JDBC プロジェクトは、複数のデータベース製品および設定方法の機能を提供します。

  1. /META-INF/services/java.sql.Driver で JDBC ドライバーをインストールします。

    karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
    Bundle ID: 223
  2. 中間 org.osgi.service.jdbc.DataSourceFactory を登録する OSGi JDBC サービスバンドルと pax-jdbc-mysql バンドルをインストールします。

    karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
    Bundle ID: 224
    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
    Bundle ID: 225
    
    karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
    [org.osgi.service.jdbc.DataSourceFactory]
    -----------------------------------------
     osgi.jdbc.driver.class = com.mysql.jdbc.Driver
     osgi.jdbc.driver.name = mysql
     service.bundleid = 225
     service.id = 242
     service.scope = singleton
    Provided by :
     OPS4J Pax JDBC MySQL Driver Adapter (225)
  3. org.osgi.service.jdbc.DataSourceFactory サービスおよびorg.ops4j.datasource ファクトリー PID を追跡する pax-jdbc バンドルと pax-jdbc-config バンドルをインストールします。

    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
    Bundle ID: 226
    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-pool-common/1.3.0
    Bundle ID: 227
    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
    Bundle ID: 228
    
    karaf@root()> bundle:services -p org.ops4j.pax.jdbc.config
    
    OPS4J Pax JDBC Config (228) provides:
    -------------------------------------
    objectClass = [org.osgi.service.cm.ManagedServiceFactory]
    service.bundleid = 228
    service.id = 245
    service.pid = org.ops4j.datasource
    service.scope = singleton
  4. ファクトリー設定 を作成します (MySQL サーバーが稼働していることを前提とします)。

    karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
    karaf@root()> config:property-set osgi.jdbc.driver.name mysql
    karaf@root()> config:property-set dataSourceName mysqlds
    karaf@root()> config:property-set url jdbc:mysql://localhost:3306/reportdb
    karaf@root()> config:property-set user fuse
    karaf@root()> config:property-set password fuse
    karaf@root()> config:update
    
    karaf@root()> config:list '(service.factoryPid=org.ops4j.datasource)'
    ----------------------------------------------------------------
    Pid:            org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313
    FactoryPid:     org.ops4j.datasource
    BundleLocation: ?
    Properties:
       dataSourceName = mysqlds
       felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
       osgi.jdbc.driver.name = mysql
       password = fuse
       service.factoryPid = org.ops4j.datasource
       service.pid = org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313
       url = jdbc:mysql://localhost:3306/reportdb
       user = fuse
  5. pax-jdbc-configjavax.sql.DataSource サービスに設定を処理したかどうかを確認します。

    karaf@root()> service:list javax.sql.DataSource
    [javax.sql.DataSource]
    ----------------------
     dataSourceName = mysqlds
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
     osgi.jdbc.driver.name = mysql
     osgi.jndi.service.name = mysqlds
     password = fuse
     pax.jdbc.managed = true
     service.bundleid = 228
     service.factoryPid = org.ops4j.datasource
     service.id = 246
     service.pid = org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313
     service.scope = singleton
     url = jdbc:mysql://localhost:3306/reportdb
     user = fuse
    Provided by :
     OPS4J Pax JDBC Config (228)

これで、実際のデータベース固有の (この時点ではプーリングなし) データソースができました。必要な場所に既に注入できます。たとえば、Karaf コマンドを使用してデータベースをクエリーできます。

karaf@root()> feature:install -v jdbc
Adding features: jdbc/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1]
...
karaf@root()> jdbc:ds-list
Mon May 14 08:46:22 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Name    │ Product │ Version │ URL                                  │ Status
────────┼─────────┼─────────┼──────────────────────────────────────┼───────
mysqlds │ MySQL   │ 5.7.21  │ jdbc:mysql://localhost:3306/reportdb │ OK

karaf@root()> jdbc:query mysqlds 'select * from incident'
Mon May 14 08:46:46 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
date                  │ summary    │ name   │ details                       │ id │ email
──────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00.0 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00.0 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00.0 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00.0 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

上記の例では、MySQL の警告が表示されます。これは問題ではありません。OSGi JDBC 固有のプロパティーだけでなく、任意のプロパティが提供される可能性があります。

karaf@root()> config:property-set --pid org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313 useSSL false

karaf@root()> jdbc:ds-list
Name    │ Product │ Version │ URL                                  │ Status
────────┼─────────┼─────────┼──────────────────────────────────────┼───────
mysqlds │ MySQL   │ 5.7.21  │ jdbc:mysql://localhost:3306/reportdb │ OK

6.4.2. 処理されたプロパティーの概要

管理 ファクトリー PID の設定からのプロパティーは、関連する org.osgi.service.jdbc.DataSourceFactory 実装に渡されます。

汎用

org.ops4j.pax.jdbc.impl.DriverDataSourceFactory プロパティー:

  • url
  • user
  • password

DB2

org.ops4j.pax.jdbc.db2.impl.DB2DataSourceFactory プロパティーには、これらの実装クラスのすべての Bean プロパティーが含まれます。

  • com.ibm.db2.jcc.DB2SimpleDataSource
  • com.ibm.db2.jcc.DB2ConnectionPoolDataSource
  • com.ibm.db2.jcc.DB2XADataSource

PostgreSQL

Nnative org.postgresql.osgi.PGDataSourceFactory プロパティーには、org.postgresql.PGProperty に指定されたすべてのプロパティーが含まれます。

HSQLDB

org.ops4j.pax.jdbc.hsqldb.impl.HsqldbDataSourceFactory プロパティー:

  • url
  • user
  • password
  • databaseName
  • 以下のすべての Bean プロパティー

    • org.hsqldb.jdbc.JDBCDataSource
    • org.hsqldb.jdbc.pool.JDBCPooledDataSource
    • org.hsqldb.jdbc.pool.JDBCXADataSource

SQL Server および Sybase

org.ops4j.pax.jdbc.jtds.impl.JTDSDataSourceFactory プロパティーには、net.sourceforge.jtds.jdbcx.JtdsDataSource の Bean プロパティーがすべて含まれています。

SQL Server

org.ops4j.pax.jdbc.mssql.impl.MSSQLDataSourceFactory プロパティー:

  • url
  • user
  • password
  • databaseName
  • serverName
  • portNumber
  • 以下のすべての Bean プロパティー

    • com.microsoft.sqlserver.jdbc.SQLServerDataSource
    • com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
    • com.microsoft.sqlserver.jdbc.SQLServerXADataSource

MySQL

org.ops4j.pax.jdbc.mysql.impl.MysqlDataSourceFactory プロパティー:

  • url
  • user
  • password
  • databaseName
  • serverName
  • portNumber
  • 以下のすべての Bean プロパティー

    • com.mysql.jdbc.jdbc2.optional.MysqlDataSource
    • com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
    • com.mysql.jdbc.jdbc2.optional.MysqlXADataSource

Oracle

org.ops4j.pax.jdbc.oracle.impl.OracleDataSourceFactory プロパティー:

  • url
  • databaseName
  • serverName
  • user
  • password
  • 以下のすべての Bean プロパティー

    • oracle.jdbc.pool.OracleDataSource
    • oracle.jdbc.pool.OracleConnectionPoolDataSource
    • oracle.jdbc.xa.client.OracleXADataSource

SQLite

org.ops4j.pax.jdbc.sqlite.impl.SqliteDataSourceFactory プロパティー:

  • url
  • databaseName
  • org.sqlite.SQLiteDataSource のすべての Bean プロパティー

6.4.3. pax-jdb-config バンドルによるプロパティーの処理方法

pax-jdbc-config バンドルは、jdbc. で始まるプロパティーを処理します。これらのプロパティーはすべてこの接頭辞が削除され、残りの名前は引き継がれます。

以下も Fuse のフレッシュインストールで始まる例になります。

karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 223
karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
Bundle ID: 224
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
Bundle ID: 225
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
Bundle ID: 226
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-pool-common/1.3.0
Bundle ID: 227
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
Bundle ID: 228

karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
karaf@root()> config:property-set osgi.jdbc.driver.name mysql
karaf@root()> config:property-set dataSourceName mysqlds
karaf@root()> config:property-set dataSourceType DataSource
karaf@root()> config:property-set jdbc.url jdbc:mysql://localhost:3306/reportdb
karaf@root()> config:property-set jdbc.user fuse
karaf@root()> config:property-set jdbc.password fuse
karaf@root()> config:property-set jdbc.useSSL false
karaf@root()> config:update

karaf@root()> config:list '(service.factoryPid=org.ops4j.datasource)'
----------------------------------------------------------------
Pid:            org.ops4j.datasource.7c3ee718-7309-46a0-ae3a-64b38b17a0a3
FactoryPid:     org.ops4j.datasource
BundleLocation: ?
Properties:
   dataSourceName = mysqlds
   dataSourceType = DataSource
   felix.fileinstall.filename = file:/data/servers/7.9.0.fuse-790071-redhat-00001/etc/org.ops4j.datasource-mysql.cfg
   jdbc.password = fuse
   jdbc.url = jdbc:mysql://localhost:3306/reportdb
   jdbc.useSSL = false
   jdbc.user = fuse
   osgi.jdbc.driver.name = mysql
   service.factoryPid = org.ops4j.datasource
   service.pid = org.ops4j.datasource.7c3ee718-7309-46a0-ae3a-64b38b17a0a3

karaf@root()> service:list javax.sql.DataSource
[javax.sql.DataSource]
----------------------
 dataSourceName = mysqlds
 dataSourceType = DataSource
 felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
 jdbc.password = fuse
 jdbc.url = jdbc:mysql://localhost:3306/reportdb
 jdbc.user = fuse
 jdbc.useSSL = false
 osgi.jdbc.driver.name = mysql
 osgi.jndi.service.name = mysqlds
 pax.jdbc.managed = true
 service.bundleid = 228
 service.factoryPid = org.ops4j.datasource
 service.id = 246
 service.pid = org.ops4j.datasource.7c3ee718-7309-46a0-ae3a-64b38b17a0a3
 service.scope = singleton
Provided by :
 OPS4J Pax JDBC Config (228)

pax-jdbc-config バンドルには以下のプロパティーが必要です

  • osgi.jdbc.driver.name
  • dataSourceName
  • dataSourceType

(関連する org.osgi.service.jdbc.DataSourceFactory メソッドを見つけ、呼び出すためには)。jdbc. がプレフィックスとして付けられたプロパティーは (プレフィックスの削除後) org.osgi.service.jdbc.DataSourceFactory.createDataSource(properties) のように渡されます。ただし、これらのプロパティーは、javax.sql.DataSource OSGi サービスなどのプロパティーとして、プレフィックスを削除せずに追加されます。

6.5. JDBC コンソールコマンドの使用

Fuse は、jdbc:* スコープのシェルコマンドが含まれる jdbc 機能を提供します。前述の例は、jdbc:query の使用を示しています。Configuration Admin 設定を作成する必要性を隠すコマンドもあります。

Fuse の新しいインスタンスから始めて、データベース固有のデータソースを汎用 DataSourceFactory サービスに登録するには、次のようにします。

karaf@root()> feature:install jdbc

karaf@root()> jdbc:ds-factories
Name │ Class │ Version
─────┼───────┼────────

karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 228

karaf@root()> jdbc:ds-factories
Name           │ Class                                   │ Version
───────────────┼─────────────────────────────────────────┼────────
com.mysql.jdbc │ com.mysql.jdbc.Driver                   │ 5.1.34
com.mysql.jdbc │ com.mysql.fabric.jdbc.FabricMySQLDriver │ 5.1.34

MySQL 固有の DataSourceFactory サービスを登録する例を以下に示します。

karaf@root()> feature:repo-add mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl
Adding feature url mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl

karaf@root()> feature:install pax-jdbc-mysql

karaf@root()> la -l|grep mysql

232 │ Active   │  80 │ 5.1.34                   │ mvn:mysql/mysql-connector-java/5.1.34

233 │ Active   │  80 │ 1.3.0                    │ mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0

karaf@root()> jdbc:ds-factories
Name           │ Class                                   │ Version
───────────────┼─────────────────────────────────────────┼────────
com.mysql.jdbc │ com.mysql.jdbc.Driver                   │ 5.1.34
mysql          │ com.mysql.jdbc.Driver                   │
com.mysql.jdbc │ com.mysql.fabric.jdbc.FabricMySQLDriver │ 5.1.34

上記の表は混乱を招くかもしれませんが、前述のように、pax-jdbc-database バンドルの 1 つのみが、単に java.sql.Driver.connect() へ委任しない 標準/XA/接続プールデータソースを作成できる org.osgi.service.jdbc.DataSourceFactory インスタンスを登録する可能性があります。

以下の例では、MySQL データソースを作成し、確認します。

karaf@root()> jdbc:ds-create -dt DataSource -dn mysql -url 'jdbc:mysql://localhost:3306/reportdb?useSSL=false' -u fuse -p fuse mysqlds

karaf@root()> jdbc:ds-list
Name    │ Product │ Version │ URL                                               │ Status
────────┼─────────┼─────────┼───────────────────────────────────────────────────┼───────
mysqlds │ MySQL   │ 5.7.21  │ jdbc:mysql://localhost:3306/reportdb?useSSL=false │ OK

karaf@root()> jdbc:query mysqlds 'select * from incident'
date                  │ summary    │ name   │ details                       │ id │ email
──────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00.0 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00.0 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00.0 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00.0 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

karaf@root()> config:list '(service.factoryPid=org.ops4j.datasource)'
----------------------------------------------------------------
Pid:            org.ops4j.datasource.55b18993-de4e-4e0b-abb2-a4c13da7f78b
FactoryPid:     org.ops4j.datasource
BundleLocation: mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
Properties:
   dataSourceName = mysqlds
   dataSourceType = DataSource
   osgi.jdbc.driver.name = mysql
   password = fuse
   service.factoryPid = org.ops4j.datasource
   service.pid = org.ops4j.datasource.55b18993-de4e-4e0b-abb2-a4c13da7f78b
   url = jdbc:mysql://localhost:3306/reportdb?useSSL=false
   user = fuse

ご覧のとおり、org.ops4j.datasource ファクトリー PID が作成されます。ただし、config:update では可能ですが、これは自動的に ${karaf.etc} に保存されません。

6.6. 暗号化された設定値の使用

pax-jdbc-config 機能は、値が暗号化された Configuration Admin 設定を処理できます。一般的な解決策は、Blueprint でも使用される Jasypt 暗号化サービスを使用することです。

alias サービスプロパティーで OSGi に登録されている org.jasypt.encryption.StringEncryptor サービスプロパティーがある場合は、データソースファクトリー PID で参照し、暗号化されたパスワードを使用することができます。以下に例を示します。

felix.fileinstall.filename = */etc/org.ops4j.datasource-mysql.cfg
dataSourceName = mysqlds
dataSourceType = DataSource
decryptor = my-jasypt-decryptor
osgi.jdbc.driver.name = mysql
url = jdbc:mysql://localhost:3306/reportdb?useSSL=false
user = fuse
password = ENC(<encrypted-password>)

復号化サービスの検索に使用するサービスフィルターは (&(objectClass=org.jasypt.encryption.StringEncryptor)(alias=<alias>)) です。<alias> は、データソース設定 ファクトリー PID からの decryptor プロパティーの値になります。

6.7. JDBC 接続プールの使用

このセクションでは、JDBC 接続プールの使用方法について説明し、次の接続プールモジュールの使用方法を説明します。

重要

この章では、データソース管理の内部に関する全体的な情報を提供します。DBCP2 接続プール機能に関する情報が提供されますが、この接続プールは適切な JTA エンリストメント機能を提供しますが、XA リカバリー 提供されないことに注意してください。

XA リカバリーを確実に実行するには、pax-jdbc-pool-transx または pax-jdbc-pool-narayana 接続プールモジュールを使用します。

6.7.1. JDBC 接続プールの使用

前述の例では、データベース固有のデータソースファクトリーを登録する方法を説明しました。データソース 自体は接続のファクトリーであるため、org.osgi.service.jdbc.DataSourceFactory は 3 種類のデータソースを生成できるメタファクトリー として扱われる可能性があります (さらに java.sql.Driver)。

  • javax.sql.DataSource
  • javax.sql.ConnectionPoolDataSource
  • javax.sql.XADataSource

たとえば、pax-jdbc-mysql は以下を生成する org.ops4j.pax.jdbc.mysql.impl.MysqlDataSourceFactory を登録します。

  • javax.sql.DataSourcecom.mysql.jdbc.jdbc2.optional.MysqlDataSource
  • javax.sql.ConnectionPoolDataSourcecom.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
  • javax.sql.XADataSourcecom.mysql.jdbc.jdbc2.optional.MysqlXADataSource
  • java.sql.Drivercom.mysql.jdbc.Driver

PostgreSQL ドライバー自体は OSGi JDBC サービスを実装し、以下を生成します。

  • javax.sql.DataSourceorg.postgresql.jdbc2.optional.PoolingDataSource (プール関連のプロパティーが指定されている場合) または org.postgresql.jdbc2.optional.SimpleDataSource
  • javax.sql.ConnectionPoolDataSourceorg.postgresql.jdbc2.optional.ConnectionPool
  • javax.sql.XADataSourceorg.postgresql.xa.PGXADataSource
  • java.sql.Driverorg.postgresql.Driver

canonical DataSource example に示されているように、プールリング汎用 データソース (JTA 環境で機能する場合) には、実際に接続 (XA) 接続の取得に データベース固有 のデータソースが必要です。

すでに後者を持っており、実際の汎用的で信頼性の高い接続プールが必要です。

canonical DataSource example は、データベース固有のデータソースで汎用プールを設定する方法を示しています。pax-jdbc-pool-* バンドルは、上記の org.osgi.service.jdbc.DataSourceFactory サービスと共にスムーズに機能します。

OSGI Enterprise R6 JDBC 仕様が org.osgi.service.jdbc.DataSourceFactory 標準インターフェースを提供するように pax-jdbc-pool-commonプロプライエタリーorg.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory インターフェースを提供します。

public interface PooledDataSourceFactory {

    javax.sql.DataSource create(org.osgi.service.jdbc.DataSourceFactory dsf, Properties config)

}

このインターフェースは、前述の重要な記述にあるものと完全に準拠しています。それは次のとおりです。

重要

アプリケーションが XA トランザクションや接続プーリングを使用する場合でも、アプリケーションは他の 2 つの JDBC データソースインターフェースではなく、javax.sql.DataSource と対話します。

このインターフェースは、単にデータベース固有の非プールデータからプーリングデータソースを作成します。また、これはデータベース固有のデータソースのファクトリーを、プーリングデータソースのファクトリーにするデータソースファクトリー (メタファクトリー) です。

注記

javax.sql.DataSource オブジェクトのプーリングをすでに返す org.osgi.service.jdbc.DataSourceFactory サービスを使用して、アプリケーションが javax.sql.DataSource オブジェクトのプーリングを設定するのを妨げるものはありません。

次の表に、プールされたデータソースファクトリーを登録するバンドルを示します。表では、o.o.p.j.p のインスタンスは org.ops4j.pax.jdbc.pool を表します。

バンドルPooledDataSourceFactoryプールキー

pax-jdbc-pool-narayana

o.o.p.j.p.narayana.impl.Dbcp(XA)PooledDataSourceFactory

narayana

pax-jdbc-pool-dbcp2

o.o.p.j.p.dbcp2.impl.Dbcp(XA)PooledDataSourceFactory

dbcp2

pax-jdbc-pool-transx

o.o.p.j.p.transx.impl.Transx(Xa)PooledDataSourceFactory

transx

上記のバンドルは、データソースファクトリーのみをインストールし、データソース自体をインストールしません。アプリケーションには、javax.sql.DataSource create(org.osgi.service.jdbc.DataSourceFactory dsf, Properties config) メソッドを呼び出すものが必要です。

6.7.2. dbcp2 接続プールモジュールの使用

汎用データソースに関するセクションでは、Apache Commons DBCP モジュール使用および設定方法の例を示します。ここでは、Fuse の OSGi 環境でこれを行う方法を説明します。

「PAX-JDBC 設定サービス」 バンドルについて考えてみましょう。以下を追跡します。

  • org.osgi.service.jdbc.DataSourceFactory サービス
  • org.ops4j.datasource ファクトリー PID

また、バンドルは、pax-jdbc-pool-* バンドルのいずれかによって登録された org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory のインスタンスも追跡します。

ファクトリー設定pool プロパティーが含まれる場合、pax-jdbc-config バンドルによって登録される最終データソースはデータベース固有のデータソースになりますが、pool=dbcp2 の場合は以下のいずれかの内部でラッピングされます。

  • org.apache.commons.dbcp2.PoolingDataSource
  • org.apache.commons.dbcp2.managed.ManagedDataSource

これは、汎用データソースの例 と一致します。pool プロパティーと、非 xa または xa データソースを選択するブール値 xa プロパティーの他に、org.ops4j.datasource ファクトリー PIDプレフィックス が付いたプロパティーが含まれることがあります。

  • pool.*
  • factory.*

各プロパティーが使用される場所は、使用される pax-jdbc-pool-* バンドルによって異なります。DBCP2 の場合、これは以下になります。

  • pool.*: org.apache.commons.pool2.impl.GenericObjectPoolConfig の Bean プロパティー (xa と 非-xa シナリオの両方)
  • factory.*:org.apache.commons.dbcp2.managed.PoolableManagedConnectionFactory (xa) またはorg.apache.commons.dbcp2.PoolableConnectionFactory (非 xa) の Bean プロパティー

6.7.2.1. BasicDataSource の設定プロパティー

次の表に、BasicDataSource の一般的な設定プロパティーを示します。

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

username

 

接続を確立するために JDBC ドライバーに渡される接続ユーザー名。

password

 

接続を確立するために JDBC ドライバーに渡される接続パスワード。

url

 

接続を確立するために JDBC ドライバーに渡される接続 URL。

driverClassName

 

使用する JDBC ドライバーの完全修飾クラス名。

initialSize

0

プールの開始時に作成される最初の接続数。

maxTotal

8

このプールから同時に割り当てることができるアクティブな接続の最大数。制限がない場合は負の値です。

maxIdle

8

余分な接続が解放されなくてもプール内でアイドル状態を維持できる接続の最大数。制限がない場合は負の値です。

minIdle

0

追加の作成なしにプールでアイドル状態を維持できる最小接続数。ゼロの場合は何も作成されません。

maxWaitMillis

無期限

例外が発生する前にコネクションが返されるまで待機する (利用可能な接続がない場合) の最大待機期間 (ミリ秒単位)。-1 は無期限に待機します。

validationQuery

 

呼び出し元に返す前に、このプールから接続を検証するのに使用される SQL クエリー。指定された場合、このクエリーは最低でも 1 行返す SQL SELECT ステートメントである必要があります。指定されていない場合、isValid () メソッドを呼び出すことで接続が検証されます。

validationQueryTimeout

タイムアウトなし

接続検証クエリーが失敗するまでのタイムアウト (秒単位) 。正の値に設定すると、検証クエリーの実行に使用する Statement の setQueryTimeout メソッド経由で、この値はドライバーに渡されます。

testOnCreate

false

オブジェクトの作成後にオブジェクトを検証するかどうかの表示。オブジェクトの検証に失敗すると、オブジェクトの作成をトリガーした Borrow に失敗します。

testOnBorrow

true

プールから借りる前にオブジェクトが検証されるかどうかを示します。オブジェクトの検証に失敗すると、これはプールからドロップされ、別のオブジェクトを借りようとします。

testOnReturn

false

プールに戻される前にオブジェクトが検証されるかどうかを示します。

testWhileIdle

false

オブジェクトがアイドルオブジェクトエビクター (存在する場合) によって検証されるかどうかを示します。オブジェクトの検証に失敗すると、これはプールから破棄されます。

timeBetweenEvictionRunsMillis

-1

アイドルオブジェクトエビクタースレッドの実行間でスリープ状態にする時間 (ミリ秒単位) 。正の値でない場合、アイドルオブジェクトエビクタースレッドは実行されません。

numTestsPerEvictionRun

3

アイドルオブジェクトエビクタースレッドの各実行中に検査するオブジェクトの数 (ある場合) 。

minEvictableIdleTimeMillis

1000 * 60 * 30

アイドルオブジェクトエビクターによってエビクションの対象となる前に、オブジェクトがプールでアイドル状態でいられる最小時間 (ある場合) 。

6.7.2.2. DBCP2 プールの設定方法の例

以下は、jdbc. がプレフィックスとして付けられたプロパティーと便利な構文を使用する DBCP2 プールの設定 (org.ops4j.datasource-mysqlファクトリー PID) の現実的な例です (useSSL=false を除く)。

# Configuration for pax-jdbc-config to choose and configure specific org.osgi.service.jdbc.DataSourceFactory
dataSourceName = mysqlds
dataSourceType = DataSource
osgi.jdbc.driver.name = mysql
jdbc.url = jdbc:mysql://localhost:3306/reportdb
jdbc.user = fuse
jdbc.password = fuse
jdbc.useSSL = false

# Hints for pax-jdbc-config to use org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory
pool = dbcp2
xa = false

# dbcp2 specific configuration of org.apache.commons.pool2.impl.GenericObjectPoolConfig
pool.minIdle = 10
pool.maxTotal = 100
pool.initialSize = 8
pool.blockWhenExhausted = true
pool.maxWaitMillis = 2000
pool.testOnBorrow = true
pool.testWhileIdle = false
pool.timeBetweenEvictionRunsMillis = 120000
pool.evictionPolicyClassName = org.apache.commons.pool2.impl.DefaultEvictionPolicy

# dbcp2 specific configuration of org.apache.commons.dbcp2.PoolableConnectionFactory
factory.maxConnLifetimeMillis = 30000
factory.validationQuery  = select schema_name from information_schema.schemata
factory.validationQueryTimeout = 2

上記の設定では、pool および xa キーは、登録した org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory サービスの 1 つを選択するためのヒント (サービスフィルタープロパティー) です。DBCP2 の場合、以下になります。

karaf@root()> feature:install pax-jdbc-pool-dbcp2

karaf@root()> bundle:services -p org.ops4j.pax.jdbc.pool.dbcp2

OPS4J Pax JDBC Pooling DBCP2 (230) provides:
--------------------------------------------
objectClass = [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
pool = dbcp2
service.bundleid = 230
service.id = 337
service.scope = singleton
xa = false
-----
objectClass = [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
pool = dbcp2
service.bundleid = 230
service.id = 338
service.scope = singleton
xa = true

完全性については、ここでは 直前の例に追加された接続プール設定の完全な例です。ここでも、新しい Fuse インストールを開始することを前提としています。

  1. JDBC ドライバーをインストールします。

    karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
    Bundle ID: 223
  2. jdbcpax-jdbc-mysql、および pax-jdbc-pool-dbcp2 機能をインストールします。

    karaf@root()> feature:repo-add mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl
    Adding feature url mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl
    
    karaf@root()> feature:install jdbc pax-jdbc-mysql pax-jdbc-pool-dbcp2
    
    karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
    ...
    [org.osgi.service.jdbc.DataSourceFactory]
    -----------------------------------------
     osgi.jdbc.driver.class = com.mysql.jdbc.Driver
     osgi.jdbc.driver.name = mysql
     service.bundleid = 232
     service.id = 328
     service.scope = singleton
    Provided by :
     OPS4J Pax JDBC MySQL Driver Adapter (232)
    
    karaf@root()> service:list org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory
    [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
    --------------------------------------------------------
     pool = dbcp2
     service.bundleid = 233
     service.id = 324
     service.scope = singleton
     xa = false
    Provided by :
     OPS4J Pax JDBC Pooling DBCP2 (233)
    
    [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
    --------------------------------------------------------
     pool = dbcp2
     service.bundleid = 233
     service.id = 332
     service.scope = singleton
     xa = true
    Provided by :
     OPS4J Pax JDBC Pooling DBCP2 (233)
  3. ファクトリー設定 を作成します。

    karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
    karaf@root()> config:property-set osgi.jdbc.driver.name mysql
    karaf@root()> config:property-set dataSourceName mysqlds
    karaf@root()> config:property-set dataSourceType DataSource
    karaf@root()> config:property-set jdbc.url jdbc:mysql://localhost:3306/reportdb
    karaf@root()> config:property-set jdbc.user fuse
    karaf@root()> config:property-set jdbc.password fuse
    karaf@root()> config:property-set jdbc.useSSL false
    karaf@root()> config:property-set pool dbcp2
    karaf@root()> config:property-set xa false
    karaf@root()> config:property-set pool.minIdle 2
    karaf@root()> config:property-set pool.maxTotal 10
    karaf@root()> config:property-set pool.blockWhenExhausted true
    karaf@root()> config:property-set pool.maxWaitMillis 2000
    karaf@root()> config:property-set pool.testOnBorrow true
    karaf@root()> config:property-set pool.testWhileIdle alse
    karaf@root()> config:property-set pool.timeBetweenEvictionRunsMillis 120000
    karaf@root()> config:property-set factory.validationQuery 'select schema_name from information_schema.schemata'
    karaf@root()> config:property-set factory.validationQueryTimeout 2
    karaf@root()> config:update
  4. pax-jdbc-configjavax.sql.DataSource サービスに設定を処理したかどうかを確認します。

    karaf@root()> service:list javax.sql.DataSource
    [javax.sql.DataSource]
    ----------------------
     dataSourceName = mysqlds
     dataSourceType = DataSource
     factory.validationQuery = select schema_name from information_schema.schemata
     factory.validationQueryTimeout = 2
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
     jdbc.password = fuse
     jdbc.url = jdbc:mysql://localhost:3306/reportdb
     jdbc.user = fuse
     jdbc.useSSL = false
     osgi.jdbc.driver.name = mysql
     osgi.jndi.service.name = mysqlds
     pax.jdbc.managed = true
     pool.blockWhenExhausted = true
     pool.maxTotal = 10
     pool.maxWaitMillis = 2000
     pool.minIdle = 2
     pool.testOnBorrow = true
     pool.testWhileIdle = alse
     pool.timeBetweenEvictionRunsMillis = 120000
     service.bundleid = 225
     service.factoryPid = org.ops4j.datasource
     service.id = 338
     service.pid = org.ops4j.datasource.fd7aa3a1-695b-4342-b0d6-23d018a46fbb
     service.scope = singleton
    Provided by :
     OPS4J Pax JDBC Config (225)
  5. データソースを使用します。

    karaf@root()> jdbc:query mysqlds 'select * from incident'
    date                  │ summary    │ name   │ details                       │ id │ email
    ──────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
    2018-02-20 08:00:00.0 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
    2018-02-20 08:10:00.0 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
    2018-02-20 08:20:00.0 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
    2018-02-20 08:30:00.0 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

6.7.3. narayana 接続プールモジュールの使用

pax-jdbc-pool-narayna モジュールは pax-jdbc-pool-dbcp2 のようにほぼすべてのことをします。XA および非 XA のシナリオの両方で DBCP2 固有の org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory をインストールします。唯一 の違いは、XA シナリオにおける追加のインテグレーションポイントがあることです。org.jboss.tm.XAResourceRecovery OSGi サービスは、Narayana トランザクションマネージャーの一部である com.arjuna.ats.arjuna.recovery.RecoveryManager によって検出されるように登録されます。

6.7.4. transx 接続プールモジュールの使用

pax-jdbc-pool-transx バンドルは、pax-transx-jdbc バンドル上で org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory サービスの実装をベースとします。pax-transx-jdbc バンドルは、org.ops4j.pax.transx.jdbc.ManagedDataSourceBuilder ファシリティーを使用して javax.sql.DataSource プールを作成します。これは JCA (Java™ Connector Architecture) ソリューションであり、後述します。

6.8. アーティファクトとしてのデータソースのデプロイ

この章では、OSGi JDBC サービスを紹介し、pax-jdbc バンドルがデータベース固有および汎用データソースの登録にどのように役立つのか、および OSGi サービスと構成管理者構成の観点から見たすべての仕組みについて説明します。データソースの両方のカテゴリー の設定は、Configuration Admin ファクトリー PID を使用して行うことができますが (pax-jdbc-config バンドルを利用して)、通常は デプロイメント方法 を使用することが推奨されます。

デプロイメント方法 では、javax.sql.DataSource サービスは、通常 Blueprint コンテナー内でアプリケーションコードから直接登録されます。Blueprint XML は、通常の OSGi バンドルの一部で、mvn: URI を使用してインストールでき、Maven リポジトリー (ローカルまたはリモート) に保存されます。このようなバンドルを Configuration Admin 設定と比較することで、バージョン管理をはるかに簡単に行うことができます。

pax-jdbc-config バンドルバージョン 1.3.0 は、データソース 設定の デプロイメントメソッドを追加します。アプリケーション開発者は (通常 Bluerpint XML を使用して) javax.sql.(XA)DataSource サービスを登録し、サービスプロパティーを指定します。pax-jdbc-config バンドルは、このような登録データベース固有のデータソースを検出し (サービスプロパティーを使用) 、汎用的なデータベース固有でない接続プール内でサービスをラップします。

完全性については、Blueprint XML を使用する 3 つのデプロイメントメソッドは次のとおりです。Fuse には、Fuse のさまざまな側面の例が含まれる quickstarts ダウンロードを提供します。quickstarts zip ファイルは Fuse Software Downloads ページからダウンロードできます。

クイックスタート zip ファイルの内容をローカルフォルダーに展開します。

以下の例では、quickstarts/persistence ディレクトリーは $PQ_HOME と呼ばれます。

6.8.1. データソースの手動デプロイメント

データソースの手動デプロイの例では、Docker ベースの PostgreSQL インストールを使用します。この方法では、pax-jdbc-config は必要ありません。アプリケーションコードは、データベース固有のデータソースと汎用データソースの両方の登録を行います。

以下の 3 つのバンドルが必要です。

  • mvn:org.postgresql/postgresql/42.2.5
  • mvn:org.apache.commons/commons-pool2/2.5.0
  • mvn:org.apache.commons/commons-dbcp2/2.1.1
<!--
    Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
-->
<bean id="postgresql" class="org.postgresql.xa.PGXADataSource">
    <property name="url" value="jdbc:postgresql://localhost:5432/reportdb" />
    <property name="user" value="fuse" />
    <property name="password" value="fuse" />
    <property name="currentSchema" value="report" />
    <property name="connectTimeout" value="5" />
</bean>

<!--
    Fuse/Karaf exports this service from fuse-pax-transx-tm-narayana bundle
-->
<reference id="tm" interface="javax.transaction.TransactionManager" />

<!--
    Non database-specific, generic, pooling, enlisting javax.sql.DataSource
-->
<bean id="pool" class="org.apache.commons.dbcp2.managed.BasicManagedDataSource">
    <property name="xaDataSourceInstance" ref="postgresql" />
    <property name="transactionManager" ref="tm" />
    <property name="minIdle" value="3" />
    <property name="maxTotal" value="10" />
    <property name="validationQuery" value="select schema_name, schema_owner from information_schema.schemata" />
</bean>

<!--
    Expose datasource to use by application code (like Camel, Spring, ...)
-->
<service interface="javax.sql.DataSource" ref="pool">
    <service-properties>
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
    </service-properties>
</service>

上記の Blueprint XML フラグメントは、canonical DataSource example と一致します。以下は、使用方法を示すシェルコマンドです。

karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 233
karaf@root()> install -s mvn:org.apache.commons/commons-pool2/2.5.0
Bundle ID: 224
karaf@root()> install -s mvn:org.apache.commons/commons-dbcp2/2.1.1
Bundle ID: 225
karaf@root()> install -s blueprint:file://$PQ_HOME/databases/blueprints/postgresql-manual.xml
Bundle ID: 226

karaf@root()> bundle:services -p 226

Bundle 226 provides:
--------------------
objectClass = [javax.sql.DataSource]
osgi.jndi.service.name = jdbc/postgresql
osgi.service.blueprint.compname = pool
service.bundleid = 226
service.id = 242
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = postgresql-manual.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 226
service.id = 243
service.scope = singleton

karaf@root()> feature:install jdbc

karaf@root()> jdbc:ds-list
Name            │ Product    │ Version                       │ URL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           │ Status
────────────────┼────────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK

karaf@root()> jdbc:query jdbc/postgresql 'select * from incident';
date                │ summary    │ name   │ details                       │ id │ email
────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

上記の一覧に示すように、Blueprint バンドルは、汎用的でデータベース固有でない接続プールである javax.sql.DataSource サービスをエクスポートします。Blueprint XML には明示的な <service ref="postgresql"> 宣言がないため、データベース固有の javax.sql.XADataSource バンドルは OSGi サービスとして登録されません

6.8.2. データソースのファクトリーデプロイメント

データソースのファクトリーデプロイメントは、標準的 な方法で pax-jdbc-config バンドルを使用します。これは、Fuse 6.x で推奨されていた方法とは少し異なります。Fuse 6.x では、プーリング設定をサービスプロパティーとして指定する必要がありました。

Blueprint XML の例を以下に示します。

<!--
    A database-specific org.osgi.service.jdbc.DataSourceFactory that can create DataSource/XADataSource/
    /ConnectionPoolDataSource/Driver using properties. It is registered by pax-jdbc-* or for example
    mvn:org.postgresql/postgresql/42.2.5 bundle natively.
-->
<reference id="dataSourceFactory"
        interface="org.osgi.service.jdbc.DataSourceFactory"
        filter="(osgi.jdbc.driver.class=org.postgresql.Driver)" />

<!--
    Non database-specific org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory that can create
    pooled data sources using some org.osgi.service.jdbc.DataSourceFactory. dbcp2 pool is registered
    by pax-jdbc-pool-dbcp2 bundle.
-->
<reference id="pooledDataSourceFactory"
        interface="org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory"
        filter="(&amp;(pool=dbcp2)(xa=true))" />

<!--
    Finally, use both factories to expose pooled, xa-aware data source.
-->
<bean id="pool" factory-ref="pooledDataSourceFactory" factory-method="create">
    <argument ref="dataSourceFactory" />
    <argument>
        <props>
            <!--
                Properties needed by postgresql-specific org.osgi.service.jdbc.DataSourceFactory.
                Cannot prepend them with 'jdbc.' prefix as the DataSourceFactory is implemented directly
                by PostgreSQL driver, not by pax-jdbc-* bundle.
            -->
            <prop key="url" value="jdbc:postgresql://localhost:5432/reportdb" />
            <prop key="user" value="fuse" />
            <prop key="password" value="fuse" />
            <prop key="currentSchema" value="report" />
            <prop key="connectTimeout" value="5" />
            <!-- Properties needed by dbcp2-specific org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory -->
            <prop key="pool.minIdle" value="2" />
            <prop key="pool.maxTotal" value="10" />
            <prop key="pool.blockWhenExhausted" value="true" />
            <prop key="pool.maxWaitMillis" value="2000" />
            <prop key="pool.testOnBorrow" value="true" />
            <prop key="pool.testWhileIdle" value="false" />
            <prop key="factory.validationQuery" value="select schema_name from information_schema.schemata" />
            <prop key="factory.validationQueryTimeout" value="2" />
        </props>
    </argument>
</bean>

<!--
    Expose data source for use by application code (such as  Camel, Spring, ...).
-->
<service interface="javax.sql.DataSource" ref="pool">
    <service-properties>
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
    </service-properties>
</service>

この例では、データソースファクトリーを使用してデータソースを作成する ファクトリー Bean を使用します。これは XA 対応 PooledDataSourceFactory によって内部的に追跡されるため、javax.transaction.TransactionManager サービスを明示的に参照する必要はありません。

以下は、Fuse/Karaf シェルの同じ例になります。

注記

ネイティブ org.osgi.service.jdbc.DataSourcFactory バンドルを登録するには、mvn:org.osgi/org.osgi.service.jdbc/1.0.0 をインストールしてから、PostgreSQL ドライバーをインストールします。

karaf@root()> feature:install jdbc pax-jdbc-config pax-jdbc-pool-dbcp2
karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 232
karaf@root()> install -s blueprint:file://$PQ_HOME/databases/blueprints/postgresql-pax-jdbc-factory-dbcp2.xml
Bundle ID: 233
karaf@root()> bundle:services -p 233

Bundle 233 provides:
--------------------
objectClass = [javax.sql.DataSource]
osgi.jndi.service.name = jdbc/postgresql
osgi.service.blueprint.compname = pool
service.bundleid = 233
service.id = 336
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = postgresql-pax-jdbc-factory-dbcp2.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 233
service.id = 337
service.scope = singleton

karaf@root()> jdbc:ds-list
Name            │ Product    │ Version                       │ URL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           │ Status
────────────────┼────────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK

karaf@root()> jdbc:query jdbc/postgresql 'select * from incident';
date                │ summary    │ name   │ details                       │ id │ email
────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

上記の一覧に示すように、Blueprint バンドルは、汎用的でデータベース固有でない接続プールである javax.sql.DataSource サービスをエクスポートします。Blueprint XML には明示的な <service ref="postgresql"> 宣言がないため、データベース固有の javax.sql.XADataSource は OSGi サービスとして登録されません

6.8.3. データソースの混合デプロイメント

データソースの混合デプロイメントでは、pax-jdbc-config 1.3.0 バンドルは、サービスプロパティーを使用して、プーリングデータソース内でデータベース固有のデータソースを ラッピング する別の方法を追加します。このメソッドは、Fuse 6.x で動作する方法と一致します。

Blueprint XML の例を以下に示します。

<!--
    Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
-->
<bean id="postgresql" class="org.postgresql.xa.PGXADataSource">
    <property name="url" value="jdbc:postgresql://localhost:5432/reportdb" />
    <property name="user" value="fuse" />
    <property name="password" value="fuse" />
    <property name="currentSchema" value="report" />
    <property name="connectTimeout" value="5" />
</bean>

<!--
    Expose database-specific data source with service properties.
    No need to expose pooling, enlisting, non database-specific javax.sql.DataSource. It is registered
    automatically by pax-jdbc-config with the same properties as this <service>, but with higher service.ranking.
-->
<service id="pool" ref="postgresql" interface="javax.sql.XADataSource">
    <service-properties>
        <!-- "pool" key is needed for pax-jdbc-config to wrap database-specific data source inside connection pool -->
        <entry key="pool" value="dbcp2" />
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
        <!-- Other properties that configure given connection pool, as indicated by pool=dbcp2 -->
        <entry key="pool.minIdle" value="2" />
        <entry key="pool.maxTotal" value="10" />
        <entry key="pool.blockWhenExhausted" value="true" />
        <entry key="pool.maxWaitMillis" value="2000" />
        <entry key="pool.testOnBorrow" value="true" />
        <entry key="pool.testWhileIdle" value="false" />
        <entry key="factory.validationQuery" value="select schema_name from information_schema.schemata" />
        <entry key="factory.validationQueryTimeout" value="2" />
    </service-properties>
</service>

上記の例では、データベース固有のデータソースのみが手動で登録されます。pool=dbcp2 サービスプロパティーは、pax-jdbc-config バンドルによって管理されるデータソーストラッカーのヒントです。このサービスプロパティーを持つデータソースサービスは、プーリングデータソース内でラップされます (この例では pax-jdbc-pool-dbcp2)。

以下は、Fuse/Karaf シェルの同じ例になります。

karaf@root()> feature:install jdbc pax-jdbc-config pax-jdbc-pool-dbcp2
karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 232
karaf@root()> install -s blueprint:file://$PQ_HOME/databases/blueprints/postgresql-pax-jdbc-discovery.xml
Bundle ID: 233
karaf@root()> bundle:services -p 233

Bundle 233 provides:
--------------------
factory.validationQuery = select schema_name from information_schema.schemata
factory.validationQueryTimeout = 2
objectClass = [javax.sql.XADataSource]
osgi.jndi.service.name = jdbc/postgresql
osgi.service.blueprint.compname = postgresql
pool = dbcp2
pool.blockWhenExhausted = true
pool.maxTotal = 10
pool.maxWaitMillis = 2000
pool.minIdle = 2
pool.testOnBorrow = true
pool.testWhileIdle = false
service.bundleid = 233
service.id = 336
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = postgresql-pax-jdbc-discovery.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 233
service.id = 338
service.scope = singleton

karaf@root()> service:list javax.sql.XADataSource
[javax.sql.XADataSource]
------------------------
 factory.validationQuery = select schema_name from information_schema.schemata
 factory.validationQueryTimeout = 2
 osgi.jndi.service.name = jdbc/postgresql
 osgi.service.blueprint.compname = postgresql
 pool = dbcp2
 pool.blockWhenExhausted = true
 pool.maxTotal = 10
 pool.maxWaitMillis = 2000
 pool.minIdle = 2
 pool.testOnBorrow = true
 pool.testWhileIdle = false
 service.bundleid = 233
 service.id = 336
 service.scope = bundle
Provided by :
 Bundle 233
Used by:
 OPS4J Pax JDBC Config (224)

karaf@root()> service:list javax.sql.DataSource
[javax.sql.DataSource]
----------------------
 factory.validationQuery = select schema_name from information_schema.schemata
 factory.validationQueryTimeout = 2
 osgi.jndi.service.name = jdbc/postgresql
 osgi.service.blueprint.compname = postgresql
 pax.jdbc.managed = true
 pax.jdbc.service.id.ref = 336
 pool.blockWhenExhausted = true
 pool.maxTotal = 10
 pool.maxWaitMillis = 2000
 pool.minIdle = 2
 pool.testOnBorrow = true
 pool.testWhileIdle = false
 service.bundleid = 224
 service.id = 337
 service.ranking = 1000
 service.scope = singleton
Provided by :
 OPS4J Pax JDBC Config (224)

karaf@root()> jdbc:ds-list
Name            │ Product    │ Version                       │ URL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           │ Status
────────────────┼────────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK

karaf@root()> jdbc:query jdbc/postgresql 'select * from incident'
date                │ summary    │ name   │ details                       │ id │ email
────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

このリストには、jdbc:ds-list 出力からわかるように、元のデータソースとラッパーデータソースという 2 つのデータソースがあります。

javax.sql.XADataSource は Blueprint バンドルから登録され、pool = dbcp2 プロパティーが宣言されています。

javax.sql.DataSourcepax-jdbc-config バンドルから登録され、以下が該当します。

  • pool = dbcp2 プロパティーがありません (ラッパーデータソースの登録時に削除されました)。
  • service.ranking = 1000 プロパティーがあるので、名前でデータソースを検索する場合などに、常に優先されるバージョンになります。
  • pax.jdbc.managed = true プロパティーがあるので、再度ラップが試行されていません。
  • pax.jdbc.service.id.ref = 336 プロパティーがあり、接続プール内でラップされる元のデータソースサービスを示します。

6.9. Java™ Persistence API でのデータソースの使用

トランザクション管理の観点からは、データソースが Java™ Persistence AP I(JPA) とどのように使用されるかを理解することが重要になります。ここでは、JPA 仕様自体の詳細や、最も知られている JPA 実装である Hibernate に関する詳細は説明しません。代わりに、JPA 永続ユニットをデータソースにポイントする方法を説明します。

6.9.1. データソース参照

META-INF/persistence.xml 記述子 (JPA 2.1 仕様 8.2.1.5 jta-data -source、jta-data-source を参照) は、2 種類のデータソース参照を定義します。

  • <jta-data-source> - これは、JTA トランザクションで使用する JTA 対応データソースに対する JNDI 参照です。
  • <non-jta-data-source> - これは、JTA トランザクション外部で使用する JTA 対応データソースに対する JNDI 参照です。このデータソースは通常、Hibernate がデータベーススキーマを自動作成するように設定する hibernate.hbm2ddl.auto プロパティーなどと、初期化フェーズでも使用されます。

これら 2 つのデータソースは、javax.sql.DataSource または javax.sql.XADataSource に関連して いません。これは、JPA アプリケーションを開発する際の一般的な誤解です。両方の JNDI 名は JNDI でバインドされた javax.sql.DataSource サービスを参照する必要があります。

6.9.2. JNDI 名の参照

OSGi サービスを osgi.jndi.service.name プロパティーに登録すると、OSGi JNDI サービスで バインドされます。OSGi ランタイム (Fuse/Karaf など) では、JNDI は name → value ペアの単純な辞書ではありません。OSGi の JNDI 名を用いてオブジェクトを参照するのには、サービス検索やその他の複雑な OSGi メカニズム (例: サービスフック) が関係します。

以下のリストには、新しい Fuse インストールでデータソースが JNDI に登録される方法が記載されています。

karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 223
karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
Bundle ID: 224
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
Bundle ID: 225
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
Bundle ID: 226
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-pool-common/1.3.0
Bundle ID: 227
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
Bundle ID: 228

karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
karaf@root()> config:property-set osgi.jdbc.driver.name mysql
karaf@root()> config:property-set dataSourceName mysqlds
karaf@root()> config:property-set osgi.jndi.service.name jdbc/mysqlds
karaf@root()> config:property-set dataSourceType DataSource
karaf@root()> config:property-set jdbc.url jdbc:mysql://localhost:3306/reportdb
karaf@root()> config:property-set jdbc.user fuse
karaf@root()> config:property-set jdbc.password fuse
karaf@root()> config:property-set jdbc.useSSL false
karaf@root()> config:update

karaf@root()> feature:install jndi

karaf@root()> jndi:names
JNDI Name                 │ Class Name
──────────────────────────┼───────────────────────────────────────────────
osgi:service/jndi         │ org.apache.karaf.jndi.internal.JndiServiceImpl
osgi:service/jdbc/mysqlds │ com.mysql.jdbc.jdbc2.optional.MysqlDataSource

ご覧のとおり、データソースは osgi:service/jdbc/mysqlds JNDI 名で利用できます。

しかし、OSGi の JPA の場合は、完全な JNDI 名を使用する必要があります。以下は、データソース参照を指定する META-INF/persistence.xml フラグメントの例です。

<jta-data-source>
    osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/mysqlds)
</jta-data-source>
<non-jta-data-source>
    osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/mysqlds)
</non-jta-data-source>

上記の設定を行わないと、次のエラーが発生する可能性があります。

Persistence unit "pu-name" refers to a non OSGi service DataSource

第7章 JMS 接続ファクトリーの使用

本章では、OSGi で JMS 接続ファクトリーを使用する方法を説明します。基本的には、以下を使用してこれを行います。

org.osgi.framework.BundleContext.registerService(javax.jms.ConnectionFactory.class,
                                                 connectionFactoryObject,
                                                 properties);
org.osgi.framework.BundleContext.registerService(javax.jms.XAConnectionFactory.class,
                                                 xaConnectionFactoryObject,
                                                 properties);

このようなサービスを登録する方法は 2 つあります。

  • jms:create Karaf コンソールコマンドを使用して接続ファクトリーを公開します。これは 設定方法 です。
  • Blueprint、OSGi Declarative Services (SCR)、または BundleContext.registerService() API 呼び出しなどのメソッドを使用して接続ファクトリーをパブリッシュします。この方法には、コードやメタデータが含まれる専用の OSGi バンドルが必要です。これは デプロイメント方法 です。

詳細は以下を参照してください。

7.1. OSGi JMS サービス

JDBC データソースを処理する OSGi の方法は、以下の 2 つのインターフェースに関連します。

  • 標準 org.osgi.service.jdbc.DataSourceFactory
  • プロプライエタリー org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory

JMS の場合は、以下を検討してください。

  • 標準 OSGi JDBC org.osgi.service.jdbc.DataSourceFactory と同じ目的を持つプロプライエタリー org.ops4j.pax.jms.service.ConnectionFactoryFactory
  • プロプライエタリー pax-jdbc org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory と同じ目的を持つ プロプライエタリー org.ops4j.pax.jms.service.PooledConnectionFactoryFactory

専用のブローカー固有の org.ops4j.pax.jms.service.ConnectionFactoryFactory 実装には、以下のようなバンドルがあります。

  • mvn:org.ops4j.pax.jms/pax-jms-artemis/1.0.0
  • mvn:org.ops4j.pax.jms/pax-jms-ibmmq/1.0.0
  • mvn:org.ops4j.pax.jms/pax-jms-activemq/1.0.0

これらのバンドルは、javax.jms.ConnectionFactoryjavax.jms.XAConnectionFactory などの JMS ファクトリー を返すことができるブローカー固有の org.ops4j.pax.jms.service.ConnectionFactoryFactory サービスを登録します。以下に例を示します。

karaf@root()> feature:install pax-jms-artemis

karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-config

OPS4J Pax JMS Config (248) provides:
------------------------------------
objectClass = [org.osgi.service.cm.ManagedServiceFactory]
service.bundleid = 248
service.id = 328
service.pid = org.ops4j.connectionfactory
service.scope = singleton

karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-artemis

OPS4J Pax JMS Artemis Support (247) provides:
---------------------------------------------
objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
service.bundleid = 247
service.id = 327
service.scope = singleton
type = artemis

7.2. PAX-JMS 設定サービス

mvn:org.ops4j.pax.jms/pax-jms-config/1.0.0 バンドルは、以下の 3 つを行う Managed Service Factory を提供します。

  • org.ops4j.pax.jms.service.ConnectionFactoryFactory OSGi サービスを追跡して、そのメソッドを呼び出します。

    public ConnectionFactory createConnectionFactory(Map<String, Object> properties);
    
    public XAConnectionFactory createXAConnectionFactory(Map<String, Object> properties);
  • org.ops4j.connectionfactory ファクトリー PID を追跡し、上記のメソッドに必要なプロパティーを収集します。Configuration Admin サービスで利用可能な方法を使用して ファクトリー設定 を作成する場合 (たとえば ${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg ファイルを作成)、最終的な手順を実行して、データベース固有のコネクションファクトリーを公開できます。
  • javax.jms.ConnectionFactory および javax.jms.XAConnectionFactory サービスを追跡し、プーリングJMS 接続ファクトリー内にラップします。

詳細は以下を参照してください。

7.2.1. AMQ 7.1 の接続ファクトリーの作成

以下は、Artemis ブローカーの接続係数を作成するための詳細で標準的な ステップごとのガイドです。

  1. pax-jms-artemis 機能および pax-jms-config 機能を使用して、Artemis ドライバーをインストールします。

    karaf@root()> feature:install pax-jms-artemis
    
    karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-config
    
    OPS4J Pax JMS Config (248) provides:
    ------------------------------------
    objectClass = [org.osgi.service.cm.ManagedServiceFactory]
    service.bundleid = 248
    service.id = 328
    service.pid = org.ops4j.connectionfactory
    service.scope = singleton
    
    karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-artemis
    
    OPS4J Pax JMS Artemis Support (247) provides:
    ---------------------------------------------
    objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
    service.bundleid = 247
    service.id = 327
    service.scope = singleton
    type = artemis
  2. ファクトリー設定 を作成します。

    karaf@root()> config:edit --factory --alias artemis org.ops4j.connectionfactory
    karaf@root()> config:property-set type artemis
    karaf@root()> config:property-set osgi.jndi.service.name jms/artemis # "name" property may be used too
    karaf@root()> config:property-set connectionFactoryType ConnectionFactory # or XAConnectionFactory
    karaf@root()> config:property-set jms.url tcp://localhost:61616
    karaf@root()> config:property-set jms.user admin
    karaf@root()> config:property-set jms.password admin
    karaf@root()> config:property-set jms.consumerMaxRate 1234
    karaf@root()> config:update
    
    karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
    ----------------------------------------------------------------
    Pid:            org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
    FactoryPid:     org.ops4j.connectionfactory
    BundleLocation: ?
    Properties:
       connectionFactoryType = ConnectionFactory
       felix.fileinstall.filename = file:${karar.etc}/org.ops4j.connectionfactory-artemis.cfg
       jms.consumerMaxRate = 1234
       jms.password = admin
       jms.url = tcp://localhost:61616
       jms.user = admin
       osgi.jndi.service.name = jms/artemis
       service.factoryPid = org.ops4j.connectionfactory
       service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
       type = artemis
  3. pax-jms-configjavax.jms.ConnectionFactory サービスに設定を処理したかどうかを確認します。

    karaf@root()> service:list javax.jms.ConnectionFactory
    [javax.jms.ConnectionFactory]
    -----------------------------
     connectionFactoryType = ConnectionFactory
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg
     jms.consumerMaxRate = 1234
     jms.password = admin
     jms.url = tcp://localhost:61616
     jms.user = admin
     osgi.jndi.service.name = jms/artemis
     pax.jms.managed = true
     service.bundleid = 248
     service.factoryPid = org.ops4j.connectionfactory
     service.id = 342
     service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
     service.scope = singleton
     type = artemis
    Provided by :
     OPS4J Pax JMS Config (248)
    注記

    追加の Artemis 設定 (特に protocol=amqp) を指定した場合は、Artemis JMS クライアントの代わりに QPID JMS ライブラリーが使用されます。その後、amqp:// プロトコルを jms.url プロパティーに使用する必要があります。

  4. 接続をテストします。

これで、必要に応じて注入できるブローカー固有の (プーリングなし) 接続ファクトリーが作成されます。たとえば、jms 機能から Karaf コマンドを使用できます。

karaf@root()> feature:install -v jms
Adding features: jms/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1]
...
karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis"

karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
Message ID                              │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination                │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼────────────────────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
ID:2b6ea56d-574d-11e8-971a-7ee9ecc029d4 │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ ActiveMQQueue[DEV.QUEUE.1] │ Never      │ 4        │ false       │         │ Mon May 14 10:02:38 CEST 2018

次のリストは、プロトコルを切り替えたときの動作を示しています。

karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
----------------------------------------------------------------
Pid:            org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
FactoryPid:     org.ops4j.connectionfactory
BundleLocation: ?
Properties:
   connectionFactoryType = ConnectionFactory
   felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg
   jms.consumerMaxRate = 1234
   jms.password = fuse
   jms.url = tcp://localhost:61616
   jms.user = fuse
   osgi.jndi.service.name = jms/artemis
   service.factoryPid = org.ops4j.connectionfactory
   service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
   type = artemis

karaf@root()> config:edit org.ops4j.connectionfactory.312eb09a-d686-4229-b7e1-2ea38a77bb0f
karaf@root()> config:property-set protocol amqp
karaf@root()> config:property-delete user
karaf@root()> config:property-set username admin # mind the difference between artemis-jms-client and qpid-jms-client
karaf@root()> config:property-set jms.url amqp://localhost:61616
karaf@root()> config:update

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼────────────────
product  │ QpidJMS
version  │ 0.30.0.redhat-1

karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
Message ID │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
───────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼─────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
           │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ DEV.QUEUE.1 │ Never      │ 4        │ false       │         │ Mon May 14 10:02:38 CEST 2018

7.2.2. IBM MQ 8 または IBM MQ 9 の接続ファクトリーの作成

ここでは、IBM MQ 8 および IBM MQ 9 に接続する方法を説明します。pax-jms-ibmmq は関連する pax-jms バンドルをインストールしますが、ライセンス上の理由から IBM MQ ドライバーはインストールされません。

  1. https://developer.ibm.com/messaging/mq-downloads/ にアクセスします。
  2. ログインします。
  3. インストールするバージョンをクリックします。たとえば、IBM MQ 8.0 Client または IBM MQ 9.0 Client をクリックします。
  4. 表示されるページの下部にあるダウンロードバージョンの表で、必要なバージョンをクリックします。
  5. 次のページで、接尾辞 IBM-MQ-Install-Java-All が付けられた最新バージョンを選択します。たとえば、8.0.0.10-WS-MQ-Install-Java-All または 9.0.0.4-IBM-MQ-Install-Java-All をダウンロードします。
  6. ダウンロードした JAR ファイルの内容を展開します。
  7. bundle:install コマンドを実行します。たとえば、/home/Downloads ディレクトリーにコンテンツを展開した場合は、以下のようなコマンドを入力します。

    `bundle:install -s wrap:file:////home/Downloads/9.0.0.4-IBM-MQ-Install-Java-All/ibmmq9/wmq/JavaSE/com.ibm.mq.allclient.jar`.
  8. 以下のように接続ファクトリーを作成します。

    1. pax-jms-ibmmq をインストールします。

      karaf@root()> feature:install pax-jms-ibmmq
      
      karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-ibmmq
      
      OPS4J Pax JMS IBM MQ Support (239) provides:
      --------------------------------------------
      objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
      service.bundleid = 239
      service.id = 346
      service.scope = singleton
      type = ibmmq
    2. ファクトリー設定 を作成します。

      karaf@root()> config:edit --factory --alias ibmmq org.ops4j.connectionfactory
      karaf@root()> config:property-set type ibmmq
      karaf@root()> config:property-set osgi.jndi.service.name jms/mq9 # "name" property may be used too
      karaf@root()> config:property-set connectionFactoryType ConnectionFactory # or XAConnectionFactory
      karaf@root()> config:property-set jms.queueManager FUSEQM
      karaf@root()> config:property-set jms.hostName localhost
      karaf@root()> config:property-set jms.port 1414
      karaf@root()> config:property-set jms.transportType 1 # com.ibm.msg.client.wmq.WMQConstants.WMQ_CM_CLIENT
      karaf@root()> config:property-set jms.channel DEV.APP.SVRCONN
      karaf@root()> config:property-set jms.CCSID 1208 # com.ibm.msg.client.jms.JmsConstants.CCSID_UTF8
      karaf@root()> config:update
      
      karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
      ----------------------------------------------------------------
      Pid:            org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863
      FactoryPid:     org.ops4j.connectionfactory
      BundleLocation: ?
      Properties:
         connectionFactoryType = ConnectionFactory
         felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-ibmmq.cfg
         jms.CCSID = 1208
         jms.channel = DEV.APP.SVRCONN
         jms.hostName = localhost
         jms.port = 1414
         jms.queueManager = FUSEQM
         jms.transportType = 1
         osgi.jndi.service.name = jms/mq9
         service.factoryPid = org.ops4j.connectionfactory
         service.pid = org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863
         type = ibmmq
    3. pax-jms-configjavax.jms.ConnectionFactory サービスに設定を処理したかどうかを確認します。

      karaf@root()> service:list javax.jms.ConnectionFactory
      [javax.jms.ConnectionFactory]
      -----------------------------
       connectionFactoryType = ConnectionFactory
       felix.fileinstall.filename = file:/data/servers/7.9.0.fuse-790071-redhat-00001/etc/org.ops4j.connectionfactory-ibmmq.cfg
       jms.CCSID = 1208
       jms.channel = DEV.APP.SVRCONN
       jms.hostName = localhost
       jms.port = 1414
       jms.queueManager = FUSEQM
       jms.transportType = 1
       osgi.jndi.service.name = jms/mq9
       pax.jms.managed = true
       service.bundleid = 237
       service.factoryPid = org.ops4j.connectionfactory
       service.id = 347
       service.pid = org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863
       service.scope = singleton
       type = ibmmq
      Provided by :
       OPS4J Pax JMS Config (237)
    4. 接続をテストします。

      karaf@root()> feature:install -v jms
      Adding features: jms/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1]
      ...
      karaf@root()> jms:connectionfactories
      JMS Connection Factory
      ──────────────────────
      jms/mq9
      
      karaf@root()> jms:info -u app -p fuse jms/mq9
      Property │ Value
      ─────────┼────────────────────
      product  │ IBM MQ JMS Provider
      version  │ 8.0.0.0
      
      karaf@root()> jms:send -u app -p fuse jms/mq9 DEV.QUEUE.1 "Hello IBM MQ 9"
      
      karaf@root()> jms:browse -u app -p fuse jms/mq9 DEV.QUEUE.1
      Message ID                                          │ Content                     │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination          │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
      ────────────────────────────────────────────────────┼─────────────────────────────┼─────────┼──────┼────────────────┼───────────────┼──────────────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
      ID:414d512046555345514d202020202020c940f95a038b3220 │ Hello IBM MQ 9              │ UTF-8   │      │                │ Persistent    │ queue:///DEV.QUEUE.1 │ Never      │ 4        │ false       │         │ Mon May 14 10:17:01 CEST 2018

IBM MQ Explorer または Web コンソールからメッセージが送信されたかどうかを確認することもできます。

7.2.3. Fuse on Apache Karaf での JBoss A-MQ 6.3 クライアントの使用

Fuse の Software Downloads ページから Fuse quickstarts をダウンロードできます。

クイックスタート zip ファイルの内容をローカルフォルダーに展開します (例: quickstarts という名前のフォルダー)。

quickstarts/camel/camel-jms の例を OSGi バンドルとしてビルドおよびインストールできます。このバンドルには、JBoss A-MQ 6.3 の JMS キューにメッセージを送信する Camel ルートの Blueprint XML 定義が含まれます。JBoss A-MQ 6.3 ブローカーの接続ファクトリーを作成する手順は次のとおりです。

7.2.3.1. 前提条件

  • Maven 3.3.1 以降がインストールされている。
  • Red Hat Fuse がマシンにインストールされている。
  • JBoss A-MQ Broker 6.3 がマシンにインストールされている。
  • カスタマーポータルから Fuse on Karaf クイックスタート zip ファイルをダウンロードし、展開済みである。

7.2.3.2. 手順

  1. quickstarts/camel/camel-jms/src/main/resources/OSGI-INF/blueprint/ ディレクトリーに移動します。
  2. camel-context.xml ファイルで id="jms" のある以下の Bean を見つけます。

        <bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
            <property name="connectionFactory">
                <reference interface="javax.jms.ConnectionFactory" />
            </property>
            <property name="transactionManager" ref="transactionManager"/>
        </bean>

    それを以下のセクションに置き換えて、JBoss A-MQ 6.3 接続ファクトリーをインスタンス化します。

    	<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
    	        <property name="connectionFactory" ref="activemqConnectionFactory"/>
    	        <property name="transactionManager" ref="transactionManager"/>
    	</bean>
    	<bean id="activemqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    		<property name="brokerURL" value="tcp://localhost:61616"/>
    		<property name="userName" value="admin"/>
    		<property name="password" value="admin"/>
    	</bean>

    JBoss A-MQ 6.3 接続ファクトリーは、tcp://localhost:61616 でリッスンするブローカーに接続するよう設定されます。デフォルトでは、JBoss A-MQ は IP ポート値 61616 を使用します。さらに、接続ファクトリーは、userName/password のクレデンシャルである admin/admin を使用するようにも設定されます。このユーザーがブローカー設定で有効になっていることを確認します (または、これらの設定をブローカー設定に合わせてカスタマイズすることもできます)。

  3. camel-context.xml ファイルを保存します。
  4. camel-jms クイックスタートをビルドします。

    $ cd quickstarts/camel/camel-jms
    $ mvn install
  5. クイックスタートが正常にインストールされた後、$FUSE_HOME/ ディレクトリーに移動し、以下のコマンドを実行して Fuse on Apache Karaf サーバーを起動します。

    $ ./bin/fuse
  6. Fuse on Apache Karaf インスタンスで、activemq-client 機能と camel-jms 機能をインストールします。

    karaf@root()> feature:install activemq-client
    karaf@root()> feature:install camel-jms
  7. camel-jms クイックスタートバンドルをインストールします。

    karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/{$fuseversion}

    {$fuseversion} は、ビルドした Maven アーティファクトの実際のバージョンに置き換えます (camel-jms クイックスタートの README ファイルを参照)。

  8. JBoss A-MQ 6.3 ブローカーを起動します (これに JBoss A-MQ 6.3 のインストールが必要です)。別のターミナルウィンドウを開き、JBOSS_AMQ_63_INSTALLDIR ディレクトリーに移動します。

    $ cd JBOSS_AMQ_63_INSTALLDIR
    $ ./bin/amq
  9. Camel ルートが起動すると、即座に work/jms/input ディレクトリーが Fuse インストールに表示されます。このクイックスタートの src/main/data directory にあるファイルを、新たに作成された work/jms/input ディレクトリーにコピーします。
  10. しばらく待つと、work/jms/output ディレクトリー以下に同じファイルが国別に分類されます。

        order1.xml, order2.xml and order4.xml in work/jms/output/others
        order3.xml and order5.xml in work/jms/output/us
        order6.xml in work/jms/output/fr
  11. log:display を使用してビジネスロギングを確認します。

        Receiving order order1.xml
    
        Sending order order1.xml to another country
    
        Done processing order1.xml

7.2.4. 処理されたプロパティーの概要

Configuration Admin ファクトリー PID からのプロパティーは、関連する org.ops4j.pax.jms.service.ConnectionFactoryFactory 実装に渡されます。

  • ActiveMQ

    org.ops4j.pax.jms.activemq.ActiveMQConnectionFactoryFactory (JMS 1.1 のみ)

    org.apache.activemq.ActiveMQConnectionFactory.buildFromMap() メソッドに渡されるプロパティー

  • Artemis

    org.ops4j.pax.jms.artemis.ArtemisConnectionFactoryFactory

    protocol=amqp の場合、プロパティーは org.apache.qpid.jms.JmsConnectionFactory インスタンスを設定するために org.apache.qpid.jms.util.PropertyUtil.setProperties() メソッドに渡されます。

    それ以外の場合は、org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory インスタンスに対して org.apache.activemq.artemis.utils.uri.BeanSupport.setData() が呼び出されます。

  • IBM MQ

    org.ops4j.pax.jms.ibmmq.MQConnectionFactoryFactory

    com.ibm.mq.jms.MQConnectionFactory または com.ibm.mq.jms.MQXAConnectionFactory の Bean プロパティーが処理されます。

7.3. JMS コンソールコマンドの使用

Apache Karaf は、jms:* スコープのシェルコマンドが含まれる jms 機能を提供します。これらのコマンドを使用して手動で設定された接続ファクトリーをチェックする例をこれまで見てきました。Configuration Admin 設定を作成する必要性を隠すコマンドもあります。

Fuse の新しいインスタンスから開始し、ブローカー固有の接続ファクトリーを登録することができます。以下のリストは、Karaf からの jms 機能のインストールと、pax-jms からの pax-jms-artemis のインストールを示しています。

karaf@root()> feature:install jms pax-jms-artemis

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
karaf@root()> service:list javax.jms.ConnectionFactory # should be empty

karaf@root()> service:list org.ops4j.pax.jms.service.ConnectionFactoryFactory
[org.ops4j.pax.jms.service.ConnectionFactoryFactory]
----------------------------------------------------
 service.bundleid = 250
 service.id = 326
 service.scope = singleton
 type = artemis
Provided by :
 OPS4J Pax JMS Artemis Support (250)

以下のリストは、Artemis 接続ファクトリーを作成して確認する方法を示しています。

karaf@root()> jms:create -t artemis -u admin -p admin --url tcp://localhost:61616 artemis

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis"

karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
Message ID                              │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination                │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼────────────────────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
ID:7a944470-574f-11e8-918e-7ee9ecc029d4 │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ ActiveMQQueue[DEV.QUEUE.1] │ Never      │ 4        │ false       │         │ Mon May 14 10:19:10 CEST 2018

karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
----------------------------------------------------------------
Pid:            org.ops4j.connectionfactory.9184db6f-cb5f-4fd7-b5d7-a217090473ad
FactoryPid:     org.ops4j.connectionfactory
BundleLocation: mvn:org.ops4j.pax.jms/pax-jms-config/1.0.0
Properties:
   name = artemis
   osgi.jndi.service.name = jms/artemis
   password = admin
   service.factoryPid = org.ops4j.connectionfactory
   service.pid = org.ops4j.connectionfactory.9184db6f-cb5f-4fd7-b5d7-a217090473ad
   type = artemis
   url = tcp://localhost:61616
   user = admin

ご覧のとおり、org.ops4j.connectionfactory ファクトリー PID が作成されます。ただし、config:update では可能ですが、これは自動的に ${karaf.etc} に保存されません。他のプロパティーを指定することはできませんが、後で追加できます。

7.4. 暗号化された設定値の使用

pax-jdbc-config バンドルと同様に、Jacsypt を使用してプロパティーを暗号化できます。

OSGi に alias サービスプロパティーで登録される org.jasypt.encryption.StringEncryptor サービスがある場合は、接続 ファクトリーのファクトリー PID で参照し、暗号化されたパスワードを使用できます。以下に例を示します。

felix.fileinstall.filename = */etc/org.ops4j.connectionfactory-artemis.cfg
name = artemis
type = artemis
decryptor = my-jasypt-decryptor
url = tcp://localhost:61616
user = fuse
password = ENC(<encrypted-password>)

復号化サービスの検索に使用するサービスフィルターは (&(objectClass=org.jasypt.encryption.StringEncryptor)(alias=<alias>)) です。<alias> は、接続ファクトリー設定の ファクトリー PID からの decryptor プロパティーの値になります。

7.5. JMS 接続プールの使用

本セクションでは、JMS 接続/セッションプーリングオプションについて説明します。選択肢は JDBC の場合よりも少なくなります。情報は以下のトピックで構成されています。

重要

XA リカバリーを使用するには、pax-jms-pool-transx または pax-jms-pool-narayana 接続プールモジュールを使用する必要があります。

7.5.1. JMS 接続プールの使用

これまで、ブローカー固有の接続 ファクトリー を登録しました。接続ファクトリー自体は 接続ファクトリー のファクトリーであるため、org.ops4j.pax.jms.service.ConnectionFactoryFactory サービスはメタファクトリーとして扱われる可能性があります。次の 2 種類の接続ファクトリーを生成できるはずです。

  • javax.jms.ConnectionFactory
  • javax.jms.XAConnectionFactory

pax-jms-pool-* バンドルは、org.ops4j.pax.jms.service.ConnectionFactoryFactory サービスとスムーズに機能します。これらのバンドルは、プロパティーのセットと元の org.ops4j.pax.jms.service.ConnectionFactoryFactoryラッパー の方法で使用して、プールされた接続ファクトリーの作成に使用できる org.ops4j.pax.jms.service.PooledConnectionFactoryFactory の実装を提供します。以下に例を示します。

public interface PooledConnectionFactoryFactory {

    ConnectionFactory create(ConnectionFactoryFactory cff, Map<String, Object> props);

}

以下の表は、プールされた接続ファクトリーを登録するバンドルを示しています。表のでは、o.o.p.j.porg.ops4j.pax.jms.pool を表しています。

バンドルPooledConnectionFactoryFactoryプールキー

pax-jms-pool-pooledjms

o.o.p.j.p.pooledjms.PooledJms(XA)PooledConnectionFactoryFactory

pooledjms

pax-jms-pool-narayana

o.o.p.j.p.narayana.PooledJms(XA)PooledConnectionFactoryFactory

narayana

pax-jms-pool-transx

o.o.p.j.p.transx.Transx(XA)PooledConnectionFactoryFactory

transx

注記

pax-jms-pool-narayana ファクトリーは、pooled-jms ライブラリーに基づくため、PooledJms(XA)PooledConnectionFactoryFactory と呼ばれます。これは、XA リカバリーの Narayana トランザクションマネージャーとの統合を追加します。

上記のバンドルは接続ファクトリーのファクトリーのみをインストールします。バンドル自体は接続ファクトリーをインストールしません。したがって、javax.jms.ConnectionFactory org.ops4j.pax.jms.service.PooledConnectionFactoryFactory.create() メソッドを呼び出す何かが必要です。

7.5.2. pax-jms-pool-pooledjms 接続プールモジュールの使用

pax-jms-pool-pooledjms バンドルを使用する方法を理解することで、pax-jms-pool-pooledjms バンドルだけでなく、pax-jms-pool-pooledjms のほぼすべてを行う pax-jms-pool-narayna バンドルも使用することができます。

pax-jms-config バンドルは以下を追跡します。

  • org.ops4j.pax.jms.service.ConnectionFactoryFactory サービス
  • org.ops4j.connectionfactory ファクトリー PID
  • pax-jms-pool-* バンドルのいずれかで登録された org.ops4j.pax.jms.service.PooledConnectionFactoryFactory のインスタンス。

ファクトリー設定pool プロパティーが含まれる場合、pax-jms-config バンドルによって登録される最終的な接続ファクトリーはブローカー固有の接続ファクトリーです。pool=pooledjms の場合、接続ファクトリーは以下のいずれかの内部でラップされます。

  • org.messaginghub.pooled.jms.JmsPoolConnectionFactory (xa=false)
  • org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory (xa=true)

pool プロパティー (および非 xa/xa 接続ファクトリーの 1 つを選択するブール値 xa プロパティー) のほかに、org.ops4j.connectionfactory ファクトリー PID には pool. で始まるプロパティーが含まれる場合があります。

pooled-jms ライブラリーでは、これらの接頭辞が付いたプロパティーを使用して (接頭辞を削除した後に)、以下のインスタンスを設定します。

  • org.messaginghub.pooled.jms.JmsPoolConnectionFactory
  • org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory

以下のリストは、jms. で始まるプロパティーと便利な構文を使用する pooled-jms プール (org.ops4j.connectionfactory-artemis ファクトリー PID) の現実的な設定です。

# configuration for pax-jms-config to choose and configure specific org.ops4j.pax.jms.service.ConnectionFactoryFactory
name = jms/artemis
connectionFactoryType = ConnectionFactory
jms.url = tcp://localhost:61616
jms.user = fuse
jms.password = fuse
# org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory specific coniguration
jms.callTimeout = 12000
# ...

# hints for pax-jms-config to use selected org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
pool = pooledjms
xa = false

# pooled-jms specific configuration of org.messaginghub.pooled.jms.JmsPoolConnectionFactory
pool.idleTimeout = 10
pool.maxConnections = 100
pool.blockIfSessionPoolIsFull = true
# ...

上記の設定では、pool および xa キーは、登録した org.ops4j.pax.jms.service.PooledConnectionFactoryFactory サービスの 1 つを選択するためのヒント (サービスフィルタープロパティー) です。pooled-jms ライブラリーの場合、以下が行われます。

karaf@root()> feature:install pax-jms-pool-pooledjms

karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-pool-pooledjms

OPS4J Pax JMS MessagingHub JMS Pool implementation (252) provides:
------------------------------------------------------------------
objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
pool = pooledjms
service.bundleid = 252
service.id = 331
service.scope = singleton
xa = false
-----
objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
pool = pooledjms
service.bundleid = 252
service.id = 335
service.scope = singleton
xa = true

以下は、接続プールの作成および設定手順の完全な例です。

  1. 必要な機能をインストールします。

    karaf@root()> feature:install -v pax-jms-pool-pooledjms pax-jms-artemis
    Adding features: pax-jms-pool-pooledjms/[1.0.0,1.0.0]
    ...
  2. jms 機能をインストールします。

    karaf@root()> feature:install jms
    
    karaf@root()> service:list org.ops4j.pax.jms.service.ConnectionFactoryFactory
    [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
    ----------------------------------------------------
     service.bundleid = 249
     service.id = 327
     service.scope = singleton
     type = artemis
    Provided by :
     OPS4J Pax JMS Artemis Support (249)
    
    karaf@root()> service:list org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
    [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
    ----------------------------------------------------------
     pool = pooledjms
     service.bundleid = 251
     service.id = 328
     service.scope = singleton
     xa = false
    Provided by :
     OPS4J Pax JMS MessagingHub JMS Pool implementation (251)
    
    [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
    ----------------------------------------------------------
     pool = pooledjms
     service.bundleid = 251
     service.id = 333
     service.scope = singleton
     xa = true
    Provided by :
     OPS4J Pax JMS MessagingHub JMS Pool implementation (251)
  3. ファクトリー設定 を作成します。

    karaf@root()> config:edit --factory --alias artemis org.ops4j.connectionfactory
    karaf@root()> config:property-set connectionFactoryType ConnectionFactory
    karaf@root()> config:property-set osgi.jndi.service.name jms/artemis
    karaf@root()> config:property-set type artemis
    karaf@root()> config:property-set protocol amqp # so we switch to org.apache.qpid.jms.JmsConnectionFactory
    karaf@root()> config:property-set jms.url amqp://localhost:61616
    karaf@root()> config:property-set jms.username admin
    karaf@root()> config:property-set jms.password admin
    karaf@root()> config:property-set pool pooledjms
    karaf@root()> config:property-set xa false
    karaf@root()> config:property-set pool.idleTimeout 10
    karaf@root()> config:property-set pool.maxConnections 123
    karaf@root()> config:property-set pool.blockIfSessionPoolIsFull true
    karaf@root()> config:update
  4. pax-jms-configjavax.jms.ConnectionFactory サービスに設定を処理したかどうかを確認します。

    karaf@root()> service:list javax.jms.ConnectionFactory
    [javax.jms.ConnectionFactory]
    -----------------------------
     connectionFactoryType = ConnectionFactory
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg
     jms.password = admin
     jms.url = amqp://localhost:61616
     jms.username = admin
     osgi.jndi.service.name = jms/artemis
     pax.jms.managed = true
     pool.blockIfSessionPoolIsFull = true
     pool.idleTimeout = 10
     pool.maxConnections = 123
     protocol = amqp
     service.bundleid = 250
     service.factoryPid = org.ops4j.connectionfactory
     service.id = 347
     service.pid = org.ops4j.connectionfactory.fc1b9e85-91b4-421b-aa16-1151b0f836f9
     service.scope = singleton
     type = artemis
    Provided by :
     OPS4J Pax JMS Config (250)
  5. 接続ファクトリーを使用します。

    karaf@root()> jms:connectionfactories
    JMS Connection Factory
    ──────────────────────
    jms/artemis
    
    karaf@root()> jms:info -u admin -p admin jms/artemis
    Property │ Value
    ─────────┼────────────────
    product  │ QpidJMS
    version  │ 0.30.0.redhat-1
    
    karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis"
    
    karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
    Message ID                                      │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
    ────────────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼─────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
    ID:64842f99-5cb2-4850-9e88-f50506d49d20:1:1:1-1 │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ DEV.QUEUE.1 │ Never      │ 4        │ false       │         │ Mon May 14 12:47:13 CEST 2018

7.5.3. pax-jms-pool-narayana 接続プールモジュールの使用

pax-jms-pool-narayna モジュールは pax-jms-pool-pooledjms のようにほぼすべてのことをします。XA および非 XA のシナリオの両方で、pooled-jms 固有の org.ops4j.pax.jms.service.PooledConnectionFactoryFactory をインストールします。唯一 の違いは、XA シナリオにおける追加のインテグレーションポイントがあることです。org.jboss.tm.XAResourceRecovery OSGi サービスは com.arjuna.ats.arjuna.recovery.RecoveryManager によって取得されるように登録されます。

7.5.4. pax-jms-pool-transx 接続プールモジュールの使用

pax-jms-pool-transx モジュールは、pax-transx-jms バンドルに基づく org.ops4j.pax.jms.service.PooledConnectionFactoryFactory サービスの実装を提供します。pax-transx-jms バンドルは、org.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilder ファシリティーを使用して javax.jms.ConnectionFactory プールを作成します。これは、「pax-transx プロジェクト」 に記載されている JCA (Java™ Connector Architecture) ソリューションです。

7.6. 接続ファクトリーのアーティファクトとしてのデプロイ

このトピックでは、実際の推奨事項について説明します。

デプロイメント方法 では、javax.jms.ConnectionFactory サービスはアプリケーションコードによって直接登録されます。通常、このコードは Blueprint コンテナー内にあります。Blueprint XML は、通常の OSGi バンドルの一部である可能性があり、mvn: URI を使用してインストールでき、Maven リポジトリー (ローカルまたはリモート) に保存されます。このようなバンドルのバージョン管理は、Configuration Admin 設定と比較すると、より簡単に行うことができます

pax-jms-config バージョン 1.0.0 バンドルは、接続ファクトリー設定の デプロイメント方法 を追加します。アプリケーション開発者は (通常 Bluerpint XML を使用して) javax.jms.(XA)ConnectionFactory サービスを登録し、サービスプロパティーを指定します。次に、pax-jms-config は、登録されたブローカー固有の接続ファクトリーを検出し、(サービスプロパティーを使用して) 汎用のブローカー固有でない接続プール内でサービスをラップします。

以下は、Blueprint XML を使用する 3 つの デプロイメント方法 です。

7.6.1. 接続ファクトリーの手動デプロイメント

この方法では、pax-jms-config バンドルは必要ありません。アプリケーションコードは、ブローカー固有および汎用の接続プールの両方の登録を担当します。

<!--
    Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory
-->
<bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory">
    <argument value="tcp://localhost:61616" />
    <property name="callTimeout" value="2000" />
    <property name="initialConnectAttempts" value="3" />
</bean>

<!--
    Fuse exports this service from fuse-pax-transx-tm-narayana bundle.
-->
<reference id="tm" interface="javax.transaction.TransactionManager" />

<!--
    Non broker-specific, generic, pooling, enlisting javax.jms.ConnectionFactory
-->
<bean id="pool" class="org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory">
    <property name="connectionFactory" ref="artemis" />
    <property name="transactionManager" ref="tm" />
    <property name="maxConnections" value="10" />
    <property name="idleTimeout" value="10000" />
</bean>

<!--
    Expose connection factory for use by application code (such as Camel, Spring, ...)
-->
<service interface="javax.jms.ConnectionFactory" ref="pool">
    <service-properties>
        <!-- Giving connection factory a name using one of these properties makes identification easier in jms:connectionfactories: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Without any of the above, name will fall back to "service.id" -->
    </service-properties>
</service>

以下は、使用方法を示すシェルコマンドです。

karaf@root()> feature:install artemis-core-client artemis-jms-client
karaf@root()> install -s mvn:org.apache.commons/commons-pool2/2.5.0
Bundle ID: 244
karaf@root()> install -s mvn:org.messaginghub/pooled-jms/0.3.0
Bundle ID: 245
karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-manual.xml
Bundle ID: 246

karaf@root()> bundle:services -p 246

Bundle 246 provides:
--------------------
objectClass = [javax.jms.ConnectionFactory]
osgi.jndi.service.name = jms/artemis
osgi.service.blueprint.compname = pool
service.bundleid = 246
service.id = 340
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = artemis-manual.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 246
service.id = 341
service.scope = singleton

karaf@root()> feature:install jms

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

上記の一覧に示すように、Blueprint バンドルは、汎用的でブローカー固有でない接続プールである javax.jms.ConnectionFactory サービスをエクスポートします。Blueprint XML には明示的な <service ref="artemis"> 宣言がないため、ブローカー固有の javax.jms.XAConnectionFactory は OSGi サービスとして登録されません

7.6.2. 接続ファクトリーのファクトリーデプロイメント

このメソッドは、標準 の方法での pax-jms-config の使用方法を示しています。これは、プーリング設定をサービスプロパティーとして指定すること要件とする、Fuse 6.x で推奨されるメソッドとは若干異なります。

Blueprint XML の例を以下に示します。

<!--
    A broker-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory that can create (XA)ConnectionFactory
    using properties. It is registered by pax-jms-* bundles
-->
<reference id="connectionFactoryFactory"
        interface="org.ops4j.pax.jms.service.ConnectionFactoryFactory"
        filter="(type=artemis)" />

<!--
    Non broker-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory that can create
    pooled connection factories with the help of org.ops4j.pax.jms.service.ConnectionFactoryFactory

    For example, pax-jms-pool-pooledjms bundle registers org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
    with these properties:
     - pool = pooledjms
     - xa = true|false (both are registered)
-->
<reference id="pooledConnectionFactoryFactory"
        interface="org.ops4j.pax.jms.service.PooledConnectionFactoryFactory"
        filter="(&amp;(pool=pooledjms)(xa=true))" />

<!--
    When using XA connection factories, javax.transaction.TransactionManager service is not needed here.
    It is used internally by xa-aware pooledConnectionFactoryFactory.
-->
<!--<reference id="tm" interface="javax.transaction.TransactionManager" />-->

<!--
    Finally, use both factories to expose the pooled, xa-aware, connection factory.
-->
<bean id="pool" factory-ref="pooledConnectionFactoryFactory" factory-method="create">
    <argument ref="connectionFactoryFactory" />
    <argument>
        <props>
            <!--
                Properties needed by artemis-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory
            -->
            <prop key="jms.url" value="tcp://localhost:61616" />
            <prop key="jms.callTimeout" value="2000" />
            <prop key="jms.initialConnectAttempts" value="3" />
            <!-- Properties needed by pooled-jms-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory -->
            <prop key="pool.maxConnections" value="10" />
            <prop key="pool.idleTimeout" value="10000" />
        </props>
    </argument>
</bean>

<!--
    Expose connection factory for use by application code (such as Camel, Spring, ...)
-->
<service interface="javax.jms.ConnectionFactory" ref="pool">
    <service-properties>
        <!-- Giving connection factory a name using one of these properties makes identification easier in jms:connectionfactories: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Without any of the above, name will fall back to "service.id" -->
    </service-properties>
</service>

前述の例では、接続ファクトリー(…​) を使用して接続ファクトリーを作成するファクトリー Bean を使用します。javax.transaction.TransactionManager サービスへの明示的な参照は必要ありません。これは XA 対応 PooledConnectionFactoryFactory によって内部的に追跡されるためです。

Fuse/Karaf シェルで検索する方法は次のとおりです。

karaf@root()> feature:install jms pax-jms-artemis pax-jms-pool-pooledjms

karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-pax-jms-factory-pooledjms.xml
Bundle ID: 253
karaf@root()> bundle:services -p 253

Bundle 253 provides:
--------------------
objectClass = [javax.jms.ConnectionFactory]
osgi.jndi.service.name = jms/artemis
osgi.service.blueprint.compname = pool
service.bundleid = 253
service.id = 347
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = artemis-pax-jms-factory-pooledjms.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 253
service.id = 348
service.scope = singleton

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

上記の一覧に示すように、Blueprint バンドルは、汎用的でブローカー固有でない接続プールである javax.jms.ConnectionFactory サービスをエクスポートします。Blueprint XML には明示的な <service ref="artemis"> 宣言がないため、ブローカー固有の javax.jms.XAConnectionFactory は OSGi サービスとして登録されません

7.6.3. 接続ファクトリーの混合デプロイメント

pax-jms-config 1.0.0 バンドルは、サービスプロパティーを使用して、プーリング接続ファクトリー内にブローカー固有の接続ファクトリーを ラッピング する別の方法を追加します。このメソッドは、Fuse 6.x での動作に使用される方法と一致します。

Blueprint XML の例を以下に示します。

<!--
    Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory
-->
<bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory">
    <argument value="tcp://localhost:61616" />
    <property name="callTimeout" value="2000" />
    <property name="initialConnectAttempts" value="3" />
</bean>

<!--
    Expose broker-specific connection factory with service properties.
    No need to expose pooling, enlisting, non broker-specific javax.jms.XAConnectionFactory. It will be registered
    automatically by pax-jms-config with the same properties as this <service>, but with a higher service.ranking
-->
<service id="pool" ref="artemis" interface="javax.jms.XAConnectionFactory">
    <service-properties>
        <!-- "pool" key is needed for pax-jms-config to wrap broker-specific connection factory inside connection pool -->
        <entry key="pool" value="pooledjms" />
        <!-- <service>/@id attribute does not propagate, but name of the connection factory is required using one of: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!-- or: -->
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Other properties, that normally by e.g., pax-jms-pool-pooledjms -->
        <entry key="pool.maxConnections" value="10" />
        <entry key="pool.idleTimeout" value="10000" />
    </service-properties>
</service>

上記の例では、ブローカー固有の接続ファクトリーのみの手動登録を確認できます。pool=pooledjms サービスプロパティーは、pax-jms-config バンドルによって管理される接続ファクトリートラッカーのヒントです。このサービスプロパティーを持つ接続ファクトリーサービスは、プール接続ファクトリー内でラップされます (この例では pax-jms-pool-pooledjms

Fuse/Karaf シェルで検索する方法は次のとおりです。

karaf@root()> feature:install jms pax-jms-config pax-jms-artemis pax-jms-pool-pooledjms

karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-pax-jms-discovery.xml
Bundle ID: 254

karaf@root()> bundle:services -p 254

Bundle 254 provides:
--------------------
objectClass = [javax.jms.XAConnectionFactory]
osgi.jndi.service.name = jms/artemis
osgi.service.blueprint.compname = artemis
pool = pooledjms
pool.idleTimeout = 10000
pool.maxConnections = 10
service.bundleid = 254
service.id = 349
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = artemis-pax-jms-discovery.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 254
service.id = 351
service.scope = singleton

karaf@root()> service:list javax.jms.XAConnectionFactory
[javax.jms.XAConnectionFactory]
-------------------------------
 osgi.jndi.service.name = jms/artemis
 osgi.service.blueprint.compname = artemis
 pool = pooledjms
 pool.idleTimeout = 10000
 pool.maxConnections = 10
 service.bundleid = 254
 service.id = 349
 service.scope = bundle
Provided by :
 Bundle 254
Used by:
 OPS4J Pax JMS Config (251)

karaf@root()> service:list javax.jms.ConnectionFactory
[javax.jms.ConnectionFactory]
-----------------------------
 osgi.jndi.service.name = jms/artemis
 osgi.service.blueprint.compname = artemis
 pax.jms.managed = true
 pax.jms.service.id.ref = 349
 pool.idleTimeout = 10000
 pool.maxConnections = 10
 service.bundleid = 251
 service.id = 350
 service.ranking = 1000
 service.scope = singleton
Provided by :
 OPS4J Pax JMS Config (251)

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

前述の例では、このコマンドは重複した名前を削除するため、jms:connectionfactories は 1 つのサービスのみを示しています。データソースの混合デプロイメントにおいて、2 つのサービスが jdbc:ds-list によって表されました。

javax.jms.XAConnectionFactory は Blueprint バンドルから登録され、pool = pooledjms プロパティーが宣言されています。

javax.jms.ConnectionFactorypax-jms-config バンドルから登録され、以下が該当します。

  • pool = pooledjms プロパティーはありません。ラッパー接続ファクトリーの登録時に削除されました。
  • service.ranking = 1000 プロパティーがあるため、名前で接続ファクトリーを検索する場合など、常に優先されるバージョンになります。
  • pax.jms.managed = true プロパティーがあるので、再度ラップが試行されていません。
  • これには、接続プール内でラップされる元の接続ファクトリーサービスを示す pax.jms.service.id.ref = 349 プロパティーがあります。

第8章 Java コネクターアーキテクチャー

JCA 仕様は、主にこの 3 つの参加者を持つシナリオを 一般化するために作成されました。

  • データベースや 一般的には EIS システムなどの外部システム
  • JavaEE アプリケーションサーバー
  • デプロイされたアプリケーション

8.1. 簡単な JDBC の類推

アプリケーションとデータベースのみが存在する最も簡単なシナリオでは、以下があります。

Application -> "Database (driver)": java.sql.Driver.connect()
"Database (driver)" -> Application: java.sql.Connection

javax.sql.DataSource を公開するアプリケーションサーバーを追加すると、次のようになります (XA などのデータソースのさまざまな側面を再呼び出しすることなく)。

Application -> "App Server": javax.sql.DataSource.getConnection()
"App Server" -> "Database (driver)": javax.sql.ConnectionPoolDataSource.getPooledConnection()
"Database (driver)" -> "App Server": javax.sql.PooledConnection
"App Server" -> Application: javax.sql.PooledConnection.getConnection()

8.2. JCA の使用の概要

JCA は、ドライバーとアプリケーションサーバーの間で双方向通信を追加することで、データベースドライバーの概念を一般化します。ドライバーは、javax.resource.spi.ResourceAdapter で表される リソースアダプター になります。

以下の 2 つの重要なインターフェースがあります。

  • javax.resource.spi.ManagedConnectionFactory、リソースアダプターによって実装されます。
  • javax.resource.spi.ConnectionManager、アプリケーションサーバーにより実装されます。

ManagedConnectionFactory インターフェースは 2 つの目的を提供します。

  • Object createConnectionFactory(ConnectionManager cxManager) メソッドは、アプリケーションコードが使用できる指定の EIS (またはデータベースまたはメッセージブローカー) の 接続ファクトリー を生成するために使用できます。返された Object は次のようになります。

    • 汎用 javax.resource.cci.ConnectionFactory (詳細は「JCA 1.6, chapter 17: Common Client Interface」を参照)
    • javax.sql.DataSourceまたは javax.jms.ConnectionFactory などの EIS 固有の接続ファクトリー。これは、pax-transx-jdbc および pax-transx-jms バンドルによって使用される 接続ファクトリー のタイプです。
  • アプリケーションサーバー によって使用される javax.resource.spi.ManagedConnection ManagedConnectionFactory.createManagedConnection() メソッドは、EIS/database/broker への実際の物理接続を作成します。

ConnectionManagerアプリケーションサーバー によって実装され、リソースアダプター によって使用されます。これは、最初に QoS 操作 (プーリング、セキュリティー、トランザクション管理) を実行し、最終的に リソースアダプターManagedConnectionFactory に委譲して ManagedConnection インスタンスを作成する アプリケーションサーバー です。フローは以下のようになります。

  1. アプリケーションコードは、ManagedConnectionFactory.createConnectionFactory() から返されるオブジェクトを使用して アプリケーションサーバー で作成され、公開される 接続ファクトリー を使用します。これは一般的な CCI インターフェース (例: javax.sql.DataSource ) である可能性があります。
  2. この 接続ファクトリー は、独自の接続で 接続 を作成しません。代わりに、リソースアダプター固有の ManagedConnectionFactory を渡す ConnectionManager.allocateConnection() に委譲します。
  3. アプリケーションサーバー によって実装される ConnectionManager は、サポートするオブジェクト の作成、トランザクションプーリングの管理などを行い、最終的に渡された ManagedConnectionFactory から 物理 (管理) 接続 を取得します。
  4. アプリケーションコードは、通常、リソースアダプターの特定の 物理接続 に委譲する アプリケーションサーバー によって作成されるラッパー/プロキシーであるコネクション を取得します。

以下は、EIS 固有の CCI でない 接続ファクトリーアプリケーションサーバー で作成した図です。EIS (ここではデータベース) へのアクセスは javax.sql.DataSource インターフェースを使用して行われます。ドライバーのタスクは 物理接続を提供することで、アプリケーションサーバー はプーリング/登録/セキュリティーを行うプロキシー (通常) 内でラップします。

participant Application
participant "App Server"

create "Connection Manager"
"App Server" -> "Connection Manager": new

"App Server" -> "Resource Adapter": ManagedConnectionFactory.createConnectionFactory(connection manager)

create "Connection Factory"
"Resource Adapter" -> "Connection Factory": new

Application -> "Connection Factory": javax.sql.DataSource.getConnection()
"Connection Factory" -> "Connection Manager": ConnectionManager.allocateConnection()
"Connection Manager" -> "App Server": configure pooling/tx/security
"Connection Manager" -> "Resource Adapter": ManagedConnectionFactory.createManagedConnection()
"Connection Manager" <- "Resource Adapter": javax.resource.spi.ManagedConnection
"Connection Manager" -> "Connection Factory": pooled/enlisted/secured connection
Application <- "Connection Factory": pooled/enlisted/secured connection

8.3. pax-transx プロジェクト

pax-transx プロジェクトは、OSGi での JTA/JTS トランザクション管理、および JDBC および JMS のリソースプーリングのサポートを提供します。pax-jdbcpax-jms との差を埋めます。

  • pax-jdbc は、javax.sql.(XA)ConnectionFactory サービスの設定オプションと検出を追加し、JDBC プーリング実装の一部が提供されます。
  • pax-jms は、javax.jms.(XA)ConnectionFactory サービスと同じことを行い、一部の JMS プーリング実装を提供します。
  • pax-transxjavax.transaction.TransactionManager 実装の設定オプションおよび検出を追加し、(最終的に) JCA ベースの JDBC/JMS 接続管理をプーリングおよびトランザクションサポートとともに提供します。

JDBC 接続プール および JMS 接続プール のセクションは引き続き有効です。JCA ベースのプールを使用するために必要な変更は、JDBC データソースと JMS 接続ファクトリーの登録時に pool=transx プロパティーを使用することだけです。

  • pax-jdbc-pool-transxpax-transx-jdbc から org.ops4j.pax.transx.jdbc.ManagedDataSourceBuilder を使用します。
  • pax-jms-pool-transxpax-transx-jms から org.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilder を使用します。

プールされたデータソース/接続ファクトリーはビルダースタイル (Java™ Bean プロパティーなし) で作成されますが、このプロパティーは JDBC でサポートされます。

  • name
  • userName
  • password
  • commitBeforeAutocommit
  • preparedStatementCacheSize
  • transactionIsolationLevel
  • minIdle
  • maxPoolSize
  • aliveBypassWindow
  • houseKeepingPeriod
  • connectionTimeout
  • idleTimeout
  • maxLifetime

以下のプロパティーは JMS でサポートされます。

  • name
  • userName
  • password
  • clientID
  • minIdle
  • maxPoolSize
  • aliveBypassWindow
  • houseKeepingPeriod
  • connectionTimeout
  • idleTimeout
  • maxLifetime

XA リカバリーが動作するには userName および password プロパティーが必要です (Fuse 6.x で aries.xa.username および aries.xa.password プロパティーが必要なように)。

Blueprint でのこの JDBC 設定 (pool=transx を考慮) は次のとおりです。

<!--
    Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
-->
<bean id="postgresql" class="org.postgresql.xa.PGXADataSource">
    <property name="url" value="jdbc:postgresql://localhost:5432/reportdb" />
    <property name="user" value="fuse" />
    <property name="password" value="fuse" />
    <property name="currentSchema" value="report" />
    <property name="connectTimeout" value="5" />
</bean>

<!--
    Expose database-specific data source with service properties
    No need to expose pooling, enlisting, non database-specific javax.sql.DataSource - it'll be registered
    automatically by pax-jdbc-config with the same properties as this <service>, but with higher service.ranking
-->
<service id="pool" ref="postgresql" interface="javax.sql.XADataSource">
    <service-properties>
        <!-- "pool" key is needed for pax-jdbc-config to wrap database-specific data source inside connection pool -->
        <entry key="pool" value="transx" />
        <!-- <service>/@id attribute doesn't propagate, but name of the datasource is required using one of: -->
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
        <!-- or: -->
        <!--<entry key="dataSourceName" value="jdbc/postgresql" />-->
        <!-- Other properties, that normally are needed by e.g., pax-jdbc-pool-transx -->
        <entry key="pool.maxPoolSize" value="13" />
        <entry key="pool.userName" value="fuse" />
        <entry key="pool.password" value="fuse" />
    </service-properties>
</service>

Blueprint でのこの JMS 設定 (pool=transx を考慮) は次のとおりです。

<!--
    Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory
-->
<bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory">
    <argument index="0" value="tcp://localhost:61616" />
    <!-- credentials needed for JCA-based XA-recovery -->
    <argument index="1" value="admin" />
    <argument index="2" value="admin" />
    <property name="callTimeout" value="2000" />
    <property name="initialConnectAttempts" value="3" />
</bean>

<!--
    Expose broker-specific connection factory with service properties
    No need to expose pooling, enlisting, non broker-specific javax.jms.XAConnectionFactory - it'll be registered
    automatically by pax-jms-config with the same properties as this <service>, but with higher service.ranking
-->
<service id="pool" ref="artemis" interface="javax.jms.XAConnectionFactory">
    <service-properties>
        <!-- "pool" key is needed for pax-jms-config to wrap broker-specific connection factory inside connection pool -->
        <entry key="pool" value="transx" />
        <!-- <service>/@id attribute doesn't propagate, but name of the connection factory is required using one of: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!-- or: -->
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Other properties, that normally are needed e.g., pax-jms-pool-transx -->
        <entry key="pool.maxPoolSize" value="13" />
        <entry key="pool.userName" value="admin" />
        <entry key="pool.password" value="admin" />
    </service-properties>
</service>

JCA ベースのリソース管理を活用する JDBC データソースと JMS 接続ファクトリーが登録されます。transx ベースのプールは XA リカバリーに関して pax-transx-tm-narayana と適切に統合されます。

必要な機能は以下のとおりです。

  • pax-jdbc-pool-tranx
  • pax-jms-pool-tranx
  • pax-transx-jdbc
  • pax-transx-jms
  • pax-jms-artemis (A-MQ 7 を使用する場合)

第9章 トランザクションを使用する Camel アプリケーションの作成

参照可能なサービスのタイプを 3 つ設定すると、アプリケーションを記述する準備が整います。以下の 3 つのタイプのサービスがあります。

  • 以下のいずれかのインターフェースの実装であるトランザクションマネージャーの 1 つ。

    • javax.transaction.UserTransaction
    • javax.transaction.TransactionManager
    • org.springframework.transaction.PlatformTransactionManager
  • javax.sql.DataSource. インターフェイスを実装する JDBC データソースが少なくとも 1 つ。多くの場合、複数のデータソースがあります。
  • javax.jms.ConnectionFactory インターフェースを実装する JMS 接続ファクトリーが少なくとも 1 つ。多くの場合、複数あります。

ここでは、トランザクション、データソース、および接続ファクトリーの管理に関連する Camel 固有の設定について説明します。

注記

ここでは、SpringTransactionPolicy などの Spring 関連の概念をいくつか説明します。Spring XML DSLBlueprint XML DSL の間には、明確が違いがあります。これらは両方 Camel コンテキストを定義する言語です。Spring XML DSL は Fuse で 非推奨 になりました。しかし、Camel トランザクションメカニズムはそのまま Spring ライブラリーを内部で使用します。

ここでの情報のほとんどは、使用される PlatformTransactionManager の種類に依存していません。PlatformTransactionManager が Narayana トランザクションマネージャーの場合、完全な JTA トランザクションが使用されます。PlatformTransactionManager がローカルの Blueprint <bean> として定義されている場合 (例: org.springframework.jms.connection.JmsTransactionManager)、ローカルトランザクションが使用されます。

トランザクションのデマケーションとは、トランザクションの開始、コミット、およびロールバックの手順を指します。このセクションでは、プログラミングと設定の両方によって、トランザクションのデマケーションを制御するためのメカニズムについて説明します。

9.1. ルートのマーキングによるトランザクションのデマケーション

Apache Camel は、ルートでトランザクションを開始するための簡単なメカニズムを提供します。Java DSL で transacted() コマンドを挿入するか、XML DSL で <transacted/> タグを挿入します。

図9.1 ルートのマーキングによるデマケーション

txn demarcation 01

トランザクションのプロセッサーは以下のようにトランザクションをデマケーションします。

  1. エクスチェンジがトランザクションプロセッサーに入ると、トランザクションのプロセッサーはデフォルトのトランザクションマネージャーを呼び出してトランザクションを開始し、トランザクションを現在のスレッドにアタッチします。
  2. エクスチェンジが残りのルートの最後に到達すると、トランザクションのプロセッサーはトランザクションマネージャーを呼び出して現在のトランザクションをコミットします。

9.1.1. JDBC リソースでのルートの例

図9.1「ルートのマーキングによるデマケーション」 は、ルートに transacted() DSL コマンドを追加することで、トランザクションになるルートの例を示しています。transacted() ノードに続くすべてのルートノードはトランザクションスコープに含まれます。この例では、以下の 2 つのノードが JDBC リソースにアクセスします。

9.1.2. Java DSL でのルート定義

以下の Java DSL の例は、transacted() DSL コマンドでルートをマークしてトランザクションルートを定義する方法を示しています。

import org.apache.camel.builder.RouteBuilder;

class MyRouteBuilder extends RouteBuilder {
    public void configure() {
        from("file:src/data?noop=true")
                .transacted()
                .bean("accountService","credit")
                .bean("accountService","debit");
    }
}

この例では、ファイルエンドポイントは、ある口座から別の口座への資金の振替を記述する、XML 形式の一部のファイルを読み取ります。最初の bean() 呼び出しでは、指定された金額が受取人の口座に加算され、2 回目の bean() 呼び出しでは、支払人の口座からから指定の金額が差し引かれます。どちらの bean() 呼び出しでも、データベースリソースに対する更新が行われます。データベースリソースがトランザクションマネージャーを介してトランザクションにバインドされることを前提とします。たとえば、6章JDBC データソースの使用 を参照してください。

9.1.3. Blueprint XML でのルート定義

前述のルートは、Blueprint XML でも表現できます。<transacted /> タグは、以下の XML のようにルートをトランザクションとしてマークします。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ...>

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="file:src/data?noop=true" />
            <transacted />
            <bean ref="accountService" method="credit" />
            <bean ref="accountService" method="debit" />
        </route>
    </camelContext>

</blueprint>

9.1.4. デフォルトのトランザクションマネージャーおよびトランザクションポリシー

トランザクションをデマケーションするには、トランザクションのプロセッサーを特定のトランザクションマネージャーインスタンスに関連付ける必要があります。transacted() を呼び出すたびにトランザクションマネージャーを指定しなくても、トランザクションプロセッサーが適切なデフォルトを自動的に選択します。たとえば、設定にはトランザクションマネージャーのインスタンスが 1 つしかない場合、トランザクションプロセッサーは暗黙的にこのトランザクションマネージャーを選択し、それを使用してトランザクションをデマケーションします。

トランザクションプロセッサーは、TransactedPolicy タイプのトランザクションポリシーで設定することもできます。これは、伝播ポリシーとトランザクションマネージャーがカプセル化します (詳細は 「トランザクション伝搬ポリシー」 を参照してください)。次のルールは、デフォルトのトランザクションマネージャーまたはトランザクションポリシーを選択するために使用されます。

  1. org.apache.camel.spi.TransactedPolicy 型の Bean が 1 つのみある場合は、この Bean を使用します。

    注記

    TransactedPolicy タイプは、「トランザクション伝搬ポリシー」 で説明されている SpringTransactionPolicy タイプのベースタイプです。そのため、ここで参照される Bean は SpringTransactionPolicy Bean である可能性があります。

  2. IDPROPAGATION_REQUIRED のある Bean タイプ org.apache.camel.spi.TransactedPolicy がある場合、この Bean を使用します。
  3. org.springframework.transaction.PlatformTransactionManager 型の Bean が 1 つのみある場合は、この Bean を使用します。

Bean ID を transacted() に引数として指定することで、Bean を明示的に指定するオプションもあります。「Java DSL の PROPAGATION_NEVER ポリシーを使用したルートの例」 を参照してください。

9.1.5. トランザクションスコープ

トランザクションプロセッサーをルートに挿入すると、トランザクションマネージャーは、エクスチェンジがこのノードを通過するたびに新しいトランザクションを作成します。トランザクションのスコープは、以下のように定義されます。

  • トランザクションは現在のスレッドにのみ関連付けられます。
  • トランザクションスコープは、トランザクションプロセッサーに続くすべてのルートノードに対応します。

トランザクションプロセッサーより前にあるルートノードはトランザクションにありません。ただし、ルートがトランザクションエンドポイントで始まる場合、ルート内のすべてのノードがトランザクション内に存在します。「ルート開始時のトランザクションエンドポイント」 を参照してください。

以下のルートを考慮してください。transacted() DSL コマンドは、データベースリソースにアクセスする最初の bean() 呼び出しの後に誤って発生するため、正しくありません。

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
                .bean("accountService", "credit")
                .transacted()  // <-- WARNING: Transaction started in the wrong place!
                .bean("accountService", "debit");
    }
}

9.1.6. トランザクションルートにスレッドプールがない

指定のトランザクションが現在のスレッドにのみ関連付けられていることを理解することが重要になります。新しいスレッドの処理が現在のトランザクションに参加しないため、トランザクションルートの途中でスレッドプールを作成してはなりません。たとえば、以下のルートは問題の原因となります。

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
                .transacted()
                .threads(3)  // WARNING: Subthreads are not in transaction scope!
                .bean("accountService", "credit")
                .bean("accountService", "debit");
    }
}

threads() DSL コマンドはトランザクションルートと互換性がないため、前述のルートなどのルートは、データベースが破損する可能性があります。threads() 呼び出しが transacted() 呼び出しの前であっても、ルートは想定どおりに動作しません。

9.1.7. ルートのフラグメントへの分割

ルートをフラグメントに分割し、各ルートのフラグメントを現在のトランザクションに参加させる場合は、direct: エンドポイントを使用することができます。たとえば、エクスチェンジを個別のルートフラグメントに送信するには、転送量が大きい (100 を超える) または小さい (100 以下) かに応じて以下のように choice() DSL コマンドとダイレクトエンドポイントを使用できます。

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
                .transacted()
                .bean("accountService", "credit")
                .choice().when(xpath("/transaction/transfer[amount > 100]"))
                .to("direct:txbig")
                .otherwise()
                .to("direct:txsmall");

        from("direct:txbig")
                .bean("accountService", "debit")
                .bean("accountService", "dumpTable")
                .to("file:target/messages/big");

        from("direct:txsmall")
                .bean("accountService", "debit")
                .bean("accountService", "dumpTable")
                .to("file:target/messages/small");
    }
}

direct エンドポイントは同期されているため、direct:txbig で始まるフラグメントと direct:txsmall で始まるフラグメントの両方は、現在のトランザクションに参加します。つまり、フラグメントは最初のルートフラグメントと同じスレッドで実行されるため、同じトランザクションスコープに含まれます。

注記

ルートのフラグメントに参加するために seda エンドポイントを使用することはできません。seda コンシューマーエンドポイントは、ルートフラグメント (非同期処理) を実行する新しいスレッドを作成します。そのため、フラグメントは元のトランザクションに参加しません。

9.1.8. リソースエンドポイント

以下の Apache Camel コンポーネントは、ルートの宛先として表示される場合にリソースエンドポイントとして機能します。たとえば、to() DSL コマンドに表示される場合です。つまり、これらのエンドポイントは、データベースや永続キューなどのトランザクションリソースにアクセスできます。リソースエンドポイントは、現在のトランザクションを開始したトランザクションプロセッサーと同じトランザクションマネージャーに関連付けられている限り、現在のトランザクションに参加できます。

  • ActiveMQ
  • AMQP
  • Hibernate
  • iBatis
  • JavaSpace
  • JBI
  • JCR
  • JDBC
  • JMS
  • JPA
  • LDAP

9.1.9. リソースエンドポイントを含むルートの例

以下の例は、リソースエンドポイントを含むルートを示しています。これにより、2 つの異なる JMS キューに送金のための注文が送信されます。credits キューは、受取人の口座に入金するために注文を処理します。debits キューは、送金人の口座から差し引く注文を処理します。対応する出金がある場合にのみ入金します。そのため、キュー操作を 1 つのトランザクションで囲む必要があります。トランザクションに成功すると、入金注文と出金注文の両方がキューに入れられます。エラーが発生した場合、どちらの注文もキューに置かれません。

from("file:src/data?noop=true")
        .transacted()
        .to("jmstx:queue:credits")
        .to("jmstx:queue:debits");

9.2. トランザクションエンドポイントによるデマケーション

ルート開始時のコンシューマーエンドポイントがリソースにアクセスすると、エクスチェンジのポーリング後にトランザクションを開始するため、transacted() コマンドを使用する意味がありません。つまり、トランザクションの開始が遅すぎて、コンシューマーエンドポイントをトランザクションスコープ内に含めることができません。この場合、正しいアプローチは、エンドポイント自体にトランザクションの開始を担当させることです。トランザクションの管理が可能なエンドポイントは、トランザクションエンドポイントと呼ばれます。

camel route tx endpoint

以下のように、トランザクションエンドポイントによるデマケーションの 2 つのモデルがあります。

  • 一般的なケース: 通常、トランザクションエンドポイントは以下のようにトランザクションをデマケーションします。

    1. エクスチェンジがエンドポイントに到達するか、またはエンドポイントがエクスチェンジを正常にポーリングする場合、エンドポイントは関連するトランザクションマネージャーを呼び出してトランザクションを開始します。
    2. エンドポイントは、新しいトランザクションを現在のスレッドにアタッチします。
    3. エクスチェンジがルートの最後に到達すると、トランザクションエンドポイントはトランザクションマネージャーを呼び出して現在のトランザクションをコミットします。
  • InOut エクスチェンジを持つ JMS エンドポイント: JMS コンシューマーエンドポイントが InOut エクスチェンジを受信し、このエクスチェンジが別の JMS エンドポイントにルーティングされた場合、これは特別なケースとして扱う必要があります。問題は、リクエスト/リプライエクスチェンジ全体を 1 つのトランザクションで囲まようとすると、ルートがデッドロックする可能性があることです。

9.2.1. JMS エンドポイントを使用したルートの例

「トランザクションエンドポイントによるデマケーション」 は、ルート (from() コマンド内で) の開始時にトランザクションエンドポイントがあることによってルートがトランザクションになる例を示しています。ルートノードはすべてトランザクションスコープに含まれます。この例では、ルートのすべてのエンドポイントが JMS リソースにアクセスします。

9.2.2. Java DSL でのルート定義

以下の Java DSL の例は、トランザクションエンドポイントでルートを開始し、トランザクションルートを定義する方法を示しています。

from("jmstx:queue:giro")
        .to("jmstx:queue:credits")
        .to("jmstx:queue:debits");

前述の例では、トランザクションスコープにはエンドポイント jmstx:queue:girojmstx:queue:credits、および jmstx:queue:debits が含まれます。トランザクションに成功すると、エクスチェンジは giro キューから永続的に削除され、credits キューおよび debits キューにプッシュされます。トランザクションが失敗すると、エクスチェンジは credits および debits キューに配置されず、エクスチェンジは giro キューに戻されます。デフォルトでは、JMS は自動的にメッセージの再配信を試みます。以下のように、JMS コンポーネント Bean jmstx はトランザクションを使用するように明示的に設定する必要があります。

<blueprint ...>
    <bean id="jmstx" class="org.apache.camel.component.jms.JmsComponent">
        <property name="configuration" ref="jmsConfig" />
    </bean>

    <bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
        <property name="transactionManager" ref="jmsTransactionManager" />
        <property name="transacted" value="true" />
    </bean>
    ...
</blueprint>

前述の例では、トランザクションマネージャーインスタンス jmsTransactionManager は JMS コンポーネントに関連付けられ、transacted プロパティーは true に設定され、InOnly エクスチェンジのトランザクションデマケーションを有効にします。

9.2.3. Blueprint XML でのルート定義

前述のルートは、以下のように Blueprint XML で同等に表現することができます。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="jmstx:queue:giro" />
            <to uri="jmstx:queue:credits" />
            <to uri="jmstx:queue:debits" />
        </route>
    </camelContext>

</blueprint>

9.2.4. DSL transacted() コマンドは必要ありません

トランザクションエンドポイントで始まるルートに transacted() DSL コマンドは必要ありません。ただし、デフォルトのトランザクションポリシーが PROPAGATION_REQUIRED (「トランザクション伝搬ポリシー」 を参照) であることを仮定すると、以下の例のように transacted() コマンドを含めることは通常問題ありません。

from("jmstx:queue:giro")
        .transacted()
        .to("jmstx:queue:credits")
        .to("jmstx:queue:debits");

ただし、このルートは、デフォルト以外の伝播ポリシーを持つ単一の TransactedPolicy Bean が Blueprint XML で作成されている場合など、このルートが予期せぬ方法で動作する可能性があります。「デフォルトのトランザクションマネージャーおよびトランザクションポリシー」 を参照してください。そのため、通常はトランザクションエンドポイントで始まるルートに transacted() DSL コマンドを含めない方がよいでしょう。

9.2.5. ルート開始時のトランザクションエンドポイント

以下の Apache Camel コンポーネントは、ルートの開始点で表示される場合にトランザクションエンドポイントとして機能します。たとえば、from() DSL コマンドに表示される場合です。つまり、これらのエンドポイントはトランザクションクライアントとして動作するよう設定でき、トランザクションリソースにアクセスすることもできます。

  • ActiveMQ
  • AMQP
  • JavaSpace
  • JMS
  • JPA

9.3. 宣言型トランザクションによるデマケーション

Blueprint XML を使用する場合は、Blueprint XML ファイルでトランザクションポリシーを宣言することにより、トランザクションをデマケーションすることもできます。たとえば、Required ポリシーなどで適切なトランザクションポリシーを Bean または Bean メソッドに適用すると、特定の Bean または Bean メソッドが呼び出されるたびにトランザクションが開始されるようにすることができます。Bean メソッドの最後に、トランザクションはコミットされます。このアプローチは、トランザクションが Enterprise Java Beans で処理される方法に似ています。

OSGi の宣言トランザクションを使用すると、Blueprint ファイルで以下のスコープでトランザクションポリシーを定義できます。

tx:transaction 属性の説明」 も参照してください。

9.3.1. bean レベルの宣言

Bean レベルでトランザクションポリシーを宣言するには、以下のように tx:transaction 要素を bean 要素の子として挿入します。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.1.0">

    <bean id="accountFoo" class="org.jboss.fuse.example.Account">
        <tx:transaction method="*" value="Required" />
        <property name="accountName" value="Foo" />
    </bean>

    <bean id="accountBar" class="org.jboss.fuse.example.Account">
        <tx:transaction method="*" value="Required" />
        <property name="accountName" value="Bar" />
    </bean>

</blueprint>

上記の例では、必要なトランザクションポリシーは accountFoo Bean と accountBar Bean のすべてのメソッドに適用されます。ここで、メソッド属性はすべての Bean メソッドと一致するワイルドカード * を指定します。

9.3.2. トップレベルの宣言

トップレベルでトランザクションポリシーを宣言するには、以下のように tx:transaction 要素を blueprint 要素の子として挿入します。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.1.0">

    <tx:transaction bean="account*" value="Required" />

    <bean id="accountFoo" class="org.jboss.fuse.example.Account">
        <property name="accountName" value="Foo" />
    </bean>

    <bean id="accountBar" class="org.jboss.fuse.example.Account">
        <property name="accountName" value="Bar" />
    </bean>

</blueprint>

上記の例では、Required トランザクションポリシーは、ID がパターン account* に一致するすべての Bean のメソッドに適用されます。

9.3.3. tx:transaction 属性の説明

tx:transaction 要素は以下の属性をサポートします。

bean

(トップレベルのみ) トランザクションポリシーが適用される Bean ID (コンマまたはスペース区切り) のリストを指定します。以下に例を示します。

<blueprint ...>
    <tx:transaction bean="accountFoo,accountBar" value="..." />
</blueprint>

ワイルドカード文字 * も使用できます。これは、各リストエントリーで最大 1 度表示される可能性があります。以下に例を示します。

<blueprint ...>
    <tx:transaction bean="account*,jms*" value="..." />
</blueprint>

Bean 属性を省略すると、デフォルトで * に設定されます (Blueprint ファイルのすべての非合成 Bean と一致)。

method

(トップレベルおよび Bean レベル) トランザクションポリシーが適用されるメソッド名 (コンマまたはスペース区切り) のリストを指定します。以下に例を示します。

<bean id="accountFoo" class="org.jboss.fuse.example.Account">
    <tx:transaction method="debit,credit,transfer" value="Required" />
    <property name="accountName" value="Foo" />
</bean>

ワイルドカード文字 * も使用できます。これは、各リストエントリーで最大 1 度表示される可能性があります。

method 属性を省略すると、デフォルトで * に設定されます (該当する Bean のすべてのメソッドと一致)。

value

(トップレベルおよび Bean レベル) トランザクションポリシーを指定します。ポリシー値は、次のように EJB 3.0 仕様で定義されているポリシーと同じセマンティクスを持ちます。

  • Required - 現在のトランザクションをサポートします。存在しない場合は新しいトランザクションを作成します。
  • Mandatory - 現在のトランザクションをサポートします。現在のトランザクションが存在しない場合に例外をスローします。
  • RequiresNew - 新しいトランザクションを作成します。現在のトランザクションが存在する場合は一時停止します。
  • Supports - 現在のトランザクションをサポートします。存在しない場合は非トランザクションを実行します。
  • NotSupported - 現在のトランザクションをサポートしません。むしろ常に非トランザクションで実行します。
  • Never - 現在のトランザクションはサポートしません。現在のトランザクションが存在する場合は例外をスローします。

9.4. トランザクション伝搬ポリシー

トランザクションクライアントが新しいトランザクションを作成する方法に影響を与える場合は、JmsTransactionManager を使用してそのトランザクションポリシーを指定できます。特に、Spring トランザクションポリシーを使用すると、トランザクションの伝播動作を指定できます。たとえば、トランザクションクライアントが新しいトランザクションを作成しようとしており、トランザクションがすでに現在のスレッドに関連付けられていることを検知した場合は、古いトランザクションを中断して新しいトランザクションを作成する必要がありますか?それとも、既存のトランザクションを引き継ぐべきですか?このような動作は、トランザクションポリシーで伝搬動作を指定して規制されます。

トランザクションポリシーは、Blueprint XML で Bean としてインスタンス化されます。その後、その Bean IDtransacted() DSL コマンドに引数として指定すると、トランザクションポリシーを参照できます。たとえば、動作 PROPAGATION_REQUIRES_NEW の対象となるトランザクションを開始する場合は、次のルートを使用できます。

from("file:src/data?noop=true")
        .transacted("PROPAGATION_REQUIRES_NEW")
        .bean("accountService","credit")
        .bean("accountService","debit")
        .to("file:target/messages");

PROPAGATION_REQUIRES_NEW 引数は、PROPAGATION_REQUIRES_NEW の動作で設定されたトランザクションポリシー Bean の Bean ID を指定します。「Blueprint XML でのポリシー Bean の定義」 を参照してください。

9.4.1. Spring トランザクションポリシー

Apache Camel では、org.apache.camel.spring.spi.SpringTransactionPolicy クラスを使用して Spring トランザクションポリシーを定義できます。これは基本的にネイティブ Spring クラスのラッパーです。SpringTransactionPolicy クラスは、2 つのデータをカプセル化します。

  • PlatformTransactionManager 型のトランザクションマネージャーへの参照
  • 伝搬動作

たとえば、以下のように PROPAGATION_MANDATORY 動作で Spring トランザクションポリシー Bean をインスタンス化できます。

<blueprint ...>
  <bean id="PROPAGATION_MANDATORY "class="org.apache.camel.spring.spi.SpringTransactionPolicy">
    <property name="transactionManager" ref="txManager" />
    <property name="propagationBehaviorName" value="PROPAGATION_MANDATORY" />
  </bean>
  ...
</blueprint>

9.4.2. 伝播動作の説明

Spring によって以下の伝播動作がサポートされます。これらの値は、元々 JavaeEE によってサポートされる伝播動作でモデル化されました。

PROPAGATION_MANDATORY
現在のトランザクションをサポートします。現在のトランザクションがない場合には例外をスローします。
PROPAGATION_NESTED

現在のトランザクションが存在する場合はネストされたトランザクション内で実行し、それ以外の場合は PROPAGATION_REQUIRED のように動作します。

注記

ネストされたトランザクションは、すべてのトランザクションマネージャーではサポートされません。

PROPAGATION_NEVER
現在のトランザクションはサポートしません。現在のトランザクションが存在する場合は例外をスローします。
PROPAGATION_NOT_SUPPORTED

現在のトランザクションはサポートしません。常に非トランザクションを実行します。

注記

このポリシーでは、現在のトランザクションを中断する必要があります。これは、すべてのトランザクションマネージャーでサポートされていない機能です。

PROPAGATION_REQUIRED
(デフォルト) 現在のトランザクションをサポートします。存在しない場合は、新たに作成します。
PROPAGATION_REQUIRES_NEW

新しいトランザクションを作成し、現在のトランザクションが存在する場合は一時停止します。

注記

トランザクションの中断は、すべてのトランザクションマネージャでサポートされているわけではありません。

PROPAGATION_SUPPORTS
現在のトランザクションをサポートします。存在しない場合は非トランザクションで実行します。

9.4.3. Blueprint XML でのポリシー Bean の定義

以下の例は、サポートされるすべての伝播動作に対してトランザクションポリシー Bean を定義する方法を示しています。便宜上、各 Bean ID は伝播の動作値で指定した値と一致しますが、実際には Bean ID に任意の値を使用できます。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <bean id="PROPAGATION_MANDATORY " class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_MANDATORY" />
    </bean>

    <bean id="PROPAGATION_NESTED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_NESTED" />
    </bean>

    <bean id="PROPAGATION_NEVER" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_NEVER" />
    </bean>

    <bean id="PROPAGATION_NOT_SUPPORTED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_NOT_SUPPORTED" />
    </bean>

    <!-- This is the default behavior. -->
    <bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
    </bean>

    <bean id="PROPAGATION_REQUIRES_NEW" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW" />
    </bean>

    <bean id="PROPAGATION_SUPPORTS" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_SUPPORTS" />
    </bean>

</blueprint>
注記

これらの Bean 定義を独自の Blueprint XML 設定に貼り付けたい場合は、トランザクションマネージャーへの参照をカスタマイズするようにしてください。つまり、txManager への参照をトランザクションマネージャー Bean の実際の ID に置き換えます。

9.4.4. Java DSL の PROPAGATION_NEVER ポリシーを使用したルートの例

トランザクションポリシーがトランザクションに何らかの影響を与えることを示す簡単な方法は、次のルートに示すように、既存のトランザクションの途中に PROPAGATION_NEVER ポリシーを挿入することです。

from("file:src/data?noop=true")
        .transacted()
        .bean("accountService","credit")
        .transacted("PROPAGATION_NEVER")
        .bean("accountService","debit");

このように使用すると、PROPAGATION_NEVER ポリシーはすべてのトランザクションを必然的に中止し、トランザクションのロールバックにつながります。アプリケーションに及ぼす影響を簡単に確認できるはずです。

注記

transacted() に渡される文字列の値は Bean ID で、伝播の動作名ではないことに注意してください。この例では、Bean ID が伝播動作名と同じになるように選択されますが、必ずしもそうである必要はありません。たとえば、アプリケーションで複数のトランザクションマネージャを使用している場合、特定の伝播動作を持つ複数のポリシー Bean が発生する可能性があります。この場合、伝播動作の後に単に Bean の名前を付けることはできません。

9.4.5. Blueprint XML で PROPAGATION_NEVER ポリシーを使用したルートの例

上記のルートは、以下のように Blueprint XML で定義できます。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="file:src/data?noop=true" />
            <transacted />
            <bean ref="accountService" method="credit" />
            <transacted ref="PROPAGATION_NEVER" />
            <bean ref="accountService" method="debit" />
        </route>
    </camelContext>

</blueprint>

9.5. エラー処理およびロールバック

トランザクションルートで標準の Apache Camel エラー処理手法を使用することもできますが、例外とトランザクションのデマケーションとの間の対話を理解することが重要になります。特に、スローされた例外によって、通常はトランザクションロールバックが発生することを検討する必要があります。以下を参照してください。

9.5.1. トランザクションをロールバックする方法

以下のいずれかの方法を使用して、トランザクションをロールバックできます。

9.5.1.1. ランタイム例外を使用したロールバックのトリガー

Spring トランザクションをロールバックする最も一般的な方法は、ランタイム (チェックされていない) 例外をスローすることです。つまり、例外は java.lang.RuntimeException のインスタンスまたはサブクラスです。java.lang.Error タイプの Java エラーも、トランザクションロールバックをトリガーします。一方、チェックされた例外は、ロールバックをトリガーしません。

以下の図は、Java エラーと例外がトランザクションにどのように影響するかをまとめたものです。ここでは、ロールバックをトリガーするクラスはグレーの影付きです。

camel exceptions
注記

Spring フレームワークには、ロールバックをトリガーする例外としない例外を指定できる XML アノテーションのシステムも提供します。詳細は、『Spring Reference Guide』の「Rolling back」を参照してください。

警告

ランタイム例外がトランザクション内で処理された場合、つまり、例外がトランザクションのデマケーションを行うコードまでパーコレートする機会がある前、トランザクションはロールバックされません。詳細は、「デッドレターキューの定義方法」 を参照してください。

9.5.1.2. rollback() DSL コマンドの使用。

トランザクションルートの途中でロールバックをトリガーする場合は、rollback() DSL コマンドを呼び出してこれを実行できます。これにより、org.apache.camel.RollbackExchangeException 例外が発生します。つまり、rollback()コマンドは、ランタイム例外をスローする標準的なアプローチを使用してロールバックをトリガーします。

たとえば、アカウントサービスアプリケーションでの送金の規模に絶対的な制限が必要であると判断したとします。次の例のコードを使用して、金額が 100 を超えたときにロールバックをトリガーできます。

from("file:src/data?noop=true")
    .transacted()
    .bean("accountService","credit")
    .choice().when(xpath("/transaction/transfer[amount > 100]"))
        .rollback()
    .otherwise()
        .to("direct:txsmall");

from("direct:txsmall")
    .bean("accountService","debit")
    .bean("accountService","dumpTable")
    .to("file:target/messages/small");
注記

前述のルートでロールバックをトリガーすると、無限ループになります。この理由は、rollback() によってスローされた RollbackExchangeException 例外がルートの開始時点で file エンドポイントに伝播するためです。File コンポーネントには、例外がスローされたエクスチェンジを再送信する信頼性機能が組み込まれています。当然ながら再送信時に、エクスチェンジは別のロールバックをトリガーし、無限ループにつながります。次の例は、この無限ループを回避する方法を示しています。

9.5.1.3. markRollbackOnly() DSL コマンドの使用

markRollbackOnly() DSL コマンドを使用すると、例外をスローせずに現在のトランザクションを強制的にロールバックできます。これは、rollback() DSL コマンドの使用。」 の例など、例外をスローする際に望ましくない結果が発生する場合に役立ちます。

以下の例は、rollback() コマンドを markRollbackOnly() コマンドに置き換えることで、rollback() DSL コマンドの使用。」 の例を変更する方法を示しています。このバージョンのルートは、無限ループの問題を解決します。この場合、送金額が 100 を超えると、現在のトランザクションはロールバックされますが、例外はスローされません。ファイルエンドポイントは例外を受け取らないため、エクスチェンジを再試行せず、失敗したトランザクションは静かに破棄されます。

次のコードは、markRollbackOnly() コマンドで例外をロールバックします。

from("file:src/data?noop=true")
    .transacted()
    .bean("accountService","credit")
    .choice().when(xpath("/transaction/transfer[amount > 100]"))
        .markRollbackOnly()
    .otherwise()
        .to("direct:txsmall");
...

ただし、前述のルート実装は理想的ではありません。ルートはトランザクションを正常にロールバックし (データベースは一貫した状態のまま)、無限ループを回避しますが、失敗したトランザクションの記録は保持されません。実際のアプリケーションでは、通常は失敗したトランザクションを追跡する必要があります。たとえば、トランザクションが成功しなかった理由を説明するために、関連する顧客に手紙を書きたいことがあります。失敗したトランザクションを追跡するための便利な方法は、デッドレターキューをルートに追加することです。

9.5.2. デッドレターキューの定義方法

失敗したトランザクションを追跡するには、onException() 句を定義すると、関連するエクスチェンジオブジェクトをデッドレターキューに迂回できます。ただし、トランザクションのコンテキストで使用する場合は、例外処理とトランザクション処理の間に対話がある可能性があるため、onException() 句を定義する方法に注意する必要があります。次の例は、再スローされた例外を抑制する必要があると仮定して、onException() 句を適切に定義する方法を示しています。

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        onException(IllegalArgumentException.class)
            .maximumRedeliveries(1)
            .handled(true)
            .to("file:target/messages?fileName=deadLetters.xml&fileExist=Append")
            .markRollbackOnly();  // NB: Must come *after* the dead letter endpoint.

        from("file:src/data?noop=true")
            .transacted()
            .bean("accountService","credit")
            .bean("accountService","debit")
            .bean("accountService","dumpTable")
            .to("file:target/messages");
    }
}

上記の例では、onException()IllegalArgumentException 例外をキャッチするよう設定され、問題のあるエクスチェンジをデッドレターファイル deadLetters.xml に送信します。当然ながら、この定義を変更して、アプリケーションで発生する例外をキャッチできます。例外の再スロー動作とトランザクションのロールバック動作は、onException() 句の次の特別な設定によって制御されます。

  • handled(true) - 再スローされた例外を抑制します。この特定の例では、再スローされた例外は、ファイルエンドポイントに伝播したときに無限ループをトリガーするため、望ましくありません。markRollbackOnly() DSL コマンドの使用」 を参照してください。ただし、場合によっては、例外の再スローが許容されることがあります (たとえば、ルートの開始位置にあるエンドポイントがリトライ機能を実装していない場合)。
  • markRollbackOnly() - 例外をスローせずに、現在のトランザクションをロールバック用にマークします。この DSL コマンドは、エクスチェンジをデッドレターキューにルーティングする to() コマンドの後に挿入する必要があることに注意してください。そうでない場合は、markRollbackOnly() が処理のチェーンを割り込みするため、エクスチェンジはデッドレターキューに到達しません。

9.5.3. トランザクションの例外のキャッチ

onException() を使用する代わりに、トランザクションルートで例外を処理する簡単な方法は、ルートに doTry() および doCatch() 句を使用することです。たとえば、以下のコードは、無限ループに陥ることなく、トランザクションルートで IllegalArgumentException をキャッチおよび処理する方法を示しています。

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
            .doTry()
                .to("direct:split")
            .doCatch(IllegalArgumentException.class)
                .to("file:target/messages?fileName=deadLetters.xml&fileExist=Append")
            .end();

        from("direct:split")
            .transacted()
            .bean("accountService","credit")
            .bean("accountService","debit")
            .bean("accountService","dumpTable")
            .to("file:target/messages");
    }
}

この例では、ルートは 2 つのセグメントに分割されます。最初のセグメント (file:src/data エンドポイントから) は受信エクスチェンジを受け取り、doTry() および doCatch() を使用して例外処理を実行します。2 つ目のセグメント (direct:split エンドポイントから) は、すべてのトランザクション処理を実行します。このトランザクションセグメント内で例外が発生すると、最初に transacted() コマンドに伝播され、それにより現在のトランザクションがロールバックされ、最初のルートセグメントの doCatch() 句によってキャッチされます。doCatch() 句は例外を再スローしないため、ファイルエンドポイントは再試行せず、無限ループが回避されます。