トップ 一覧 検索 ヘルプ RSS ログイン

技術的雑談-RMIのFastStartの変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
!!!技術的雑談-RMIのFastStart

!!環境
*WindowsXP
*JDK1.4.2_08

!!目的
とりあえずJava RMI使ってみましょう。

!!Java RMIとは?

Java(JDK)で標準でサポートしている「Remote Method Invocation」のことです。~
CORBAみたいな感じに、別のマシン(同じマシンでもですが)にあるオブジェクトのメソッドを実行できます。

CORBAとの大きな違いは、

* Java専用である事(厳密には違うみたいですが…)
* JDKのほかにコンポーネントが不要な事(既に実装されている)
* リモート公開されるClassのスタブを利用する側に持っていかなくてもいい!!(自動でClassスタブが転送されるみたいです!?)←これ、便利かも。


!!必要なもの

* JDK(1.4でも5.0でも可。バージョンをまたいでの通信も出来るようです。)
このなかで、以下のものを特に使います。
** rmicコマンド -- RMIで公開するObject Classからスタブを作成します。(JDK1.5同士の通信では不要らしい)
** rmidコマンド -- RMIで使われるデーモンです
** rmiregistryコマンド -- RMIで使われるデーモンです

!!デーモンの起動

RMIを使うには、RMIのObjectを公開する側のマシンでrmid、rmiregistryデーモンが上がっている必要があります。
ここではService化しないで、とりあえずコマンドプロンプトからバッチで起動しています。

! rmid

 REM Windowsの場合
 SET CLASSPATH=
 start rmid

! rmiregistry

 REM Windowsの場合
 SET CLASSPATH=
 start rmiregistry

※セキュリティーが設定されている場合は「-J-Djava.security.policy=<ポリシーファイル>」で、権限を追加する必要があるかもしれません。

※CLASSPATHをクリアしているのは、共有classを自動転送する再に邪魔になるらしいのです。転送するclassは-Djava.rmi.server.codebaseで明示的に指定します。

!! Servantの起動

Servantを実装したClassを

 -Djava.rmi.server.codebase=file:///<共有するClassのある場所>/

付きのjavaで起動します。
*classファイルを使う場合は、classesディレクトリへのパスを「最後は\で終わるように」指定します。
*.jarになっている場合、それを指定する事も出来ます。

とにかく、「共有するClass('''ソースではなく、コンパイルされたCLASS!''')のある場所を指定する」と考えてください。(CLASSPATH環境変数と一緒です。)

RMIの場合、JacORBの「lsns」みたいな登録Servant一覧コマンドみたいなのが見当たらないので、以下のようなものを作ってみました。


 /!!
  * RMIでRMIRegistryに登録されているObjectを一覧するApplication
  */
 package test;
 
 import java.rmi.registry.LocateRegistry;
 import java.rmi.registry.Registry;
 
 /!!
  * RMI Registry List Sample<br>
  * @author Tsubasa
  */
 public class RMIListObject {
 
     /!!
      * main<br>
      * <br>
      * Command line 引数:<br>
      * RMIListObject → LocahostのObject一覧<br>
      * RMIListObject HOST → そのHostのRMIに登録されているObjectの一覧<br>
      * RMIListObject HOST -d URL → そのHOSTのそのURLに登録されているObjectを削除<br>
      * @param args Command line parameters
      */
     public static void main(String[] args) {
         
         String host = "";
         if (args.length == 0) {
             host = "localhost";
         } else {
             host = args[0];
         }
         
         if( host.equalsIgnoreCase("-help")) {
             printUsage();
             System.exit(0);
         }
         
         String url = "";
         if (args.length == 3) {
             if ((!args[1].equals("-d")) && (!args[1].equals("-D"))) {
                 printUsage();
                 System.exit(1);
             }
             url = args[2];
         } else if ((args.length != 1) && (args.length != 0) ){
             printUsage();
             System.exit(1);
         }
         
         try {
             Registry registry = LocateRegistry.getRegistry(host);
             
             if (url.equals("")) {
                 // List一覧
                 String[] list = registry.list();
                 for(int i = 0; i < list.length; ++i) {
                     System.out.println(list[i]);
                 }
                 
             } else {
                 registry.unbind(url);
             }
             
         } catch (Exception e) {
             System.err.println("Client exception:");
             e.printStackTrace(System.err);
         }
 
     }
     
     protected static void printUsage() {
         System.err.println("Usage : RMIListObject [HOST [-d URL]]");
     }
 
 }


!! Clientの起動

こっちは普通のJava Applicationのように起動するだけです。


!! プログラミング

! Interfaceの定義(Interface)

IDLは書かずに普通にJavaのInterfaceを書きます。

* java.rmi.Remoteをextendします
* メソッドは全てjava.rmi.RemoteExceptionをthrowするようにする

これだけ守ればOKみたいです。~
JDK1.5で多少扱いが変わったようなことが書いてありましたが…。


! Servantの実装(Impl)

先程作ったInterfaceをimplimentするClassを書けばOK。
簡単。


! Serverの作成

ServantをRMIRepositryに登録する部分を書きます。

+ 公開するServant実体Object(Servant Impl)をnewします。
+ Servant Objectを公開できる形にします。 「【ServantInterface】 ref = (【ServantInterface】)java.rmi.server.UnicastRemoteObject.exportObject(【servantImpl】, 【portNum】);」
++ 【ServantInterface】 -- ServantのInterface Class
++ 【ServantImpl】 -- 先程newしたServantの実体Object
++ 【portNum】 -- ServantがbindされるネットワークポートNo.(0で自動決定)
+ RMIRepositryに登録する。「java.rmi.registry.Registry registry = java.rmi.registry.LocateRegistry.getRegistry(host);」でhostのRepositryを取得し、「registry.bind("<URL>", ref);」でbindします。<URL>は公開するURL(//〜/〜)です。'''←別項参照!!'''
+ 既に公開されているServantを再度上書き登録するには「.rebind」メソッドを使います。(公開されていないものをrebindしてもExceptionになる)

これで、外からこのObjectが参照できます。

Servantの登録を削除するには、java.rmi.registry.Registry#unbind(<URL>)で行います。

尚、JDK1.4が絡む環境(ClientがJDK1.4)では、Servantをjavacでコンパイルした後に、Servantの実装Class(【ServantImpl】)に対して「rmic」コマンドを通す必要があります。Servant,Clientが両方ともJDK1.5の環境ではrmicは不要との記述もありますが、確認していません。

!! Clientの実装

+ java.rmi.registry.LocateRegistry.getRegistry(host)でObjectの登録してあるRegistryを取得します。
+ 【ServantInterface】 obj = (【ServantInterface】)java.rmi.registry.registry.lookup(<URL>); でURLにbindされているObjectを取得します。

あとはそのObjectを使うだけ。

使い終わったら……「obj = null;」すればいいのかなぁ…(ちょっと弱気、でもとりあえず問題なさげ)


* 全体的に

Servantの起動の際に-Dオプションさえ付け忘れなければ大してハマるポイントはないと思われます。~
ただ、なんでもかんでも「RemoteException」なので、原因別に細かいエラー処理を行いたい場合には工夫が必要かもしれません。

その他はCORBAよりも簡単な印象を受けます。

!!履歴
2005/7/14 -- 初版

[[技術的雑談]]へ戻る

!!突っ込み
{{comment}}

[[技術的雑談]]へ戻る

{{trackback}}

[[技術的雑談]]へ戻る