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) の 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 言語から OMG IDL 言語へのマッピングの仕様に準拠) が付属しています。

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

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

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

-noValueMethods -- IDL の valuetype 内にメソッドとコンストラクタの IDL が生成されないようにする。
-always -- 既存のスタブ、Tie、または 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 インタフェース> を使って新しいスタブおよびスケルトンを生成すると動作しなくなってしまいました。なぜですか。

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

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

-iiop -poa フラグ

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

POA (Portable Object Adapter) の 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 を作成する必要があります。
               import javax.naming.*;
               ...
               Context ic = new InitialContext();
      
    2. RMI レジストリの lookup() および bind() を使用している部分すべてを、JNDI の lookup() および bind() を使用するように変更します。たとえば、RMI サーバーで次のコードを使用するのではなく、
               import java.rmi.*;
               ...
               Naming.rebind("MyObject", myObj);
      
      次のコードを使用します。
               import javax.naming.*;
               ...
               ic.rebind("MyObject", myObj);
      
    3. クライアントがアプレットの場合は、クライアントアプレットから this を JNDI の CosNaming プラグインに渡す必要があります。上記のコードを、次のコードで置き換えます。
              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 スタブにすることができます。

    サーバー側では、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);
    
  3. リモート実装クラスを javax.rmi.PortableRemoteObject から継承するように変更するか、実装クラスを作成したあとで PortableRemoteObject.exportObject() を呼び出して実装オブジェクトを明示的にエクスポートします。このトピックの詳細については、「IIOP スタブを ORB に接続」を参照してください。
     
  4. コード内で、リモートインタフェースの Java キャストが使用されている箇所を、すべて javax.rmi.PortableRemoteObject.narrow() を使用するように変更します。
     
  5. 分散ガベージコレクション (DGC) に依存したり、いずれの RMI DGC 機能も使用したりしないようにします。使用されなくなったオブジェクトは、PortableRemoteObject.unexportObject() を使用してアンエクスポートしてください。
     
  6. -iiop オプションを指定して rmic コマンドを実行し、RMI スタブおよび Tie を再生成します。スタブおよび Tie ファイルが、次の名前で作成されます。
         _<implementionName>_Tie.class
         _<interfaceName>_Stub.class
    
  7. サーバーを起動する前に、次のコマンドを使用して、CosNaming サーバーを (それ専用のプロセス内で) 起動します。
         orbd -ORBInitialPort port#
    
    ORBD の起動時にポート番号を指定する必要があります。
  8. クライアントおよびサーバーのアプリケーションを起動するときは、次のシステムプロパティーを指定します。
         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 サーバーを起動したときに使用したホスト名です。

  9. クライアントがアプレットの場合は、アプレットタグに次のプロパティーを指定します。
         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 スタブを ORB に正しく接続する必要があります (この作業は 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 機能を使用しないでください。
    • RMISocketFactory
    • UnicastRemoteObject
    • Unreferenced
    • 分散ガベージコレクション (DGC) インタフェース

その他の注意事項

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

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

ほかの ORB との相互運用

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

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

UnicastRemoteObject と PortableRemoteObject の使い分け

UnicastRemoteObject は、RMI プログラミングのオブジェクト実装のスーパークラスとして使用し、このシナリオでは、分散アプリケーションが同質な Java 環境で開発され、JRMP がリモート呼び出しメカニズムとして使用されます。PortableRemoteObject は、分散アプリケーションを開発するときに、IIOP が望ましい呼び出しメカニズムである場合に使用します。IIOP によって、さらに標準化された呼び出し基盤が提供され、Java 以外の分散アプリケーションコンポーネントとの相互運用が可能になります。RMI-IIOP は、OMG CORBA 標準に基づく分散アプリケーションコンポーネントとの相互運用性を提供し、Java や、C、C++、Python などのほかの言語で開発されます。

サーバーがデュアルスタック相互運用性を提供するように設計することによって、JRMP と IIOP の両方でサービスのデュアルエクスポートを同時に提供するように Java サーバーコンポーネントを構成することもできます。 UnicastRemoteObjectPortableRemoteObjectexportObject メソッドを使用してサービスオブジェクトを「登録」するようサーバーアプリケーションコンポーネントを構造化することで、それぞれ JRMP と IIOP を使用して、サービスオブジェクトを呼び出すことができます。このアプローチにより、分散アプリケーションの開発者は、継承ではなく exportObject メソッドを使用して、アプリケーションオブジェクトをエクスポートします。PortableRemoteObject.exportObject() メソッドを呼び出してそのサービスを使用可能にするサーバーオブジェクトは、通常そのオブジェクトの名前を CosNaming サービスに公開します。UnicastRemoteObject.exportObject() メソッドによってエクスポートされるサーバーオブジェクトは、通常その名前を rmiregistry に公開し、RMI /JRMP のデフォルトネームサービス (java.rmi.Naming) を提供します。

JMX フレームワークでは、このような形態のリモート呼び出しの二重性が提供されます。

既知の問題点

背景情報

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

Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.