AppletとRMIに関する考察
RMIの基礎
RMIは、Javaの分散コンピューティングの道具である。
RMIの基本はRPC(Remote Procedure Call)なので、基本的にはpeer to peer であり、クライアント-サーバモデルではない。
しかし、リモードメソッドを提供する側をサーバ、利用する側をクライアントと呼ぶ。この関係は、リモードメソッドの引数としてリモートオブジェクトを渡すことにより、簡単に反転するため、こちらのJava VM がサーバ、こちらのJava VM がクライアント、と言うような関係にはならない。
RMIのチュートリアルは、JDK1.4のRMIのページあたりに詳しいので、ここではそこに書いてあるようなことは述べない。
RMIの登場人物
RMIの登場人物は、以下のような感じである。
- サーバプログラム
サーバプログラムは、リモートオブジェクトのインスタンスを作成し、レジストリに登録(export)する。リモートオブジェクトのメソッドの実行は、サーバプログラムのVMで行われる。
- クライアントプログラム
クライアントプログラムは、レジストリからリモートオブジェクトを取得(lookup)し、メソッドを呼び出す。レジストリから取得できるのは、リモートオブジェクトそのものではなくスタブと呼ばれるクラスである。
- レジストリ
リモートオブジェクトに名前を付けて登録しておくところである。一般的には、ネーミングサービス、ディレクトリサービスなどと呼ばれるもの。
- リモートオブジェクト
RMIの基本となるオブジェクト。リモートインターフェイス、インターフェイスの実装、スタブクラス、スケルトンクラスから成る。
サーバプログラムのセキュリティマネージャ
チュートリアルや、RMIのドキュメントを読むと、リモートオブジェクトをレジストリにexportするサーバは、RMISecurityManagerを設定し、VMのシステムプロパティでセキュリティポリシーを設定することになっている。
この目的は(私が読み取れた限りでは)、以下の二つである。(ここは大幅に外している可能性があるので、違ったら指摘して欲しい)
- リモートオブジェクト及びリモートオブジェクトが必要とするクラスを、RMIクラスローダによって動的に読み込めるようにする。
- 動的に読み込んだクラスが、悪いことをしないように、「できること」を制限する。
この私の理解が正しいとすると、「(ネットワークから)動的にクラスを読み込む必要がなく」、「システムを構成するコンポーネントが信用でき、制限をかける必要がない」場合には、セキュリティマネージャを設定する必要はないと言うことだ。
RMIクライアントの動的なクラスのロード
チュートリアルから私が理解した範囲では、RMIクライアントがネットワークから動的にクラスをロードするためには、以下の条件を満たす必要がある。
- RMIレジストリを実行するVMが、関連するクラスをCLASSPATHから読み込むことができない。
- RMIレジストリにリモートオブジェクトをexportするVMが、システムプロパティjava.rmi.server.codebaseに関連するクラスが存在するパスを指定している。
- RMIクライアントのCLASSPATHから、関連するクラスを読み込むことができない。
ここで言う関連クラスとは、以下の3つである。
- リモートインターフェイス
- リモートオブジェクトのスタブ
- リモートインターフェイスで定義されるメソッドの引数のクラス、およびそのクラスが参照しているクラス
※ しかし、クライアントのコンパイルには、リモートインターフェイスは必須であり、リモートインターフェイスを動的にロードする必要性は感じられない。
ここで注意しなければならないのは、クライアント、サーバが共に同一の開発プロジェクトであり、同時に出荷されるものであるならば、そもそも動的なクラスのロードは必要ないのではないかと言うことだ。
少なくとも、必要なクラスの全てが、あらかじめ決まっていて、サーバ、クライアントの両方のVMのCLASSPATHから発見できるならば、動的なロードの必要性はない。
アプレット
ここで、アプレットについて考えてみよう。
アプレットは、WEBブラウザ上のJavaVMで実行されるが、WEBブラウザのCLASSPATH上には一切のクラスは存在しない(Javaの標準クラスは除く)
その代わりに、APPLETタグのcodebaseの指定が、CLASSPATHと同様に扱われ、HTTP経由で動的にクラスがロードされる。
つまり、アプレットにはそもそも動的なクラスのロードの機構が存在することになる。となると、上述したようにアプレットの開発時に全てのクラスが提供されているような場合、RMIを使うアプレットは、RMIの動的クラスロード機構を使う必要はないのではないかと言う結論に至る。
RMIアプレットのシンプルな解
以上のことから、RMIを利用したアプレットの、シンプルな構成は以下のようになる。
- サーバホスト
サーバホストでは、WEBサーバ及びサーバプログラムが動作する。
- WEBサーバ
WEBサーバでは、必要な全てのクラスがhttpでアクセスできるようにする。
- クライアントプログラム(アプレット)のCLASSイメージ
- サーバVM
サーバVMでは、リモートオブジェクト及びRMIレジストリが動作する。
- サーバプログラム
- RMIレジストリ
- クライアントホスト
- WEBブラウザ
- クライアントプログラム(アプレット)
シンプル解のメリットとデメリット
メリット:
- サーバ、クライアントが、一つのクラスツリーで開発できる。
デメリット:
- サーバプログラムを含めて、全てのクラスをhttpで公開してしまうことになる。
RMIアプレットの最適(?)解
設計時に、クラスを以下の3種類に分ける。
- サーバプログラムのみに必要なクラス
※ ソースは存在しないが、RMIスケルトンクラスはこれに当たる。
- クライアントプログラムのみに必要なクラス
※ ソースは存在しないが、RMIスタブクラスはこれに当たる。
- サーバ/クライアントの両方で必要なクラス
リモートインターフェイスや、リモートインターフェイスのメソッドの引数など。
サーバ実行時の配置を、以下のようにする。
- 上記1,3 をサーバプログラムの動作位置に配置する。(httpサーバからは見えない位置に)
- 上記2,3 をhttpサーバに配置する。
Makefileやantのbuild.xmlで実現できるかも知れないが、コストもかかり混乱の元になるので、本当に必要か判断すること。
とりあえずサンプル
false@wizard-limit.net