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 SEの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インタフェースです。

質問: Java SEで動作する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フラグ

-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言語とIDLのマッピング」(OMG)を参照してください。

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

-noValueMethodsフラグ

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

rmicの詳細は、RMICツールのページ(Solaris、LinuxまたはMac OS X版/Windows版)を参照してください。

idljコンパイラ

RMI-IIOPソフトウェアには、IDL-to-Javaコンパイラが組み込まれています。このコンパイラでは、RMI-IIOPとの相互運用に必要なCORBA Objects By Value機能がサポートされます。このコンパイラはJavaで記述されているため、任意のプラットフォームで実行できます。このコンパイラの使用法の詳細は、idljのマニュアル・ページ(Solaris、LinuxまたはMac OS X用またはWindows用)を参照してください。

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, 2020, Oracle and/or its affiliates. All rights reserved.