Java IDL および Java RMI-IIOP テクノロジ:

ポータブルインタセプタ (PI) の使用


最終更新日: 4/25/2002

注: このドキュメントは、高度な知識を持つ CORBA 開発者を対象としています。

JavaTM CORBA Object Request Broker (ORB) は、フック (遮断点) を提供し、ORB サービスはこのフックを使って ORBの通常の実行の流れを遮断することができます。これらの「ポータブルインタセプタ」は、追加の ORB の動作をプラグインするための機構と、クライアントとサーバ間の通信を変更することによって ORB の動作を変更するための機構を提供します。このあとのでは、ポータブルインタセプタのさまざまな使い方について説明します。

ポータブルインタセプタのサポートは、CORBA 仕様に最近追加された重要な機能です。RequestInterceptor を使用すると、ORB が仲介する任意の呼び出しを遮断するポータブル ORB フックを簡単に記述して接続することができます。IORInterceptor を使用すると、CORBA オブジェクト参照に注釈を付けるためのコードを記述できます。

このドキュメントを読む前に、ポータブルインタセプタの仕様 (ptc/2001-03-04) をお読みになることを強くお勧めします。

このドキュメントでは、次のようなトピックについて説明します。


インタセプタの型

現在、登録できるインタセプタには次の 3 つの型があります。それぞれの例についてはこのあとので説明します。


Java での ORBInitializers の登録

ORBInitializer インタフェースを利用すると、インタセプタの登録と ORB の初期化が簡単になります。

インタセプタにより、ORB サービスは ORB プロセスにアクセスして、実質的に ORB の一部になることができます。インタセプタは ORB の一部なので、ORB.init が ORB を返すときインタセプタはすでに登録済みです。ORB.init への呼び出しで返された後は、インタセプタはその ORB に登録できません。

ORBInitializers は Java ORB プロパティを経由して登録されます。ORBInitializer インタフェースを実装する関連オブジェクト ORBInitializer を登録すると、インタセプタが登録されます。ORB は、登録された ORBInitializer をそれぞれ初期化中に呼び出し、インタセプタを登録するために使用する ORBInitInfo オブジェクトを渡します。

このプロパティ名は以下の形式をとります。

org.omg.PortableInterceptor.ORBInitializerClass.<Service>
<Service> は次のプロパティを実装するクラスの文字列名です。
org.omg.PortableInterceptor.ORBInitializer
名前の衝突を防ぐため、逆方向の DNS 命名規則が使用されます。たとえば、X 社に初期化子が 3 つある場合、次のプロパティを定義できます。

注: ORBInitializerClass プロパティに値を関連付けても、すべて無視されます。

ORB.init では、org.omg.PortableInterceptor.ORBInitializerClass で始まる ORB プロパティを収集し、それぞれのプロパティから <Service> の部分を抽出、さらに、オブジェクトはクラス名として <Service> 文字列でインスタンスを生成し、そのオブジェクトで pre_init メソッドおよび post_init メソッドを呼び出します。例外があっても ORB は無視して処理を行います。

インタセプタのスコープに関する注意

インタセプタの順序に関する注意

インタセプタ登録時の注意


PortableInterceptor Current (PICurrent)

PortableInterceptor::Current オブジェクト (これ以降 PICurrent と呼ぶ) は、ポータブルインタセプタが、スレッドのコンテキスト情報を要求コンテキストに転送するために使用する Current オブジェクトです。ポータブルインタセプタは、PICurrent を常に使用する必要はありませんが、インタセプタの遮断点で、クライアントのスレッドコンテキストの情報が必要な場合は、PICurrent を使用してその情報を伝播することができます。PICurrent を使用すると、ORB のスレッドモデルにかかわらず移植性のあるサービスコードを記述できます。

注: PICurrent は通常、CORBA のクライアントコードまたはサーバコードから直接使用されることはありません。一般には、このあと説明する AService というインタセプタの例で紹介されているように、インタセプタベースのサービス実装により使用されます。

PICurrent の取得

PICurrentは、呼び出しを行う前に、ORB::resolve_initial_references ( PICurrent ) の呼び出しによって取得されます。遮断点で、RequestInfo オブジェクトに対する get_slot 操作を実行すると、スレッドスコープから要求スコープに移動された PICurrent に関するデータを取得できます。PICurrent は、resolve_initial_references を使用して取得することもできますが、この場合はインタセプタのスレッドスコープの PICurrent が取得されます。

要求スコープとスレッドスコープの比較

スレッドスコープの PICurrent (TSC) は、スレッドのコンテキスト内に存在する PICurrent です。要求スコープの PICurrent (RSC) は、要求に関連付けられている PICurrent です。クライアント側では、要求の開始時に、スレッドスコープの PICurrent が、スレッドコンテキストから要求スコープの PICurrent に論理的にコピーされ、ClientRequestInfo オブジェクトに接続されます。サーバ側では、要求スコープの PICurrentServerRequestInfo に接続されてから、要求の処理が行われます。要求スコープの PICurrent は、receive_request_service_contexts 遮断点のリストが処理されたあとに、スレッドスコープの PICurrent に論理的にコピーされます。PICurrent のスコープの詳細については、Updated Interceptors specification の セクション 21.4.4.5 の「Flow of PICurrent between Scopes」を参照してください。


ポータブルインタセプタの例

ここでは、ロギングサービスアプリケーションの例について説明します。このアプリケーションのサンプルコードは、複雑かつ「特殊なケース」を扱うため、非常に複雑になっています。このサンプルアプリケーションでは以下のシナリオを扱います。

  1. 呼び出しをログに記録するロギングサービス。クライアントとサーバのどちらもこのロギングサービスを明示的に使用しない

  2. クライアントからサーバに情報を渡す「空の」サービス。クライアントとサーバは、このサービスを明示的に使用するが、インタセプタを使用してサービスが実装されていることを認識していない

注: 以下の例では、コードを簡単に試してセットアップできるように、ORBInitializer を明示的に登録しています。一般的にはこのような処理は行いません。通常、この情報は、アプリケーションの起動時に -D プロパティとして Java 仮想マシンに渡されます。この方法を使うと、サービス (たとえばロギングサービス) が存在するかどうか、あるいはアプリケーションが明示的に使用するサービス (たとえば AService インタフェース) がインタセプタとして実装されるかどうかという事実にアプリケーションが束縛されなくなります。

ロギングサービスの例を紹介する目的は、遮断点内から発信呼び出し (つまり、CORBA 参照に対する呼び出し) を行うときに無限再帰を回避する方法を説明することです。これは、あらゆるケースを網羅しようとすると、かなり複雑になることがあります。

「空の」サービスの例を紹介する目的は、コンテキスト情報をクライアントとサーバの間でやり取りするサービスの実装方法を説明することです。

クライアントとサーバの間でのコンテキスト情報のやり取り

典型的なインタセプタベースのサービスでは、コンテキスト情報をクライアントとサーバの間でやり取りします。AService の例は、この情報がクライアントのスレッドからクライアントのインタセプタに流れ、ワイヤを経由してサーバのインタセプタに入り、サーバントのスレッドに届くというフローや、その逆のフローについて説明するものです。

サービスがインタセプタを使用して実装されているということを、クライアントとサーバントがどちらも意識しないという点が重要です。そのどちらも、ローカルオブジェクト参照 (この例では aService 参照) を介してサービスと対話しているだけです。

AService の説明図

AService の説明図

AService の説明図にある各ステップについて、以下に説明します。

  1. クライアントが aService.begin() を呼び出します。

    1.a. aService.begin() は、PICurrent に予約済みのスロットに、サービスのコンテキスト情報を設定します。

  2. クライアントは、何かの参照上にあるメソッド (つまり、ref.method()) を呼び出します。この呼び出しは、aService.begin() を呼び出した以降のサービスコンテキスト内で行われます。

  3. ref.method call のために、send_request に入ります。

  4. 呼び出された参照がサービスとの対話を必要とするかどうかを判別するために、get_effective_component(s) が呼び出されます。

  5. ステップ 1a で設定されたコンテキストを取得するために、get_slot が呼び出されます。PICurrent に設定されたスロットが論理的にコピーされて、ClientRequestInfo がそのスロットを利用できるようになります。

  6. サービスインタセプタが、適切なコンテキスト (ほとんどの場合、ステップ 1a で設定されたコンテキストと、ステップ 4 で取得されたコンポーネントによって決まる) を含むサービスコンテキストを追加します。サービスインタセプタは add_request_service_context を使用して、ワイヤ上を送信される要求にこのコンテキスト情報を追加します。

  7. ref.method request がサーバに到着し、receive_request_service_contexts 遮断点を起動します。

  8. get_request_service_context を使用して、ステップ 6 で追加されたサービスコンテキストが取得されます。

  9. ServerRequestInfo.set_slot を使用して、ステップ 8 で取得されたサービスコンテキストが、論理的なスレッドローカルのデータに転送されます。

  10. receive_request に入ります。

  11. 「ref」サーバントの「メソッド」に入ります。

  12. サーバントのメソッドは、aService.verify() を呼び出してサービスと対話します。

  13. サービスは、PICurrent に対し get_slot を使用して、クライアント側から送信されたコンテキスト情報を取得します。さらに、サービスは、PICurrent に対して set_slot を使用して、返信するためのコンテキスト情報を設定することもあります。

  14. サーバントの処理が完了すると、send_* 遮断点に入ります。サービスが返信用のコンテキスト情報を設定した場合は、返信の内容にサービスコンテキストが追加されます。この点は、この例では示されていません。

  15. 応答がクライアントに到着すると、receive_* 遮断点に入ります。サービスが返信用のコンテキスト情報を設定した場合は、この時点でその情報が取得されます。この点は、この例では示されていません。

インタセプタの発信呼び出し時の無限再帰を回避する

サービスによっては、遮断点内から別の CORBA オブジェクト参照に対する呼び出しを行う必要があります。遮断点内から発信呼び出しを行うときには、無限再帰を回避するための処置をとる必要があります。それらの発信呼び出しが遮断点を経由するからです。LoggingService の例では、このケースについて説明します。

LoggingService の例は、クライアントプログラムに登録された ClientRequestInterceptors と、サーバプログラムに登録された ServerRequestInterceptors とで構成されます。これらのインタセプタは、クライアントおよびサーバから LoggingService の実装に情報を送信します。LoggingService の実装は、その情報をログに記録します。

ただし、LoggingService の実装はそれ自体が CORBA サーバであるので、ロガーに対する呼び出しはログに記録しないようにする必要があります。次の図は、無限再帰を回避するためにとるべき処置を示しています。

次の図では、インタセプタ内から外部のロガーを呼び出すという、再帰を回避するべき最も単純なケースを示します。ここで説明する手順は、クライアント ORB は ClientRequestInterceptors だけを含み、サーバ ORB は ServerRequestInterceptors だけを含み、LoggingService はクライアント ORB とサーバ ORB の両方に対して外部にあるというケースに役立ちます。

LoggingService の説明図

ロギングサービスの説明図

LoggingService の説明図にある各ステップについて、以下に説明します。

  1. クライアントは、何かの参照上にあるメソッドを呼び出します。

  2. ステップ 1 で呼び出されたメソッドのために、クライアントインタセプタの send_request メソッドに入ります。

  3. 発信呼び出しを示すために予約済みの PICurrent 上のスロットが true に設定されます。

  4. get_slot により、ClientRequestInfo 上の同じスロットがチェックされます。この値は、ステップ 1 でのクライアントのスレッドの状態を表しているため、設定されていません。したがって、ロガーに対する呼び出しが実行されることになります。

  5. ステップ 4 で行われたロガー呼び出しのために、send_request に再帰的に入ります。

  6. 発信呼び出しを示すために予約済みの PICurrent 上のスロットが true に設定されます。このスロットは、いつも無条件で設定されることに注意してください。それは、発信呼び出しを行うインタセプタが 2 つ以上あるときに必要な処置です。各インタセプタは他のインタセプタの存在を認識していないので、いつも PICurrent を設定することにします。

  7. get_slot により、ClientRequestInfo 上のスロットがチェックされます。今回は、値が設定されています (ステップ 3 より)。したがって、この呼び出し (ロガーそのものに対する呼び出し) はログに記録しません。ステップ 5 で入った send_request ポイントが終了します。

  8. ORB は、ステップ 4 で行われたログ呼び出しをロギングサービスに送信します。

  9. ステップ 2 で入った send_request ポイントが終了します。ORB は、ステップ 1 で行われた最初のクライアント呼び出しを、サーバの該当する参照に対して送信します。

  10. サーバの receive_request_service_contexts ポイントに入ります。これにより、着信要求がログに記録されます。発信呼び出しを行うクライアントインタセプタが含まれておらず、ロガーの実装そのものも含まれていないサーバの場合、再帰を回避するスロットを設定する必要はありません。この図は、そのようなケースを表しています。

  11. -   13.    したがって、サーバ側の遮断点にはすべて余分なチェックなしで入り、クライアントによって最初に呼び出されたメソッドからの応答がクライアント ORB に返信されます。

  12. クライアントの receive_* ポイントに入ります。この時点で発信呼び出しを行う必要がある場合には、ステップ 2 〜 9 と同様の処理を行います。

発信呼び出しの参照先が同じ場所にある場合に再帰を回避する

次の図は、クライアントによって呼び出される参照と同じ ORB に LoggingService があるというケースを示しています。一般に、特定のオブジェクト参照が、その ORB によってホストされている他のオブジェクトと同じ場所にないということを識別するのは不可能です。したがって、あらゆるケースを網羅するために、さらに処置をとる必要があります。

この図は、サーバ側だけを示したものです。クライアント側の手順は、前の図にある手順と同じです。

LoggingServiceColocated の説明図

LoggingServiceColocated の説明図

LoggingServiceColocated の説明図にある各ステップについて、以下に説明します。

  1. クライアント ORB から要求がサーバに到着し、receive_request_service_contexts 遮断点を起動します。

  2. 発信呼び出しを示すために予約済みの PICurrent 上のスロットが true に設定されます。

  3. ServerRequestInfo.get_request_service_context を使用して、発信呼び出しを示すサービスコンテキストが存在するかどうかがチェックされます。サービスコンテキストが存在しないため、遮断点内からロガーが呼び出されます。

  4. ロガー要求のために、send_request 遮断点に入ります。

  5. get_slot により、発信呼び出しインジケータがチェックされます。

  6. 発信呼び出しインジケータは (ステップ 2 で) 設定されているため、add_request_service_context を使用して、発信呼び出しを示すサービスコンテキストが追加されます。クライアントのスレッドとサーバのスレッドの間には論理的な関係がないので、この処理が必要になります。

  7. ロガー呼び出しが receive_request_service_contexts に到着します。

  8. 発信呼び出しを示すために予約済みの PICurrent 上のスロットが true に設定されます。

  9. ServerRequestInfo.get_request_service_context を使用して、発信呼び出しを示すサービスコンテキストが存在するかどうかがチェックされます。サービスコンテキストが存在するため、これ以上の処理は行われません。receive_request_service_contexts から出ます。

  10. ロガー要求が receive_request に進みます。receive_request 遮断点でロガーが呼び出される場合は、receive_request_service_context 内で行われるのと同様の処理が必要です。この処理は図には示されていません (ただし、コード例ではすべての遮断点をログに記録する)。

  11. ロガー要求が LoggingService サーバントに到着します。このサーバントにより、最初のクライアント要求 (ロガーの呼び出しではない) がログに記録されます。

  12. ロガー要求が ServerRequestInterceptor.send_* (ほとんどの場合は send_reply) に進みます。

  13. ロガー要求が ClientRequestInterceptor.receive_* (ほとんどの場合は receive_reply) に進みます。

    ステップ 13 の後、ステップ 3 で残っている元の要求が処理されることになります。

この例で説明した内容の要点は、クライアントインタセプタとサーバインタセプタの両方を、発信呼び出しを示す PICurrent スロットおよびサービスコンテキストと一緒に使用する必要があるということです。

複数の ORB を使用することで再帰を回避する

再帰を回避するもっと簡単な方法として、発信呼び出しの参照先を、ロギングインタセプタが登録されていない別の ORB に関連付けるという方法があります。こうすれば、発信呼び出しがインタセプタに入っていくことがありません。

これは簡単な解決方法に思えますが、一般にインタセプタは、起動時に VM に渡すプロパティによって登録されます。そうすると、その VM 内で作成されるすべての ORB はすべてのインタセプタを含んでいることになるため、この方法はうまくいきません。

この方法がうまくいくようにするには、ORB.init の際にクライアントコード内でインタセプタを明示的に登録します。しかし、そのような方法でインタセプタベースのサービスを登録することは一般的でないため、推奨されていません。


コード例

これまでに紹介した図で説明されているコードが、以下のファイルに含まれています。これらの例をコンパイルして実行する方法は、コードの後に記載します。この例に含まれるファイルは次のとおりです。



アプリケーションのコンパイルと実行

ロギングの例とサービスの例が含まれているこれらのコード例は、次のような Makefile を使用してコンパイルおよび実行することができます。


次に、上記の Makefile を使用して Solaris オペレーティングシステム上でこの例をビルドし、実行するための手順を紹介します。ここで紹介するコマンドを、コマンドプロンプトから実行してください。% 記号は、記載されているコマンドをコマンドプロンプトから実行するべきであることを示すために使用しています。

  1. % make clean
  2. % make build
  3. % make runorbd &
  4. % make runloggingservice &
  5. % make runarbitraryobject &
  6. % make runclient

    このステップの後、次のような出力が表示されます。

      resolve send_request
      resolve receive_reply
      arbitraryOperation1 send_request
      Service present: 1
      arbitraryOperation1 receive_reply
      arbitraryOperation2 send_request
      Service present: 1
      arbitraryOperation2 receive_other
      arbitraryOperation3 send_request
      Service not present
      arbitraryOperation3 receive_reply
      arbitraryOperation3 send_request
      Service present: 2
      arbitraryOperation3 receive_exception
      Client done.
      

  7. % jobs

    このステップの後、次のような出力が表示されます。

      [1]   Running                 make runorbd &
      [2]-  Running                 make runloggingservice &
      [3]+  Running                 make runarbitraryobject &
      

  8. % kill %2 %3
  9. % make runcolocatedservers &
  10. % make runclient

    このステップの後、次のような出力が表示されます。

      log receive_request_service_contexts
      log receive_request
      resolve send_request
      log send_reply
      log receive_request_service_contexts
      log receive_request
      resolve receive_reply
      log send_reply
      log receive_request_service_contexts
      log receive_request
      arbitraryOperation1 send_request
      log send_reply
      arbitraryOperation1 receive_request_service_contexts
      arbitraryOperation1 receive_request
      Service present: 1
      arbitraryOperation1 send_reply
      log receive_request_service_contexts
      log receive_request
      arbitraryOperation1 receive_reply
      log send_reply
      log receive_request_service_contexts
      log receive_request
      arbitraryOperation2 send_request
      log send_reply
      arbitraryOperation2 receive_request_service_contexts
      arbitraryOperation2 receive_request
      log receive_request_service_contexts
      Service present: 1
      arbitraryOperation2 send_reply
      log receive_request
      arbitraryOperation2 receive_other
      log send_reply
      log receive_request_service_contexts
      log receive_request
      arbitraryOperation3 send_request
      log send_reply
      arbitraryOperation3 receive_request_service_contexts
      arbitraryOperation3 receive_request
      Service not present
      arbitraryOperation3 send_reply
      log receive_request_service_contexts
      log receive_request
      arbitraryOperation3 receive_reply
      log send_reply
      log receive_request_service_contexts
      log receive_request
      arbitraryOperation3 send_request
      log send_reply
      arbitraryOperation3 receive_request_service_contexts
      arbitraryOperation3 receive_request
      Service present: 2
      arbitraryOperation3 send_exception
      log receive_request_service_contexts
      log receive_request
      arbitraryOperation3 receive_exception
      log send_reply
      Client done.
      

  11. % jobs

    このステップの後、次のような出力が表示されます。

      [1]-  Running                 make runorbd &
      [4]+  Running                 make runcolocatedservers &
      

  12. % kill %1 %4
  13. % make clean


関連項目:
ORBInitInfo
org.omg.PortableInterceptor パッケージ
ポータブルインタセプタの仕様 (ptc/2001-08-31)


ポータブルインタセプタの詳細については、分散コンピューティングに関する Java Developer's Forum (http://forum.java.sun.com/) を参照してください。