CORBA again

(2017-05) 現代の環境でコンパイルできるよう, 更新した。10年ぶり、か。

CORBAでのクラス継承を試す。

インタフェイス

Counter / ReverseCounterインタフェイスのオブジェクトに対して, RPCで呼び出す.

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

interface ReverseCounter: Counter {
};

ReverseCounterincr()で減算し,decr()で加算するようにする.

omniORB (C++)

次の環境;

  • Fedora 25 Linux
  • omniORB 4.2.1
  • gcc 6.3.1

サーバ

まず、オブジェクトを実装する。POA_インタフェイス名 から継承する。

C
[POPUP]
  1. class CounterImpl: public virtual POA_Counter,
  2. public virtual PortableServer::RefCountServantBase
  3. {
  4. protected:
  5. short count_;
  6. public:
  7. CounterImpl(): count_(0) { }
  8. virtual ~CounterImpl() { }
  9. virtual CORBA::Short value(){ return count_; }
  10. virtual void incr() { count_++; }
  11. virtual void decr() { count_--; }
  12. virtual char* name() { return CORBA::string_dup("通常Counter"); }
  13. protected:
  14. CounterImpl(short v): count_(v) { }
  15. };
  16. ////////////////////////////////////////////////////////////////////////
  17. // ReverseCounterImpl
  18. class ReverseCounterImpl: public virtual POA_ReverseCounter,
  19. public virtual PortableServer::RefCountServantBase,
  20. public virtual CounterImpl
  21. {
  22. typedef CounterImpl super;
  23. public:
  24. ReverseCounterImpl(): super(1000) { }
  25. virtual ~ReverseCounterImpl() { }
  26. virtual void incr() { count_--; }
  27. virtual void decr() { count_++; }
  28. virtual char* name() { return CORBA::string_dup("Reverse(逆進)Counter"); }
  29. };

CORBA::Object_var で受けて, _narrow() で目的のクラスにダウンキャストする。

インタフェイス名_var とインタフェイス名_ptr がある. _varはスコープから抜けるときに解放され, _ptr はそうではない。

実装クラスからインタフェイスクラスを得るには, _this()を呼び出す。

サーバ側では, クライアントからオブジェクトを探せるように, 次の二つのいずれかの方法を採る;

  1. name service にオブジェクトを登録する. クライアントでは名前で検索する.
  2. ファイルに, オブジェクトを object_to_string() したものを書き出す. この文字列は, Interoperable Object Reference (IOR) と呼ばれ, サーバのプロセスなどの情報が含まれる。
C
[POPUP]
  1. int main(int argc, char* argv[])
  2. {
  3. // Initialise the ORB.
  4. CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB4");
  5. CORBA::Object_var obj;
  6. #ifdef USE_NAME_SERVICE
  7. CosNaming::NamingContext_var rootContext;
  8. // Obtain a reference to the root context of the Name service:
  9. obj = orb->resolve_initial_references("NameService");
  10. rootContext = CosNaming::NamingContext::_narrow(obj);
  11. if( CORBA::is_nil(rootContext) ) {
  12. cerr << "Failed to narrow the root naming context." << endl;
  13. return 1;
  14. }
  15. #endif // USE_NAME_SERVICE
  16. try {
  17. // Obtain a reference to the root POA.
  18. obj = orb->resolve_initial_references("RootPOA");
  19. PortableServer::POA_var rootPOA = PortableServer::POA::_narrow(obj);
  20. // We allocate the objects on the heap. Since these are reference
  21. // counted objects, they will be deleted by the POA when they are no
  22. // longer needed.
  23. CounterImpl* myCounter = new CounterImpl();
  24. ReverseCounterImpl* myReverseCounter = new ReverseCounterImpl();
  25. // Activate the objects. This tells the POA that the objects are
  26. // ready to accept requests.
  27. PortableServer::ObjectId_var myCounter_iid
  28. = rootPOA->activate_object(myCounter);
  29. PortableServer::ObjectId_var myReverseCounter_iid
  30. = rootPOA->activate_object(myReverseCounter);
  31. #ifdef USE_NAME_SERVICE
  32. bindObjectToName(orb, rootContext, myCounter->_this(), COUNTER_NAME);
  33. bindObjectToName(orb, rootContext, myReverseCounter->_this(), REVERSE_NAME);
  34. #else
  35. // Obtain a reference to each object and output the stringified
  36. // IOR to ior-file
  37. FILE* fp = fopen(IOR_FILE, "w");
  38. if (!fp) {
  39. perror("ior-file open failed");
  40. return 1;
  41. }
  42. CORBA::String_var sior;
  43. sior = orb->object_to_string(myCounter->_this());
  44. fprintf(fp, "%s\n", (const char*) sior);
  45. sior = orb->object_to_string(myReverseCounter->_this());
  46. fprintf(fp, "%s\n", (const char*) sior);
  47. fclose(fp);
  48. #endif // USE_NAME_SERVICE
  49. // Obtain a POAManager, and tell the POA to start accepting
  50. // requests on its objects.
  51. PortableServer::POAManager_var poaMgr = rootPOA->the_POAManager();
  52. poaMgr->activate();
  53. // main loop
  54. printf("ready.\n");
  55. orb->run();
  56. rootPOA->destroy(true, true);
  57. delete myCounter;
  58. delete myReverseCounter;
  59. #ifndef USE_NAME_SERVICE
  60. remove(IOR_FILE);
  61. #endif // !USE_NAME_SERVICE
  62. }
  63. catch(CORBA::SystemException&) {
  64. cerr << "Caught CORBA::SystemException." << endl;
  65. }
  66. catch(CORBA::Exception&) {
  67. cerr << "Caught CORBA::Exception." << endl;
  68. }
  69. catch(omniORB::fatalException& fe) {
  70. cerr << "Caught omniORB::fatalException:" << endl;
  71. cerr << " file: " << fe.file() << endl;
  72. cerr << " line: " << fe.line() << endl;
  73. cerr << " mesg: " << fe.errmsg() << endl;
  74. }
  75. catch(...) {
  76. cerr << "Caught unknown exception." << endl;
  77. }
  78. orb->destroy();
  79. return 0;
  80. }

クライアント

インタフェイス名_ptr を使って、各メソッドを呼び出す.

C
[POPUP]
  1. // ReverseCounter_ptr を与えることもできる。
  2. void do_test(Counter_ptr counter)
  3. {
  4. CORBA::String_var name = counter->name();
  5. printf("name = %s\n", name.in());
  6. int i;
  7. printf("incr(): ");
  8. for (i = 0; i < 5; i++) {
  9. counter->incr();
  10. printf(" %d", counter->value());
  11. }
  12. printf(".\n");
  13. printf("decr(): ");
  14. for (i = 0; i < 3; i++) {
  15. counter->decr();
  16. printf(" %d", counter->value());
  17. }
  18. printf(".\n");
  19. }

main関数.

C
[POPUP]
  1. int main(int argc, char* argv[])
  2. {
  3. // omniORBは第3引数を省略できない
  4. CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB4");
  5. CORBA::Object_var obj;
  6. #ifdef USE_NAME_SERVICE
  7. CosNaming::NamingContext_var rootContext;
  8. // Obtain a reference to the root context of the Name service:
  9. obj = orb->resolve_initial_references("NameService");
  10. rootContext = CosNaming::NamingContext::_narrow(obj);
  11. if( CORBA::is_nil(rootContext) ) {
  12. cerr << "Failed to narrow the root naming context." << endl;
  13. return 1;
  14. }
  15. #endif // USE_NAME_SERVICE
  16. try {
  17. #ifdef USE_NAME_SERVICE
  18. CORBA::Object_var obj_nc = getObjectReference(orb, rootContext,
  19. COUNTER_NAME);
  20. CORBA::Object_var obj_rc = getObjectReference(orb, rootContext,
  21. REVERSE_NAME);
  22. #else
  23. FILE* fp = fopen(IOR_FILE, "r");
  24. if (!fp) {
  25. perror("file open");
  26. return 1;
  27. }
  28. CORBA::Object_var obj_nc = orb->string_to_object(read_line(fp));
  29. CORBA::Object_var obj_rc = orb->string_to_object(read_line(fp));
  30. fclose(fp);
  31. if (!obj_nc || !obj_rc) {
  32. printf("ior error.\n");
  33. return 1;
  34. }
  35. #endif // USE_NAME_SERVICE
  36. Counter_var nc = Counter::_narrow(obj_nc);
  37. ReverseCounter_var rc = ReverseCounter::_narrow(obj_rc);
  38. if (CORBA::is_nil(nc) || CORBA::is_nil(rc)) {
  39. printf("cannot invoke on a nil object\n");
  40. return 1;
  41. }
  42. do_test(nc);
  43. do_test(rc);
  44. }
  45. catch (CORBA::SystemException&) {
  46. cerr << "Caught CORBA::SystemException." << endl;
  47. }
  48. catch (CORBA::Exception& e) {
  49. cerr << "Caught CORBA::Exception." << endl;
  50. }
  51. catch (...) {
  52. printf("unknown exception.\n");
  53. }
  54. orb->destroy();
  55. return 0;
  56. }

実行方法

name serviceを使う場合は, あらかじめ root ユーザで name server を起動しておく。

# omniNames -always

/var/omninames/ 以下にデータを書き込む。

omniORB は, /etc/omniORB.cfg ファイルの設定を見るので, name service を使う場合でも, プログラムの起動時にポート番号などを与える必要はない。

$ ./server 
ready.
$ ./client 
name = 通常Counter
incr():  1 2 3 4 5.
decr():  4 3 2.
name = Reverse(逆進)Counter
incr():  999 998 997 996 995.
decr():  996 997 998.

Java

Java SE 8.

サーバ

Javaは多重継承がないので, CounterImpl を継承できない。同じコードでも再度定義する。

Java
[POPUP]
  1. class CounterImpl extends CounterPOA
  2. {
  3. protected short count_;
  4. public CounterImpl() { super(); count_ = 0; }
  5. public short value() { return count_; }
  6. public void incr() { count_++; }
  7. public void decr() { count_--; }
  8. public String name() { return "Counter"; }
  9. };
  10. class ReverseCounterImpl extends ReverseCounterPOA
  11. {
  12. protected short count_;
  13. public ReverseCounterImpl() { super(); count_ = 1000; }
  14. public short value() { return count_; }
  15. public void incr() { count_--; }
  16. public void decr() { count_++; }
  17. public String name() { return "ReverseCounter"; }
  18. };

mainメソッド. 流れは C++版と同じ.

Java
[POPUP]
  1. // サーバ実装
  2. class Server
  3. {
  4. public static void main(String[] args) {
  5. try {
  6. ORB orb = ORB.init(args, null);
  7. // Get reference to rootpoa & activate the POAManager
  8. POA rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
  9. rootPOA.the_POAManager().activate();
  10. // create servants.
  11. //CounterImpl counterImpl = new CounterImpl();
  12. //ReverseCounterImpl revCounterImpl = new ReverseCounterImpl();
  13. // create a tie, with servant being the delegate.
  14. CounterPOATie ncTie
  15. = new CounterPOATie(new CounterImpl(), rootPOA);
  16. ReverseCounterPOATie rcTie
  17. = new ReverseCounterPOATie(new ReverseCounterImpl(),
  18. rootPOA);
  19. // obtain the objectRef for the tie.
  20. // this step also implicitly activates the object.
  21. Counter ncRef = ncTie._this(orb);
  22. ReverseCounter rcRef = rcTie._this(orb);
  23. if (Config.USE_NAME_SERVICE) {
  24. org.omg.CORBA.Object nameRef
  25. = orb.resolve_initial_references("NameService");
  26. NamingContext naming = NamingContextHelper.narrow(nameRef);
  27. // (id, kind) の組が一致しなければならない.
  28. NameComponent nc = new NameComponent(Config.COUNTER_NAME,
  29. "Object");
  30. NameComponent[] pathNC = { nc };
  31. naming.rebind(pathNC, ncRef);
  32. NameComponent rc = new NameComponent(Config.REV_COUNTER_NAME,
  33. "Object");
  34. NameComponent[] pathRC = { rc };
  35. naming.rebind(pathRC, rcRef);
  36. }
  37. else {
  38. // IORファイルに書き出す
  39. FileWriter out = new FileWriter("../counter.ior");
  40. // 型が abstract String object_to_string(Object obj) なので、
  41. // 型検査がなってない.
  42. String ncIOR = orb.object_to_string(ncRef);
  43. String rcIOR = orb.object_to_string(rcRef);
  44. out.write(ncIOR + "\n");
  45. out.write(rcIOR + "\n");
  46. out.close();
  47. }
  48. // メインループ
  49. orb.run();
  50. //System.out.println("ready.");
  51. //java.lang.Object sync = new java.lang.Object();
  52. //synchronized(sync) {
  53. // sync.wait();
  54. //}
  55. }
  56. catch (Exception e) {
  57. e.printStackTrace(System.err);
  58. }
  59. }
  60. };

クライアント

こちらも, C++と同じ流れ。

Java
[POPUP]
  1. // クライアント実装
  2. class Client
  3. {
  4. public static void main(String[] args) {
  5. try {
  6. ORB orb = ORB.init(args, null);
  7. org.omg.CORBA.Object ncRef;
  8. org.omg.CORBA.Object rcRef;
  9. if (Config.USE_NAME_SERVICE) {
  10. org.omg.CORBA.Object nameRef
  11. = orb.resolve_initial_references("NameService");
  12. NamingContext naming = NamingContextHelper.narrow(nameRef);
  13. NameComponent nc = new NameComponent(Config.COUNTER_NAME,
  14. "Object");
  15. NameComponent[] pathNC = { nc };
  16. ncRef = naming.resolve(pathNC);
  17. NameComponent rc = new NameComponent(Config.REV_COUNTER_NAME,
  18. "Object");
  19. NameComponent[] pathRC = { rc };
  20. rcRef = naming.resolve(pathRC);
  21. }
  22. else {
  23. // IORファイルから読み込む
  24. BufferedReader in = new BufferedReader(
  25. new FileReader("../counter.ior"));
  26. String ncIOR = in.readLine();
  27. String rcIOR = in.readLine();
  28. ncRef = orb.string_to_object(ncIOR);
  29. rcRef = orb.string_to_object(rcIOR);
  30. }
  31. Counter[] counter = new Counter[2];
  32. counter[0] = CounterHelper.narrow(ncRef);
  33. counter[1] = ReverseCounterHelper.narrow(rcRef);
  34. for (int k = 0; k < 2; k++) {
  35. System.out.println(counter[k].name());
  36. for (int i = 0; i < 10; i++) {
  37. counter[k].incr();
  38. System.out.print(" " + counter[k].value());
  39. }
  40. System.out.println(".");
  41. for (int i = 0; i < 5; i++) {
  42. counter[k].decr();
  43. System.out.print(" " + counter[k].value());
  44. }
  45. System.out.println(".");
  46. }
  47. }
  48. catch (Exception e) {
  49. e.printStackTrace(System.err);
  50. }
  51. }
  52. };

実行方法

name serviceを使う場合は, name server のホスト名, ポート番号を引数で与える. IORをファイルでやりとりする場合は、オプションを何も与えなくても大丈夫。

次の例は, omniORB の name server を使う場合。

$ java Server -ORBInitialPort 2809 -ORBInitialHost lolhost
$ java Client -ORBInitialPort 2809 -ORBInitialHost localhost

IORファイルを使う場合, name service を使う場合のいずれでも、omniORB / Java 間で当然, メソッドの呼び出しができる。

gtk3クライアント

次の環境;

  • gtk3 3.22.15
  • glade 3.20.0

ORBit2 はすでに廃れているので, omniORBを使う。

ウィンドウは, glade3 で, GtkBuilder形式で作る。

本体 (C++).

C
[POPUP]
  1. #include "../../config.h"
  2. #include <gtk/gtk.h>
  3. #include "support.h"
  4. #include "gtk2sample.h"
  5. #include "../misc.h"
  6. CounterSet csets[2];
  7. // UI
  8. GtkBuilder* builder;
  9. int main(int argc, char* argv[])
  10. {
  11. GtkWindow* window1;
  12. #ifdef ENABLE_NLS
  13. bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
  14. bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  15. textdomain (GETTEXT_PACKAGE);
  16. #endif
  17. gtk_init(&argc, &argv);
  18. add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
  19. CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB4");
  20. #ifdef USE_NAME_SERVICE
  21. CORBA::Object_var obj;
  22. obj = orb->resolve_initial_references("NameService");
  23. CosNaming::NamingContext_var rootContext =
  24. CosNaming::NamingContext::_narrow(obj);
  25. if ( !rootContext ) {
  26. printf("resolve NameService failed.\n");
  27. return 1;
  28. }
  29. CosNaming::Name name;
  30. name.length(1);
  31. name[0].id = CORBA::string_dup(COUNTER_NAME);
  32. name[0].kind = "Object"; // string copied
  33. obj = rootContext->resolve(name);
  34. csets[0].counter = Counter::_narrow(obj);
  35. name.length(1);
  36. name[0].id = CORBA::string_dup(REVERSE_NAME);
  37. name[0].kind = "Object"; // string copied
  38. obj = rootContext->resolve(name);
  39. csets[1].counter = ReverseCounter::_narrow(obj);
  40. #else
  41. FILE* fp = fopen(IOR_FILE, "r");
  42. if (!fp) {
  43. perror("file open");
  44. return 1;
  45. }
  46. // Counter_ptr と Counter_var の二つの型がある。前者はポインタ.
  47. // _narrow() は Counter_ptr または ReverseCounter_ptr を返す.
  48. csets[0].counter = Counter::_narrow(orb->string_to_object(read_line(fp)));
  49. csets[1].counter = ReverseCounter::_narrow(orb->string_to_object(read_line(fp)));
  50. fclose(fp);
  51. #endif // USE_NAME_SERVICE
  52. if (CORBA::is_nil(csets[0].counter) || CORBA::is_nil(csets[1].counter)) {
  53. printf("ior error.\n");
  54. return 1;
  55. }
  56. /*
  57. * The following code was added by Glade to create one of each component
  58. * (except popup menus), just so that you see something after building
  59. * the project. Delete any components that you don't want shown initially.
  60. */
  61. builder = gtk_builder_new();
  62. GError* error = nullptr;
  63. gtk_builder_add_from_file(builder, "gtk3-window.ui", &error);
  64. g_assert_no_error(error);
  65. // リンク時に -rdynamic オプションが必要.
  66. gtk_builder_connect_signals(builder, nullptr);
  67. window1 = GTK_WINDOW( gtk_builder_get_object(builder, "window1") );
  68. gtk_widget_show_all( GTK_WIDGET(window1) );
  69. gtk_main ();
  70. // ポインタは解放する.
  71. Counter_Helper::release(csets[0].counter);
  72. Counter_Helper::release(csets[1].counter);
  73. return 0;
  74. }

実行結果

昔の

Linux版はgtk+(GUI)クライアントも実装してみた。

 C++の場合は多重継承を使って実装する。plain Cだと(C++ならコンパイラが自動でやってくれる)vtableを手で書く。Javaは多重継承が使えないので,TIE(委譲)モデルで実装する。

 IORを文字列にして渡すというヘボい方法なら,JDK 1.2をサーバーにしたとき以外はどの組み合わせでも問題なく接続できる。

\クライアント
サーバー\
LinuxWindows
omniORB mico ORBacus ORBit omniORB JDK 1.2
omniORB
on Linux
(未)
mico
on Linux (注1)
ORBacus
on Linux
(未)
ORBit
on Linux
(未) (未)
omniORB
on Windows
JDK 1.2
on Windows
× (注2)× (注3)× (注3)× (注3)
注1: micoをサーバーにするとき,BOAではなくPOAを使う必要あり。BOAではクライアント側がBAD_OPERATION例外を発生する
注2: CORBA/COMM_FAILURE例外
注3: ハングアップ?

 ネームサービスを介してオブジェクトをやり取りするのは,ORBの組み合わせによって上手くいったりいかなかったりして,2000年1月現在では難しい。

2000.11.19
ORBit, mico, omniORB, JavaによるCounter/ReverseCounterリモートオブジェクトの相互接続のテスト
2000.01.03
gtk+クライアント
1999.05.16
ORBit
1999.04.23
mico

ソースコードをまとめたもの: