ORBit2

(2002.5.12) 新規作成;ORBitを活用したプログラムの開発について,これまでの日記の内容をまとめた。

(2005.1.1) 更新;ORBit2を使うようにした。

(2014.7) ORBit2はすでに捨てられており、C/C++用で実用的なCORBA ORBとしては, omniORBがいいでしょう。

ORBitとは何か

(2002.5.19 この節追加。2005.11.5 この節加筆。)

ORBitは,CORBA ORB (Object Request Broker) のひとつ。一言でいえば,プロセス越しにオブジェクトのメソッドを呼び出すための中継器。ネットワークを経由することもできる。

クライアントではローカル・オブジェクトを操作しているように見える。実際にはどうするか。サーバ側ではスケルトン、クライアント側ではスタブを用意する。オブジェクトへのメッセージはスタブを経てORBが受け取り、クライアント側のORBがサーバにリクエストを送信し、実際にはサーバでオブジェクトの操作を行う。戻り値は再びクライアントのORBに伝えられ、クライアントに戻される。

CORBAでは,クライアントと(オブジェクト)サーバを作るためのプログラミング言語が異なっていてもいい(プログラミング言語非依存)。

IDL (Interface Definition Language; ISO/IEC 14750) という専用の言語でオブジェクトのインターフェイスを記述する。そのうえで、IDLコンパイラで実装するプログラミング言語に翻訳する。C言語であれば、ヘッダファイルとスタブ・スケルトンのためのいくつかのソースファイルが生成される。もっとも、IDLがプログラミング言語に依存しないといっても、実際問題、IDLコンパイラとORBがないプログラミング言語では作れないけれど。

Note.

IDLでインターフェイスを定義し、実装する言語に変換する、というのはめんどくさい。Javaでは、通信方法だけCORBA ORB間の通信方法であるIIOPに準拠して、IDLを使わずに分散オブジェクトが書けるようになっている (Java RMI over IIOP)。

プログラミング言語依存、あるいはIIOP以外の通信方法でよければ、ほかの手段が面倒がない。例えば、dRubyは、Ruby限定だが、IDLを書く必要もなく簡単に分散オブジェクトを作れる。

オープンソースのORBはいくつかあるが,GNOMEデスクトップ環境ではORBitのみが使える。ORBit2は、glib 2.0以降に依存している。

IDL

(2005.11.6 この節追加。)

IDLはCORBAの一部としてOMGが標準化している。モジュールを示すmodule、インターフェイスを定義するinterface、別名を定義するtypedef、定数のconstなどなど。パッと見はC風あるいはJavaっぽい感じになる。

module M {
  typedef long Foo;
  const long thing = 1;
  interface A {
    void doit(in Foo arg1);
  };
};

型は、例えば、次のものが使える。整数は、shortが16ビット、longが32ビットと長さが決まっている。

  • 実数 float, double, long double
  • 整数 short, long, long long, unsigned short, unsigned long, unsigned long long
  • 文字 char
  • 真偽 boolean
  • オクテット octet
  • 文字列 string

メソッドの引数については、その向きを宣言する。

  • in -- 引数は、クライアントからサーバに渡される
  • out -- 引数は、サーバからクライアントに渡される
  • inout -- 引数は、双方向で渡される

単純なサンプル(サーバ編)

(2000.11.05 随感。2002.05.19 加筆。2002.05.25 加筆。)

CounterオブジェクトとReverseCounterオブジェクトとを作成してみる。Counterオブジェクトはincr()で加算、decr()で減算できるようにし、ReverseCounterオブジェクトはそれぞれ逆の動作になるようにしてみる。

次のようなインターフェイスファイルを書けばいい。counter.idlとする。IDLは,あくまでもインターフェイスを記述するものなので,ReverseCounterインターフェイスにincr(), decr()をもう一度書く必要はない。

interface Counter {
  readonly attribute short value;
  void incr();
  void decr();
  string name();
};

interface ReverseCounter: Counter {
};

次に,Counter, ReverseCounterオブジェクトサーバーを実装する。ORBit2は、plain Cで実装を書くので,とても長くなる。ファイル名はserver.ccとする。

まずは、Counterオブジェクトから。オブジェクトのメンバを保存するための構造体(名前は何でもいいが、ここではCounterData)を用意し、実装する関数(CounterImpl_...();ORBitが名前を決める。)で、その構造体を操作する。

 20| struct CounterData {
 21|     short value;
 22|     CounterData(short v_ = 0): value(v_) { }
 23| };
 24| 
 25| #define Counter_DATA(x) ((CounterData*) ((POA_Counter*) (x))->vepv->Counter_epv->_private)
 26| 
 27| CORBA_short CounterImpl_get_value(PortableServer_Servant servant,
 28|                                   CORBA_Environment* ev) {
 29|     return Counter_DATA(servant)->value;
 30| }
 31| 
 32| void CounterImpl_incr(PortableServer_Servant servant, CORBA_Environment* ev) {
 33|     Counter_DATA(servant)->value++;
 34| }
 35| 
 36| void CounterImpl_decr(PortableServer_Servant servant, CORBA_Environment* ev) {
 37|     Counter_DATA(servant)->value--;
 38| }
 39| 
 40| CORBA_char* CounterImpl_name(PortableServer_Servant servant,
 41|                              CORBA_Environment* ev) {
 42|     return CORBA_string_dup("Counter");
 43| }

メソッドを作ったら、次は、オブジェクトを生成する関数、破壊する関数を書きます。関数名は何でもいいのですが、ここでは、領域を確保するnew(), コンストラクタctor(), デストラクタdtor(), 領域を解放するdelete()とします。

 45| void CounterImpl_ctor(POA_Counter__vepv* vepv, int inivalue = 0)
 46| {
 47|     static PortableServer_ServantBase__epv super_epv = { NULL, NULL, NULL };
 48|     static POA_Counter__epv epv = {
 49|         NULL, // Counterクラスのprotectedデータを格納
 50|         CounterImpl_get_value,
 51|         CounterImpl_incr,
 52|         CounterImpl_decr,
 53|         CounterImpl_name };
 54| 
 55|     vepv->_base_epv = new PortableServer_ServantBase__epv();
 56|     *vepv->_base_epv = super_epv;
 57|     vepv->Counter_epv = new POA_Counter__epv();
 58|     *vepv->Counter_epv = epv;
 59|     vepv->Counter_epv->_private = new CounterData(inivalue);
 60| }
 61| 
 62| POA_Counter* CounterImpl_new(CORBA_Environment* ev)
 63| {
 64|     POA_Counter* servant = new POA_Counter();
 65|     servant->_private = NULL; // ORBitが使用
 66|     servant->vepv = new POA_Counter__vepv();
 67|     CounterImpl_ctor(servant->vepv);
 68|     POA_Counter__init(servant, ev);
 69|     return servant;
 70| }
 71| 
 72| void CounterImpl_dtor(POA_Counter__vepv* vepv)
 73| {
 74|     delete ((CounterData*) vepv->Counter_epv->_private);
 75|     delete vepv->Counter_epv;
 76|     delete vepv->_base_epv;
 77| }
 78| 
 79| void CounterImpl_delete(POA_Counter* servant, CORBA_Environment* ev)
 80| {
 81|     POA_Counter__fini(servant, ev);
 82|     CounterImpl_dtor(servant->vepv);
 83|     delete servant->vepv;
 84|     delete servant;
 85| }

同様に,ReverseCounterも記述する。データを保存する部分は,Counterが流用できる。

 83| void ReverseCounterImpl_incr(PortableServer_Servant servant, CORBA_Environment* ev) {
 84|     Counter_DATA(servant)->value--;
 85| }
 86| 
 87| void ReverseCounterImpl_decr(PortableServer_Servant servant, CORBA_Environment* ev) {
 88|     Counter_DATA(servant)->value++;
 89| }
 90| 
 91| CORBA_char* ReverseCounterImpl_name(PortableServer_Servant servant,
 92|                                     CORBA_Environment* ev) {
 93|     return CORBA_string_dup("ReverseCounter");
 94| }

ORBitの場合,vtableを自分で書かなければならない。ReverseCounterのコンストラクタを示す。

ReverseCounterImpl_incr,ReverseCounterImpl_decrなどの関数を,incr, decrなどのIDLで宣言したメソッドに対応付ける。

103| void ReverseCounterImpl_ctor(POA_ReverseCounter__vepv* vepv)
104| {
105|     static POA_ReverseCounter__epv epv = { NULL };
106| 
107|     CounterImpl_ctor((POA_Counter__vepv*) vepv, 1000);
108|     vepv->Counter_epv->incr = ReverseCounterImpl_incr; // オーバーライドする
109|     vepv->Counter_epv->decr = ReverseCounterImpl_decr;
110|     vepv->Counter_epv->name = ReverseCounterImpl_name;
111|     vepv->ReverseCounter_epv = new POA_ReverseCounter__epv();
112|     *vepv->ReverseCounter_epv = epv;
113| }
114| 
115| POA_ReverseCounter* ReverseCounterImpl_new(CORBA_Environment* ev)
116| {
117|     POA_ReverseCounter* servant = new POA_ReverseCounter();
118|     servant->_private = NULL; // ORBitが使用
119|     servant->vepv = new POA_ReverseCounter__vepv();
120|     ReverseCounterImpl_ctor(servant->vepv);
121|     POA_ReverseCounter__init(servant, ev); // 内部でPOA_Counter__init()を呼び出している
122|     return servant;
123| }

ここまでで,オブジェクトを定義できた。次は,これらのオブジェクトを活性化し,クライアントからの呼び出しに応じれるようにする。

リモートオブジェクトを識別するために、ここでは手を抜いて、IOR (Interoperable Object Reference) をファイルに保存し、クライアントでそれを使うことにする。IORには,サーバーのIPアドレス,ポート番号などが含まれており,クライアントがサーバを識別できる。なお、本来は、ネーミングサービス (NameService) やそのほかのオブジェクト探索、活性化の枠組みを使うべき。

write_ior()関数でIORをファイルに書き出す。PortableServer::POA::servant_to_reference()でサーバントからCORBAオブジェクトへの参照を取り出し,それをCORBA::ORB::object_to_string()でIOR文字列に変換する。使い終わったCORBAオブジェクトはCORBA::Object::release()で、stringオブジェクトはCORBA_free()関数で後始末をする。

 94| void write_ior(
 95|     FILE* fp,
 96|     CORBA_ORB orb,
 97|     PortableServer_POA poa,
 98|     PortableServer_Servant servant,
 99|     CORBA_Environment* ev)
100| {
101|     CORBA_Object obj = PortableServer_POA_servant_to_reference(poa, servant, ev);
102|     catchException(*ev);
103|     char* ior = CORBA_ORB_object_to_string(orb, obj, ev);
104|     catchException(*ev);
105| 
106|     fprintf(fp, "%s\n", ior);
107| 
108|     CORBA_Object_release(obj, ev);
109|     CORBA_free(ior);
110| }

残りは、サーバのメイン関数部分。

112| void test_ior_file(int argc, char* argv[])
113| {
114|     CORBA_Environment ev;
115|     CORBA_exception_init(&ev);
116| 
117|     // ORBを初期化する
118|     CORBA_ORB orb = CORBA_ORB_init(&argc, argv, "orbit-local-orb", &ev);
119|     catchException(ev);
120| 
121|     // root POAへの参照を得る
122|     PortableServer_POA poa = (PortableServer_POA)
123|             CORBA_ORB_resolve_initial_references(orb, "RootPOA", &ev);
124|     catchException(ev);
125| 
126|     // CORBAオブジェクト実装(サーバント)を生成する
127|     POA_Counter* myCounter = CounterImpl_new(&ev);
128|     catchException(ev);
129|     POA_ReverseCounter* myReverseCounter = RCImpl_new(&ev);
130|     catchException(ev);
131| 
132|     // サーバントを活性化する
133|     PortableServer_ObjectId* nc_id = 
134|         PortableServer_POA_activate_object(poa, myCounter, &ev);
135|     catchException(ev);
136|     PortableServer_ObjectId* rc_id =
137|         PortableServer_POA_activate_object(poa, myReverseCounter, &ev);
138|     catchException(ev);
139| 
140|     // それぞれのオブジェクトのIORをファイルに書き出す。
141|     FILE* fp = fopen(ior_file, "w");
142|     if (!fp) {
143|         perror("ior-file open failed");
144|         exit(1);
145|     }
146|     write_ior(fp, orb, poa, myCounter, &ev);
147|     write_ior(fp, orb, poa, myReverseCounter, &ev);
148|     fclose(fp);
149| 
150|     // リクエストの受け付けを開始する。
151|     PortableServer_POAManager poa_manager =
152|         PortableServer_POA__get_the_POAManager(poa, &ev);
153|     PortableServer_POAManager_activate(poa_manager, &ev);
154| 
155|     // main loop
156|     printf("ready.\n");
157|     CORBA_ORB_run(orb, &ev);
158| 
159|     // 後処理
160|     PortableServer_POA_deactivate_object(poa, nc_id, &ev);
161|     PortableServer_POA_deactivate_object(poa, rc_id, &ev);
162|     CORBA_free(nc_id);
163|     CORBA_free(rc_id);
164| 
165|     CounterImpl_delete(myCounter, &ev);
166|     RCImpl_delete(myReverseCounter, &ev);
167| 
168|     CORBA_exception_free(&ev);
169| }
234| int main(int argc, char* argv[])
235| {
236|     test_ior_file(argc, argv);
237|     return 0;
238| }

サンプル(クライアント編)

(2005.11.6 加筆)

サーバが書けたので、クライアントを作ってみよう。

まずは、Counterオブジェクトをテストする部分から。いずれもCounterインターフェイスを持っているので、同じ関数でテストできる。incr()メソッド、decr()メソッド、それにget_value()とname()を呼び出してみる。

  4| #include "../config.h"
  5| 
  6| #include <stdio.h>
  7| #include <stdlib.h>
  8| #include <assert.h>
  9| #include <orbit/orbit.h>
 10| 
 11| #ifdef USE_NAME_SERVICE
 12|   #include <ORBitservices/CosNaming.h>
 13| #endif
 14| 
 15| #include "../misc.h"
 16| 
 17| #include "counter.h"
 18|     // orbit-idlによって生成された.hファイル
 19|     // counter.idl -> counter.h
 20| 
 21| //////////////////////////////////////////////////////////////////////
 22| 
 23| CORBA_Environment ev;
 24| 
 25| void testCounter(Counter counter) {
 26|     printf("name = %s\n", Counter_name(counter, &ev));
 27| 
 28|     int i;
 29|     printf("incr(): ");
 30|     for (i = 0; i < 10; i++) {
 31|         Counter_incr(counter, &ev);
 32|         printf(" %d", Counter__get_value(counter, &ev));
 33|     }
 34|     printf(".\n");
 35| 
 36|     printf("decr(): ");
 37|     for (i = 0; i < 3; i++) {
 38|         Counter_decr(counter, &ev);
 39|         printf(" %d", Counter__get_value(counter, &ev));
 40|     }
 41|     printf(".\n");
 42| }
 43| 
 44| void catchException(CORBA_Environment& ev) {
 45|     if (ev._major != CORBA_NO_EXCEPTION) {
 46|         printf("CORBA exception: %s\n", CORBA_exception_id(&ev));
 47|         CORBA_exception_free(&ev);
 48|         exit(1);
 49|     }
 50| }

クライアントでのサーバの探索はIORファイルを使うことにしたので、IORファイルを読み込んでオブジェクトへの参照を得る関数を書く。

 87| const char* ior_file = "../counter.ior";
 88| 
 89| void get_object_by_ior(CORBA_ORB orb, Counter& nc, ReverseCounter& rc) {
 90|     FILE* fp = fopen(ior_file, "r");
 91|     if (!fp) {
 92|         perror("ior-file open");
 93|         return;
 94|     }
 95|     nc = CORBA_ORB_string_to_object(orb, read_line(fp), &ev);
 96|     catchException(ev);
 97|     rc = CORBA_ORB_string_to_object(orb, read_line(fp), &ev);
 98|     catchException(ev);
 99|     fclose(fp);
100| }

最後はメイン部分。

103| int main(int argc, char* argv[])
104| {
105|     CORBA_exception_init(&ev);
106| 
107|     CORBA_ORB orb = CORBA_ORB_init(&argc, argv, "orbit-local-orb", &ev);
108|     catchException(ev);
109| 
110|     Counter counter = NULL;
111|     ReverseCounter rc = NULL;
112| 
113| #ifdef USE_NAME_SERVICE
114|     get_object_by_nameservice(orb, counter, rc);
115| #else
116|     get_object_by_ior(orb, counter, rc);
117| #endif
118| 
119|     if (!counter || !rc) {
120|         printf("ior error.\n");
121|         return 1;
122|     }
123| 
124|     testCounter(counter);
125|     testCounter(rc);
126| 
127|     CORBA_Object_release(counter, &ev);
128|     CORBA_Object_release(rc, &ev);
129| 
130|     return 0;
131| }

ORB, Object, POAインターフェイス

TODO: この節を更新すること。

orb-idl

で,実行するときはorbit-name-serverを動かしておいて,server, client共に-ORBNamingIORオプションでネームサーバーのIORを与える。問題はネームサーバーのIORをどうやって取ってくるのか? Javaだとネームサーバーのホスト名とポート番号で接続できるが,orbit-name-serverへはIORでしか接続できんよ?

サイト内関連文書

Bonobo components
GNOMEプラットフォームでは、ORBit2をベースにコンポーネント技術を組み立てている。
Rubyで分散オブジェクト
Ruby用の分散オブジェクト技術dRubyでいろんなことをする。