17-3 サンプルの実行手順


・ 実行手順の解説に入る前に、 JDK1.2 で提供されている RMI関連のツールとクラスライブラリの パッケージについて簡単に概説しておきましょう。 JDK には RMIのプログラムの実行を助けるために以下のような ツールが提供されています。

JDK1.2 の RMI関連のツール
コマンド名機能提供された時期
rmicRMIコンパイラJDK1.1
rmiregistryネームサーバJDK1.1
rmidActivationサーバJDK1.2

 rmic は分散オブジェクトのバイトコードを元にして、 RMIの機能を実現するための別のクラスのバイトコードを生成するコンパイラです。 「スタブ(Stub)」と「スケルトン(Skeleton)」と呼ばれる バイトコードが自動的に生成されます。
 rmiregistryは RMIを通じてサービスをやりとりするアプリケーションの 通信の仲立ちをするサーバーです。 RMIの分散オブジェクトは一意の URLによって表現される名前を rmiregistryサーバーに登録し、サービスの呼び出しは rmiregistryサーバーへの問い合わせによって開始されます。 したがって RMIのプログラムを実行するためには rmiregistry(もしくは同じサービスを 提供するプロセス)がバックグラウンドで起動されている必要があります。
 rmidも RMIの機構を動かすために必要なサーバーです。 このサーバーの役割は JDK1.2 から追加された Activationの機構を実現することです。 この新しい機能はサービス開始の要請が発生した時点で VMの起動ととサービスを提供する分散オブジェクトの生成を動的に行います。 通常のサーバープログラムの代わりに rmid がサービスの受け付けを行うわけです。 ですからやはりバックグラウンドで起動されている必要があります。 ただし JDK1.1までの RMIの機構を用いるだけならば rmid は必要ありません。

 次に JDK1.2のクラスライブラリの中で RMIに関連するパッケージを 調べてみましょう。

JDK1.2 の RMI関連のパッケージ
パッケージ名機能提供された時期
java.rmiRMIの基本的な機能の提供JDK1.1
java.rmi.server 分散オブジェクトが備えるべき機能の提供JDK1.1
java.rmi.dgc 分散環境でのガーベジコレクションの機能の提供JDK1.1
java.rmi.registry ネームサーバーのためのregistryオブジェクトの提供JDK1.1
java.rmi.activation Activationの機能の提供JDK1.2

 RMIのアプリケーションが主に利用するのは java.rmi と java.rmi.sever に提供されているいくつかの基本的なクラス(およびインターフェイス)です。 この後解説するサンプルを見ていただければわかりますが、 それらのクラスの利用方法もかなりパターン化されています。 通常のプログラムで java.rmi.dgc と java.rmi.registryを利用することはあまりないでしょう。
 java.rmi.activation パッケージは JDK1.2から追加されました。 Activationと呼ばれる新しい機能の実現を可能にします。

・ では RMIのサンプルを実行してみましょう。 わざわざ節を改めて実行手順を解説するのは、通常の Javaのアプリケーションの 場合に比べてかなり煩雑だからです。 まず手順の項目だけ簡単に示しておきましょう。

  1. Javaのソースのコンパイル
  2. RMIのコンパイル
  3. rmiregistryサーバーの起動
  4. サーバープログラムの起動
  5. クライアントプログラムの起動

 最初の Javaのコンパイルに関しては解説は不要でしょう。 問題は「RMIのコンパイル」という新しい作業からです。 具体的には JDKに提供されている rmic というツールを用い、 次のようにして実行します。


rmic  RemoteDictionaryImpl

 RMIのコンパイルの対象となるのは分散オブジェクトのバイトコードです。 このコンパイルの結果として新しいバイトコードが2個生成されます。 それらの名前はルールが決まっています。 元になるクラス名の後に "_Stub" と "_Skel" が付いたものです。 今のサンプルの場合は RemoteDictionaryImpl.class を元にして、 RemoteDictionaryImpl_Stub.class と RemoteDictonaryImpl_Skel.classとなります。
 この2個のクラスは、それぞれクライアント側とサーバー側の ネットワークへの出入口に対応します。 ただしプログラムの表面には直接現れませんから、 コーディングの作業の時にはその存在を意識する必要はありません。 実際、直接バイトコードが生成されてしまうため、 その中で何が行われているのか簡単には知ることができません。
 RemoteDictionaryImpl_Stubは RemoteDictionaryを実装したクラスです。 クライアント側の VMに生成される RemoteDictionaryは 、実はこのクラスのオブジェクトです。 サーバー側にある分散オブジェクトの実体の「分身」のようなものと考えれば 理解しやすいでしょう。 このオブジェクトのメソッドの実行は、 現実にはネットワークを通じたサービス要求を発生するわけですが、 見かけ上はあたかも分散オブジェクトのメソッドを直接呼び出しているかのように 見えます。 RemoteDictionaryImpl_Skelはクライアントからの通信の監視を行い、 RMIによるメソッドの呼び出しを受け取って分散オブジェクトに通知する働きをします。

 アプリケーションを実行する前にもう1つ行うべきことがあります。 ネームサービスを行う専用のサーバーの起動です。 JDKでは rmiregistry という名前のツールが用意されています。 この名前のコマンドを呼び出すだけですが、バックグラウンドのプロセスとして 起動した方が便利でしょう。具体的には次のようにします。


rmiregistry  &           (UNIXの場合)
start  rmiregistry       (Windowsの場合)

 いよいよアプリケーションの実行です。 最初にサーバープログラム DictionaryServer を起動しますが、 いつもと同じように起動しようとすると、おそらくエラーになってしまうでしょう。 これは JDK1.2β4からセキュリティの取り扱いが厳格になったおかげです。 デフォルトの設定だとネームサーバーへのアクセスが許可されないかもしれません。 きちんとアクセスするためにはセキュリティの方針を SecurityManagerに 知らせる必要があります。 そのためにセキュリティの方針を定めたファイルを用意し、 VM起動時にその情報を javaコマンドのオプションに与えます。 セキュリティの方針を定めるファイルは、たとえば下記のような形式になります。 これを適当な名前(たとえば policy)で用意してください。


grant {
    permission java.security.AllPermission;
};

 上のファイルは特に条件を付けずに全ての許可を与えるという意味になり、 あまり良い例ではありません。 (アプリケーションの起動の実験が確実にできることを優先した設定です。) このファイルの設定を VMに読み込ませるには、 システムプロパティ java.security.policyの値をコマンドラインから -D オプションを用いて与えます。
 実はこれで全てではありません。もう1つ別のシステムプロパティの 値も渡す必要があります。 このプロパティは java.rmi.server.codebase という名前で 分散オブジェクトの Stubファイルがある場所の情報を指定します。 この情報は今起動しようとしているサーバーからネームサーバーへ伝えられ、 クライアントのプログラムが Stubファイルを動的にロードする手助けをします。
 というわけで、サーバーの起動はたとえば下記のような形で実行される ことになります。 (URL名やファイル名はそれぞれの開発環境に合わせて調整してください。)


java  -Djava.rmi.server.codebase=http://www.wakhok.ac.jp/java/rmi/sample/
      -Djava.security.policy=policy  DictionaryServer

 サーバーが無事起動できたら、最後にクライアントです。 クライアントは別のマシンから起動されることを仮定しています。 (もちろん同じマシンから起動してもかまいません。 その場合でも自分自身のマシン名をしてしてください。) 第1引数にサーバーが起動されているマシン名を、 第2引数に検索のキーとなる英単語を指定してください。


java  SingleLineClient  servername  elephant

 万事がうまくいけばサーバーから返答があるはずです(図5)。 検索する単語を別のものにして繰り返し起動し、 正しく機能することを確認してください。