(2014.7) ORBit2はすでに捨てられており、C/C++用で実用的なCORBA ORBとしては, omniORB がいいでしょう。
(2005.1.1) 更新;ORBit2を使うようにした。
(2002.5.12) 新規作成;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がないプログラミング言語では作れないけれど。
IDLでインターフェイスを定義し、実装する言語に変換する、というのはめんどくさい。Javaでは、通信方法だけCORBA ORB間の通信方法であるIIOPに準拠して、IDLを使わずに分散オブジェクトが書けるようになっている (Java RMI over IIOP)。
プログラミング言語依存、あるいはIIOP以外の通信方法でよければ、ほかの手段が面倒がない。例えば、dRubyは、Ruby限定だが、IDLを書く必要もなく簡単に分散オブジェクトを作れる。
オープンソースのORBはいくつかあるが,GNOMEデスクトップ環境ではORBitのみが使える。ORBit2は、glib 2.0以降に依存している。
(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ビットと長さが決まっている。
メソッドの引数については、その向きを宣言する。
(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| }
TODO: この節を更新すること。
で,実行するときはorbit-name-serverを動かしておいて,server, client共に-ORBNamingIORオプションでネームサーバーのIORを与える。問題はネームサーバーのIORをどうやって取ってくるのか? Javaだとネームサーバーのホスト名とポート番号で接続できるが,orbit-name-serverへはIORでしか接続できんよ?