Java

RMI-IIOP プログラマーズガイド

RMI-IIOP ドキュメント

このドキュメントでは、Internet Inter-ORB Protocol (IIOP) を使用してリモートオブジェクトにアクセスすることができる、Java Remote Method Invocation (RMI) プログラムの作成方法について説明します。 RMI プログラムにごくわずかな制約を課すことによって、任意の言語の CORBA クライアントから RMI-IIOP サーバにアクセスできるようになります。 RMI-IIOP によって、RMI を簡単に使用できるようになり、CORBA/IIOP 言語の相互運用性が実現されます。

RMI-IIOP について

RMI

RMI を使用して、Java プログラミング言語で分散プログラムを作成できます。 RMI は簡単に使用することができ、Interface Definition Language (IDL) の知識は必要ありません。また、Java の「Write Once, Run Anywhere」という利点を継承できます。 クライアント、リモートインタフェース、およびサーバは、すべて Java で記述できます。 RMI では、リモート Java オブジェクトとの通信を行うとき、Java Remote Method Protocol (JRMP) が使用されます。 RMI プログラム作成についての概要は、「RMI チュートリアル」の Web ページを参照してください。 このドキュメントでは、簡単な「Hello World」RMI プログラムの作成について説明されています。

RMI は通信プロトコルとして CORBA-IIOP を使用しないので、その他の言語との相互運用性に欠けています。

IIOP、CORBA、および Java IDL

IIOP は、転送に TCP/IP を使用する CORBA の通信プロトコルです。 クライアントとサーバの通信の標準を指定します。 CORBA は、Object Management Group (OMG) によって開発された、標準の分散オブジェクトアーキテクチャです。 リモートオブジェクトとのインタフェースは、プラットフォームに依存しないインタフェース定義言語 (IDL) で記述されています。 IDL から特定のプログラミング言語へのマッピングが実装されており、その言語はプログラミング言語が CORBA/IIOP に結合されています。

Java(TM) 2 Platform, Standard Edition (J2SE), v.1.4 の CORBA/IIOP 実装は、Java IDL として知られています。 idlj コンパイラおよび Java IDL を使用して、Java プログラミング言語から CORBA オブジェクトを定義、実装、およびアクセスを行うことができます。

「Java IDL」の Web ページでは、CORBA/IIOP プログラミングについて Java を中心にしてわかりやすく説明されています。 Java IDL プログラムの作成についての概要は、「入門: Hello World」の Web ページを参照してください。

RMI-IIOP

以前は、分散プログラミングソリューションを開発する場合、Java プログラマは、RMI または CORBA/IIOP (Java IDL) から選択しなければなりませんでした。 現在では、いくつかの制約に従うことによって、RMI サーバオブジェクトは IIOP プロトコルを使用することができ、任意の言語で作成された CORBA クライアントオブジェクトと通信することができます。 このソリューションは、RMI-IIOP と呼ばれます。 RMI-IIOP は、RMI 方式の使いやすさと、CORBA の言語間の相互運用性がともに実現されています。

目次

rmic コンパイラ

RMI-IIOP ソフトウェアには、準拠規定により、IIOP スタブおよびタイの生成機能と IDL の発行機能を持つ rmic コンパイラ (Java Language to OMG IDL Language Mapping Specification に準拠) が付属しています。

以下は、CORBA/IIOP 機能をサポートする主な rmic フラグです。

-iiop -- IIOP スタブ/タイ を生成する
-iiop -poa -- Portable Object Adapter (POA) で動作する IIOP スタブ/タイを生成する
-idl -- IDL を生成する

以下のオプションは、-idl オプションと同時に使用されます。

-noValueMethods -- IDL valuetype 内のメソッドおよびコンストラクタの IDL の生成を停止する
-always -- 既存のスタブ、タイ、idl が入力クラスより新しい場合も強制的に再生成を行う。-iiop-idl の両方またはいずれかのフラグが存在する場合のみ有効
-idlModule <fromJavaPackage<.class>> <toIDLModule> -- IDLEntity パッケージマッピングを指定 (例: -idlModule foo.bar my::real::idlmod)
-idlFile <fromJavaPackage<.class>> <toIDLFile> -- IDLEntity ファイルマッピングを指定 (例: -idlFile test.pkg.X TEST16.idl)

rmic コンパイラの詳細については、rmic のドキュメントを参照してください。

-iiop フラグ

-iiop オプションを指定して rmic を実行すると、Java リモートメソッドプロトコル (JRMP) スタブおよびスケルトンクラスの代わりに IIOP スタブおよびタイクラスが生成されます。 スタブクラスは、リモートオブジェクトに対するローカルプロキシです。 スタブクラスは、サーバに呼び出しを送信するときに、クライアントによって使用されます。 各リモートインタフェースにはスタブクラスが必要です。スタブクラスによってリモートインタフェースが実装されます。 クライアントからのリモートオブジェクトへの参照は、実際にはスタブへの参照になります。 タイクラスは、サーバ側で着呼を処理し、その呼び出しを適切な実装クラスにディスパッチするときに使われます。 各実装クラスには、タイクラスが必要です。

スタブクラスは、abstract インタフェースにも生成されます。 abstract インタフェースは、java.rmi.Remote を継承したインタフェースではありませんが、そのインタフェースのメソッドはすべて java.rmi.RemoteException か、java.rmi.RemoteException のスーパークラスをスローします。 また、java.rmi.Remote を継承せず、メソッドを持たないインタフェースも、abstract インタフェースです。

質問: J2SE 1.3 で動作する RMI-IIOP アプリケーションを持っています。 rmic -iiop -poa を使って新しいスタブおよびスケルトンを生成すると動作しなくなってしまいました。 なぜですか。

回答: 以下に挙げるように、RMI-IIOP プログラムのコンパイルおよび実行方法は 2 通りあります。 以下の方法を組み合わせるのは避けてください。

  1. デフォルトの RMI-IIOP の動作。 デフォルトの RMI-IIOP の動作を使用するアプリケーションの例は、「チュートリアル: 入門: RMI-IIOP の使用法」で参照できます。

  2. POA ベースの RMI-IIOP。 非標準の POA ベースの RMI-IIOP の動作を使用するアプリケーションの例は、「チュートリアル: 入門: RMI-IIOP の使用法」で参照できます。

-iiop -poa フラグ

今回の J2SE のリリースには、-iiop -poa オプションが追加されています。 -iiop フラグに -poa オプションを指定すると、継承が org.omg.CORBA_2_3.portable.ObjectImpl から org.omg.PortableServer.Servant に変わります。 この種のマッピングは非標準であり、Java Language to OMG IDL Language Mapping Specification には指定されていません。

Portable Object Adapter (POA) の PortableServer モジュールは、ネイティブの Servant 型を定義します。 Java プログラミング言語では、Servant 型は、Java の org.omg.PortableServer.Servant クラスにマッピングされています。 これはあらゆる POA サーバント実装の基底クラスとなり多数のメソッドを提供します。これらのメソッドはアプリケーションプログラマが呼び出すだけではなく、POA 自身からも呼び出され、場合によってはサーバントの動作を制御するためにユーザがオーバーライドすることもあります。

-idl フラグ

rmic-idl オプションを指定すると、指定されているクラスおよび参照されているすべてのクラスに対して OMG IDL が生成されます。 別の言語で書かれた CORBA クライアントから RMI-IIOP サーバに接続する必要がある場合も、このオプションを使用できます。

注: rmic -idl で OMG IDL が生成されたあと、IDL-to-Java コンパイラではなく、IDL-to-C++ またはその他の言語へのコンパイラで生成された IDL を使用します。 「ラウンドトリップ」は非推奨であり、不要です。 IDL 生成機能は、C++ などのその他の言語で使用されることになっています。 Java クライアントまたはサーバは、元の RMI-IIOP 型を使用できます。

IDL を使用すると、オブジェクトに対して API を指定するときに、プログラミング言語に依存せずに、単に宣言することができます。 IDL は、メソッドおよびデータの仕様として使用します。CORBA バインディングを提供する任意の言語で、メソッドおよびデータの作成および呼び出しを行うことができます。 これらの言語には、Java および C++ が含まれています。 詳細は、Java Language to IDL Mapping (OMG) を参照してください。

注:生成された IDL は、IDL に対する CORBA 2.3 拡張機能がサポートされている IDL コンパイラを使用しないとコンパイルできません。

-noValueMethods フラグ

-idl とともに -noValueMethods フラグを指定した場合、IDL 生成の間に発行される valuetype の中に、メソッドおよび初期化子が取り込まれません。 このフラグは、valuetype のオプションです。valuetypeが発行されない場合は、省略されます。

rmic についての詳細は、RMIC ツールのページ (Solaris 版/Windows 版) を参照してください。

idlj コンパイラ

RMI-IIOP ソフトウェアには、IDL-to-Java コンパイラが組み込まれています。 このコンパイラでは、RMI-IIOP との相互運用に必要な CORBA Objects By Value 機能がサポートされます。 このコンパイラは Java で記述されているため、任意のプラットフォームで実行できます。 このコンパイラの使用法についての詳細は、「IDL-to-Java コンパイラのユーザーズガイド」を参照してください。

RMI プログラムから IIOP を使用する

ここでは、RMI アプリケーションを RMI-IIOP に変換するための一般的な手順について説明します。
  1. ネームサービスに RMI レジストリを使用している場合は、CosNaming に切り替える必要があります。 createORB() メソッド内で org.omg.ORBClass を渡してはなりません。 ORB.init( args, null ) を代わりに使用してください。 次の操作を行う必要があります。
    1. クライアントおよびサーバの両方のコードで、次のコードを使用して JNDI 用の InitialContext を作成する必要があります。
    2.          import javax.naming.*;
               ...
               Context ic = new InitialContext();
    3. RMI レジストリの lookup() および bind() を使用している部分すべてを、JNDI の lookup() および bind() を使用するように変更します。 たとえば、RMI サーバで次のコードを使用するのではなく、
    4.          import java.rmi.*;
               ...
               Naming.rebind("MyObject", myObj);
      次のコードを使用するようにします。
               import javax.naming.*;
               ...
               ic.rebind("MyObject", myObj);
    5. クライアントがアプレットの場合は、クライアントアプレットから this を JNDI の CosNaming プラグインに渡す必要があります。 上記のコードを、次のコードで置き換えます。
    6.         import java.util.*;
              import javax.naming.*;
              ...
              Hashtable env = new Hashtable();
              env.put("java.naming.applet", this);
              Context ic = new InitialContext(env);
  2. ネームサービスに RMI レジストリを使用していない場合は、初期リモートオブジェクト参照のブートストラップの方法を変更することができます。 たとえば、サーバのコードでは Java 直列化を使用して RMI オブジェクト参照を ObjectOutputStream に書き込み、それをクライアントのコードに渡し、直列化復元して RMI スタブにすることができます。
  3. サーバ側では、PortableRemoteObject.toStub() 呼び出しを使用してスタブを取得し、writeObject() を使用してそのスタブを ObjectOutputStream に直列化します。 これらの処理を行うコードは、たとえば次のようになります。

    org.omg.CORBA.ORB myORB = org.omg.CORBA.ORB.init(new String[0], null);
    Wombat myWombat = new WombatImpl();
    javax.rmi.CORBA.Stub myStub = (javax.rmi.CORBA.Stub)PortableRemoteObject.toStub(myWombat);
    myStub.connect(myORB);
    // myWombat is now connected to myORB.  To connect other objects to the
    // same ORB, use PortableRemoteObject.connect(nextWombat, myWombat);
    FileOutputStream myFile = new FileOutputStream("t.tmp");
    ObjectOutputStream myStream = new ObjectOutputStream(myFile);
    myStream.writeObject(myStub);
    クライアント側では、readObject() を使用して、ObjectInputStream から、リモート参照の直列化復元をオブジェクトに対して行います。次のようなコードが使用されます。
    FileInputStream myFile = new FileInputStream("t.tmp");
    ObjectInputStream myStream = new ObjectInputStream(myFile);
    Wombat myWombat = (Wombat)myStream.readObject();
    org.omg.CORBA.ORB myORB = org.omg.CORBA.ORB.init(new String[0], null);
    ((javax.rmi.CORBA.Stub)myWombat).connect(myORB);
    // myWombat is now connected to myORB.  To connect other objects to the
    // same ORB, use PortableRemoteObject.connect(nextWombat, myWombat);
  4. リモート実装クラスを javax.rmi.PortableRemoteObject から継承するように変更するか、実装クラスを作成したあとで PortableRemoteObject.exportObject() を呼び出して実装オブジェクトを明示的にエクスポートします。 このトピックの詳細については、「IIOP スタブを ORB に接続」を参照してください。

  5.  
  6. コード内で、リモートインタフェースの Java キャストが使用されている箇所を、すべて javax.rmi.PortableRemoteObject.narrow() を使用するように変更します。

  7.  
  8. 分散ガベージコレクション (DGC) に依存したり、いずれの RMI DGC 機能も使用したりしないようにします。 使用されなくなったオブジェクトは、PortableRemoteObject.unexportObject() を使用してアンエクスポートしてください。

  9.  
  10. -iiop オプションを指定して rmic コマンドを実行し、RMI スタブおよび Tie を再生成します。 スタブおよび Tie ファイルが、次の名前で作成されます。
  11.      _<implementionName>_Tie.class
         _<interfaceName>_Stub.class
  12. サーバを起動する前に、次のコマンドを使用して、CosNaming サーバを (それ専用のプロセス内で) 起動します。
         orbd -ORBInitialPort port#
    ORBD の起動時にポート番号を指定する必要があります。
  13. クライアントおよびサーバのアプリケーションを起動するときは、次のシステムプロパティを指定します。
  14.      java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
              -Djava.naming.provider.url=iiop://<hostname>:1050
               <appl_class>
    この例では、ネームサービスポート番号 1050 を使用します。ステップ 7 でそれ以外のポートを指定した場合、プロバイダの URL 内のポート番号を使用する必要があります。 プロバイダの URL の <hostname> は、ステップ 7 で CosNaming サーバを起動したときに使用したホスト名です。

  15. クライアントがアプレットの場合は、アプレットタグに次のプロパティを指定します。
  16.      java.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
         java.naming.provider.url=iiop://<hostname>:1050
    この例では、ネームサービスポート番号 1050 を使用します。ステップ 7 でそれ以外のポートを指定した場合、プロバイダの URL 内のポート番号を使用する必要があります。 プロバイダの URL の <hostname> は、ステップ 7 で CosNaming サーバを起動したときに使用したホスト名です。

新しい RMI-IIOP アプリケーションの作成方法に関するチュートリアルは、「入門: RMI-IIOP の使用法POA ベースのサーバ側モデルの使用例」または「チュートリアル: 入門: RMI-IIOP の使用法」で参照できます。

IIOP スタブを ORB に接続

アプリケーションが JRMP スタブではなく IIOP スタブを使用する場合、IIOP スタブの操作を開始する前に IIOP スタブに正しく接続する必要があります (この作業は JRMP スタブの場合は不要)。 この節では、IIOP スタブを使用する場合に必要な「接続」手順について説明します。

PortableRemoteObject.exportObject() 呼び出しは、Tie オブジェクトを作成し、将来の使用に備えてキャッシュに格納するだけです。 作成された Tie には、委譲や ORB との関連付けがありません。 これを「明示的呼び出し」と呼びます。

PortableRemoteObject.exportObject() は、サーバインスタンスの作成時に自動的に生成されます。 これは、PortableRemoteObject コンストラクタを基底クラスとして呼び出したときに生成されます。 これを「暗黙の呼び出し」と呼びます。

PortableRemoteObject.toStub() は、あとでアプリケーションによって呼び出されたときに対応する Stub オブジェクトを生成し、キャッシュに格納されている Tie オブジェクトと関連付けます。 ただし、Tie は接続されておらず、委譲を持たないので、新しく作成されたスタブも委譲や ORB を持ちません。

スタブに委譲が設定されるのは、アプリケーションが Stub.connect(orb) を呼び出すときのみです。 このため、ORB 接続が確立される前にスタブを操作しようとしても失敗します。

「Java Language to IDL Mapping Specification」では、Stub.connect() メソッドは次のように既定されています。

connect メソッドにより、スタブは指定された ORB オブジェクト orb を使ってリモート接続を確立できる状態になります。 通常、接続は、リモートメソッド呼び出しでスタブが引数として受け渡されるときに暗黙的に確立されます。ただし、明示的な呼び出しによって接続を確立するほうが都合がよい場合もあります (直列化復元後など)。 スタブがすでに orb に接続されていて、orb の委譲セットを持っている場合、接続は機能しません。 スタブがその他の ORB に接続されている場合、RemoteException がスローされます。 それ以外の場合、このスタブと ORB オブジェクト orb の委譲が生成されます。

POA が有効になっていないサーバントの場合、必須設定として Stub.connect(orb) が必要になります。

IIOP を介して RMI プログラムを実行するときの制約

IIOP を介して既存の RMI プログラムを実行するときは、次の制約に従う必要があります。
  1. リモートインタフェース内のすべての定数の定義は、基本データ型または文字列でなければなりません。また、コンパイル時に評価する必要があります。
  2. Java-to-IDL マッピング規則によって生成される IDL の復号化名と競合する Java 名を使用しないでください。 Java-to-IDL の名前のマッピング規則については、Java Language to IDL Mapping の 28.3.2 節を参照してください。
  3. 複数の基底リモートインタフェースから、同じメソッド名をリモートインタフェースに 2 回以上継承しないでください。
  4. 名前を使用するときは、大文字小文字の区別に注意してください。 型の名前およびその型の変数を使用するときは、大文字小文字は区別されません。 ただし、それ以外のほとんどの名前の組み合わせでは、大文字小文字は区別されます。
  5. IIOP 経由でオブジェクト参照を転送するときに、オブジェクト参照の実行時共有に依存すると、正しく保持されないことがあります。 他のオブジェクトの実行時共有は、正しく保持されます。
  6. 次の RMI 機能を使用しないでください。

その他の注意事項

サーバはスレッドに対して安全でなければならない

同一のリモートオブジェクトに対するリモートメソッド呼び出しは並列に実行されることがあるため、リモートオブジェクトの実装は、その実装がスレッドに対して安全であることを確認する必要があります。

他の ORB との相互運用

RMI-IIOP は、CORBA 2.3 仕様をサポートしている他の ORB と相互運用します。 以前の ORB では、Objects By Value に対する IIOP エンコーディングがサポートされていないので、相互運用できませんでした。 このサポートは、IIOP を介して RMI 値のクラス (文字列を含む) を送信するときに必要です。

注: 異なる言語で書かれた ORB 間での通信も可能なはずですが、Java ORB と他のベンダーの ORB との相互運用性はまだテストされていません。

UnicastRemoteObject と PortableRemoteObject の使い分け

UnicastRemoteObject は、RMI プログラミングでオブジェクト実装のスーパークラスとして使用されます。 PortableRemoteObject は RMI-IIOP プログラミングで使用されます。 PortableRemoteObject を使用する場合は、実行時にトランスポートプロトコルを JRMP か IIOP に変更できます。

既知の問題点

背景情報

関連する情報については以下のサイトを参照してください。

RMI-IIOP ホーム

コメントの送付先: rmi-iiop@sun.com