1.2.2. Object Broker

この節ではObject Brokerを使ったCORBAアプリケーションのプログラミング・開発について説明します。

1.2.2.1. ORB上のプログラム

ORB上でのプログラムは、JavaやC++の通常のプログラミングとほとんど変わりません。 メソッドを呼び出したとき、通常のアプリケーションでは同一プロセス内で実行されますが、ORBを使用するアプリケーションではORBの仲介によりサーバプロセスで実行されます。
ORBのクライアントは、通常のアプリケーションと同様にオブジェクトに対してメソッドを呼び出しますが、この呼び出しに用いられるオブジェクトは、メソッド処理の実装部を持たずに、ORBを介したメッセージの送受信を行って、あたかも同一プロセス内で処理が行われたかのようにふるまいます。この通信に関する部分のプログラムのことをスタブと呼びます。
一方、サーバ側にもORBからメッセージを受け取り、メソッド処理の結果を返すための通信部分を持ちます。この通信部分のプログラムのことをスケルトンといいます。メソッド処理実装部をスケルトンからの派生クラスとして実装することで、メソッド処理実装部とスケルトンを関連付けます。


図1.2.2.1-1

既存のクライアントプログラムやサーバプログラムを利用する場合を除いて、クライアントプログラム、サーバプログラムの両方を作成する必要があります。

1.2.2.2. ORBの動作

ORBはクライアントから実行されたサーバオブジェクトへの要求を仲介します。クライアントはサーバオブジェクトのオペレーション呼び出しおよび属性の操作を要求することができます。また、サーバオブジェクトのオペレーションの処理中に他のサーバオブジェクトを呼び出すときは、そのサーバオブジェクトはクライアントにもなります。


図1.2.2.2-1

本文中、オブジェクトリファレンスを持ち、クライアントから呼び出すことのできるオブジェクトをCORBAオブジェクトと呼びます。また、オブジェクトの実装を提供するプログラムを、サーバプログラムと呼びます。クライアントプログラム内には、サーバプログラム内のCORBAオブジェクトをあらわすためのJava/C++オブジェクト(スタブ)が作成されます。クライアントは、このスタブオブジェクトを通してサーバオブジェクトにアクセスします。

1.2.2.3. オブジェクトリファレンス

実装オブジェクト
実装オブジェクトとは、IDLで定義したインタフェースを実装したサーバオブジェクトです。サーバントと呼ぶこともあります。
実装オブジェクトは、サーバプロセス中に1つだけ存在します。
POAに設定した方針(ポリシー)によって、実装オブジェクトのふるまいが決められます。通常、実装オブジェクトは、クライアントから呼び出される前に生成し、POAと関連づけられます。POAの方針がサーバントマネージャ(org.omg.PortableServer.ServantManager)を利用するように設定されているときは、クライアントから呼び出しを受けた時点で生成することもできます。
オブジェクトリファレンスとは
オブジェクトリファレンスは、実装オブジェクトを参照するオブジェクトで、アクセスに使うプロキシです。実装オブジェクトはサーバプロセス中に1つだけ存在しますが、オブジェクトリファレンスは他のプログラム(プロセス)に渡すことができ、複数のコピーを同時に複数のプログラム上で使用することができます。
これらのメソッドからは、1つのオブジェクトリファレンスが返却されます。Object Brokerではオブジェクトリファレンスの作成は、サーバプロセスをはじめ、クライアントおよびインストーラなど第三者のプログラムでも可能です。ただし、実装オブジェクトに対応付けられたオブジェクトリファレンスは、通常はサーバプロセス上でしか作成することができません。
オブジェクトリファレンスの取得
クライアントからサーバプロセス上の実装オブジェクトを呼び出すためには、このサーバオブジェクトのオブジェクトリファレンスを取得する必要があります。クライアントのプログラミング上はオブジェクトリファレンス=スタブと考えてもかまいません。
サーバにおいても、クライアントからの要求によって作成されたオブジェクトを戻り値として返すときなどにオブジェクトリファレンスが必要になります。
オブジェクトリファレンスの取得には、次の方法があります。
サーバでは、名前サービスのbind、rebindを呼び出してオブジェクトリファレンスを登録します。クライアントでは、名前サービスのresolveを呼び出してオブジェクトリファレンスを取得します。また、オブジェクトのURLを指定してorg.omg.CORBA.ORB.string_to_objectを呼び出すことで名前サービスからオブジェクトリファレンスを取得することもできます。
オブジェクトリファレンスは、内部構造がORBの実装に依存するため、サーバとクライアント間でオブジェクトリファレンスをそのままの形式で渡すことはできません。サーバでは、オブジェクトリファレンスをorg.omg.CORBA.ORB.object_to_stringで文字列形式に変換し、ファイルなどを介してクライアントに受け渡します。クライアントでは、文字列形式のオブジェクトリファレンスをorg.omg.CORBA.ORB.string_to_objectで復元します。これにより、名前サービスが使用できない場合に通信が可能となります。

1.2.2.4. プログラム作成の流れ

ORBを利用したアプリケーション作成のながれを簡単に説明します。
  1. インタフェース定義を行います。
  2. 1.で作成したIDL定義ファイルをIDLコンパイラでコンパイルします。
  3. アプリケーションの処理を記述します。
  4. サーバプログラムおよびクライアントプログラムの両者を記述します。
  5. Java/C++コンパイラによりコンパイルします。
インタフェース定義はCORBAの仕様で定められているインタフェース定義言語(Interface Definition Language:IDL)で記述します。特定のプログラミング言語(C、C++、Java、SmallTalkなど)に依存しない形式でメソッドの引数や戻り値などのインタフェースを記述するための言語です。IDLの言語仕様にしたがって記述されたインタフェースをIDLコンパイラにかけることで、特定のプログラミング言語にマッチしたスタブ/スケルトンを自動生成します。スタブ/スケルトンをリンクすることで比較的簡単にクライアント/サーバアプリケーションを作成できます。
Object Broker Java TM は、Java言語仕様に基づいたソースコードを出力するコンパイラを提供しています。
Object Broker C++は、C++言語仕様に基づいたソースコードを出力するIDLコンパイラを提供しています。

1.2.2.5. Java言語を用いたアプリケーションの作成と実行

開発環境の設定
Javaプログラムを作成するためには、JDKもしくはJava IDE(統合開発環境)が必要になります。WebOTX開発環境を使用することもできます。これらの設定方法については、各製品の説明書を良くお読みください。
CLASSPATH環境変数
Object Broker Javaのアプリケーションを作成するためには、以下のファイルをCLASSPATH環境変数に登録する必要があります。
Windowsでは、これらのファイルは<WebOTXインストールフォルダ>\modulesに含まれています。 HP-UX、Solaris、Linuxの場合は、/opt/WebOTX/modulesに含まれています。
PATH環境変数
Object Broker Javaのアプリケーションを作成するためには、以下のディレクトリをPATH環境変数に登録する必要があります。
ORB実装クラスの指定
Object Broker Javaのアプリケーションを実行するためには、ORB実装クラスを指定する必要があります。以下のいずれかの方法で指定してください。

1.2.2.6. Object Broker C++の機能

本項の記述は、Object Broker C++にのみ当てはまります。
オブジェクトリファレンスの取得
クライアントがあるCORBAオブジェクトを呼び出すためには、このCORBAオブジェクトのオブジェクトリファレンスを取得する必要があります。クライアントのプログラミング上はオブジェクトリファレンス=スタブと考えてもかまいません。 一方、サーバにおいても、クライアントからの要求によって作成されたオブジェクトを戻り値として返すときなどにオブジェクトリファレンスは必要になります。 オブジェクトリファレンスの取得方法は3通りあります。
  1. オブジェクト呼び出しの戻り値、引数などで取得する。
    1. 一般のオブジェクト呼び出しの戻り値、引数で取得する。
      ユーザが作成した任意のオブジェクトリファレンスを返すオペレーションを利用し取得する方法です。
    2. CORBA::ORB::resolve_initial_references の戻り値として取得する。
      CORBA::ORB::resolve_initial_references オペレーションはサービス識別子を引数としてとり、指定したサービスに対する初期オブジェクトリファレンスを戻り値として返します。
      calc2サンプルのクライアントプログラム (エラー処理は省略)
      int main (int argc, char **argv) {
             CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "", env);
             CORBA::Object_var obj = 
                     orb->resolve_initial_references("NameService", env);
             CosNaming::NamingContext_var nmctx =
                     CosNaming::NamingContext::_narrow(obj);
             ...
      }
      
    3. 名前サービスの戻り値として取得する。
      名前サービスに登録されたオブジェクトリファレンスを取得するためにはCosNaming::NamingContext::resolve オペレーションを使用します。プログラム例は「名前によるオブジェクトリファレンスの参照」を参照してください。
  2. 文字列形式から復元する。
    サーバで作成したオブジェクトリファレンスを文字列に変換してファイルなどに書き出し、クライアントで読み取るときに文字列形式から復元する場合に用います。オブジェクトリファレンスは内部構造がORBの実装に依存するため、サーバとクライアント間でオブジェクトリファレンスをそのままの形式で渡すことはできません。サーバでは、オブジェクトリファレンスをCORBA::ORB::object_to_string で文字列形式に変換します。クライアントでは、文字列形式のオブジェクトリファレンスをCORBA::ORB::string_to_object で復元します。これにより、CORBA2.0に準拠した他社のORBとの通信が可能となります。
  3. POAのオペレーションを使用してオブジェクトリファレンスを得る
    POAのオペレーションを使用して、あるサーバントに対応するオブジェクトリファレンスを得たり、新たにオブジェクトリファレンスを作成したりすることができます。
    他社のORBとの接続は、接続のための初期オブジェクトリファレンスを文字列形式で取得し、以後、そのオブジェクトに対する呼び出しを通して順次必要なオブジェクトリファレンスを取得することで可能になります。初期オブジェクトに関してはプログラムに埋め込む、ファイルで転送する、ORB以外の通信で転送するなどの方法が考えられます。

CORBAオブジェクトと実装オブジェクト
CORBAオブジェクトはPortableServer::POA::create_reference またはPortableServer::POA::create_reference_with_id などのPOAクラスのメソッドにより作成されます。作成時に1つのオブジェクトリファレンスが返却されます。WebOTX Object BrokerではCORBAオブジェクトの作成はサーバプロセスをはじめ、クライアントおよびインストーラなど第三者のプログラムでも可能です。ただし、実装オブジェクトに対応付けられたオブジェクトリファレンスは、通常はサーバプロセス上でしか作成することができません。
オブジェクトリファレンスは、「オブジェクトリファレンスの取得」で列挙した方式により他のプログラムに渡すことができます。オブジェクトリファレンス自体はJavaまたはC++オブジェクトですが、これはCORBAオブジェクトの実体ではなく、アクセスに使うプロクシですので、複数のコピーが同時に複数のプログラム上で利用できます。実体となる実装オブジェクト(サーバント)はサーバプロセス中に1つだけ存在します。
サーバプロセス上では実装オブジェクト(サーバント)を作成します。
1つのCORBAオブジェクトに対して1つのJavaまたはC++実装オブジェクト(サーバント)が存在します。実装オブジェクト(サーバント)のふるまいはPOAの方針に依存します。通常、POAに対応づける実装オブジェクトは、CORBAオブジェクトへの呼び出しが発生する前に作成します。PortableServer::ServantManager を利用するPOAのときは、呼び出しが発生した時に作成することもできます。

サーバオブジェクトの活性化
WebOTX Object Broker C++ ではIDLコンパイラが生成するスケルトンクラスの_thisメソッドは、基底クラスであるPortableServer::DynamicImplementationクラスの_thisメソッドをオーバーライドしています。
スケルトンクラスの_thisメソッドは、インタフェース固有の型("インタフェース名"_ptr)でオブジェクトリファレンスを返します。
リクエスト処理中にこのメソッドを呼び出したときは、呼び出し対象のオブジェクトを返します。
リクエスト処理以外(main関数など)の場所でこのメソッドを呼び出したとき、以下の動作をします。
POAの各方針については、リファレンスマニュアル-「POA」を参照してください。

タイムアウトの設定方法
実用的なアプリケーションでは、ネットワークエラーやサーバのクラッシュを検出する必要があります。ネットワーク環境下では、これらの状況の検出はタイムアウトによって行われるのが普通です。WebOTX Object Broker では、メソッドを呼び出してから応答が返る前までの間に、何かしらのエラーが起きたと判断するまでの時間を設定するAPIを提供します。
WebOTX Object Broker C++のタイムアウト設定としては以下のものがあります。
  1. スレッド単位でタイムアウトを設定する。
  2. スレッド単位のタイムアウトを明示的に設定しないときに使われる初期値をプロセス単位で指定する。
  3. また、プロセス単位の設定を省略したときに、すべてのプロセスで一律に使われるシステム単位の設定をする。
クライアントでのタイムアウト設定
サーバでのタイムアウト設定

フック
WebOTX Object Broker C++ のフックには以下の種類があります。
  1. メッセージパケットを操作するフック
  2. メソッドインプリメンテーション呼びだし前後に組み込むフック
  3. サーバのクライアントへのリプライ送信後に組み込むフック
  4. 呼び出すオブジェクトをサーバ側で変更するためのフック
  5. クライアントとの接続が切れたときに呼ばれるフック


図1.2.2.6-1

フックのログ出力:
   ロギング機構の設定([関連製品 > Object Broker C++ > 運用ガイド > 1. 環境設定について ]を参照)でロギングのモードを“INFORMATION”にすることで、ユーザ定義関数の処理に移った/抜けたという情報をログファイルに出力することができます。


メッセージパケットを操作するフック
メッセージパケットを操作するフックは4種類あります。
  1. クライアントがサーバにリクエストメッセージを送信する直前のフック
  2. サーバがリクエストメッセージを受け取った直後のフック
  3. サーバがリプライメッセージを送信する直前のフック
  4. クライアントがサーバからのリプライメッセージを受け取った直後のフック
これらを使うと、メッセージパケットを書き換える サーバメソッドを呼び出さずにリプライメッセージを作成することができます。
ユーザ定義関数を登録するには、Ob_MesBufHookオブジェクトを作成します。このとき、上記a.からd.のどれなのかを示す値をコンストラクタの引数として渡します。設定する値は以下のとおりです。
表1.2.2.6-1
フックの種類
クライアントがサーバにリクエストメッセージを送信する直前のフック CLNT_PRE_SEND
サーバがリクエストメッセージを受け取った直後のフック SERV_AFT_RECV
サーバがリプライメッセージを送信する直前のフック SERV_PRE_SEND
クライアントがサーバからのリプライメッセージを受け取った直後のフック CLNT_AFT_RECV
つぎにフックオブジェクトに関数を登録します。
   a.とd.に挿入できるユーザ定義関数インタフェースはつぎのとおりです。

void func(Ob_MesBuf&, CORBA::Request_ptr, CORBA::Environment&);
b.とc.に挿入できるユーザ定義関数インタフェースはつぎのとおりです。
void func(Ob_MesBuf&, CORBA::Environment&);
フックオブジェクトには複数の関数が登録でき、関数は登録された順番に呼び出されます。また、フックオブジェクトも複数作成することができ、作られた順番にすべてのオブジェクトに登録されている関数が呼び出されます。関数はフックオブジェクトのライフタイムの間だけ登録されています。
ユーザ定義関数の登録のしかたと、a.とd.のフックを使った例を次に示します。
a.のフックを使う場合、IIOP::MessageHeeaderとIIOP::ReplyHeader(GIOP1.2の場合はIIOP::RequestHeader_1_2)各メンバに値を設定する必要があります。
// クライアントがリクエストを送信する前に呼び出すフック
void clnt_pre_send_hook(Ob_MesBuf& mb, CORBA::Request_ptr req, CORBA::Environment& env)
{
       // メッセージバッファの内容をpacket.logファイルに出力する
       mb.dump("packet.log");
       // リクエストメッセージに対するリプライメッセージをクライアントで
       // 作成し、サーバメソッドを呼ばないようにする
       ...
       // リプライメッセージは、Ob_MesBuf& CORBA::Request::__reply()に
       // 書き込む
       IIOP::MessageHeader ihead;
       ihead.flags = (CORBA::Octet)((Obi_my_byteorder()&1)|(ihead.flags&~1));
       //リプライメッセージを表す
       ihead.message_type = IIOP::Reply;
       //GIOPマイナーバージョン1の場合
       ihead.GIOP_version.minor = 1;
       CORBA::Ulong* message_size_addr = 0;
       if(!(ihead._encode(req->__reply(), message_size_addr))) {
          //error処理
       }
       if(!(req->__reply().begin_encode_message(message_size_addr))) {
          //error処理
       }
       IIOP::ReplyHeader rhead;
       //rhead.service_context  *必要ならServiceContextを設定
       rhead.request_id = リクエストメッセージのrequest_idと同じ値を設定
       //通常のリプライ(エラーなし)の場合
       rhead.reply_status = IIOP::NO_EXCEPTION;
       rhead._encode(req->__reply());
       CORBA::Long nRet;
       ...
       req->__reply() <<= nRet;
       ...
       //戻り値等のencode終了後に呼ぶ
       req->__reply().end_encode_message();
     
       //サーバメソッドを呼ばないようにする
       req->__not_call();
     
}
// クライアントがリプライを受信した後に呼び出すフック
void clnt_aft_recv_hook(Ob_MesBuf& mb, CORBA::Request_ptr req, CORBA::Environment& env)
{
       // clnt_pre_send_hookで作ったリプライメッセージの内容を
       // packet.logファイルに出力する
       req->__reply().dump("packet.log");
      
       // __not_call()にしてあるときはreq->__reply()の返り値を
       // デコードするので、req->__reply()をmbにコピーする必要はない
}
main()
{
       ...
       Ob_MesBufHook hook(CLNT_PRE_SEND);
       hook <<= clnt_pre_send_hook;
       Ob_MesBufHook hook2(CLNT_AFT_RECV);
       hook2 <<= clnt_aft_recv_hook;
       ...
}
サーバ側のフックも同様の手順で登録できます。

メソッドインプリメンテーション呼びだし前後に組み込むフック
サーバスケルトンからメソッドを呼び出す前後で、引数やリターン値を書き換えたり、オブジェクトを差し替えたりすることができます。このフックはインタフェースに依存します。
以後の説明では、
"interface"はIDLで定義したインタフェース名
"INTERFACE"はIDLで定義したモジュール名とインタフェース名を大文字にして'_'でつないだもの
"METHOD"はIDLで定義したメソッド名を大文字にしたもの
を表すとします。

ユーザ定義関数を登録するには、まず、インタフェースごとにIDLコンパイラによって自動生成されるフックのオブジェクトを作成します。そのとき、呼び出しの前か後かを示す値をコンストラクタの引数として渡します。引数として渡す値は以下のとおりです。
表1.2.2.6-2
フックの種類
メソッドインプリメンテーション呼びだし前のフック OB_"INTERFACE"_PRE_"METHOD"
メソッドインプリメンテーション呼びだし後のフック OB_"INTERFACE"_AFT_"METHOD"
つぎにフックオブジェクトに関数を登録します。 ここに登録できるユーザ定義関数インタフェースは次に示す規則で決定されます。
表1.2.2.6-3
引数 タイプ 意味
第一引数 インタフェース_ptr型 オブジェクトへのポインタ
第二引数 オペレーションのリターン型 リターン値
それ以降 オペレーションの引数型 引数
最後の引数 CORBA::Environment& 例外
登録するときはOb_ProcAddr型でキャストする必要があります。
フックオブジェクトには複数の関数が登録でき、関数は登録された順番に呼び出されます。また、フックオブジェクトも複数作成することができ、作られた順番にすべてのオブジェクトに登録されている関数が呼び出されます。 関数はフックオブジェクトのライフタイムの間だけ登録されています。
以下に例を示します。
/* IDL */
interface MyIntf {
       string myop(in long
l);
};
/* C++ */
void hook_func(MyIntf_ptr& o, char* ret, CORBA::ULong l, CORBA::Environment& env)
{
       ...
}
// OB_MYINTF_PRE_MYOPはmyop()を呼び出す前のフックに対する値。
// 呼び出し後はOB_MYINTF_AFT_MYOP。
main()
{
       ...
       Ob_MyIntfHook hook(OB_MYINTF_PRE_MYOP);
       hook <<= (Ob_ProcAddr)hook_func;
       ...
}

サーバのクライアントへのリプライ送信後に組み込むフック
特定の実装メソッドが呼ばれたときに、リプライ送信後の操作を追加することができます。リプライ送信後に呼び出したいフックは、実装メソッド内で指定します。
ユーザ定義関数を登録するには、Ob_SentHookオブジェクトを作成するときに関数のアドレスを引数として渡します。
このフックは他のフックとは異なり、関数を1つだけ登録することができます。そして、フックオブジェクトのライフタイムに関係なく、登録した関数を一度呼び出すと関数の登録が解除されます。 このフックに登録できるユーザ定義関数インタフェースはつぎのとおりです。
void func(CORBA::Environment&);
サーバを終了させるメソッドの例を示します。動作の概要は以下のとおりです。
メソッド内でフック関数をセットします。
クライアントにリプライメッセージを送信したあとでフック関数が呼び出されます。
呼び出されたフック関数の中でexitします。
// IDL
interface MyIntf {
       ...
       void exit(string mesg);
};
// C++
void server_exit(CORBA::Environment& env)
{
       exit(0);
}
 
void MyIntf::exit(const char* mesg, CORBA::Environment& env)
{
       ...
      
       if (strcmp(mesg, "exit") == 0) {
                Ob_SentHook hook(server_exit);
       }
       ...
}

呼び出すオブジェクトをサーバ側で変更するためのフック
サーバを呼び出すためのオブジェクトリファレンスと、オブジェクトインプリメンテーションのオブジェクトリファレンスが異なるときに使います。サーバにリクエストが届いて、リクエストメッセージのうちインタフェースに依存しない部分をデコードした後、オブジェクトインプリメンテーションを見つける前に、オブジェクトリファレンスを差し替えることができます。
このフックはインタフェースに依存しません。
ユーザ定義関数を登録するには、Ob_SvrHookオブジェクトを作成し、作成したフックオブジェクトに関数を登録します。
フックオブジェクトには複数の関数が登録でき、関数は登録された順番に呼び出されます。また、フックオブジェクトも複数作成することができ、作られた順番にすべてのオブジェクトに登録されている関数が呼び出されます。 関数はフックオブジェクトのライフタイムの間だけ登録されています。
このフックに登録できるユーザ定義関数インタフェースはつぎのとおりです。
void func(CORBA::Object_ptr&, CORBA::Environment&);
登録の仕方を例で示します。
void change_object(CORBA::Object_ptr& o,
CORBA::Environment& env)
{
       // 新たにオブジェクトをセットする前に、
       // 渡って来たオブジェクトをリリースする
       CORBA::release(o);
      
       o = ... //オブジェクトをセットする
}
main()
{
       ...
      
       Ob_SvrHook hook;
       hook <<= change_object;
       ...
      
}

クライアントとの接続が切れたときに呼ばれるフック
サーバアプリケーションがクライアントからのコネクションの切断を検出するときにこのフック関数を使用します。切断時のフック関数は以下の形式で定義します。
void myhook(Ob_SvrCon* con, CORBA::Environment&) { // ここに切断されたときの処理を記述します。}
切断時のフック関数はOb_SvrSocketClosedHook型のオブジェクトを作成することで登録されます。Ob_SvrSocketClosedHookのコンストラクタの引数は上記のフック関数へのポインタです。登録は検出したい切断が発生するまでに行っておきます。
main関数で登録するときの例を示します。
例) main関数で切断時のフック関数を登録する。
main() {
       CORBA::ORB_var orb = CORBA::ORB_init(...);
       ...
      
       Ob_SvrSocketClosedHook hobj(myhook);
}
次に、クライアントから呼ばれるオペレーション内で、そのオペレーションを呼び出したクライアントが切断されたときのフック関数の設定を行います。フックを呼び出す設定には、Ob_SvrCon::need_closed_hook関数を使います。この設定を行った場合だけ、クライアントとの接続が切断されたときに、上記フック関数が呼ばれるようになります。need_closed_hook関数を呼ばない場合あるいは、0を引数として呼び出した場合は、フック関数は呼ばれません。
AAA::op1(..., CORBA::Environment& env) {
       Ob_SvrCon* con = env.__connection();
      
       if (con) {
                // フック関数を呼ぶか呼ばないかのフラグ設定
                con->need_closed_hook(1);
       }
       ...
      
}

インタフェースリポジトリの利用方法
インタフェースリポジトリはCORBAオブジェクトのインタフェース情報を管理します。インタフェースリポジトリにはインタフェース情報の登録、削除、および参照という機能があります。
動的起動インタフェース(DII)を使った呼び出しを行うとき、ターゲットとなるオブジェクトのインタフェース情報が必要になります。このようなとき、インタフェースリポジトリから情報を取り出します。
WebOTX Object Brokerではinstif, rmifコマンドを使うことでインタフェース情報の登録と削除を簡単に行うことができます。したがって、以降の説明では参照する方法だけを説明します。

リポジトリオブジェクトの取得
インタフェースリポジトリは、リポジトリオブジェクトを頂点とする階層構造をしています。インタフェースリポジトリを利用するには、まず、リポジトリオブジェクトのオブジェクトリファレンスを取得する必要があります。
CORBA::ORB_ptr orbobj = CORBA::ORB_init(...);
// リポジトリオブジェクトのオブジェクトリファレンスを得る
CORBA::Object_var ir_in_obj =
       orbobj->resolve_initial_references("InterfaceRepository", env);
ここで得られたオブジェクトリファレンスはCORBA::Object型なのでこのままでは使えません。CORBA::Repository::_narrow()を使って型を変換します。
CORBA::Repository_var repository = CORBA::Repository::_narrow(ir_in_obj);

インタフェース情報の取得
インタフェース情報の取得方法には、あるオブジェクトが内包しているオブジェクトの一覧を返すものや、内包しているオブジェクトから手がかりになる文字列を使って検索するものなどさまざまなものがあります。
ここでは、これらのうち CORBA::Container::describe_contents() および CORBA::InterfaceDef::describe_interface() を使った例を示します。それ以外のオペレーションは リファレンス集 開発編(共通) の 「インタフェースリポジトリ」を参照してください。
下記のIDL定義のみがinstifコマンドによりインタフェースリポジトリに登録されているものとします。
interface myintf {
       void op(in short i);
};
このデータをインタフェースリポジトリから取り出す例を以下に示します。なお、例外処理や複数のインタフェースやオペレーションが含まれていた場合の考慮などは省略します。
// C++
 
#include <orb.h>
#include <stdio.h>
 
// 引数モード別
static char* opemode[] = {
       "in", "out", "inout", 0
};
 
// 型別
static char* tckind[] = {
       "null", "void", "short", "long", "ushort", "ulong",
       "float", "double", "boolean", "char", "octet", "any",
       "TypeCode", "Principal", "object", "struct", "union",
       "enum", "string", "sequence", "array", "typedef",
       "exception", "longlong", "ulonglong", "longdouble",
       "wchar", "wstring", "fixed", 0
};
 
void main(int argc, char** argv)
{
       CORBA::Environment env;
 
       // 初期化
       CORBA::ORB_var orbobj = CORBA::ORB_init(argc, argv, "", env);
 
       // リポジトリオブジェクトの取り出し
       CORBA::Object_var ir_in_obj =
                orbobj->resolve_initial_references("InterfaceRepository", env);
 
       CORBA::Repository_var repository =
                CORBA::Repository::_narrow(ir_in_obj);
 
       // リポジトリオブジェクトからInterfaceDefオブジェクトだけを取り出す
       // CORBA::dk_Interface      : 取り出すのはインタフェースのみ
       // 1                        : 継承されたオブジェクトは含まない
       // -1L                      : 数量制限なし
      
CORBA::Container::DescriptionSeq_ptr descseq =
                repository->describe_contents(CORBA::dk_Interface, 1, -1L, env);
 
       // シーケンスの最初のオブジェクトをnarrowする
       CORBA::InterfaceDef_var intfobj =
                CORBA::InterfaceDef::_narrow((*descseq)[(CORBA::ULong)0].contained_object);
 
       // インタフェース情報を取り出す
       CORBA::InterfaceDef::FullInterfaceDescription_ptr intfdesc =
                intfobj->describe_interface(env);
 
       // インタフェース名
       printf("interface %s {\n", intfdesc->name);
 
       // オペレーションのシーケンスから1つ取り出す
       CORBA::OperationDescription opedesc = intfdesc->operations[(CORBA::ULong)0];
 
       // オペレーションのモード
       if(opedesc.mode == CORBA::OP_ONEWAY)
                printf("\toneway ");
       else
                printf("\t ");
 
       // オペレーションの戻り値の型
       printf("%s ", tckind[opedesc.result->kind(env)]);
 
       // オペレーション名
       printf("%s(", opedesc.name);
 
       // パラメータのシーケンスから1つ取り出す
       CORBA::ParameterDescription paradesc = opedesc.parameters[(CORBA::ULong)0];
 
       // オペレーションの引数のモード
       printf("%s ", opemode[paradesc.mode]);
 
       // オペレーションの引数
       printf("%s %s);\n", tckind[paradesc.type->kind(env)], paradesc.name);
       printf("};\n");
}
CORBA::Container::describe_contents()は、そのオブジェクトが内包しているオブジェクトの一覧をCORBA::Container::DescriptionSeqへのポインタで返します。上記の例ではInterfaceDefに限定して検索をしています。
CORBA::Container::DescriptionSeqはCORBA::Container::Description構造体のシーケンスです。CORBA::Container::Description構造体の定義は以下のとおりです。
struct CORBA::Container::Description {
       CORBA::Contained contained_object; // 内包されたオブジェクト
       CORBA::DefinitionKind kind;    // 内包されたオブジェクトの種類
       CORBA::Any value;              // 内包されたオブジェクトが持つ
                                      // 型情報
};
contained_objectにはInterfaceDefオブジェクトが入っています。そこで、CORBA::InterfaceDef::describe_interface()を使って、そのCORBA::InterfaceDefオブジェクトが内包しているオペレーションのリストを取り出します。オペレーションのリストはCORBA::InterfaceDef::FullInterfaceDescriptionへのポインタとして返されます。
CORBA::InterfaceDef::FullInterfaceDescriptionは構造体です。下記にその定義を示します。
struct CORBA::InterfaceDef::FullInterfaceDescription
{
       Obi_String name;                        // インタフェース名
       Obi_String id;                          // リポジトリID
       Obi_String defined_in;                  // インタフェースを包含している
                                               // ContainerのリポジトリID
       Obi_String version;                     // バージョン
       CORBA::OpDescriptionSeq operations;     // オペレーションのシーケンス
       CORBA::AttrDescriptionSeq attributes;   // 属性のシーケンス
       CORBA::RepositoryIdSeq base_interfaces; // 基底インタフェースの
                                               // シーケンス
       CORBA::TypeCode type;                   // タイプコード
};
さらに、operationsから目的のオペレーションの型情報を取り出します。
operationsはCORBA::OperationDescription構造体のシーケンスです。CORBA::OperationDescription構造体の定義を以下に示します。
struct CORBA::OperationDescription {
       Obi_String name;                     // オペレーション名
       Obi_String id;                       // リポジトリID
       Obi_String defined_in;               // オペレーションを包含している
                                            // ContainerのリポジトリID
       Obi_String version;                  // バージョン
       CORBA::TypeCode result;;             // 戻り値の型を表すタイプコード
       CORBA::OperationMode mode;           // オペレーションのモード
                                            // CORBA::OP_NORMAL:双方向呼び出し
                                            // CORBA::OP_ONEWAY:単方向呼び出し
       CORBA::ContextIdSeq context;         // コンテキスト名のシーケンス
       CORBA::ParDescriptionSeq parameters; // パラメータのシーケンス
       CORBA::ExcDescriptionSeq exceptions; // ユーザ定義例外のシーケンス
parametersからパラメータ情報を取り出します。
parametersはCORBA::ParameterDescription構造体のシーケンスです。CORBA::ParameterDescription構造体の定義を以下に示します。
struct CORBA::ParameterDescription {
       CORBA::Identifier name;    // 引数の名前
       CORBA::TypeCode type;      // 引数の型を表すタイプコード
       CORBA::IDLType type_def;   // 引数の型を表すオブジェクト
       CORBA::ParameterMode mode; // 引数のモード
                                  // CORBA::PARAM_IN   :in引数
                                  // CORBA::PARAM_OUT  :out引数
                                  // CORBA::PARAM_INOUT:inout引数
};
CORBA::OperationDescription構造体のメンバexceptionsがヌル・ポインタでなければ、exceptionsからユーザ定義例外情報を取り出します。
exceptionsはCORBA::ExceptionDescription構造体のシーケンスです。CORBA::ExceptionDescription構造体の定義を以下に示します。
struct CORBA::ExceptionDescription {
       CORBA::Identifier name;          //例外名
       CORBA::RepositoryId id; ;        //リポジトリID
       CORBA::RepositoryId defined_in;  // この例外を返すオペレーションのリポジトリID
       CORBA::VersionSpec version;      // バージョン
       CORBA::TypeCode type;            //この例外を示すタイプコード
};
これらのインタフェース情報により、DIIによる呼び出しに必要なパラメータを組み立てることができます。

Dynamic Invocation Interface (DII)
IDLコンパイラが生成したスタブを使うときにはDIIを意識する必要はありません。ここでは、直接DIIを使う方法を説明します。
DIIを使うと、静的なオペレーション情報がクライアント側にリンクされていなくても、オペレーションを動的に呼び出すことができます。DIIはアプリケーション作成時に呼び出すインタフェースが決まっていないような特殊なアプリケーションを作成する場合にだけ使います。このようなアプリケーションの例としては、インタフェースの情報を動的に与えてオブジェクトのテストする汎用のテストツールなどが考えられます。

基本的な呼び出し
DIIを使って呼び出す手順は複数あります。大別すると、引数リストなどを先に作っておいてCORBA::Requestオブジェクトを作成する方法とCORBA::Requestオブジェクトを先に作っておく方法に別れます。
先に引数リストを作成するにはCORBA::ORB::()かCORBA::ORB::create_operation_list()を使います。前者はCORBA::NVList::add()などを使って引数を1つずつ追加していかなければいけません。後者はインタフェースリポジトリに登録されているインタフェース情報をもとに自動的に追加されます。
CORBA::Requestオブジェクトを作成するには、CORBA::Object::_request()またはCORBA::Object::_create_request()を使います。CORBA::Object::_create_request()は引数リストにヌル・ポインタを設定しておいて、後から引数を追加することもできます。
作成したCORBA::Requestに引数を追加するには、CORBA::Request::arguments()で引数リストを取り出す方法と、CORBA::Request::add_in_arg()などを使う方法があります。以下の例では後者を使います。引数リストを直接操作する方法は「引数リストを表すインタフェース」を参照してください。
以下ではもっとも基本的な呼び出し手順を示します。
DIIによるオペレーションの呼び出しを行うには、CORBA::Requestオブジェクトにオペレーションの情報(引数や戻り値の型など)をセットする必要があります。
CORBA::Requestオブジェクトを作成するには、呼び出すオブジェクトのオブジェクトリファレンスが必要です。オブジェクトリファレンスの取得方法は静的な呼び出しと同じです。不特定のオブジェクトを呼び出すクライアントを作成する場合、名前サービスのCosNaming::Naming_Context::resolve()を使うのが一般的です。
また、不特定のオブジェクトを呼び出すクライアントを作成する場合は、インタフェースの定義を取得する必要があります。インタフェース定義を取得するには、CORBA::Object::_get_interface()を使います。
通常、CORBA::Object::_get_interface()は静的にリンクされているインタフェースを返します。不特定のオブジェクトを呼び出すクライアントを作成するときは、あらかじめCORBA::ORB::__use_IR()によってインタフェースリポジトリの参照を許可しておきます。これにより、CORBA::Object::_get_interface()はあらかじめインタフェースリポジトリに登録されていたインタフェースを返すようになります。CORBA::ORB::__use_IR()はObjectSpinner独自のメソッドなので、他社のORBに移植するときは注意が必要です。
CORBA::Environment env;
 
...
CORBA::Object_var obj = ...; // 何らかの手段でオブジェクトリファレンスを取得
// インタフェースリポジトリを使った検索を許可する
orb->__use_IR(1, env);
// インタフェースを定義しているオブジェクトの取得
CORBA::InterfaceDef_var intfobj = obj->_get_interface(env);
 
// インタフェース情報を取り出す
CORBA::InterfaceDef::FullInterfaceDescription_var intfdesc =
       intfobj->describe_interface(env);
intfdescにはオブジェクトリファレンスが指し示すオブジェクトのすべてのインタフェース情報が入っています。この中から、目的のオペレーションを選びます。
CORBA::ULong count = intfdesc->operations.length();
CORBA::ULong i;
CORBA::OperationDescription opedesc;
 
for(i=0; i<count; i++) {
 
       // オペレーションのシーケンスから1つ取り出す
       opedesc = intfdesc->operations[i];
 
       // オペレーション名が一致するものを探す(仮に"myop"を探すことにします)
       if(strcmp(opedesc.name, "myop") == 0)
                break;
}
名前が一致するオペレーションを取得できたら、CORBA::Requestオブジェクトを作成します。引数には、オペレーション名を渡します。
CORBA::Request_var req = obj->_request(opedesc.name, env);
作成したCORBA::Requestオブジェクトに戻り値の型をCORBA::TypeCode_ptr型で指定します。
req->set_return_type(opedesc.result, env);
オペレーションの引数を登録します。引数にはin, out, inoutの3種類があり、それぞれ別の関数が用意されています。引数はadd_XXX_arg()(XXXは引数の種類によって異なる)を呼び出すたびに引数リストの後ろへ追加されます。add_XXX_arg()関数は、CORBA::Anyへの参照を返します。そして、返されたCORBA::Anyに引数を挿入します。各型のanyへの挿入の方法は、「C++マッピング」の「any」を参照してください
count = opedesc.parameters.length();
 
CORBA::ParameterDescription paradesc;
CORBA::Any value;
 
for(i=0; i<count; i++) {
       // パラメータのシーケンスから1つ取り出す
       paradesc = opedesc.parameters[i];
 
       // パラメータの設定
       switch(paradesc.mode){
       case CORBA::PARAM_IN:
                switch(paradesc.type->kind()){
                case CORBA::tk_short:
                          CORBA::Short in_param = ...;
                          req->add_in_arg(paradesc.name, env) <<= in_param;
                          break;
                ...
                }
                break;
       case CORBA::PARAM_INOUT:
                switch(paradesc.type->kind()){
                case CORBA::tk_short:
                          CORBA::Short inout_param = ...;
                          req->add_inout_arg(paradesc.name, env)
                                                     <<= inout_param;
                          break;
                ...
                }
                break;
       default:
                switch(paradesc.type->kind()){
                case CORBA::tk_short:
                          CORBA::Short out_param = 0; // dummy
                          req->add_out_arg(paradesc.name, env) <<= out_param;
                          break;
                ...
                }
                break;
       }
}
引数の組み立てが終わったら、サーバの呼び出しを行います。サーバの呼び出し方には単方向のものと双方向の2種類があります。これらはIDL定義をしたときに決まっています。呼び出しをするときには、以下のようにこれらの違いを調べ、呼び出し方にあった方法を使う必要があります。
if(opedesc.mode == CORBA::OP_ONEWAY)
       req->send_oneway(env);        //単方向呼び出しのとき
else{
       CORBA::Status status = req->invoke(env);       // 双方向呼び出しのとき
}
双方向呼び出しで、かつ、戻り値を持っていた場合は、戻り値を取得する必要があります。戻り値は、CORBA::Any型で取得します。anyから各型へ取り出す方法は、「C++マッピング」の「any」を参照してください。
if(opedesc.result->kind() != CORBA::tk_void){
       switch(opedesc.result->kind()) {
       case CORBA::tk_short:
                CORBA::Short result;
                req->return_value(env) >>= result;
                break;
       ...
       }
}

リクエストの発行

同期オペレーション
「基本的な呼び出し」の例のとおり、CORBA::Request::invoke()を使います。CORBA::Request::invoke()はサーバにリクエストを送信し、応答が返るまで待ちます。onewayのオペレーションには使用できません。


onewayオペレーション
「基本的な呼び出し」の例に示したとおり、CORBA::Request::send_oneway()を使います。CORBA::Request::send_oneway()はサーバにリクエストを送信するだけで応答を期待しません。したがって、応答を必要とするメソッドの呼び出しには利用できません。メソッドの呼び出し方針がonewayかどうかはIDL定義をした時点で決まってしまいます。


遅延同期オペレーション
遅延同期オペレーションは、送信と受信を分ける方法です。遅延同期オペレーションでは、CORBA::Request::send_deferred()による送信オペレーションの発行後すぐに制御が戻ります。そして、任意の時点で、CORBA::Request::poll_response()を呼び出してポーリングするかCORBA::Request::get_response()を呼び出して待ち合わせをします。CORBA::Request::poll_response()は単に応答メッセージが受信可能かどうかを調べるだけです。応答メッセージを取得するにはCORBA::Request::get_response()を呼び出す必要があります。


複数リクエストの発行
リクエストのシーケンスを作成しておき、複数のリクエストを順番に発行することができます。サーバからの応答を必要とする呼び出しの場合には、CORBA::ORB::send_multiple_requests_deferredを使います。onewayの呼び出しの場合はORBA::ORB::send_multiple_requests_oneway()を使います。
個々のリクエストについて応答メッセージを調べるには遅延同期オペレーションのときと同様にCORBA::Request::poll_response()とCORBA::Request::get_response()を使います。また、複数のメッセージのうちのどれか1つ以上という場合はCORBA::ORB::poll_next_response()とCORBA::ORB::get_next_response()を使います。


Dynamic Skeleton Interface (DSI)
IDLコンパイラが生成したスケルトンを使うときにはDSIを意識する必要はありません。ここでは、直接DSIを使う方法を説明します。
DSIを使うと、静的なオペレーション情報がサーバ側にリンクされていなくとも、オペレーションを動的に呼び出すことができます。ちょうどクライアント側で使われるDII(Dynamic Invocation Interface)の逆向きの機能を実現しています。
DIIはCORBA::Requestオブジェクトにオペレーションの情報(引数や戻り型など)をセットし、CORBA::Request::invoke関数を呼び出すことにより、オブジェクト呼び出しの入口を共通化しています。
DSIも、PortableServer::DynamicImplementation::invoke関数を共通の入口としています。ただし、オペレーションの情報はCORBA::ServerRequestオブジェクトに入っているということがDIIとは異なります。

DSIに必要なクラス
DSIを利用するためにはPortableServer::DynamicImplementationクラスを継承したクラスを作成します。このクラスがリクエストの処理を実装するDSIサーバント(DSIの実装オブジェクト)になります。DSIサーバントは、つぎの各メンバ関数をオーバライドする必要があります。
namespace PortableServer {
       class DynamicImplementation : public virtual PortableServer::ServantBase {
                //実装オブジェクトのオペレーションを呼び出す
                virtual void invoke(CORBA::ServerRequest_ptr, CORBA::Environment&);
 
                virtual CORBA::RepositoryId _primary_interface(
                          const PortableServer::ObjectId&,
                          PortableServer::POA_ptr,
                          CORBA::Environment&
                );
       };
};

PortableServer::DynamicImplementationの派生クラスの実装
このクラスでは、各オペレーションのほかに、invoke関数と_primary_interface関数の実装が必要です。次の例を使って説明します。
// IDL
interface MyInterface {
       MyType2 MyOperation(in MyType arg);
};
// C++
class MyDynamicImpl : public PortableServer::DynamicImplementation {
public:
       MyDynamicImpl() { // 必要ならばメンバの領域の割り当て、初期化を行います }
       ~MyDynamicImpl() { // 必要ならばメンバの領域の解放を行います }
 
       virtual void invoke(
                CORBA::ServerRequest_ptr,
                CORBA::Environment&
       );
 
       virtual CORBA::RepositoryId _primary_interface(
                const PortableServer::ObjectId&,
                PortableServer::POA_ptr,
                CORBA::Environment&
       );
       MyType2 MyOperation(MyType&, CORBA::Environment&); // 呼び出されるオペレーション
};

invoke関数
invoke関数は、オペレーションが呼び出されるとき、ランタイムライブラリから呼ばれる、ユーザコードへの入口となります。invoke関数の処理は自由に記述することができます。
オペレーションの情報はsrから取り出すことができます。呼び出しが行われたオブジェクトのオブジェクトリファレンスは、PortableServer::DynamicImplementation::_thisメソッドを使って取得できます。
envには、invoke関数内で起きた例外をセットして呼び出し側に通知します。(オプション) ここでは、実際にC++オブジェクトのメソッドを呼び出すことを目的としたinvoke関数の実装例を説明します。ただし、エラー処理は省略してあります。
// IDL
interface MyInterface {
       MyType2 MyOperation(in MyType arg);
};
// C++
void MyDynamicImpl::invoke(CORBA::ServerRequest_ptr sr, CORBA::Environment&) {
       try {
                // どのオペレーションが呼ばれたか調べる
                const char* op_name = sr->operation();
 
                if (strcmp(op_name, "MyOperation") == 0) {
                          MyType arg;
                          MyType2 ret;
                          //引数のデコードに必要な情報をセットする
                          CORBA::NVList_ptr param;
                          CORBA::NamedValue_ptr np = param->add(CORBA::ARG_IN);
                          CORBA::Any* ap = np->value();
                          *ap <<= arg;
                          //引数をデコードする
                          sr->arguments(param);
 
                          // IN引数を取り出す
                          arg = *(MyType*)param->item(0)->value()->value();
                          //オペレーションを呼び出す
                          ret = MyOperation(arg);
                          //戻り値をsrにセットする
                          CORBA::Any* result = new CORBA::Any;
                          *result <<= ret;
                          sr->set_result(*result);
                 }
       } catch (CORBA::NO_MEMORY& e) { // CORBA::NO_MEMORY例外のとき
                //エラー処理または例外をsrにセットする
                // CORBA::NO_MEMORY例外をsrにセットする場合
                CORBA::Any* exc = new CORBA::Any(CORBA::_tc_NO_MEMORY, new CORBA::NO_MEMORY(e), 1);
                sr->set_exception(*exc);
       } catch (CORBA::Exception& e) { // その他の例外のとき
                //エラー処理または例外をsrにセットする
       }
}
次に、DSIサーバントのインスタンスが複数あり、インスタンスにより処理を切り換える場合の例を以下に示します。
// IDL
interface MyInterface {
       MyType2 MyOperation(in MyType arg);
};
// C++
class MyDynamicImpl : public PortableServer::DynamicImplementation {
public:
       ...
       virtual void invoke(
                CORBA::ServerRequest_ptr,
                CORBA::Environment&
       );
       MyType2 MyOperation1(MyType&, CORBA::Environment&); // 呼び出されるオペレーション
       MyType2 MyOperation2(MyType&, CORBA::Environment&); // 呼び出されるオペレーション
       ...
};
 
main(...) {
       CORBA::ORB_ptr orb = CORBA::ORB_init(...);
       PortableServer::POA_ptr rootPOA = ... // ルートPOAを取得
 
       CORBA::PolicyList plist;
 
       plist.length(1);
 
       plist[0] = rootPOA->create_id_assignment_policy(PortableServer::USER_ID);
 
       PortableServer::POA_ptr poa = rootPOA->create_POA("myPOA", PortableServer::POAManager::_nil(), plist);
 
       // 文字列をオブジェクトIDに変換
       PortableServer::ObjectId_var oid1 =
                PortableServer::string_to_ObjectId("myimpl1");
      PortableServer::ObjectId_var oid2 =
                PortableServer::string_to_ObjectId("myimpl2");
 
       MyDynamicImpl* impl1 = ...  // MyOperation1を呼び出すインスタンスを作成
       MyDynamicImpl* impl2 = ...  // MyOperation2を呼び出すインスタンスを作成
 
       // サーバントを活性化
       poa->activate_object_with_id(oid1, impl1);
       poa->activate_object_with_id(oid2, impl2);
       ...
}
 
void
MyDynamicImpl::invoke(CORBA::ServerRequest_ptr sr, CORBA::Environment&) {
       try {
                CORBA::ORB_var orb = CORBA::ORB_init(...);
 
                CORBA::Object_var curobj = orb->resolve_initial_references("POACurrent");
                PortableServer::Current_var cur = PortableServer::Current::_narrow(curobj);
 
                CORBA::Object_var obj = _this();
 
                PortableServer::POA_var poa = cur->get_POA();
 
                PortableServer::ObjectId_var oid = poa->reference_to_id(obj);
                CORBA::String_var impl_id = ProtableServer::ObjectId_to_string(*oid);
 
                //どのオペレーションが呼ばれたか調べる
                const char* op_name = sr->operation();
 
                if (strcmp(op_name, "MyOperation") == 0) {
                          MyType arg;
                          MyType2 ret;
 
                          //引数のデコードに必要な情報をセットする
                          CORBA::NVList_ptr param;
                          CORBA::NamedValue_ptr np = param->add(CORBA::ARG_IN);
                          CORBA::Any* ap = np->value();
                          *ap <<= arg;
 
                          //引数をデコードする
                          sr->arguments(param);
 
                          // IN引数を取り出す
                          arg = *(MyType*)param->item(0)->value()->value();
 
                          // impl_idにより呼び出すオペレーションを切り換える
                          if (strcmp(impl_id, "myimpl1") == 0) {
                                   ret = MyOperation1(arg);
                          } else {
                                   ret = MyOperation2(arg);
                          }
 
                          //戻り値をsrにセットする
                          CORBA::Any* result = new CORBA::Any;
                          *result <<= ret;
                          sr->set_result(*result);
               }
       } catch (CORBA::Exception& e) { // その他の例外のとき
                          //エラー処理または例外をsrにセットする
       }
}

_primary_interface関数
_primary_interface関数は、DSIサーバントに対応づけられたオブジェクトの呼び出し時に、DSIサーバントのあつかうインタフェースを取り出すためにORBから呼ばれます。引数には、呼び出しのきっかけとなったオブジェクトのオブジェクトIDとそのオブジェクトに対応づけられたPOAが渡されます。DSIサーバントが複数のインタフェースをあつかう場合、引数により適切なインタフェースを返すように記述します。
ここでは、複数のインタフェースをあつかうDSIサーバントの_primary_interface関数の実装例を説明します。ただし、エラー処理は省略してあります。
// IDL
 
interface MyInterface {
       MyType2 MyOperation(in MyType arg);
};
 
interface MyInterface2 {
       MyType2 MyOperation2(in MyType arg);
};
 
// C++
 
main(...) {
       CORBA::ORB_ptr orb = CORBA::ORB_init(...);
      
PortableServer::POA_ptr rootPOA = ... // ルートPOAを取得
 
       CORBA::PolicyList plist;
       plist.length(1);
 
       plist[0] = rootPOA->create_id_assignment_policy(PortableServer::USER_ID);
 
       PortableServer::POA_ptr poa =
                rootPOA->create_POA("myPOA", PortableServer::POAManager::_nil(), plist);
 
       PortableServer::ObjectId_var oid1 =
                PortableServer::string_to_ObjectId("myintf1");
       PortableServer::ObjectId_var oid2 =
                PortableServer::string_to_ObjectId("myintf2");
 
       MyDynamicImpl* impl1 = ...  // MyInterfaceをあつかうインスタンスを作成
       MyDynamicImpl* impl2 = ...  // MyInterface2をあつかうインスタンスを作成
 
       // サーバントを活性化
       poa->activate_object_with_id(oid1, impl1);
       poa->activate_object_with_id(oid2, impl2);
       ...
}
 
CORBA::RepositoryId MyDynamicImpl::_primary_interface(const PortableServer::ObjectId& oid,
                                        PortableServer::POA_ptr poa, CORBA::Environment&) {
       // オブジェクトIDを文字列に変換
       CORBA::String_var str = PortableServer::ObjectId_to_string(oid);
 
       // 呼び出されたオブジェクトに対応したサーバントを調べる
       if (strcmp(str, "intf1") == 0) { // サーバントがimpl1のとき
           return CORBA::string_dup("IDL:MyInterface:1.0");
       } else { // それ以外のとき
           return CORBA::string_dup("IDL:MyInterface2:1.0");
       }
}

WebOTX Object Broker C++独自の注意事項

名前付け規則
大文字小文字を問わず、ObおよびObiで始まるシンボルはWebOTX Object Brokerが使用しますので、アプリケーションプログラムではこれらのシンボルを定義しないようにしてください。
CORBA仕様のクラスにWebOTX Object Broker独自のメンバを追加する場合メンバ名は'__'(2つの'_')で始まっています。
すなわち、ObおよびObiで始まるクラス/関数および'__'で始まるメンバ関数はWebOTX Object Broker独自ですので、これらを使った部分に関しては移植性がありません。なお、ObiはWebOTX Object Broker実装上内部的に使用するクラス/関数に使用していますので、これらの仕様に関しては断りなく変更されることがあります。したがって、WebOTX Object Brokerの将来の版でも利用できるとは限りません。Obiで始まるクラス/関数はアプリケーションプログラム内で使用しないでください。

CORBA2.2 C++マッピングのベンダ選択部
CORBAではC++コンパイラの仕様の違いを考慮し、ORBベンダに対していくつかの選択肢を設けています。WebOTX Object Brokerでは次の選択をしています。

例外マッピング:
例外のマッピングとしては(1)C++の例外を使う方法、(2)例外用の引数を使う方法が選択可能となっています。
WebOTX Object Brokerでは、C++の例外を使う方法を採用しています。
モジュールマッピング:
モジュールのマッピングとしては、(1)C++のnamespaceを使う方法、(2)入れ子クラスを使う方法、(3)'_'による名前の連結とが選択可能です。
WebOTX Object BrokerではC++のnamespaceを使う方法を採用しています。


クライアントでのソケット管理
WebOTX Object Brokerの既定値の設定では同一クライアントプロセスから同一サーバプロセス間では1つのコネクションしか使いません。クライアントプログラムが複数のスレッドで動作しているときで、複数のスレッドが同一サーバプロセスに対し同時に要求を出した場合、その1つのコネクションを各スレッドで共有して利用します。
共有を行いたくない場合は、
orbobj->__reuse_socket(0, env);
として共有を解除します。この呼び出しを行うと、ORBの通信を行うたびにコネクションを作ります。
このため繰り返し呼び出しをする場合などは性能が低下する可能性があります。
ソケットの通信先はホスト、ポートの組で管理されます。文字列での管理ではなく、そのホスト名から検索可能な(複数の)IPアドレス単位で管理していますので、正式名、ニックネームといったホスト名の使い分けをしても同一ソケットが使われますし、マルチホームホストなどで、複数のIPアドレスを持つホストの場合で、違うIPアドレスへのコネクションがある場合でもホスト名を指定した場合は共有されます。ただし、マルチホームホストでドット表記のIPアドレスが複数指定してある場合には共有されません。
ソケット共有を行うと、サーバプロセス終了の前後に呼び出しを行った場合、終了前に作ったコネクションを終了後に使うため、サーバが再起動しているにもかかわらず通信がエラーになることがあります。このようなときは、再度通信を行うと正常に通信できます。ソケットを共有しない設定にすることも有効です(詳しい説明は リファレンス集 開発編(共通) - 4. WebOTX Object Broker C++ - 4.1.3. CORBA::ORBクラス の __reuse_socket(CORBA::Boolean) を参照してください)。

_narrowの実装
与えられたオブジェクトリファレンスに対応した実装オブジェクトが存在するメモリ空間内で_narrowを呼び出すと、実装オブジェクトへのポインタが_duplicateされて返されます。
そうでなければ、与えられたオブジェクトリファレンスに対応したProxyオブジェクトが新しく作られ、返されます。
このときのProxyオブジェクトの型は、オブジェクトリファレンスがサポートしているインタフェースのIDによって決まります。インタフェースIDは、オブジェクトリファレンスを生成するときに指定する、InterfaceDefに対応しています。
次の例で説明します。
// IDL
interface A {};
interface B {};
interface C : A, B {};
interface D : C {};
CのInterfaceDefでオブジェクトリファレンスを生成し、各_narrowに渡すと、次に示す結果となります。
またサーバとクライアントでリンクしているcmn.Cの数や種類が異なると次のことが起こりえます。
サーバはつぎのA, Bのインターフェースをサポートしているとします。

 
// a.idl
interface A {};
 
// b.idl
#include "a.idl"
interface B : A {
       string op2();
};
 
// c.idl
#include "a.idl"
interface C {
       A op();
};
サーバ内の'Cの実装クラス'::op()オペレーションは、次のように、Bオブジェクトリファレンスを返すとします。
A_ptr 'Cの実装クラス'::op(CORBA::Environment& env)
{
       B_ptr o = ...;
       return A::_duplicate(o, env);
}
実装オブジェクトがBであれば、クライアント側のスタブオブジェクトはBProxyとなりますので、クライアント側にもbcmn.oが必要になります。

anyを受け取るプログラムでのanonymousな型の扱い
たとえば、サーバが次のようなanyを受け取るオペレーションをもつインターフェースをサポートしているとします。
// serv.idl
 
interface A {
       void func(in any arg);
};
 
そして、次のような型のIDL定義から生成されたスタブをリンクしているクライアントが、サーバを呼び出したとします。
// clnt.idl
struct st {
       long l1;
       long l2;
};
 
// C++ client code
A_ptr o = ...;
CORBA::Environment env;
st arg;
 
arg.l1 = 1234;
arg.l2 = 5678;
 
CORBA::Any aarg;
 
aarg <<= arg;
 
o->func(aarg, env);
clntcmn.oをリンクして作ったサーバのfuncオペレーションは、次のことができます。
void 'Aの実装クラス'::func(const CORBA::Any& arg, CORBA::Environment&)
{
       st* p;
       arg >>= p;
       CORBA::Long a = p->l1;
       CORBA::Long b = p->l2;
       ...
 
}
しかし、clntcmn.oをリンクしないで作ったサーバは、st型を扱うことができないので、上記の方法で構造体のメンバをアクセスすることができません。このように、直接扱うことのできない型のデータが入っているanyをanonymous anyと呼びます。
WebOTX Object Brokerでは、anonymous anyのvalueには、CDR(CORBA2.2のCommon Data Representation (CDR)の節を参照)に準じた方法でエンコードした値が入っています。そして、各要素の値はOb_MesBufクラスを使って取り出すことができます。
void 'Aの実装クラス'::func(const CORBA::Any& arg,
CORBA::Environment&)
{
       // argにはstが入っていることを前提としています
       Ob_MesBuf mb;
      
       mb.from_any_value(arg.value());
      
       CORBA::Long a, b;
      
       mb >>= a;
       mb >>= b;
       ...
      
}
上記の例は、構造体なので、mbから各要素を順番に取り出しています。
また、逆にanonymousなデータ型をanyに設定するときのために、次の2つの関数が規定されています。

第一引数には設定するデータ型を表わすTypeCodeを渡しますが、IDL定義されていない型にはタイプコード定数がないので、プログラムの中で動的に作ります。たとえば、上記のst型のタイプコードは次のように作成します。
// C++
CORBA::StructMemberSeq members;
members.length(2);
 
members[0].name = CORBA::string_dup("l1"); // l1メンバのメンバ名
members[0].type = CORBA::_tc_Long; // l1メンバのタイプコード
members[1].name = CORBA::string_dup("l2"); // l2メンバのメンバ名
members[1].type = CORBA::_tc_Long; // l2メンバのタイプコード
 
CORBA::Object_var orbobj = ...; // CORBA::ORB_initより
 
CORBA::Environment env;
 
CORBA::TypeCode_var tc = orbobj->create_struct_tc("IDL:st:1.0", "st", members, env);
 
第2引数にはanonymousなデータ型のポインタを渡します。WebOTX Object Brokerでは、anonymousなデータはOb_MesBufオブジェクトを使って作成します。たとえば、上記のst型のときには次のように作成します。
// C++
 
CORBA::Long l1, l2;
 
l1 = 1234;
l2 = 5678;
 
Ob_MesBuf mb;
 
mb <<= l1;
mb <<= l2;
 
このようにそれぞれ作成したタイプコードとanonymousデータを次のようにanyに渡します。
CORBA::Any any(tc, mb.to_any_value(), 1);
あるいは
any.replace(tc, mb.to_any_value(), 1);
これらの関数の第二引数にOb_MesBufオブジェクトで作成したデータ以外のものを渡すと、コンストラクタは値をセットしません。replace関数は例外を返します。
なおコンストラクタはOb_MesBufオブジェクトで作成したデータ以外のものを渡されたときにはログファイルにメッセージを書き出します。
上記の例では、構造体を使って説明しましたが、他の型のOb_MesBufオブジェクトへの入れ方は取り出し方と同じです。

性能チューニング
WebOTX Object Brokerでは1つ1つのIIOPメッセージ全体を一旦メモリ上に置いて送受信を行っています。このとき、アプリケーションプログラムによってはメモリのフラグメンテーションのため、メモリがまだあるにもかかわらず、大きなメッセージ用の連続領域が取れないことがあります。そこで、WebOTX Object Brokerでは、小さなメモリを組み合わせて、仮想的な連続領域を構築しています。またソケットのI/Oはこのメモリ領域の大きさ単位で行っているため、アプリケーションによっては性能に影響する場合があります。この大きさを変えることにより性能チューニングが可能な場合があります。大きさは設定値MesBufSizeで指定します。また、メモリ領域の初期割り当て個数をMesBufNumで指定することができます (詳しくは「リファレンス集 運用管理・設定編」 - 「1. コンフィグレーション(設定一覧)」 - 1.10. Object Brokerをご参照ください)。

Ob_MesBufクラスの使い方
CORBA2.0以降で規定されているIIOPのCDRにしたがった方法で、バイト配列を作成したり、そこからデータを読み出したりするときにOb_MesBufクラスを使うと便利です。
Ob_MesBufクラスでは主に次のことができます。


IDL既定義型のマーシャリング
Ob_MesBufクラスを使ったIDL既定義型のマーシャリングは、次のように記述します。
// C++
Ob_MesBuf mb; // エンコード用オブジェクトを生成
CORBA::Long l = 1000;
mb <<= l; // lをエンコードする

IDL既定義型のアンマーシャリング
Ob_MesBufクラスを使ったIDL既定義型のアンマーシャリングは、次のように記述します。
// C++
Ob_MesBuf mb(128); // デコード用オブジェクトを生成
CORBA::Long l;
mb >>= l; // CORBA::Long型をデコードする

IDLユーザ定義型のマーシャリング
Ob_MesBufクラスを使ってユーザ定義型をエンコードするには、その型をIDL定義ファイルに定義し、cmnファイルをリンクして使います。IDL基本型用エンコードルーチンはライブラリで提供されています。
また、Ob_MesBufオブジェクトを使うときには、エンコード用に使うか、デコード用に使うかを、あらかじめ指定する必要があります。エンコード用に指定する方法は2通りあります。
コンストラクタで指定する方法
  初期化処理関数と終了処理関数を使う方法
それぞれの方法を説明します。
コンストラクタで指定する方法 コンストラクタでエンコード用に指定する方法を次のencode_sample1()関数の例を使って説明します。
// IDL
 
struct st {
       long l;
       string s;
};
 
enum en { ee1, ee2, ee3 };
 
 
// C++
 
void encode_sample1()
{
       st data1;
       en data2;
      
       data1.l = 1234;
       data1.s = CORBA::string_dup("abcdefg");
       data2 = ee2;
      
       Ob_MesBuf mb; // 1. エンコード用コンストラクタ
      
       // IDL定義した型は、Ob_MesBuf型に対してoperator<<=が使えるようになる
       mb <<= data1; // 2.
       mb <<= data2;
       ...
 
}
初期化処理関数と終了処理関数を使う方法 初期化処理関数と終了処理関数を使ってエンコード用に指定する方法を、次のencode_sample2()関数の例を使って説明します。
// IDL
 
struct st {
       long l;
       string s;
};
 
enum en { ee1, ee2, ee3 };
 
 
// C++
 
void encode_sample2(Ob_MesBuf& mb)
{
       st data1;
       en data2;
      
       data1.l = 1234;
       data1.s = CORBA::string_dup("abcdefg");
       data2 = ee2;
      
       CORBA::ULong length;
      
       // 1. ここからエンコード用として使用することを宣言します
       mb.begin_encode_message(&length);
      
       mb <<= data1;
       mb <<= data2;
      
       // 2. これでエンコード用としての使用を終了することを宣言します
       mb.end_encode_message();
      
       ...
      
}
mbが内部的に管理するバッファのカレントポインタからエンコード用として使用することを宣言します。このときの引数の領域には、end_encode_message()関数を呼び出したとき、使用バッファ長が書き込まれます。この領域は必ず指定してください。
end_encode_message()関数はbegin_encode_message()関数に対する終了処理関数です。begin_encode_message()関数の引数で渡ってきた領域に、begin_encode_message()からend_encode_message()までに使われたバッファ長を書き込みます。

IDLユーザ定義型のアンマーシャリング
Ob_MesBufクラスを使ってユーザ定義型をデコードするには、その型をIDL定義ファイルに定義し、cmnファイルをリンクして使います。IDL基本型用のデコードルーチンはライブラリで提供されています。
また、Ob_MesBufオブジェクトを使うときには、エンコード用に使うか、デコード用に使うかを、あらかじめ指定する必要があります。デコード用に指定する方法は2通りあります。
コンストラクタで指定する方法
  初期化処理関数と終了処理関数を使う方法
それぞれの方法を説明します。
コンストラクタで指定する方法 コンストラクタでデコード用に指定する方法を次のdecode_sample1()関数の例を使って説明します。
// IDL
 
struct st {
       long l;
       string s;
};
 
enum en { ee1, ee2, ee3 };
 
// C++
 
void decode_sample1()
{
       char* buffer = ....; // エンコードされたデータの入っているバッファ
      
       // バッファ長は1024バイトだとする
      
       Ob_MesBuf mb(1024); // 1. デコード用コンストラクタ
       mb.buf_copy(buffer, 1024); // 2. bufferからデータをコピーする
       mb.reset(); // 3. カレントポインタを先頭に戻します
      
       st data1;
       en data2;
      
       // IDL定義した型は、Ob_MesBuf型に対してoperator>>=が使えるようになる
       mb >>= data1; // 4.
       mb >>= data2;
      
       ...
}
Ob_MesBufのインスタンスを生成します。デコード用のインスタンスはバッファ長を指定するコンストラクタを使って生成します。
エンコードされたデータをバッファからmbにコピーします。


バッファの先頭からデコードしたいので、mbが内部的に管理しているバッファのカレントポインタを先頭にします。
IDL定義された型はOb_MesBufに対するoperator>>=()が出力されますので、例のように簡単に記述することができます。
初期化処理関数と終了処理関数を使う方法 初期化処理関数と終了処理関数を使ってデコード用に指定する方法を、次のdecode_sample2()関数の例を使って説明します。
// IDL
 
struct st {
       long l;
       string s;
};
 
enum en { ee1, ee2, ee3 };
 
 
// C++
 
void decode_sample2(Ob_MesBuf& mb)
{
       // mbにはすでにデコードしたいデータが入っているとする
       mb.begin_decode_message(100, 0); // 1.
      
       st data1;
       en data2;
      
       mb >>= data1;
       mb >>= data2;
      
       mb.end_decode_message(); // 2.
      
       ...
}
ここからデコード用として使うことを宣言します。このときの第一引数にはデコードするバイト数を指定しますが、この値は内部的には使っていません。第二引数にはデコードを実行するマシンのバイトオーダを指定します。これを動的に求めるには次の関数が便利です。
CORBA::Boolean my_byteorder()
{
       const CORBA::Boolean big_endian = 0;
       const CORBA::Boolean little_endian = 1;
      
       long l = 1;
      
       if ((*(char *)&l) == l) {
                return little_endian;
       } else {
                return big_endian;
       }
}

encapsulation
CDRにencapsulationという手法があります。これはCDRのデータの先頭にバイトオーダを付け、データをoctetのシーケンスにカプセル化するためのものです。
encapsulationの形式でエンコードする encapsulationの形式でエンコードする方法をencode_sample3()の例を使って説明します。
// IDL
 
struct st {
       long l;
       string s;
};
 
enum en { ee1, ee2, ee3 };
 
 
// C++
 
void encode_sample3()
{
       st data1;
       en data2;
      
       data1.l = 1234;
       data1.s = CORBA::string_dup("abcdefg");
      
       data2 = ee2;
      
       Ob_MesBuf mb;
       CORBA::ULong length;
      
       // 1. ここからencapsulationすることを宣言します
       mb.begin_encode_encapsulation(&length);
      
       mb <<= data1;
       mb <<= data2;
      
       // 2. これでencapsulationを終了することを宣言します
       mb.end_encode_encapsulation();
      
       // encapsulationしたものをsequence<octet>型にコピーしたいとします
       // IDL定義で typedef sequence<octet> encap;と宣言したとします
       encap encap_data;
      
       // 3. encap_dataの要素の領域をlength分確保します
       encap_data.length(length);
      
       // 4. mbからencap_dataにコピーします
       mb.buf_copy_to(&encap_data[0], length);
      
       ...
 
 
}
mbをencapsulation用として使うことを宣言します。このときの引数の領域には、end_encode_encapsulation()関数内で使用バッファ長が書き込まれます。この領域は必ず指定してください。また、encapsulationに必要なバイトオーダの情報は、実行したマシンの値が自動的に入ります。
end_encode_encapsulation()関数はbegin_encode_encapsulation()関数に対する終了処理関数です。begin_encode_encapsulation()関数の引数で渡ってきた領域に、encapsulationに使われたバッファ長を書き込みます。
encapsulationしたデータをsequence<octet>型のインスタンスにコピーしたいときは、まずencap_dataのバッファ長をlength分確保します。
mbからencap_dataにencapsulationしたデータをコピーします。このとき、buf_copy_to()関数は、mbが内部的に管理しているバッファの最初からlength分コピーされることに注意してください。   
encapsulationの形式でデコードする encapsulationの形式でエンコードされたデータをデコードする方法をdecode_sample3()の例を使って説明します。
// IDL
 
struct st {
       long l;
       string s;
};
 
enum en { ee1, ee2, ee3 };
 
 
// C++
 
void decode_sample3(Ob_MesBuf& mb)
{
       // mbにはすでにデコードしたいデータが入っているとする
       mb.begin_decode_encapsulation(100); // 1.
      
       st data1;
       en data2;
      
       mb >>= data1;
       mb >>= data2;
      
       mb.end_decode_message(); // 2.
      
       ...
      
}
ここからencapsulationされているデータをデコードすることを宣言します。このときの引数はencapsulationされているデータサイズを指定しますが、この値は内部的には使っていません。
end_decode_ensapsulation()関数はbegin_decode_encapsulation関数に対する終了処理関数です

マーシャリングされたデータの大きさを調べる
マーシャリングされたデータの大きさを調べるにはused_size()関数を使います。以下に、マーシャリングされたデータをCORBA::Octet型配列で取り出す例を示します。
Ob_MesBuf mb; // エンコード用オブジェクトの生成
mb <<= (CORBA::Long)100; // データのエンコード
CORBA::ULong len = mb.used_size(); // Byte長を得る
CORBA::Octet* buffer = new CORBA::Octet[len];
mb.buf_copy_to(buffer, len); // bufferにエンコードデータをコピーする

SSLの利用方法
利用するSSL製品の指定 Object Broker C++では、SSL通信で利用する製品として、SecureWare/セキュリティパックとOpenSSLが選択可能です。 利用する製品は、設定のSSLProviderで指定します。 指定方法は [ ドメイン構築・基本設定ガイド > 7. WebOTXの内部サービス > 7.6. WebOTX Webサーバ > 7.6.4. 運用 > 7.6.4.5. SSL(HTTPS通信) 設定方法 ] を参照してください。
証明書/鍵の指定 SSL通信で使用される証明書と鍵の指定は、SSLProviderがSSL-Cの場合は、鍵IDで指定します。
SSLProviderがOpenSSLの場合は、ファイルで指定します。 指定方法は [ ドメイン構築・基本設定ガイド > 7. WebOTXの内部サービス > 7.6. WebOTX Webサーバ > 7.6.4. 運用 > 7.6.4.5. SSL(HTTPS通信) 設定方法 ] を参照してください。
サポートしている証明書のファイルの形式は、PEM形式のみです。 証明書ファイルにprivate keyが含まれていない場合、プライベート鍵も指定する必要があります。 プライベート鍵も指定は、 [ ドメイン構築・基本設定ガイド > 7. WebOTXの内部サービス > 7.6. WebOTX Webサーバ > 7.6.4. 運用 > 7.6.4.5. SSL(HTTPS通信) 設定方法 ] を参照してください。

SSLポート番号の指定方法
ソースコードに記述する場合 SSLポート番号をソースコードに記述する場合、以下のようにします。
#define SSL_PORT_NUMBER ...
 
int main(int argc, char** argv)
{
    ...
    orb = CORBA::ORB_init(argc, argv, "ObjectSpinner");
    orb->__use_ssl(SSL_PORT_NUMBER);
    ...
    Ob_ssl_initialize();
    ...
}
起動時の引数で指定する場合 SSLポート番号を起動時の引数で指定する場合、以下のようにします。
<apName> -ORBSSLPort=port_num
環境変数で指定する場合 SSLポート番号を環境変数で指定する場合、以下の設定名を使用します。 設定方法の詳細に関しては、リファレンス集 運用管理・設定編 - コンフィグレーション - 1.10.3. Object Broker C++における環境設定を参照してください。 "ImplName"SSLPort SSLポート番号を指定します。 ※ "ImplName"はインプリメンテーション名を表します。 ※ポート番号には1以上の値を使用してください。 ※システムによっては、一定の範囲の値が予約されている場合があります。
鍵IDの指定方法
ソースコードに記述する場合 鍵IDをソースコードに記述する場合、以下のようにします。 鍵IDが固定の場合に有効です。 サーバの鍵IDを指定する場合
#define KEY_ID "..."
 
int main(int argc, char** argv)
{
    ...
    orb = CORBA::ORB_init(argc, argv, "ObjectSpinner");
    orb->__server_cert_key(KEY_ID);
    ...
    Ob_ssl_initialize();
    ...
}
クライアントの鍵IDを指定する場合
#define KEY_ID "..."
 
int main(int argc, char** argv)
{
    ...
    orb = CORBA::ORB_init(argc, argv, "ObjectSpinner");
    orb->__client_cert_key(KEY_ID);
    ...
    Ob_ssl_initialize();
    ...
}
起動時の引数で指定する場合 鍵IDを起動時の引数で指定する場合、以下のようにします。 サーバの鍵IDを指定する場合
<apName> -ORBServerCertKey=KeyID
クライアントの鍵IDを指定する場合
<apName> -ORBClientCertKey=KeyID
環境変数で指定する場合 鍵IDを環境変数で指定する場合、以下の設定名を使用します。 設定方法の詳細に関しては、リファレンス集 運用管理・設定編 - コンフィグレーション - 1.10.3. Object Broker C++における環境設定を参照してください。 "ImplName"ServerCertKey サーバの鍵IDを指定します ClientCertKey クライアントの鍵IDを指定します ※ "ImplName"はインプリメンテーション名を表します。
クライアント証明書の要求
ソースコードに記述する場合 クライアント証明書要求をソースコードに記述する場合、以下のようにします。
int main(int argc, char** argv)
{
    ...
    orb = CORBA::ORB_init(argc, argv, "ObjectSpinner");
    orb->__cert_request((CORBA::Boolean)1);
    ...
    Ob_ssl_initialize();
    ...
}
起動時の引数で指定する場合 クライアント証明書要求を起動時の引数で指定する場合、以下のようにします。
<apName> -ORBCertRequest
環境変数で指定する場合 クライアント証明書要求を環境変数で指定する場合、以下の設定名を使用します。 設定方法の詳細に関しては、リファレンス集 運用管理・設定編 - コンフィグレーション - 1.10.3. Object Broker C++における環境設定を参照してください。 "ImplName"CertRequest "on"を指定するとクライアント証明書を要求します
アクセプトモードの選択
ソースコードに記述する場合 接続待ちポートの選択は、以下のようにします。 SSLポートへの接続のみ受付ける場合
int main(int argc, char** argv)
{
    ...
    orb = CORBA::ORB_init(argc, argv, "ObjectSpinner");
    orb->__accept_mode(Ob_AcceptSSLPortOnly);
    ...
    Ob_ssl_initialize();
    ...
}
SSLポートと非SSLポートの両方への接続を受付ける場合
int main(int argc, char** argv)
{
    ...
    orb = CORBA::ORB_init(argc, argv, "ObjectSpinner");
    orb->__accept_mode(Ob_AcceptBothPort);
    ...
    Ob_ssl_initialize();
    ...
}
起動時の引数で指定する場合 アクセプトモードを起動時の引数で指定することは出来ません。 環境変数で指定する場合 アクセプトモードを環境変数で指定することはできません。
SSLを使用できないクライアント接続
SSLを使用した接続しか受付けないサーバに、SSLを使用できないクライアントが接続した場合、クライアントに以下の例外が返ります。 CORBA_NO_RESPONSE(1141) CORBA_NO_PERMISSION(1161)
WebOTX Object Brokerが提供するサービスの使い方
名前サービスの利用方法
名前サービスの利用方法について説明します。 名前サービスは名前コンテキスト(Naming Context)というオブジェクトを使って、オブジェクトに階層的な名前をつける機能を提供します。名前サービスの名前階層はファイルシステムにたとえることができます。ファイルシステムのディレクトリに相当するオブジェクトが名前コンテキストです。名前コンテキストのうち、ファイルシステムのルートディレクトリに相当するものを特にルートコンテキストと呼びます。名前サービスでは、ファイルシステムと同様に名前コンテキストの下に名前コンテキストを置くことができます。名前空間の構造を図に示します。

図1.2.2.6-2 名前空間の構造
図1.2.2.6-2

名前空間の構造
ルートコンテキストの取得
名前の階層はルートコンテキストから始まりますので、名前サービスを利用するにはルートコンテキストを取得しなければなりません。
// C++
CORBA::ORB_ptr orbobj = CORBA::ORB_init(...);
 
// 名前サービスのルートコンテキストを得る
CORBA::Object_ptr nmsv_in_obj =
       orbobj->resolve_initial_references("NameService", env);
ここで得られたオブジェクトリファレンスはorg.omg.CORBA.Object型なのでこのままでは使えません。
// C++
CosNaming::NamingContext_ptr nmsv_root_ctx =
       CosNaming::NamingContext::_narrow(nmsv_in_obj);
名前の登録
任意のオブジェクトに対する名前の登録はbindまたはrebindにより行います。 階層を持った名前も扱うことができるため、WebOTX Object Broker C++ではsequence<CosNaming::NameComponent>であるCosNaming::Name型を使って名前をつけます。各階層をあらわすNameComponent型にはidとkindフィールドがあり(どちらもstring型)どちらのフィールドも使い方はアプリケーションで自由に決めてかまいません。以下は、上記nmsv_root_ctxにid: "myobject"、kind: ""で名前をつける例です。
// C++
CosNaming::Name myname;
myname.length(1);

myname[0].id = (const char*)"myobject";
myname[0].kind = (const char*)"";

CORBA::Object_ptr myobj;

nmsv_root_ctx->bind(myname, myobj, env);
名前コンテキストの登録はbind_contextまたはrebind_contextにより行います。使い方はbindやrebindとほとんど同じです。
// C++
CosNaming::Name myname;
myname.length(1);

myname[0].id = (const char*)"mycontext";
myname[0].kind = (const char*)"";

// 名前コンテキストの生成
CosNaming::NamingContext_var myctx = nmsv_root_ctx->new_context(env);

// 名前の登録
nmsv_root_ctx->bind_context(myname, myctx, env);
上記の例では名前コンテキストの生成と名前の登録を別々に行っていますが、この2つを同時に行うbind_new_contextというオペレーションもあります。
// C++
CosNaming::Name myname;
myname.length(1);

myname[0].id = (const char*)"mycontext";
myname[0].kind = (const char*)"";

// 名前コンテキストの生成と名前の登録
CosNaming::NamingContext_var myctx =
   nmsv_root_ctx->bind_new_context(myname, env);
rebindやrebind_contextは、すでに同じ名前が存在したときに新しいオブジェクトと入れ替わってしまい、もとのオブジェクトとの関連付けは失われてしまうので注意が必要です。bind, bind_contextでは、同じ名前が存在した場合は例外が発生します。上記の例では名前階層が1段だけでしたが、複数段を指定することが可能です。複数段を指定するときは、途中の名前コンテキストは実在していなければなりません。また、名前サービスではファイルシステムの"../"に相当する指定はできません。 例) nmsv_root_ctxの配下に"mycontext"と命名された名前コンテキストがあるものとします。
// C++
CosNaming::Name n;

n.length(2);

// 名前の作成
n[0].id = (const char *)"mycontext";
n[0].kind = (const char *)"";
n[1].id = (const char *)"new_context";
n[1].kind = (const char *)"";

CosNaming::NamingContext_var myctx2 =
   nmsv_root_ctx->bind_new_context(n, env);
以下に名前コンテキストctx1, ctx2が同一のオブジェクトobjを指す例を示します。
例)
    // C++
    // 変数の定義
    CosNaming::NamingContext_var   root1, root2, ctx1, ctx2;
    CosNaming::Name       n11, n12, n21, n22;
    CORBA::Object_var     obj;
    CORBA::Environment    env;
 
    // 基点になる名前コンテキストとターゲットになるオブジェクトの初期化
    root1 = ...;
    root2 = ...;
    obj = ...;
 
    // 名前コンテキストの作成
    n11.length(1);
    n11[0].id = (const char *)"alpha";
    n11[0].kind = (const char *)"";
    ctx1 = root1->bind_new_context(n11, env);
 
    n12.length(1);
    n12[0].id = (const char *)"beta";
    n12[0].kind = (const char *)"";
    ctx2 = root2->bind_new_context(n12, env);
 
    // 2つの名前コンテキストが同一のオブジェクトをbindする
    n21.length(1);
    n21[0].id = (const char *)"nameX";
    n21[0].kind = (const char *)"";
    ctx1->bind(n21, obj, env);
 
    n22.length(1);
    n22[0].id = (const char *)"name1";
    n22[0].kind = (const char *)"";
    ctx2->bind(n22, obj, env);
 
何らかの手段でオブジェクトリファレンスを獲得することにより、別のサーバで動作している名前サービスのコンテキストをbind_contextで異なる名前サービスにつなぐこともできます。この操作はリモートファイルシステムのマウントにたとえることができます。別の名前サービスの一部をあたかも同一の名前サービスの名前空間の一部のように扱うことができます。
名前によるオブジェクトリファレンスの参照
名前によるオブジェクトリファレンスの参照にはresolveを使います。
CosNaming::Name myname;
myname.length(1);

myname[0].id = (const char*)"myobject";
myname[0].kind = (const char*)"";

CORBA::Object_ptr myobj = nmsv_root_ctx->resolve(myname, env);
階層の作成
名前コンテキストの階層を作るには、すでに「名前の登録」で説明したとおり、new_contextで名前コンテキストを作成してbind_contextを呼び出すか、bind_new_contextを呼び出します。bind_new_contextはnew_contextを呼び出した後で、bind_contextを呼び出したのと同様です。new_contextによって生成された名前コンテキストは、名前サーバを再起動してもデータが失われないように、ルートコンテキストの直下にあるLostAndFoundという特別な名前コンテキストに一時的に置かれています。 LostAndFoundに登録された名前コンテキストは、bind_contextで名前をつけて他のコンテキストに登録されたときに、LostAndFoundから自動的に削除されます。 前述したとおり、LostAndFoundは特別な名前コンテキストであり、外部からの変更は受け付けません。LostAndFoundに登録される名前は、名前サービスが自動的に生成し、削除します。したがって、LostAndFoundに登録されている名前は意識する必要はありません。
名前コンテキストの一覧
ある名前コンテキストの一覧を取得するには、listを使います。ORBでは、名前サーバと通信を行って一覧を取得します。一度に大量の要求をすると、通信を行う時間がかかり、なかなか処理が完了しないことがあります。なかなか処理が終了しないと、クライアントの応答性に問題がでます。したがって、listは一度に転送する最大要素数を指定できるようになっています。一度に転送できなかったときは、残りの要素の転送用にBindingIteratorオブジェクトが生成されます。BindingIteratorオブジェクトに対してnext_nまたはnext_oneオペレーションを繰り返すことで、すべての要素を取得することができます。BindingIteratorオブジェクトが返されたときは、不要になった時点で必ずorg.omg.CosNaming.BindingIterator.destroyオペレーションを実行してオブジェクトを削除しなければなりません。削除しなかったBindingIteratorオブジェクトは永久に残ります。
例)
    // C++
    CORBA::Environment env;
 
    CosNaming::NamingContext_var ctx1 = ...
 
    // ctx1に10個の名前を登録しておく。
 
    ...
 
    CosNaming::BindingList_var bl;
 
    CosNaming::BindingIterator_var bi;
 
    // 1つだけblに取り出して、残りはbiから参照することにした
    ctx1->list(1L, bl, bi, env);
 
    printf("id : %s\nkind : %s\n",
       bl[0].binding_name[0].id, bl[0].binding_name[0].kind);
 
    // biから1つだけ取り出す
    CosNaming::Binding_var b;
 
    bi->next_one(b, env);
 
    printf("id : %s\nkind : %s\n",
       b->binding_name[0].id, b->binding_name[0].kind);
 
    // biから5つまとめて取り出す
    bi->next_n(5L, bl, env);
 
    for(int i = 0; i < 5; i++){
 
    printf("id : %s\nkind : %s\n",
       bl[i].binding_name[0].id, bl[i].binding_name[0].kind);
 
    }
 
    // biの削除
    bi->destroy(env);
名前の削除
名前の削除には、名前コンテキストであるか通常のオブジェクトであるかに関わらずunbindを使います。 CORBAオブジェクトの削除と名前サービスに登録されている情報は連動しているわけではありません。 名前サービスは、名前とオブジェクトリファレンスのペアを単に記憶しているだけですので、unbindを呼び出しても、名前とオブジェクトリファレンスの関係が絶たれるだけで、登録されているオブジェクト自体が削除されるわけではありません。また、登録されているCORBAオブジェクトが削除されたとしても名前サービスには登録された名前とオブジェクトリファレンスのペアは残ったままになります。 名前コンテキストのunbindの呼び出しは、そのコンテクストにオブジェクトやコンテクストが存在する場合にも成功します。unbindによって名前を失った名前コンテキストは、ルートコンテキスト直下にあるLostAndFoundというコンテキストに再登録されます。LostAndFoundへの再登録には、名前サービスによって自動的に割り当てた名前が使われます。名前コンテキスト以外のオブジェクトのunbindではLostAndFoundにつながれることはありません。 名前コンテキストオブジェクトの削除には名前コンテキストオブジェクトのdestroyオペレーションを使います。名前コンテキストのdestroyでは、削除しようとする名前コンテキストの配下に名前が登録されていてはいけません。もし、削除しようとする名前コンテキストの配下に名前がつながっているときはNotEmpty例外が発生します。 注意: 名前の付いていない名前コンテキストはLostAndFoundに自動的につながれます。しかし、LostAndFoundに自動的につながれた名前に対してはunbindを呼び出す必要はありません。destroyの内部で自動的に削除されます。必要なくなった名前コンテキストは削除するべきです。 名前コンテキストのunbindとdestroyは、どちらを先に行ってもかまいません。しかし、配下に名前がつながっている可能性がある場合、unbindを先に行ってしまうと配下につながっている名前の検索や削除が困難になってしまいます。したがって、配下につながっている名前は、事前にunbindを使って削除しておくようにしてください。
イベントサービスの利用方法
イベントサービスはイベントと呼ぶデータをひとかたまりとして、イベントの送信者(以後生産者と呼びます)からイベントの受信者(以後消費者と呼びます)へ送るサービスです。WebOTX Object Brokerでは生産者と消費者の仲介を行うイベントチャネルサーバとして提供されています。
プッシュモデルとプルモデル
プッシュモデルとは消費者がCORBAオブジェクトとして実装され、生産者は消費者オブジェクトのpushオペレーションを呼び出すことによりイベントを送るモデルです。

図1.2.2.6-3 プッシュモデル
図1.2.2.6-3

プッシュモデル プルモデルとは生産者がCORBAオブジェクトとして実装され、消費者が生産者オブジェクトのpullオペレーションを呼び出すことによりイベントを受け取るモデルです。

図1.2.2.6-4 プルモデル
図1.2.2.6-4

プルモデル WebOTX Object Brokerでは、各生産者、消費者はイベントチャネルサーバを介して通信を行いますので、相手のモデルを気にすることなく通信することができます。たとえば、プッシュモデルの生産者とプルモデルの消費者との間でイベント通信を行なうことができます。

図1.2.2.6-5 イベントチャネル
図1.2.2.6-5

イベントチャネル また、複数の生産者、複数の消費者が1つのイベントチャネルを介してイベントを送ることができます。1つの生産者が生産したイベントは、イベントチャネル内のバッファに一時的に貯えられ、その時点で接続しているすべての消費者に対して送られます。イベントはすべての消費者が受信した段階でイベントチャネル内のバッファから取り除かれます。

図1.2.2.6-6 複雑な使い方
図1.2.2.6-6

複雑な使い方 注意 生産者、および消費者は明示的にdisconnect_X_Y(X: push or pull, Y: supplier or consumer)オペレーションを呼ぶまで接続しているものとして扱われます。disconnect_X_Yを呼ばずに、消費者を終了すると、イベントを取得していない消費者がいるものとして扱われ、やがてバッファあふれが起こり新規のイベントが送られなくなりますのでご注意ください。イベントチャネル内のバッファの大きさは設定で変更できます。
イベントチャネルサーバの使い方
WebOTX Object Brokerのイベントチャネルサーバは、Windows版ではeventsv.exe、UNIX版(HP-UX版, Solaris版, Linux版)ではeventsvという名前です。これらを利用するためには次の手順が必要です。 オブジェクト生成 WebOTX Object Brokerのイベントチャネルサーバを利用するためには、org.omg.CosEventChannelAdmin.EventChannelをサポートするイベントチャネルオブジェクトを生成する必要があります。しかし、WebOTX Object Broker JavaTM はイベントチャネルオブジェクトを作成することができません。したがって、C++を使ったアプリケーションによって登録する必要があります。以下にC++を使ってオブジェクトを生成する方法を示します。 イベントチャネルサーバは自動起動で利用します。 自動起動引数はデフォルトの位置で渡します。つまり、CORBA::ORB::__server_argument_index(CORBA::ULong)は既定値(0)のままにします。 活性化方針はCORBA_SharedServerを指定します。 スレッド処理方針はpoolあるいはpermethodを指定します。
// C++によるコーディング例
 
CORBA_ORB_ptr orb = CORBA_ORB_init(...);
 
orb->__implementation_name("eventchimpl");
 
orb->__server_path_name("サーバ上でのeventsv.exeまたはeventsvのパス名");
 
CORBA::Object_var obj =
orb->resolve_initial_references("RootPOA");
 
PortableServer::POA_var rootPOA =
PortableServer::POA::_narrow(obj);
 
// LifespanPolicyとしてPERSISTENTをもつPOAを作成
CORBA::PolicyList plist;
 
plist.length(1);
 
plist[0] = rootPOA->create_lifespan_policy(PortableServer::PERSISTENT);
 
PortableServer::POA_var poa =
    rootPOA->create_POA("MyPOA",
PortableServer::POAManager::_nil(), plist);
 
CORBA::Object_var eventch_obj =
   
poa->create_reference("IDL:omg.org/CosEventChannelAdmin/EventChannel:1.0");
 
char *objstr = orb->object_to_string(eventch_obj);
 
// これをJavaアプリケーションに渡す
生産者としての使い方
  1. プッシュモデルの生産者
    プッシュモデルの生産者はpushオペレーションを実行するクライアントとして実装します。次の手順でイベントチャネルとの接続を行います。
    1. イベントチャネルオブジェクトのオブジェクトリファレンス(org.omg.CosEventChannelAdmin.EventChannelインタフェース)を取得します。
    2. イベントチャネルオブジェクトのfor_suppliersオペレーション(C++)生産者管理オブジェクトを取得します。
    3. 生産者管理オブジェクトのobtain_push_consumerオペレーション(C++)でプロクシプッシュ消費者オブジェクトを取得します。
    4. 3.で取得したプロクシプッシュ消費者オブジェクトに対してこの生産者のプッシュ生産者オブジェクトを引数としてconnect_push_supplierオペレーション(C++)を呼び出します。このとき、プッシュ生産者オブジェクトはnilオブジェクトでもかまいません。
    5. 以後、3.で取得したプロクシプッシュ消費者オブジェクトのpushオペレーション(C++)を呼び出して生産したイベントを送ります。
    6. 終了時には3.で取得したプロクシプッシュ消費者オブジェクトのdisconnect_push_consumerオペレーション(C++)を実行します。
  2. プルモデルの生産者
    プルモデルの生産者はorg.omg.CosEventComm.PullSupplierインタフェースをサポートするオブジェクトとして実装します。次の手順でイベントチャネルとの接続を行います。
    1. イベントチャネルオブジェクトのオブジェクトリファレンス(org.omg.CosEventChannelAdmin.EventChannelインタフェース)を取得します。
    2. イベントチャネルオブジェクトのfor_suppliersオペレーション(C++)で生産者管理オブジェクトを取得します。
    3. 生産者管理オブジェクトのobtain_pull_consumerオペレーション(C++)でプロクシプル消費者オブジェクトを取得します。
    4. 3.で取得したプロクシプル消費者オブジェクトに対してこの生産者のプル生産者オブジェクトを引数としてconnect_pull_supplierオペレーション(C++)を呼び出します。
    5. 以後、イベントチャネルはpull(C++)またはtry_pullオペレーション(C++)により、このプル生産者からイベントを受け取ります。これらのオペレーションの実装コードで生産したイベントを返してください。
    6. 終了時には3.で取得したプロクシプル消費者オブジェクトのdisconnect_pull_consumerオペレーション(C++)を実行します。
    プルモデルの生産者を使うと、各生産者に対応して、イベントチャネルサーバプロセス内に専用のスレッドが1つ生成されます。
消費者としての使い方
  1. プッシュモデルの消費者
    プッシュモデルの消費者はorg.omg.CosEventComm.PushConsumerインタフェースをサポートするオブジェクトとして実装します。次の手順でイベントチャネルとの接続を行います。
    1. イベントチャネルオブジェクトのオブジェクトリファレンス(org.omg.CosEventChannelAdmin.EventChannelインタフェース)を取得します。
    2. イベントチャネルオブジェクトのfor_consumersオペレーション(C++)で消費者管理オブジェクトを取得します。
    3. 消費者管理オブジェクトのobtain_push_supplierオペレーション(C++)でプロクシプッシュ生産者オブジェクトを取得します。
    4. 3.で取得したプロクシプッシュ生産者オブジェクトに対してこの消費者のプッシュ消費者オブジェクトを引数としてconnect_push_consumerオペレーション(C++)を呼び出します。
    5. 以後、イベントチャネルはpushオペレーション(C++)により、このプッシュ消費者に対してイベントを送ります。pushオペレーションの実装コードでイベントを受け取ったときの処理を行ってください。
    6. 終了時には3.で取得したプロクシプッシュ生産者オブジェクトのdisconnect_push_supplierオペレーション(C++)を実行します。
    プッシュモデルの消費者を利用すると、各消費者に対応して、イベントチャネルサーバプロセス内に1つ、専用のスレッドが生成されます。
  2. プルモデルの消費者
    プルモデルの消費者はpullまたはtry_pullオペレーションを実行するクライアントとして実装します。次の手順でイベントチャネルとの接続を行います。
    1. イベントチャネルオブジェクトのfor_consumersオペレーション(C++)で消費者管理オブジェクトを取得します。
    2. イベントチャネルオブジェクトのfor_consumersオペレーション(C++)で消費者管理オブジェクトを取得します。
    3. 消費者管理オブジェクトのobtain_pull_supplierオペレーション(C++)でプロクシプル生産者オブジェクトを取得します。
    4. 3.で取得したプロクシプル生産者オブジェクトに対してこの消費者のプル消費者オブジェクトを引数としてconnect_pull_consumerオペレーション(C++)を呼び出します。このとき、プル消費者オブジェクトはnilオブジェクトでもかまいません
    5. 以後、3.で取得したプロクシプル生産者オブジェクトのpull(C++)またはtry_pullオペレーション(C++)を呼び出してイベントを受け取ります。pullオペレーションではイベントが発生するまで、この関数から戻りません。通常設定されている、呼びだしタイムアウトが設定してある場合で、タイムアウトが発生すると、イベントが1つ失われます。この場合、try_pullを使うようにしてください。
    終了時には3.で取得したプロクシプル生産者オブジェクトのdisconnect_pull_supplierオペレーション(C++)を実行します。