前回の記事では、SimpleCalc という簡単なサンプルを作成しました。これは単純なサンプルで、2 つの IDL long を受け取って 1 つの long を戻す、ただ 1 つのadd() メソッドから成っています。ところで CORBA について教えたり学んだりするときに問題となるのが、クライアントとサーバーの分散を前提とする場合、いきなり最初から複雑になってしまうことです。すぐにネットワークを扱わなければなりません。そこで、今回はネットワークについて見ていきましょう。

Dave Bartlett (dbartlett@pobox.com)Consultant, author, and lecturer

Dave Bartlett 氏は米国ペンシルベニア州の Berwyn に在住しながらコンサルタント、著作家、講演者として活躍しています。「Hands-On CORBA with Java」という 5 日間のコースの主催者であり、このコースは公開セッションの形でも、組織内のセミナーの形でも開催されています。現在は、このコースの資料を「Thinking in CORBA with Java」という本にまとめる作業を行っています。Dave 氏はペンシルベニア州立大学で Engineering および Business の修士号を取得しました。ご質問、または特定のトピックに関する興味をお持ちの読者は、dbartlett@pobox.com で Dave 氏に連絡することができます。



2000年 8月 01日

ネットワーク

少し掘り下げるとわかりますが、非常に多くのことが行われているものの、その方法は単純化されています。前回の例の簡単な calculator サーバーはリモートに呼び出されるように設計され、クライアントとサーバーの環境の違いにいちいち対応する必要がないよう、CORBA が特別の役割を果たします。こうしたクライアントからサーバーへのリモート呼び出しは、1980 年代から使われているリモート・プロシージャー呼び出し (Remote Procedure Call、RPC) プロトコルに基づきます。RPC は、さまざまな通信モデルを使って何年もテストした結果として生まれました。つまり、実稼働環境でよく吟味された真のテクノロジーです。我々が使用している CORBA モデルはこのモデルを基盤としています。

図 1. ネットワーク
ネットワーク

Interoperable Object Reference (IOR)

では、この例のメソッド呼び出しを追ってみましょう。まずクライアントは、calculator の 1 つのインスタンスが必要なことを理解する必要があります。そのためにcalculatorHelper.java narrow() メソッドを使用します。ior は、ファイルcalcref.ior から取り出した Interoperable Object Reference (IOR) のストリング表記です。このファイルは、クライアントがサーバーを検出して接続する方法として、サーバーが作成したものです。orbstring_to_object() のメソッド呼び出しは、単にストリングior を受け取って、これをオブジェクト参照子に変換するだけです。クライアントSimpleCalcClient.java のコードの一部は次のようになります。

calculator calc = calculatorHelper.narrow(orb.string_to_object(ior));
System.out.println("Calling into the server");
System.out.println( calc.add(2,3) );

その IOR (Interoperable Object Reference) の中身はどうなっているでしょうか。IOR は、型、プロトコル・サポート、および利用できる ORB (Object Request Broker) サービスについての情報を提供するデータ構造です。IOR は ORB によって作成、使用、保守されます。多数の ORB ベンダーが、IOR の中を見られるユーティリティーを提供しています。OOC (Object Oriented Concepts, Inc.) の Orbacus (「参考文献」を参照) には IORDump.exe が含まれているほか、Visibroker を使用しているユーザーには PrintIOR.exe が提供されます。また、IOR をユーザーに代わって構文解析してくれる Web サイトもあります。筆者が利用しているのは Xerox Parc のサイトです (「参考文献」を参照)。筆者は Orbacus を使っているので、サンプル SimpleCalc で作成した IOR を解析するために IORDump を実行します。その結果の出力は次のとおりです。

C:\_work\corbasem\_sources\calcsimpl>iordump -f calcref.ior
IOR #1:
byteorder: big endian
type_id: IDL:corbasem/gen/calcsimpl/calculator:1.0
IIOP profile #1:
iiop_version: 1.2
host: 192.168.0.10
port: 4545
object_key: (36)
171 172 171  49  57 54   49  48 "½¼½19610"
 48  53  56  49  54  0   95  82 "05816._R"
111 111 116  80  79  65   0   0 "ootPOA.."
202 254 186 190  57  71 200 248 ".|..9G.."
  0   0   0   0                 "...."
Native char codeset:
 "ISO 8859-1:1987; Latin Alphabet No. 1"
Char conversion codesets:
 "X/Open UTF-8; UCS Transformation Format 8 (UTF-8)"
 "ISO 646:1991 IRV (International Reference Version)"
Native wchar codeset:
 "ISO/IEC 10646-1:1993; UTF-16, UCS Transformation Format 16-bit form"
Wchar conversion codesets:
 "ISO/IEC 10646-1:1993; UCS-2, Level 1"
 "ISO 8859-1:1987; Latin Alphabet No. 1"
 "X/Open UTF-8; UCS Transformation Format 8 (UTF-8)"
 "ISO 646:1991 IRV (International Reference Version)"

この IOR の中には、type_id、IIOP のバージョン、ホストのアドレスとポート番号、およびオブジェクト・キーが組み込まれています。type_id ストリングは、リポジトリー ID 形式とも呼ばれるインターフェース・タイプです。本質的に、リポジトリー ID はインターフェースの固有識別子です。この ID は (COM プログラマーになじみのある) DCE UUID 形式、または特定のローカル形式にすることができます。IIOP バージョンによって、IOR の読み手 (通常は ORB) は IOR の形式を正確に把握することができます。OMG は常に仕様を改善しており、IIOP の各バージョンごとに、以前のバージョンとは違った形でバイト ;-) を解釈する必要があるかもしれません。ホストのアドレスとポート番号は、希望のオブジェクトとの通信を行う ORB を指定します。オブジェクト・キーおよび残りの部分のほとんどは、サービス固有情報として OMG 規格によって作成されたものです。このサービス固有データは、ORB がサーバーをサポートするのを支援します。プロプラエタリー IOR のこうした構成要素は、たとえば ORB のタイプとバージョンをコードに含んだり、OMG セキュリティー・サービスのインプリメンテーションを支援したりします。上記の情報のほとんどの部分は、クライアントとサーバーが互いに理解できるようにするための文字コード・セット変換を指定しています。

Xerox Parc IOR パーサーを使ってこの IOR を実行すると、次のような結果が出力されます。

IIOP_ParseCDR:  byte order BigEndian, 
                repository id , 
                1 profile
_IIOP_ParseCDR:  profile 1 is 124 bytes, 
                 tag 0 (INTERNET), 
                 BigEndian byte order
(iiop.c:parse_IIOP_Profile):  bo=BigEndian, 
                              version=1.2, 
                              hostname=192.168.165.142,
                              port=4545, 
   object_key=<...1961005816._RootPOA......9G......>
(iiop.c:parse_IIOP_Profile): encoded object key is
<<«%AC«19610058
    16%00_RootPOA
    %00%00Êþº¾9GÊ
    %F8%00%00%00%00>
(iiop.c:parse_IIOP_Profile): non-native cinfo is object key is 
   <#AB#AC#AB196100
   5816#00_RootPOA
   #00#00#CA#FE#BA
   #BE9G#C8#F8#00#
   00#00#00>;
  no trustworthy most-specific-type info; 
  unrecognized ORB;
  reachable with IIOP 1.2 at host "192.168.165.142", port 4545

IOR の中で最も重要なのは、クライアントがサーバーに接続できるようにする部分です。この部分は、Xerox Parc IOR リーダー (「参考文献」を参照) からの出力として見ることができます。ただし、残りの情報のほとんどは Orbacus のプロプラエタリーであり、他の IOR リーダーでは解読できません。このプロプラエタリー部分は IOR に添付された一連のデータとして表示され、その IOR を作成した ORB のみがこのデータを理解できます。


スタブ

これで IOR の中身がわかりました。IOR の目的は、クライアントがサーバーに接続してメソッド呼び出しを実行できるようにすることです。クライアントは、クライアントから呼び出すことのできる Add メソッドを持った現実のオブジェクトに IOR を変換する必要があります。この変換には、IDL コンパイラーが生成する Java ファイルのうち 2 つを使用します。クライアントはまず calculatorHelper オブジェクトを使用して、この IOR を_calculatorStub プロキシー・オブジェクトに限定化 (narrow) します。

Orbacus に付属の jidl コンパイラーによって生成されるnarrow() メソッドは次のとおりです。

public static calculator narrow(org.omg.CORBA.Object _ob_v) {
  if(_ob_v != null) {
      try {
          return (calculator)_ob_v;
      } catch(ClassCastException ex) {
      }
      if(_ob_v._is_a(id())) {
         org.omg.CORBA.portable.ObjectImpl _ob_impl;
         _calculatorStub _ob_stub = new _calculatorStub();
         _ob_impl = (org.omg.CORBA.portable.ObjectImpl)_ob_v;
         _ob_stub._set_delegate(_ob_impl._get_delegate());
           return _ob_stub;
       }
       throw new org.omg.CORBA.BAD_PARAM();
   }
   return null;
}

上からわかるように、最も重要な作業は、新しい_calculatorStub オブジェクトを作成する部分です。_calculatorStub は、サーバーに常駐する現実の calculator オブジェクトに代わるプロキシー・オブジェクトとしての役割を果たします。プロキシー・パターンになじみのない読者は、"4 人組" が著した「設計パターン (Design Patterns)」の本を読まれることをぜひお勧めします (「参考文献」を参照)。プロキシー・パターンを一言で言うと、最終的に呼び出しを処理したりサービスを実行したりする他の現実のオブジェクトを代表する (つまり代理的な役割を果たす) オブジェクトを作成することです。プロキシー・パターンは重要なパターンであり、しばしば利用されています。分散設計のすべての面でプロキシー・パターンが使われます。読者もまた、何らかの形でこれを利用したことがあるのではないでしょうか。その設計をプロキシー・パターンとは呼ばなかったかもしれませんが。

_calculatorStub が作成されると、クライアントの calculator インターフェースを表すようになります。加算 (add) メソッドは、IOR で定義されたアドレスのサイバースペースで実行されるサーバーの中にインプリメントされています。この時点では、呼び出されるのはadd() メソッドです。ここで 2 つの点に注意してください。まず、add メソッドを呼び出すのですから、何らかの形でこれが_calculatorStub に含まれなければなりません。次に、他の同期的メソッド呼び出しの場合と同様、呼び出しが戻されるまではクライアントがブロックされます。これは要求 / 応答プロトコルであり、単一プロセスのアプリケーションとよく似ています。クライアントをプログラムしたうえで要求 / 応答プロトコルを使ってそれを実行するという方法は、ライブラリーと API 呼び出しを使って作成される通常のプログラミング開発環境と同じくらい汎用的かつ自然な感じがするでしょう。一方、非同期の呼び出しを使用したいケースもあると思われますが、それも可能です。この種の呼び出しを実行する機能は確かに利用できますが、今後の記事でそれについて扱います。


マーシャル: GIOP と CDR

アーキテクチャーのここまでの時点で、サービスがすぐそこで利用できるとクライアントに思い込ませることに成功しました。しかしサービスはまだ利用できません。そこで次のステップでは、データとメソッド呼び出しを、ネットワークで送って受信側で実行できる形にしなければなりません。これは重要な操作です。こうしたモデルのために、何年も努力が払われてきました。読者は OSI モデルを何度も目にしたことがあるでしょう。図 2 には、OMG のモデルの隣に対応する OSI モデルが示されています。

図 2. OSI および GIOP プロトコル・スタックの構造
OSI および GIOP プロトコル・スタックの構造

クライアントがインターフェース操作を呼び出すとき、操作データ (入力 (in) パラメーターや入出力 (inout) パラメーター) をサーバーに送る必要があります。ここで難しいのが、サーバーがデータの解釈や位置合わせを間違えずに操作データを取り出すことができるように、共通の形式でデータを表すにはどうすればよいかという点です。サーバーのプラットフォームはさまざまに異なる可能性があるため、クライアントとサーバーのアーキテクチャー上の違いを考慮に入れる必要があります。CORBA ではこれに対処するために、データを変換つまりマーシャルして共通形式にする方法を厳密に定義しています。データはあとで接続の反対側で再編成つまりアンマーシャルされます。これらの操作を行うとき、データは最も基本的な構造、つまりバイト・ストリーム (オクテット・ストリームともいう) で表されます。

CORBA 仕様はオクテット・ストリームのことを「抽象的概念であり、何らかの IPC メカニズムまたはネットワーク・トランスポートを介して他のプロセスやマシンに送られるメモリー・バッファーに対応する場合が多い」と定義しています。IDL オクテットは、Java バイトに厳密に対応しています。このどちらも、クライアントやサーバーによってマーシャルされない 8 ビット値です。これらのパラメーターを一連のオクテットにストリーム化する最大の目的は、情報交換のための基本的な構造を提供することです。

ここで、生成された_calculatorStub コードを調べてみましょう。これは筆者自身が作成したものではないことに注意してください。これを生成したのは Orbacus に付属の IDL から Java へのコンパイラー jidl です。

//
// IDL:corbasem/gen/calcsimpl/calculator/add:1.0
//
public int add(int _ob_a0, int _ob_a1) {
  System.out.println("Inside _calculatorStub.add()");
  while(true) {
     if(!this._is_local()) {
      org.omg.CORBA.portable.OutputStream out = null;
      org.omg.CORBA.portable.InputStream in = null;
      try {
          out = _request("add", true);
          out.write_long(_ob_a0);
          out.write_long(_ob_a1);
          in = _invoke(out);
          int _ob_r = in.read_long();
          return _ob_r;
      } catch(org.omg.CORBA.portable.RemarshalException _ob_ex) {
          continue;
      } catch(org.omg.CORBA.portable.ApplicationException _ob_aex) {
          final String _ob_id = _ob_aex.getId();
          in = _ob_aex.getInputStream();
          throw new org.omg.CORBA.UNKNOWN("Unexpected User Exception: " + _ob_id);
      } finally {
          _releaseReply(in);
     }
  } else {
      org.omg.CORBA.portable.ServantObject _ob_so = _servant_preinvoke
("add", _ob_opsClass);
      if(_ob_so == null)
          continue;
        calculatorOperations _ob_self = (calculatorOperations)_ob_so.servant;
       try {
           return _ob_self.add(_ob_a0, _ob_a1);
       } finally {
         _servant_postinvoke(_ob_so);
      }
    }
  }
}

上記のうち_request() 呼び出しとwrite_long() 呼び出し、および_invoke() に続くread_long() に注目してください。_request() 呼び出しは、呼び出されるメソッド、および応答が戻されるかどうかを示すブール値を入力として受け取ります。そして CORBA 仕様で指定されたorg.omg.CORBA.portable.OutputStream オブジェクトが戻されます。これは可搬性のために必要な操作です。Java クラスの場合、ダウンロードされて、実行場所の共通ライブラリーに依存することが多いためです。これは IO の場合と同様、ORB にも当てはまります。そこで CORBA 仕様では、他の言語の可搬性クラスよりも Java 言語の可搬性クラスをより広範囲に定義しています。

General Inter-ORB Protocol (GIOP)

General Inter-ORB Protocol (GIOP) の目的は、種々雑多なコンピューターとそのアーキテクチャーからなる混沌とした世界でメッセージを交換するための、構造および形式を定義することです。GIOP の構造と形式を TCP/IP の上で適用すると、IIOP になります。GIOP のバージョンには 1.0 と 1.1 があります。つまりメッセージがどちらの GIOP バージョンに従うかによって、メッセージ形式が異なる可能性があります。

ここで、要求がどのように処理されて正しい形式の CORBA 要求になるかを理解するために、GIOP について調べてみましょう。ここでは要求を詳しく調べていますが、応答は要求のミラー・イメージに過ぎないので、要求が処理されるしくみがわかれば、応答についても理解できるでしょう。

GIOP 要求メッセージは、GIOP メッセージ・ヘッダー、GIOP Request ヘッダー、Request 本体の 3 つの部分に分かれます。GIOP メッセージ・ヘッダーは、それが GIOP メッセージであることを示します。そこには GIOP のバージョン、メッセージ・タイプ、メッセージ・サイズ、さらに 1.0、1.1、1.2 のいずれを使用するかに応じて、バイト順 (GIOP 1.0) またはバイト順といくつかの予約済みビット・フラグからなるビット・フラグ・フィールドが含まれます。GIOP 1.1 はメッセージの断片化を追加サポートし、GIOP 1.2 は双方向通信を追加サポートします。新しいバージョンはすべて、それ以前のバージョンと下位互換性があります。

Common Data Representation (CDR)

Common Data Representation (CDR) は、CORBA 呼び出しで使われるデータ型の形式マッピングです。クライアントが要求を出したとき、その要求がどこに行くか、どのサーバーがそれに応答するかをクライアントは知りません。インターフェースを実装している 1 つの適切なサーバーがこの要求に応答できるようにするために、仕様として CORBA が設計され、さらにメッセージ構造および伝送を定義する仕様の 1 セクションとして GIOP が設計されました。仕様は操作の中でデータをどのようにパッケージすべきかを定義することによって、データ変換上の不明瞭さを避け、どんなサーバーでもパラメーターを取り出してリモート・オペレーションを起動できるようにしなければなりません。この変換上の問題に対処する従来からの方法に、ポインターがあります。クライアントからのポインターは、別のマシンの別のプロセスで実行されるサーバーにとってどういう意味があるのでしょうか。何の意味もありません。あるいは、異なるアドレッシング方式 (ビッグ・エンディアンとリトル・エンディアン) を持つさまざまなマシンの間でやり取りされる引数についてはどうでしょうか。これらのデータ型は、サーバーが理解して処理できるようなストリームに変換されなければなりません。確かに CORBA 仕様は、Common Data Representation の分野をきわめて詳細に扱っています。今回の記事ではそこまで詳しく見る必要はありませんが、もっと詳しく調べたい読者は、この仕様または Ruh、Herron、Klinker 共著による「IIOP 詳説 (IIOP Complete)」(「参考文献」を参照) をご覧ください。

すべてのデータがパッケージされたら、IOR からの情報を使って接続を確立します。IOR の構造からわかるように、通常は、トランスポート機構として TCP を使用することになります。しかし他のトランスポートも利用できます (詳しくは CORBA 仕様をご覧ください)。ORB デーモンが IOR の指定するオブジェクト実装を検出して、クライアントとサーバーの接続を確立します。接続が確立されると、GIOP は、クライアントが要求に使う一連のメッセージ、およびサーバーが応答に使う一連のメッセージを定義します。クライアントは、Request、LocateRequest、CancelRequest、Fragment、および MessageError タイプのメッセージを送ります。サーバーの方は Reply、LocateReply、CloseConnection、Fragment、または MessageError タイプのメッセージを送信できます。

GIOP メッセージを分割すると、たとえば次のようになります。

0x47 0x49 0x4f 0x50 -> GIOP, the key
0x01 0x00           -> GIOP_version
0x00                -> Byte order (big endian)
0x00                -> Message type (Request message)
0x00 0x00 0x00 0x2c -> Message size (44)
0x00 0x00 0x00 0x00 -> Service context
0x00 0x00 0x00 0x01 -> Request ID
0x01                -> Response expected
0x00 0x00 0x00 0x24 -> Object key length in octets (36)
0xab 0xac 0xab 0x31 0x39 0x36 0x31 0x30
0x30 0x35 0x38 0x31 0x36 0x00 0x5f 0x52
0x6f 0x6f 0x74 0x50 0x4f 0x41 0x00 0x00
0xca 0xfe 0xba 0xbe 0x39 0x47 0xc8 0xf8
0x00 0x00 0x00 0x00 -> Object key defined by vendor
0x00 0x00 0x00 0x04 -> Operation name length (4 octets long)
0x61 0x64 0x64 0x00 -> Value of operation name ("add")
0x20                -> Padding bytes to align next value

これでしくみを理解していただけたことでしょう。このメッセージ・ストリームは高度に構造化されています。実装がどこでどのように実行されるかにかかわらず、サーバー側で実装に変換できるようなメッセージをクライアントが作成するためには、こうした構造化が不可欠です。サーバーの方でも、戻り値やクライアントへの応答に使うパラメーターに対して同じ処理を施す必要があります。このメッセージ形式は、OMG の目的である可搬性とインターオペラビリティーを達成するうえできわめて重要なリンクの役割を果たします。こうした可搬性によって、連載の最初の記事で述べた「自由」を実現することができるのです。ハードウェアやデータベース、それにプログラム言語について気にする必要はありません。自分に関係のある情報だけに気を配ればよいのです。


IIOP

まだ終わりではありません。CORBA メソッド呼び出しの要所は GIOP です。GIOP は IPX や TCP/IP のような特定のネットワーク・プロトコルに基づくわけではありません。インターオペラビリティーを実現するために、OMG は すべてのベンダーがサポートする特定のトランスポートの上に GIOP を定義する必要があります。たとえ詳細かつコンパクトなメッセージ仕様が存在しても、各ベンダーが別々のトランスポート機構を使ってそれを実装していると、インターオペラビリティーは実現しません。したがって、OMG は最も広く利用されている通信トランスポート・プラットフォームである TCP/IP の上で GIOP を標準化しました。こうして GIOP に TCP/IP を加えると IIOP になります。実にシンプルです。

公表されたオブジェクトのサービスを利用するクライアントは、IOR の中の値を使ってオブジェクトに接続しようとします。これで議論が一巡して再び IOR に戻ってきました。IOR は IIOP にとって重要です。オブジェクトのメソッドを呼び出すすべてのクライアントは、IOR で詳しく指定されたホストとポート・アドレスに向けて Request メッセージを送信します。ホスト・マシン上では、サーバー・プロセスがポートで要求を受信待機し、要求が受信されると、それらのメッセージをオブジェクトに送ります。つまり、サーバーは常にアクティブに要求を受信待機している必要があります。

物事にはプラス面とマイナス面があり、すべてのものには欠点があります。インターオペラビリティーと IIOP もまた例外ではありません。OMG は IIOP を生み出して普及させました。ORB ベンダーがほとんど自らこうしたことに取り組んだ時代、サーバー側の可搬性が存在しなかった時代に比べると、確かにこれは進歩です。しかし、サーバーが場所 (ロケーション) に依存しないようにしたい場合、どうすればよいでしょうか。1 つのサーバーから別のサーバーへオブジェクトを移動してオブジェクト間のロード・バランスを取りたいとき、ホストやポートの値が IOR に埋め込まれている点が思わぬ問題となります。幸いなことに、この問題はすでに解決済みです。ただし、各ベンダーごとに解決方法が異なります。


結論

ロード・バランスについては今後に取り上げる予定です。何年か前、たとえば評価期間中に CORBA を試してみた読者は、今あらためて CORBA を調べて、心地よい驚きを感じたのではないでしょうか。この仕様はずいぶん進歩して、1 つの ORB 用に作成したサーバー・コードを、別の ORB を実行する別のサーバーに移植することができるようになりました。このソリューションは単純明快であり、何年も普及してきた従来からあるプロトコルの基礎の上に築かれています。クライアントとサーバーの間の標準的な交換構文は、OMG が詳しく定めた特定の要件に基づいています。ネットワーク・アドレシング・プロトコル (IIOP) をメッセージング・プロトコル (GIOP) から分離することによって、OMG の仕様の効果が増幅されています。同時に、これによって、CORBA は確実に起こるであろう情報産業界の変化にもついて行くことができます。何より便利なのは、ここまで説明してきた CORBA オブジェクト呼び出しを完成させるために、筆者は何のコードも書く必要がなかったことです。ネットワークの設計とマーシャルに関する詳細は、ORB の中に、および IDL を使ったインターフェース標準化機能の中に含まれているのです。来月の記事では、IDL について取り上げましょう。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services
ArticleID=245535
ArticleTitle=IOR、GIOP、IIOP の中を探る
publish-date=08012000