IDL - Java間のマッピング: 第1回

コンポーネント・インターフェース定義のJava要素への変換について

この記事は、IDL - Java間マッピングを扱う連載の初回です。今月の記事では、基本データ型、構造、およびデータ転送について見ていきます。来月の記事では、より複雑な型について取り上げます。言語マッピングは決して些細なことではありません。CORBA仕様はかなりの部分を割いて、さまざまな言語マッピングについて述べています。

Dave Bartlett (dbartlett@pobox.com), Consultant, author, and lecturer, 自由职业者

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



2000年 10月 01日

CORBA仕様はインターフェース定義言語 (Interface Definition Language、IDL) について詳しく述べた後、IDLとさまざまなプログラム言語 (C、C++、ADA、COBOL、Lisp、Smalltalk、Javaなど) との間のマッピングについて詳述しています。IDLは、インターフェースやすべてのオペレーション・パラメーターを詳しく記述する点で非常に優れています。IDLインターフェースの提供する情報を使えば、インターフェースのオペレーションを利用するクライアントと、そのインターフェースを実装するサーバーを開発することができます。ただしクライアントやサーバーはIDLで書かれるわけではありません。IDLはあくまで記述言語であり、クライアントとサーバーは、完全な機能を備えたプログラム言語を使って書かれます。IDL言語マッピングは、定義されるインターフェースについての、整合的かつ移植性に優れたフレームワークを実現します。こうしてマップされるインターフェースは、サーバー側に実装したり、クライアント側から他のメソッドと同じようにして呼び出すことができます。それぞれのプログラム言語ごとに、定義済みのIDL概念を簡単に変換できるマッピングが必要です。IDL概念をクライアントの言語構造体にマッピングする方法は、そのプログラム言語でどんな構造体や機能を使用できるかによって異なります。たとえばIDL例外は、例外という概念のない言語の構造体にマップされることもあれば、例外の概念を持つ言語の例外にマップされることもあります。ですから、各言語の特性や効率に適応させる必要があるのです。

IDLとプログラム言語間のマッピングに必要な要件

マッピングの構造は、すべての言語でほぼ同じです。マッピングにおいては、以下のものをその言語でどのように表現するかを定義しなければなりません。

  • すべてのIDL基本データ型
  • すべてのIDL構造化データ型
  • IDLで定義されている定数
  • IDLで定義されているオブジェクトへの参照
  • オペレーションの呼び出し (パラメーター送信および結果の受信を含む)
  • 例外 (オペレーションで例外が発生した場合にどうなるか、例外パラメーターをどのように利用するか、など)
  • 属性の利用
  • ORBで定義されたオペレーションのシグニチャー (動的呼び出しインターフェース、オブジェクト・アダプターなど)

完全な言語マッピングが可能であれば、プログラマーは、特定のプログラム言語に都合の良い方法でORBのすべての機能を利用できます。ソースの移植性を維持するために、ORBのすべての実装は、特定の言語に関して同じマッピングをサポートしなければなりません。

MotherIDL

この記事全体を通して、MotherIDL (motheridl.idl) というファイルを使用します。このIDLファイルにはすべての機能が揃っていますが、マッピングの各部分を調べて解説するために作成されました。読者がこのファイルの概要をご覧になれば、後で説明する際によく理解していただけるかもしれません。

マッピングについて調べるためには、IDL - Javaコンパイラーが必要です。すべてのCORBA ORBには、IDLからいずれかの言語へのコンパイラーが付属しています。ほとんどの場合はC++ またはJavaですが、他のプログラム言語もあります。Python用およびDelphi用に新しく追加されました。この記事では、Object Oriented Concepts, Inc. のOrbacus ORBに付属しているJIDL (参考文献を参照) を使用しますが、実際にはどんなIDL - Javaコンパイラーでも使用できます。ただし、CORBA 2.3準拠であることを確認してください。これより前のバージョンで動作するコンパイラーの場合、結果がかなり違ってくる可能性があります。

まず最初に行う作業は、motheridl.idl をIDL - Javaコンパイラーにかけることです。

jidl --output-dir . . \. . \. . MotherIDL.idl

たったこれだけで、元のIDLファイルを反映するたくさんのJavaファイルが生成され、一般的なCORBA設計がサポートされるようになります。


IDL-to-Javaライブラリー構造

まず最初に、IDLのmodule キーワードからJavaのpackage キーワードへのマッピングについて調べましょう。これは簡単かつ完全なマッピングです。IDLファイルの中にもしmodule キーワードがあれば、IDL - Javaコンパイラーはディレクトリーを作成し、package キーワードを使用するJavaクラスのライブラリー構造を作成します。(Javaプログラムでpackage キーワードがどのように使われるかを完全に理解していない読者は、今ここで復習することをお勧めします。) このマッピングは、IDLによって形作られる構造のかなりの部分を占め、IDL - Java間マッピングに非常に大きな影響を与えます。

motheridl.idl を例として使用する場合、このIDLファイルの中には次のような構造が見られます。

module corbasem {
  module gen {
    module motheridl {
      module holderexample {
      ...
      };
      module conflicts {
      ...
      };
      module basictypes {
      ...
      };
      module constructedtypes {
      ...
      };
      module holderexample2 {
      ...
      };
      module MI {
      ...
      };
      module MI2 {
      ...
      };
    };    };
};

これが、以下のようなディレクトリー構造に変換されます。

E:\_work\TICORBA\Projects\corbasem\gen\motheridl>dir /AD /S
 Volume in drive E has no label.
 Volume Serial Number is B415-7161
Directory of E:\_work\TICORBA\Projects\corbasem\
07/15/2000  01:41p      <DIR>          .
07/15/2000  01:41p      <DIR>          ..
               0 File(s)              0 bytes
Directory of E:\_work\TICORBA\Projects\corbasem\gen
07/15/2000  01:41p      <DIR>          .
07/15/2000  01:41p      <DIR>          ..
               0 File(s)              0 bytes
Directory of E:\_work\TICORBA\Projects\corbasem\gen\motheridl
07/15/2000  01:41p      <DIR>          .
07/15/2000  01:41p      <DIR>          ..
07/15/2000  01:41p      <DIR>          basictypes
07/15/2000  01:41p      <DIR>          conflicts
07/15/2000  01:41p      <DIR>          constructedtypes
07/15/2000  01:41p      <DIR>          holderexample
07/15/2000  01:41p      <DIR>          holderexample2
07/15/2000  01:41p      <DIR>          MI
07/15/2000  01:41p      <DIR>          MI2
               0 File(s)              0 bytes

IDLとJavaのマッピングで考慮すべき主な点の1つは、整合性です。どのライブラリーについても言えることですが、ライブラリー内のクラスを変更するとき、既存のメソッドを削除しないことによって安定性を保つ必要があります。つまり、インターフェースには手を加えません。CORBAの場合はこれをさらに一歩進める必要があります。というのも、多数のORBベンダーがこのマッピングを利用してさまざまなライブラリーを作ることが予想されるからです。ORBの異なる実装の間で整合性、つまり移植性が必要とされます。仮に、IDL - Java間マッピングで、ORBベンダーA社がパッケージorg.VendA.Excep でUserExceptionを提供する一方、ベンダーB社はパッケージorg.VendB.UtilTypes でUserExceptionを提供するような状態では、クライアントやサーバーのコードは移植可能ではありません。クライアントまたはサーバーを別のORBに移行するためには、コードを更新して再コンパイルする必要があります。これではCORBAを選ぶ意味がありません。移植性は是非とも必要です。そういうわけで、OMGはライブラリー構造を指定しています。

Javaプログラム言語の場合、さまざまなJavaクラスを組み合わせてライブラリーの概念を作り上げ、誰がそのライブラリーにアクセスできるかを管理するのは、package キーワードです。Javaライブラリーには、IDLで記述したものをJavaプログラム言語で作り上げるために必要な材料がすべて揃っています。しかし、あるベンダーのライブラリーから別のベンダーのライブラリーへの移植性を実現するためには、ライブラリーの構造つまりパッケージ方法を何らかの形で指定して、それに固く従うようにしなければなりません。こうした方法によってのみ、クライアントはコードを信頼して、今のORBベンダーから次のベンダーにコードを書き直す必要がないことが保証されます。

IDL - Java間マッピングの移植性セクションが指定している一連のAPIは、ライブラリー構造と最小限の機能を提供し、これによって、移植可能なスタブやスケルトンをJava ORBで使用することができます。Javaクラスの場合、特定のORBベンダーとは無関係のソースからダウンロードされることが多いため、Javaプログラム言語の相互運用性は、他の言語の場合よりも重要な要件となっています。こうした理由で、スタブやスケルトンが使用するインターフェースをぜひ定義する必要があります。もしこれらを定義しない場合、スタブ (またはスケルトン) を使用するためには、次の2つのシナリオのいずれかに従う必要があります。第1のシナリオは、ORBベンダーの提供する (または、使用中のORBと互換性のある) IDL - Javaコンパイラーまたは同等のツールによって、スタブ (またはスケルトン) を生成することです。こうすれば、生成されたスタブはORBベンダーのライブラリーの構造に適合します。第2のシナリオは、すべてのORBランタイムをスタブまたはスケルトンと一緒にダウンロードすることです。この2つのシナリオはどちらも好ましくありません。理想を言うと、IDLをクライアントに送って (またはクライアントにダウンロードさせて)、クライアントの選択するツールを使ってスタブを作成してもらい、クライアントの環境からこちらのサーバーに接続してもらいたいものです。

このように、Java言語へのマッピングは、標準的Javaパッケージ・セットorg.omg.* として実装される標準構造に大きく依存しています。IDL - Java間マッピングで重要な位置を占めるのが、PIDL、ネイティブ型、およびORB移植性インターフェースを含んでいるzipファイルです。このzipファイルは、パッケージの正確な内容を表す定型文のようなものです。ただし、この業界では何でもそうなのですが、IDL - Java間マッピングも今後のバージョンで一部変更される可能性があります。読者が現在のマッピングを理解しても、将来のバージョンとの間でバイナリー非互換性が発生するかもしれないことに注意しなければなりません。


基本データ型

基本型のマッピングは極めて単純です。 下記の表を見れば、いかにシンプルなマッピングであるかがおわかりいただけるでしょう。

IDL型Java型例外

boolean

Boolean

 

char

Char

CORBA::DATA_CONVERSION

wchar

Char

CORBA::DATA_CONVERSION

octet

Byte

 

string

java.lang.string

CORBA::MARSHAL, CORBA::DATA_CONVERSION

wstring

java.lang.string

CORBA::MARSHAL, CORBA::DATA_CONVERSION

short

Short

 

unsigned short

Short

large number mismatch・test

long

Int

 

unsigned long

Int

large number mismatch・test

long long

Long

 

unsigned long long

Long

large number mismatch・test

float

Float

 

double

Double

 

long double

** マップされない

現時点では、この型が基本型としてライブラリーに加えられるか、それとも追加 (たとえばjava.math.BigFloat) として加えられるかはっきりしていません

すべての基本型を練習用に使用するモジュールを、motheridl.idl の例で確認してください。

整数

IDLの符号付き整数は、何の問題もなくJava型にマッピングされます。IDLshort はJavashort に、IDLlong はJavaint に、またIDLlong long はJavalong にそれぞれマップされます。とても単純ですから、これで悩むことはまずないでしょう。

符号なしIDL型の場合には、問題があります。Javaプログラム言語は符号なし型をサポートせず、Javaのすべての整数型には符号が付いています。ほとんどの状況では、これは問題とはなりません。しかし最高位ビットも数値範囲に含まれるIDL符号なし整数型を変換する場合、ミスマッチ (不一致) が発生します。このミスマッチをチェックして訂正しない限り、Java側では、その整数型の範囲上限に近い値を表す代わりに、負の数値を表してしまいます。

たとえば、あるIDLインターフェースからunsigned short が戻されるとしましょう。この型の値の範囲は0から65535までです。これを受け取るJavaの側では、符号付きshort の値の範囲が -32768から32767までです。ここで、32767から65535までの間にくる値はすべて、Javashort の負の値にマップされることがおわかりいただけるでしょう。こうして発生する可能性のあるインピーダンス・ミスマッチを検査しなければなりません。同じことが、unsigned shortunsigned long、さらにunsigned long long についても該当します。

これにはどんな意味があるのでしょうか。まず、新しいIDLを書くときには、符号なし整数を使用しないことをお勧めします。こうすれば、後でいろいろと悩む必要はありません。次に、既存のIDLインターフェースを使用またはサポートするとき、符号なし整数が入って来るかどうかテストしなければなりません。そして、符号なし整数がJavaプログラムで負の数値として適切に処理されるようにするか、そうでなければ、より大きな値を扱うことのできる別の型の変数にコピーしなければなりません。

ブール値とオクテット

この2つのIDL型からJava型への変換は単純です。IDLのブール定数TRUE およびFALSE は、それぞれJavaブール・リテラルtrue およびfalse にマップされます。

IDLのoctet 型は8ビット値であり、Javaのbyte 型にマップされます。

文字型およびストリング型

文字のマッピングには少し問題があります。どの文字セットが使われるか、および、その文字セット全体を処理するのに必要なビット数はいくらか、という2つの問題です。さまざまな言語によって多種多様な文字が使われており、こうした言語ごとの違いは、国際的な標準化団体の努力でさまざまな文字セットにマップされるようになりました。文字セットでは、特定言語のそれぞれの文字または記号を1つの数値として表します。ある言語の文字記号の数がいくつあるかによって、その言語に必要なビット数が決まります。文字セットには、8ビット文字セットと16ビット文字セットがあります。

IDL文字は1つの文字セットの要素を表す8ビット値であるのに対して、Javaの文字はUnicode文字を表す16ビット符号なし値です。型セーフティーを実現するために、Java CORBAランタイムはIDLchars からマップされるすべてのJavachars の範囲が有効であることを想定します。この方向のマッピングの場合、8ビット値から16ビット値に変換するわけですから、問題はありません。Javaプログラムに渡されるどんな値も、十分なスペースがありますから、問題なく処理できます。しかしJavachar からIDLchar に変換するとき、そのJavachar が、文字セットの定義する範囲の外にはみ出してしまう可能性があります。その場合、CORBA::DATA_CONVERSION 例外がthrowされます。IDLwchar 型は単純に16ビット文字セットにマップされ、Javaではプリミティブ型char にマップされます。もし文字セットの定義した範囲の外にwchar が出てしまった場合には、CORBA::DATA_CONVERSION 例外がthrowされます。

IDLstringjava.lang.String にマップされます。IDLstring は、char が連続するシーケンスであることに注意してください。つまり、IDLstring はIDLchar およびIDLsequence の要件を満たさなければなりません。したがって、ストリング内の文字の範囲検査とストリング・シーケンスの境界検査が、マーシャル時に行われます。文字範囲の違反はCORBA::DATA_CONVERSION 例外を発生させ、境界違反はCORBA::BAD_PARAM 例外を発生させます。同じ考慮事項と違反が、IDLwstring にも当てはまります。

浮動小数点型

OMG IDLの浮動小数点型とJavaの浮動小数点型はどちらもIEEE 754-1985 Standard for Binary Floating Point Arithmetic に従うため、浮動小数点の変換に関しては何も問題がありません。ただし、現在のところ、Javaプログラム言語はIDLlong double 型をサポートしていません。今後、この型がプリミティブ型として追加されるか、それともjava.math.* (おそらくjava.math.BigFloat) の新しいパッケージとして追加されるか、また、いつ追加されるかなどの点は、現時点でははっきりわかりません。将来の改訂を待つことにしましょう。

マッピングの妥当性を検証する

次のようにして、ファイルmotheridl.idl を、Orbacus orbに付属のIDL - Javaコンパイラーにかけます。

jidl --output-dir . . \. . \. . MotherIDL.idl

こうして、Javaインターフェース定義がファイルUseAllTypesOperations.java の中に生成されます。

こうしてできたファイルを調べると、マッピングの妥当性を検証できます。すべてのJavaは期待どおりにマップされています。少しだけ気になる点があるとすれば、inout およびout パラメーター・プレースの型でしょう。これらはいずれもホルダーです。


ホルダー・クラス

どの言語の場合でもパラメーターの引き渡しはおもしろいテーマですが、言語から独立したOMG IDLのようなアーキテクチャーから言語マッピングを作り出すときには、とても魅惑的な問題が存在します。Javaプログラムは、何でも値で渡します。つまり、プリミティブをメソッドに渡すとき、そのプリミティブのローカル・コピーを作成します。メソッドのパラメーターがJavaオブジェクトの場合、オブジェクトそのものではなく、そのオブジェクトの参照を渡します。つまり、渡されるのは参照のコピーですが、その参照は値で渡されます。

CORBAはin パラメーターおよび戻り型をcall-by-value (値による呼び出し) として指定し、CORBAout はcall-by-result (結果による呼び出し) です。またinout パラメーター型はcall-by-valueでサーバーに渡され、call-by-resultで戻されます。out パラメーターおよびinout パラメー ターの引き渡しモードは、Javaのパラメーター引き渡しメカニズムに直接的にマップすることはできません。out およびinout のパラメーター引き渡しモードをサポートするには、追加のholder クラスが必要です。

IDL - Java間マッピングでは、すべてのIDL基本型用、および特定形式のユーザー定義型用のホルダー・クラスを定義しています。IDL - Javaコンパイラーはユーザー定義型用のホルダー・クラスをいくつか生成しますが、後でJavaプログラムの中でそれらを上記のパラメーター・モード用に利用することができます。クライアントは、(Javaプログラムで値によって渡される) それぞれのIDLout パラメーターまたはinout パラメーター用に、適切なホルダーJavaクラスのインスタンスを1つ作成して渡します。ホルダー・インスタンスの内容は呼び出しの際に変更され (ただしインスタンスそのものは不変)、クライアントは、呼び出しが戻した後に、変更された (であろう) 内容を使用します。

IntHolderクラスの例が、CORBA-Javaライブラリーの中に含まれています。ここでpublic Javaint の値、および_type() メソッドから戻される値 (long) に注目してください。これは、Javaint がIDLlong にマップされるためです。

このほか、org.omg.CORBA パッケージに含まれるすべてのIDL基本データ型用に、ホルダー・クラスを利用できることを忘れないでください。また、(typedefで定義されたものを除く) すべての名前付きユーザー定義IDL型用に、ホルダー・クラスが生成されることも忘れないでください。

ユーザー定義IDL型の場合、ホルダー・クラス名は、マップされた (Java ) 型名にHolder を付加したものになります。基本IDLデータ型の場合、ホルダー・クラス名は (先頭文字が大文字の) Java型名をもとにして、これにデータ型がマップされるとHolder が付け加えられます (たとえば、IntHolder)。

それぞれのホルダー・クラスには、インスタンスからのコンストラクターと、publicインスタンス・メンバー (value) が1つずつあります。後者は型付き値です。デフォルト・コンストラクターはvalueフィールドを、Java言語で定義されたデフォルトの型値に設定します。つまりブール値はfalse、数値とcharは0、ストリングはnull、そしてオブジェクト参照子はnull に設定します。移植可能なスタブとスケルトンをサポートするために、ホルダー・クラスはorg.omg.CORBA.portable.Streamable インターフェースも実装します。


結論

ここまでは、IDL - Java間マッピングを扱う連載の第1回に過ぎません。Javaプログラム言語を使ってCORBAベースのアプリケーションやコンポーネントを作成するには、IDL - Java間の言語マッピングを理解しなければならないことがおわかりいただけたでしょう。CORBA仕様には、さまざまな言語マッピングがあります。どの言語マッピングも、開発者の作業のいろいろな面にかかわってきます。あなたのコードの移植性は、あなた自身が作成するライブラリー構造と、OMGのライブラリー構造とに依存しています。こうしたマッピングは、インターフェース定義を言語固有の実装に変換するという目的にかなっています。そうした変換の美しさは、もっと実用的な実装のために犠牲にされても、いたしかたないでしょう。符号なし整数のケースで見たように、あなたのアプリケーションを期待どおりに動作させるためには、一定レベルの注意を払う必要があります。

言語マッピングとは、一種の翻訳です。1つの言語から、実用的で理解しやすい別の言語の実装に変換するわけです。ちょうど、人間の話す複数の言語の間で、一部の熟語や文の構造がうまく翻訳されないように、一部の構造体は簡単にはマップされません。それに対して解決策が必要なわけですが、そうした解決策はマッピングをより複雑にするものの、実装での使用を簡単にしてくれます。ホルダー・クラスに関して、これが当てはまります。ホルダー・クラスを理解するためには最初は努力が必要ですが、長い目で見れば、汎用的かつ整合性のある解決策となります。

来月の記事では、IDL - Java間マッピングについてもっと深く掘り下げましょう。ヘルパー・クラス、より複雑なマッピング (たとえばenumunion)、さらにユーザー定義型について見ていく予定です。

参考文献

コメント

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=240732
ArticleTitle=IDL - Java間のマッピング: 第1回
publish-date=10012000