ネットワークで接続された計算機群の上にオブジェクトを分散配置し, それらのオブジェクトの位置にかかわらずネットワークを介してアクセス(メソッド起動)することを可能にする幾つかの分散オブジェクト技術を示す。Javaそのものでは他の計算機上に存在するオブジェクトに直接アクセスすることはできないから, アクセスのための機構, 言語表現の拡張などが必要となる。
Javaでは,リモートメソッド起動の機能が, 四つのパッケージ(java.rmi,java.rmi.dgc,java.rmi.registory,java.rmi.server), 及びそれに合わせて拡張された言語処理系によって提供されている。以降では, これをJava-RMIと呼ぶ。オブジェクトをネットワークで転送するために必要となるシリアライザも, クラスとして提供される。
HORBでは, Java言語をいっさい変更せず, ユーザが書いたJava言語のプログラムがほとんどそのまま分散して動作する。シリアライザは, 独自のものを用いている。
OZは, クラスの共有・配送というOZ/OZ+/OZ++システムで開発された機能をJavaで実装している。そのため, 言語は, OZ++言語をJava風にしたものであるが, Java言語とは異なる。リモートメソッド起動の機構は, 独自のものであるが, シリアライザはJavaのものを使っている。
1種類のオブジェトモデルを用いれば, 確かにモデルとしては純粋である。しかし現実のソフトウェアにおいては, 例えばサーバの記述には同期・相互排除の記述が必要であり, レガシーシステムを組み込む場合, 大規模なシステム記述などには, 別の方策が必要になる。
Java及びそれをそのまま引き継いだHORBは, 単一モデルということができる。基本データ型はオブジェクトでないので, 厳密には違う。Java及びHORBにおいては, 分散オブジェクト指向環境を実現するためには, リモートメソッド起動用に拡張されたライブラリ又は言語処理系を利用しなければならず, 前述のサーバ記述の特殊性を考えると, 言語に直接組み込まれているOZと比較して, コンパイル及びプログラム記述の手間が煩雑となる。
OZでは, 分散環境においてサービスを提供しワールドワイドなアクセスが行われるサーバオブジェクトと, そのサービスの実行に利用するオブジェクトとを, それぞれ異なる種類のオブジェクトとする2種類のオブジェクトモデルを採用している。言語上も, それぞれのオブジェクトのテンプレート記述を区別する。サーバオブジェクトはセルと呼び, そのテンプレート記述は,cellという言語要素によって行う。それ以外のオブジェクトのテンプレート記述は, classによって行う。cellは,classと同様に,フィールド又はメソッドを定義でき,継承によって拡張できる。セルは永続性の単位であり, 一つのセルを永続化することによって, そのセルに属するすべてのオブジェクトを同時に永続化する。セルのライフサイクルは, システムにおいて定義され, それを実現するフックメソッドを定義したcellを暗黙に継承する。
分散したオブジェクトにアクセスするためには, それぞれのオブジェクトに名前をつけなくてはならない。その名前に基づいて, ネットワークを介したリモートメソッド起動の機構が働く。
Java-RMIでは, 計算機毎にその計算機上にある分散オブジェクトを管理するregistoryというオブジェクトを設ける。registoryは, その計算機上のオブジェクトとその計算機上で一意な名前との対応関係を保持する。分散オブジェクトは, 起動時にその名前及び場所をregistoryに登録する。分散オブジェクトにアクセスする際には, registoryにそのオブジェクトの所在を問い合わせる。分散オブジェクトの名前は, registoryのサービスを行う通信ポートのアドレス(IPアドレス及びポート番号)に, そのregistoryが管理する名前を加えたものになる。分散オブジェクトは, URL形式で, rmi://IP_address:port_number/name_on_the_machine と表わされる。
HORBでは, 分散オブジェクトは, HORBサーバと呼ばれるランタイムの管理のもとに作られ, 名前の管理もされる。HORBサーバ毎に, その上にある分散オブジェクトに名前付けがされる。名前は, 生成時に与えられるか, 自動的に無機質な名前が与えられる。分散オブジェクトにアクセスする際には, HORBサーバにそのオブジェクトの所在を問い合わせる。分散オブジェクトの名前は, HORBサーバのサービスを行う通信ポートのアドレス(IPアドレス及びポート番号)に, そのHORBサーバが管理する名前を加えたものになる。分散オブジェクトは, URL形式で horb://IP_address:port_number/name_on_the_horb_server と表わされる。
OZでは, 分散オブジェクトは, 各ユーザによってディレクトリ管理される。分散オブジェクトは, エグゼキュータと呼ぶランタイム上で動作するが, ネットワーク上でファイルが共有される範囲内であれば, どのエグゼキュータでどの計算機で動作しているかにかかわらず, 名前は同じである。分散オブジェクトは, 生成時に名前が与えられる。同一ユーザの範囲(OZホームと呼ぶ。)であれば, アドレス解決はファイルを用いて行える。異なるユーザ間では, アドレス解決を行うオブジェクトresolverが, アドレス解決を行う。ユーザは, その属する組織名(DNS名)とその組織内のユーザ名との組で識別され, 分散オブジェクトは, ユーザ名にそのユーザの与えた名前を加えたものになる。分散オブジェクトは, DNS_name:user_name:object_nameの形式の識別子(GOLと呼ぶ。)をもつ。オブジェクト名にユーザ名を含むことによって, ユーザの識別を容易にしている。
リモートアクセスでは, 遠隔のオブジェクトに直接アクセスすることはできないので, 次のことを何らかの形で行わなければならない。
こうした引数及び値のやりとりを行うために, 呼出し側及び呼び出される側に, メソッド起動の代行を行う機能をもつオブジェクトを用意する。呼出し側の代行オブジェクト(メソッド起動の代わりに引数などの送信を行い, 値を受信して, あたかもそれがメソッド起動の値であるかのように返す。)をプロクシ(proxy), 呼び出される側の代行オブジェクト(引数などを受信したら, 代理でメソッド起動を行い, 値を送信する。)をスケルトン(skeleton)とOMG CORBAでは呼び, Java-RMI及びHORBではその用語を用いている。
Java-RMIでは, skeleton及びproxyのクラスが, サーバのクラスのコンパイル時に生成され, ランタイムがproxy及びskeletonのオブジェクトを生成して, リモートメソッド起動を行う。proxy-skeleton間には仮想的にコネクションが張られるが, マルチプレクスしている。
HORBでは, proxy及びskeletonのクラスがサーバのクラスのコンパイル時に生成される。例えば, Server.javaというサーバクラスのJavaソースファイルをhorbcでコンパイルすると, Server.class, Server_Proxy.class, Server_Skeleton.classが生成される。リモートオブジェクトとして動作するServer.javaは, 通常のJavaプログラムでよく, リモートオブジェクト化するための変更は必要ない。クライアント側では, Serverクラスをnewする代わりに, Server_Proxyクラスをnewして, リモートオブジェクトを生成する。生成後は, 通常のJavaオブジェクトと同様のアクセスが可能である。proxy-skeleton間にはコネクションが張られる。
OZでも, proxyクラス(Java)がサーバのコンパイル時に生成される。OZでは, proxyオブジェクトごとに呼出し側から呼び出される側に転送し, proxyがskeletonの機能ももって実装されているが, proxy及びskeletonの形態には変わりはない。コネクションはエグゼキュータ間で張られ, オブジェクト間のコネクションは仮想的なものとなる。
対話的なアプリケーション又は並列処理の記述に使用する場合には, きめ細かな同期・相互排除の記述及び実行ができなければならない。
Java-RMIは, リモートメソッドの起動方法として, 同期メソッド起動だけを備えている。
HORBは, 同期メソッド起動に加えて, リモートオブジェクトのメソッドを呼び出した直後にクライアントに制御が戻ってくる非同期メソッド起動を備えている。
OZは, Java-RMIと同様に同期メソッド起動だけであるが, 同期・相互排除用の変数を複数定義することができ,これを使ってスレッド間のきめ細かな同期・相互排除を行う。
分散オブジェクトがネットワーク環境で動作する際には, 動作環境が多岐にわたり, 変動もあるため, エラーなどが発生しやすく, その際には原因の究明が難しい。エラーの発生したことをオブジェクトに通知し, その処理をオブジェクトに任せる方法を例外処理といい, 特に分散オブジェクトにまたがった例外処理をグローバル例外処理という。例外処理の記述において, Javaではメソッドシグネチャとしてthrows節を記述する必要があるが, OZではそれが不要な点を除くと両者の違いはない。
リモートメソッド起動をまとめて, 表3.1に示す。リモートメソッド起動のようすを, 摸式的に図3.1に示す。
表3.1 Java, HORB及びOZのリモートメソッド起動の比較
図3.1 Java, HORB及びOZのリモートメソッド起動の機構
リモートメソッド起動に伴って, オブジェクトが計算機間を移動する。オブジェクト指向のシステムであるので, そのオブジェクトはインタフェースで規定されたクラス自体のインスタンスだけでなく, そのサブクラスのものである可能性がある。それらのオブジェクトが転送される際には, 受信側に常にそのオブジェクトのクラスが存在する保証はない。必要に応じてクラスを配送する機構は, オブジェクト指向のもつ拡張性を分散環境に適用する際に, 必然的に要求される。
これを実現するには,必要なクラスの識別方法及びクラス配送の機構が必要となる。
クラスの識別に関しては, Javaアプレット及びJava-RMIがサーバ(Home page内)で一意としているのに対して, OZではワールドワイドに一意な識別方法をとる。
Java-VMにおいて, クラスはパッケージ名及びクラス名からなるfull-qualified class nameによって識別される。クラスローダ毎に, 同じfull-qualified class nameをもつクラスは同一であると判断される。つまり, 同じfull-qualified class nameをもつ異なるクラスが複数存在した場合, その中でJava-VMにロードされるのは高々一つに限られる。ネットワークを介してオブジェクトを転送する際にはオブジェクトのシリアライズ及びデシリアライズが行われるが, 送信側でのシリアライズの際に使われたクラスと, 受信側でのデシリアライズに使うクラスとが一致していないと, デシリアライズに失敗する。この問題を避けるためには, クラスのfull-qualified class nameの重複を避け, full-qualified class nameによってクラスを一意に決定可能にする必要がある。
Javaアプレット, Java-RMI, HORBでは, full-qualified class nameはプログラマが任意に決定可能になっており, クラス及びそのインスタンスが転送される範囲において,プログラマがその一意性を保証しなければならない。Java-RMI及びHORBで複数のリモートメソッド起動の連鎖になる場合にも, その連鎖の範囲内で一意性を保証しなければならない。Javaアプレットの場合は, クラスの転送はサーバからクライアントに対してだけ生じ, クライアント(ブラウザ)では, アプレット毎に異なるクラスローダが使われるので, 一意性は一つのアプレットの記述の範囲で保証すれば十分である。
OZでは, クラスを任意の場所に配送するために, クラスの一意性をシステムが保証する。OZでは, クラスを配送できる公開クラス及び配送されないローカルクラスの2種類に分けて管理している。公開クラスは, 一意性をもつ。プログラム開発段階のクラスはすべてローカルクラスであり, その名前の一意性はプログラマが与える。ローカルクラスは, jp.go.ipa.oz.user.で始まり, それにreleased が続かないパッケージ名をもつ。他のユーザへの配送を許す公開クラスは, ローカルクラスを公開クラス用に再コンパイルすることによって行われる。そのfull-qualified class nameは, jp.go.ipa.oz.user.released.にOZホーム名の":"及び"."を"_"に代えた語を続け, その後にローカルクラスのfull-qualified class nameのjp.go.ipa.oz.user.以降を続けたものに変更される。OZホーム名はネットワークワイドに一意であるから, 生成された公開クラスのfull-qualified class nameも一意なものとなる。
Javaアプレットは,WebサーバからWebのブラウザにダウンロードできるプログラム片とする。Javaのクラスがネットワーク上で配送されるという意味では,クラス配送であるが,配送されたアプレットはブラウザ上でローカルにしか動作しない。
Java-RMIでは, ダイナミッククラスローディングによってクラスを実行時にダウンロードできる。しかし,サーバからクライアントへのクラスの配送は行われるが,逆方向, すなわち, メソッド起動の引数として送られるオブジェクトのクラスをクライアントからサーバに配送する機能は備えていない。クラスローダをユーザが拡張することは可能であるので, 勝手に作ることはできる。
HORBは, クラス配送の手段を提供していない。
OZにおいては, 未知のクラスのオブジェクトをネットワーク経由で受け取った場合, 公開されている他人のクラスを用いてプログラミングする場合などに, クラスが必要になった際にクラスローダがクラスのロードを行うが, クラスがローカルディスクになかったときには, ネットワーク経由でクラスの配送を受ける機構を備えている。
クラスの配送は, OZのセルであるクラス配送エージェントが行う。クラス配送エージェントは, 他のOZホームのクラス配送エージェントからクラスの配送を受ける機能と, 逆に他のクラス配送エージェントからの要求によってローカルディスク上のクラスを配送する機能とをもつ。クラスを探索するために, 問い合わせなければならないクラス配送エージェントを順に記した検索リストをもつ。
クラスローダは, 最初にローカルディスク内にクラスファイルが存在するかどうか調べ, あればそれをロードし,なければ, そのOZホームのクラス配送エージェントにクラス配送要求を出す。エグゼキュータからクラス配送の要求を受け, それがローカルにないときには, 検索リストにあるクラス配送エージェントに順に問合わせを行う。該当するクラスをもつクラス配送エージェントが見つかれば, そのクラス配送エージェントからクラスの配送を受け, それをローカルディスクに保管してエグゼキュータにその旨を伝える。クラス配送エージェントからクラス配送完了の知らせを受けたエグゼキュータは, クラスをロードして実行を継続する。
HORBは, Javaの言語規定をそのまま踏襲し, サーバProxyを指定する形式が違うだけである。OZは, これまでの経緯を踏襲し, OZコンパイラはJava言語にコンパイルするが, Javaとは違っている。その違いの主要な部分を, 表3.2に示す。
表3.2 JavaとOZとの言語上の構文の差異及び意味の差異
(a) 構文の差異
(b) 意味の差異
Java-RMI及びHORBは, Java上に構築されているため, javacコンパイラと, Proxy, skeletonを生成するためのコンパイラとを併用する。OZは, 独自の言語であるが, 独自のコンパイラとjavacコンパイラとを併用する。
Javaではrmicを, HORBではhorbcを, OZではozcをそれぞれ用いるが, それらが適切なランタイムの呼出しを含むJavaのクラスを生成すること, 必要に応じてproxy及びskeletonのクラスを作ることは, 共通している。
複数のサーバからクラスを取り寄せることが必要になる。類似のアプリケーションでは, クラスに同じ名前をつけることが多い。あるクラスが使用されていることを知ることは, 分散環境では極めて難しい。つまり, 分散環境では, 同名のクラス, 及び同じクラスでも複数のバージョンが混在して動作する機構を備えておかなければならない。Java及びHORBでは, 名前を変更しなければならず, 一貫性をもって行うことはできないが, OZでは次の方法によってこれが可能となる。
OZでは, 一つのクラス定義をインタフェース及び実装の二つの定義に分割して処理し, それぞれをJavaのインタフェース定義及びクラス定義に対応づけ, クラス定義の中でpublicなメソッドのシグネチャ定義だけをインタフェースとし, その他の定義をすべて実装として扱う。OZのクラス名と, インタフェース及び実装のJavaのクラス名との対応は, スクールという名前表によって指定する。例えば, OZでAというクラスを定義し, そのインタフェース及び実装のJavaのクラス名をそれぞれA_if及びA1_clとしてスクールで指定してコンパイルを行うとする。次にAの実装を変更し, 実装のJavaのクラス名をA2_clに変更してコンパイルを行うと, A2_clとして出力されるコードにその変更が反映し, それ以降Aから生成するインスタンスの実装にはA2_clを利用することになる。一方, Aの古い実装を利用していたコードは, A1_clのコードが元のまま残っているため, Aの変更の影響を受けることなく実行できる。このスクールを介してクラス名の変更を一貫性を維持して行うことによって, クラスの実装の旧バージョン及び新バージョンのコードが互いに影響を受けることなく混在して動作可能な機構を実現している(図3.2参照)。
図3.2 OZにおけるバージョンの実現方法
Java言語で実現したコンポーネントフレームワーク及びAPIをJavaBeansと呼ぶ。コンポーネントをJavaで実装することには, Javaの利点であるプラットフォーム非依存性, セキュリティ, ネットワーク機能, GUI機能などを生かす。
JavaBeansでのコンポーネントをBeanと呼ぶ。Beanは, JavaBeansをサポートするコンテナに自由に貼り付けられる。WWWブラウザ, ワードプロセサ, 開発ツールなどがコンテナの例である。コンテナは, イントロスペクション(introspection)という機能を使って, Beanがもつ内部情報(プロパティ, メソッド, イベントなどの定義情報)を調べられる。イントロスペクション自体は, Javaのリフレクション(refrection)APIを使って実装されている。JavaBeansでは, プロパティに対してそのプロパティを参照・設定するメソッドの命名規則を定めることによって, これを可能にしている。
Beanのプロパティを変更することをカスタマイゼーションという。Bean自体からプロパティ情報を取り出せるので, カスタマイゼーションを行うためのコードをBean毎に個別に用意する必要はない。Beanを永続化するには, Javaのシリアライザを使用する方法と, イントロスペクションを利用してアプリケーション独自のフォーマットで状態を保存する方法とがある。
マイクロソフトのDCOM上のコンポーネント技術としてActiveXがある。ActiveXはWindowsを唯一のプラットフォームとしているが, プログラミング言語は規定しない。一方, JavaBeansはプラットフォームには依存しないが, Java言語でなければ開発と利用ができない。これら二つのコンポーネントを相互に利用するための技術も開発されている。
HORBは, 他のWindowsマシン上にあるActiveXコンポーネント(OLEコンポーネント)をJavaプログラムからリモート実行する機能と, Javaのコンポーネント技術であるJavaBeans間でオブジェクト間通信を行う機能とをもつ。
ActiveXコンポーネントは, Windowsのコンポーネント技術であって, Excel, PowerPointなど多くのWindowsアプリケーションが, ActiveXコンポーネントとして動作する。他のWindowsマシン上のActiveXコンポーネントをリモートに実行するには, MicrosoftのDistributed COM(DCOM)というミドルウェアを利用することが多い。DCOMは, Windows NTには標準的に組み込まれている。Windows NT以外のUnix, Macintosh, メインフレームなどからWindowsマシンのActiveXコンポーネントを利用するためには, それらのOSの上で利用可能なDCOMをインストールしておく必要がある。HORBを用いると, どんなOSの上で動作するJavaプログラムからでも, Windows上のActiveXコンポーネントを利用できる。
ActiveXのコンポーネントのタイプ情報は, タイプライブラリtlbファイルに記録されている。タイプ情報を MicrosoftのJactiveXコマンドにかけると, DCOM用のスタブファイルのJavaソースコードが生成される。それをjavacコンパイラでコンパイルすると, スタブファイルのJavaクラスが得られる。通常はそれをレジストリに登録するが, スタブファイルのJavaクラスにhorbcコンパイラを適用することによって, ActiveXコンポーネントにアクセスするためのHORBのProxy, Skeletonクラスを得ることができる。このProxyクラスを使用することによって, DCOMが組み込まれていないOSの上のJavaプログラムから, ActiveXコンポーネントのメソッドをリモートに実行することができる。ActiveXコンポーネントは, 変更する必要はない。
JavaBeansにおいて分散コンポーネントを実現するためには, コンポーネント間での通信が必要となる。アプレットではないJavaBeansコンポーネント間での通信には, 通常のHORBのリモートオブジェクト通信を行えばよい。アプレットのJavaBeansコンポーネント間では, アプレットのセキュリティの制限によって, 直接TCP/IPのコネクションを開設することが不可能であるため, サーバマシンを介してアプレット間でリモートオブジェクト通信を行う。アプレット上のJavaBeansコンポーネントからサーバへのリモートオブジェクト呼出しは, 通常のHORBのリモートオブジェクト呼出しとする。サーバからアプレット上のコンポーネントを呼ぶには, HORBのInvitation機能を用いる。Invitationは, 通常はセキュリティの制限によってコネクションを受付けることができないアプレットのメソッドをサーバから呼び出すことを可能にする。これらの機能によって, コンポーネントとサーバとの間で任意のリモートメソッドを, 双方向に呼び出すことが可能となる。
OZでは, クラスは必要に応じてクラス配送機構を使って配送する。クラスを相互利用・再利用するには, 二つの場合がある。開発者による相互利用・再利用, 及びエンドユーザによる相互利用である。
開発者向けにはComponent catalogというサーバが用意されている。Component catalogにはサブジェクトが登録される。サブジェクトは, プログラム言語上の名前とワールドワイドで一意なクラスの識別子との対応表であり, コンパイル時に用いるスクールにマージして使われる。サブジェクトをComponent catalogから入手することによって, そのサブジェクトに含まれるクラスがプログラム開発で利用可能となる。新たに開発されたクラスをComponent catalogに登録することによって, ネットワーク上でのクラスの相互利用・再利用が進む。
エンドユーザ向けにはApplication catalogというサーバが用意されている。Application catalogにはアプリケーションを生成するクラスのクラス識別子が登録される。エンドユーザは, ランチャにApplication catalogから得たクラス識別子を与えることによって, アプリケーションのオブジェクトを生成できる。このとき, 必要に応じて,アプリケーションの実行に必要なクラスが転送される。
サーバのオブジェクトを相互利用するには, そのサーバの機能(クラス)及び名前(GOL)を知ればよい。サーバにアクセスするために, Traderというサーバが用意されている。Traderは, 必要とする機能を指定することによって, そのサーバにアクセスするためのオブジェクトであるアクセススタブを返す。複製をもつサーバで一つのサーバが不具合を起こした場合のサーバ選択などの機能は, アクセススタブによって提供される。