入門: RMI-IIOP の使用法
POA ベースのサーバー側モデルの使用例


このチュートリアルでは、おなじみの「Hello World」プログラムの分散システム版を、JavaTM の RMI (Remote Method Invocation、リモートメソッド呼び出し) を Internet Inter-ORB Protocol (IIOP) 経由で使用して作成する手順を説明します。RMI-IIOP は、CORBA (Common Object Request Broker Architecture) 機能を、ほかの多くのプログラミング言語およびプラットフォームに対する Java 接続性に追加します。RMI-IIOP により、Web 対応の分散 Java リモート Management Group が可能になります。ランタイムコンポーネントには、IIOP 通信を使った分散コンピューティング用の Java ORB が含まれています。

RMI-IIOP は、IIOP を背後のトランスポートとして使用して、RMI インタフェースに対するプログラムを作成したい Java プログラマ向けです。RMI-IIOP はさまざまな言語で実装される CORBA オブジェクトとの相互運用性を提供しますが、リモートインタフェースをあらかじめ Java RMI インタフェースとして定義しておく必要があります。EJB のリモートオブジェクトモデルは RMI ベースなので、Enterprise JavaBeans (EJB) を使うプログラマには特に有用です。

分散アプリケーションを作成するためのもう 1 つの選択肢として、Java TM IDL があります。Java IDL は、CORBA インタフェース定義言語 (IDL) で定義されたインタフェースに基づいて Java プログラミング言語でプログラムを記述したい CORBA プログラマ向けです。これは「通常どおりの」CORBA プログラミングで、C++ や COBOL のような他の言語とまったく同じ方法で Java をサポートしています。


チュートリアル:「Hello World」アプリケーション

ここで例として紹介する分散型の「Hello World」プログラムでは、クライアントアプリケーションから IIOP 経由でサーバー (そのクライアントアプリケーションのダウンロード元のホスト上で稼動している) に対してリモートメソッド呼び出し行います。このクライアントアプリケーションを実行すると、「Hello World!」が表示されます。

このチュートリアルの構成は、次のとおりです。

  1. ソースファイルを作成する手順
  2. クラスファイルをコンパイルする手順
  3. ネームサービス、サーバー、およびクライアントを起動する手順

ソースファイルの作成またはダウンロード

ここで行う作業は 4 つあります。

  1. リモートクラスの関数を Java プログラミング言語で作成されたインタフェースとして定義する
  2. 実装クラスを作成する
  3. サーバークラスを作成する
  4. リモートサービスを利用するクライアントプログラムを作成する
このチュートリアルで作成するソースファイル (ここからダウンロードできる) は次のとおりです。

リモートクラスの関数を Java プログラミング言語で作成されたインタフェースとして定義する

Java プログラミング言語では、リモートオブジェクトは、Remote インタフェースを実装するクラスのインスタンスです。作成するリモートインタフェースでは、ほかのマシンから呼び出したい各メソッドを宣言します。リモートインタフェースには、次の特性があります。

この例では、すべてのソースファイルを同じディレクトリ (たとえば、$HOME/mysrc/RMIHelloPOA) 内に作成します。リモートインタフェース HelloInterface のインタフェース定義は、次のとおりです。このインタフェースには、ただ 1 つのメソッド sayHello が含まれています。

リモートメソッド呼び出しでは、ローカルメソッド呼び出しとは異なる方法でエラーが発生することがあります。これは、ネットワーク通信上の問題や、サーバーの問題がエラーの原因になるからです。このため、リモートメソッドは、通信上の障害を、java.rmi.RemoteException をスローすることによって報告します。分散システム上での障害および復元の詳細については、「A Note on Distributed Computing」を参照してください。

実装クラスを作成する

リモートオブジェクトの実装クラス HelloImpl.java は、少なくとも次の条件を満たしていなければなりません。

HelloImpl.java のソースは次のとおりです。そのあと、上記の各ステップについて説明します。

リモートインタフェースを実装する

Java プログラミング言語では、あるインタフェースを実装することをクラスが宣言すると、そのクラスとコンパイラの間で契約が結ばれます。この契約によって、そのクラスは、そのインタフェース内で宣言された各メソッドシグニチャーに対して、メソッドの本体 (つまり定義) を提供することを約束します。インタフェースのメソッドは、暗黙のうちに public および abstract として宣言されているため、実装クラスでその契約が果たされない場合、そのクラスは定義に基づき abstract になります。そのクラスが abstract として宣言されていない場合は、コンパイラによってその事実が指摘されます。

この例では、実装クラスは HelloImpl です。このクラスは、どのリモートインタフェースを実装するのかを宣言します。HelloImpl クラスの宣言は、次のとおりです。

  public class HelloImpl extends PortableRemoteObject
    implements HelloInterface{
便宜上、実装クラスはリモートクラスを継承できます。この例では、リモートクラスは javax.rmi.PortableRemoteObject です。PortableRemoteObject を継承していることにより、HelloImpl クラスを、通信に IIOP ベースのトランスポートを使うリモートオブジェクトを作成するために利用できます。

リモートオブジェクトのコンストラクタを定義する

リモートクラスのコンストラクタは、リモート以外のクラスのコンストラクタと同じ機能を提供します。つまり、そのクラスの新しく作成されたインスタンスごとに変数を初期化して、コンストラクタを呼び出したプログラムにそのクラスのインスタンスを返します。

さらに、リモートオブジェクトのインスタンスは「エクスポート」される必要があります。リモートオブジェクトをエクスポートすると、そのオブジェクトは、匿名ポート上でリモートオブジェクトへの着呼を監視することによって、着信したリモートメソッド要求を受け入れることができるようになります。javax.rmi.PortableRemoteObject を継承すると、そのクラスは作成時に自動的にエクスポートされます。

オブジェクトのエクスポートは、java.rmi.RemoteException をスローする可能性があるため、コンストラクタがほかに何も行わない場合でも、RemoteException をスローするコンストラクタを定義する必要があります。コンストラクタを定義しなかった場合は、javac は、次のエラーメッセージを生成します。

	HelloImpl.java:3: unreported exception java.rmi.RemoteException; must be
	caught or declared to be thrown. 
        
        public class HelloImpl extends PortableRemoteObject implements HelloInterface{
               ^ 
        1 error
復習: リモートオブジェクトの実装クラスが行う必要のある事柄は、次のとおりです。 HelloImpl クラスのコンストラクタは、次のとおりです。
  public HelloImpl() throws java.rmi.RemoteException { 
    super(); 
  }
次の点に注意してください。

java.rmi.RemoteException は、実行時例外ではなく、確認済み例外です。

スーパークラスの引数なしのコンストラクタ super() への呼び出しは、省略したとしてもデフォルトで発生しますが、この例では、クラスの前にスーパークラスが構築されることを明確にするために、この呼び出しを省略せずに記述しました。

各リモートメソッドに実装を提供する

リモートオブジェクトの実装クラスは、リモートインタフェースで指定された各リモートメソッドを実装するコードを含みます。たとえば、sayHello メソッドの実装例は、次のようになります。この例では、呼び出し側に「It works! Hello World!」という文字列が返されます。
  public void sayHello() throws java.rmi.RemoteException {
    System.out.println( "It works!  Hello World!!" );
  }
リモートメソッドに渡す引数、またはリモートメソッドからの戻り値は、Java プラットフォーム用のどのデータ型であっても構いません。さらに、インタフェース java.io.Serializable を実装したオブジェクトであれば、オブジェクト型であっても構いません。java.lang および java.util 内のコア Java クラスの大部分は、Serializable インタフェースを実装しています。RMI では、次のようになります。

サーバークラスを作成する

サーバークラスは、リモートオブジェクト実装のインスタンスを生成し、そのインスタンスをネームサービスの名前にバインドする main メソッドを持ちます。この main メソッドを含むクラスは、実装クラスそのものである場合も、まったく別のクラスである場合もあります。

この例では、mainHelloServer.java の一部として含まれており、次の処理を実行します。

HelloServer.java のソースは次のとおりです。そのあと、上記の各ステップについて説明します。

適切なポリシーを持つ Portable Object Adapter (POA) を作成する

サーバーの main メソッドでは、まず、適切なポリシーを持つ Portable Object Adapter (POA) を作成する必要があります。例を示します。
      Policy[] tpolicy = new Policy[3];
      tpolicy[0] = rootPOA.create_lifespan_policy(
        LifespanPolicyValue.TRANSIENT );
      tpolicy[1] = rootPOA.create_request_processing_policy(
        RequestProcessingPolicyValue.USE_ACTIVE_OBJECT_MAP_ONLY );
      tpolicy[2] = rootPOA.create_servant_retention_policy(
        ServantRetentionPolicyValue.RETAIN);
      POA tPOA = rootPOA.create_POA("MyTransientPOA", null, tpolicy);

Portable Object Adaptor (POA) は、複数の ORB 実装で使用できるオブジェクトアダプタを提供するために設計されていて、異なるベンダーの実装に対応する場合も最低限の書き直しで済むようになっています。POA のサポートは、J2SE version 1.4 で導入されました。

POA は、少なくともクライアントの立場からは持続オブジェクトが可能になるようにしています。つまり、サーバーが物理的に何度再起動されても、または多くの異なるオブジェクト実装によって実装が提供されても、そのクライアントが関係する範囲では、これらのオブジェクトは常に稼動しており、オブジェクトに格納されたデータ値が維持されています。

POA を利用すると、オブジェクトの実装者は、ずっと多くの制御が可能になります。以前は、オブジェクトを実装することは、メソッドの要求に応答して実行されるコードだけの責任でした。今では、それに加えて、オブジェクトの実装者が、オブジェクトの識別、状態、記憶領域、およびライフサイクルをもっと制御することができます。

この例では、次のようなポリシー値を設定しています。

POA ポリシーの詳細は、CORBA/IIOP 2.3.1 仕様 (http://www.omg.org/cgi-bin/doc?formal/99-10-07) の第 11 章「Portable Object Adapter」を参照してください。

POA マネージャーを起動する

POA オブジェクトには、POAManager オブジェクトが関連付けられています。POA マネージャーは、1 つ以上の POA オブジェクトに関連付けられることがあります。POA マネージャーは、関連付けられている POA の処理状態をカプセル化します。このステップでは、POA マネージャーを起動します。このステップを実行しないと、POA マネージャーはデフォルトでは HOLD 状態になっているため、Servant に対するすべての呼び出しがハングアップしてしまいます。

      tPOA.the_POAManager().activate();

リモートオブジェクトのインスタンスを作成し、Tie を起動する

サーバーの main メソッドでは、リモートオブジェクト実装のインスタンス (つまり「サーバント」) を作成する必要があります。例を示します。
    HelloImpl helloImpl = new HelloImpl();
コンストラクタはリモートオブジェクトをエクスポートします。これは、リモートオブジェクトが作成された時点で、そのリモートオブジェクトは着呼を受け入れる準備ができていることを意味します。

RMI-IIOP テクノロジを使用している実装は、その実装をインタフェースに関連付けるために委譲 (「Tie モデル」と呼ばれる) を使用します。上記のように、実装のインスタンスを作成したときは、そのインスタンスを CORBA インタフェースに関連付けるために Tie オブジェクトを作成する必要もあります。次のコード行は、Tie を起動するためのものです (ただし、POA ポリシーが USE_ACTIVE_OBJECT_MAP_ONLY の場合のみ)。

      _HelloImpl_Tie tie = (_HelloImpl_Tie)Util.getTie( helloImpl );
      String helloId = "hello";
      byte[] id = helloId.getBytes();
      tPOA.activate_object_with_id( id, tie );

Tie オブジェクトを起動するのに使用したのと同じオブジェクト ID を使ってオブジェクト参照を公開する

呼び出し側 (クライアント、ピア、またはクライアントアプリケーション) がリモートオブジェクトのメソッドを呼び出すには、呼び出し側はまずリモートオブジェクトへの参照を取得する必要があります。

リモートオブジェクトがサーバーに登録されたあとは、呼び出し側は、オブジェクトを名前で検索し (ネームサービスを利用する)、リモートオブジェクトへの参照を取得してはじめて、そのオブジェクトのメソッドをリモートから呼び出せるようになります。この例では、Object Request Broker Daemon (orbd) を使用しています。orbd は、ブートストラップサービス、一時ネームサービス、持続ネームサービス、およびサーバーマネージャーが入っているデーモンプロセスです。

たとえば、次のコードは、「HelloService」という名前をリモートオブジェクトへの参照にバインドします。

      Context initialNamingContext = new InitialContext();
      initialNamingContext.rebind("HelloService", 
        tPOA.create_reference_with_id(id, 
          tie._all_interfaces(tPOA,id)[0]) );
      System.out.println("Hello Server: Ready...");

rebind メソッド呼び出しの引数については、次の点に注意してください。

クライアントからの要求を受け付ける準備をする

次のコード行がメインスレッドから呼び出されると、ORB は、そのメインスレッドを使用して作業を実行できるようになります。

      orb.run();

リモートサービスを利用するクライアントプログラムを作成する

この例のクライアントアプリケーションは、リモートから sayHello メソッドを呼び出して、クライアントアプリケーションが実行されたときに「Hello World!」という文字列を表示します。クライアントアプリケーションのコードは、次のとおりです。

まず、クライアントアプリケーションは、リモートオブジェクト実装 (「HelloService」として公開されている) への参照を、Java Naming and Directory Interface [TM] (JNDI) 呼び出しを使用してネームサービスから取得します。Naming.rebind メソッドと同じく、Naming.lookup メソッドも、検索するオブジェクトの名前を表す java.lang.String 値を引数として取ります。検索したいオブジェクトの名前を Naming.lookup() に提供すると、その名前にバインドされたオブジェクトが返されます。


クラスファイルのコンパイル

この例のソースコードは、これで完成しました。ディレクトリには、次の 4 つのファイルが入っているはずです。 ここでは、リモートオブジェクト実装のファイル HelloImpl.java をコンパイルして、rmic を実行するのに必要な .class ファイルを作成します。次に、rmic コンパイラを実行して、スタブとスケルトンを作成します。スタブとは、リモートオブジェクトのクライアント側のプロキシのことで、RMI-IIOP 呼び出しをサーバー側のディスパッチャーに転送します。続いて、ディスパッチャーは、その呼び出しを実際のリモートオブジェクト実装に転送します。最後に、残りの .java ソースファイルをコンパイルして、.class ファイルを作成します。

このセクションで実行する作業は次のとおりです。

  1. リモートオブジェクト実装をコンパイルする
  2. rmic を使ってスタブおよびスケルトンを生成する
  3. ソースファイルをコンパイルする

リモートオブジェクト実装をコンパイルする

スタブファイルとスケルトンファイルを作成するには、リモートオブジェクト実装の入ったコンパイル済みクラスファイルの完全指定のパッケージ名について、rmic を実行する必要があります。この例では、リモートオブジェクト実装の入ったファイルは HelloImpl.java です。スタブとスケルトンを生成するためには、まず、次のようにして HelloImpl.java をコンパイルする必要があります。

    javac -d . -classpath . HelloImpl.java

-d .」オプションは、生成されたファイルを、コンパイラを実行しているのと同じディレクトリに置くことを示します。「-classpath .」オプションは、HelloImpl.java が依存しているファイルが、このディレクトリ内にあることを示します。

rmic を使ってスタブおよびスケルトンを生成する

CORBA 対応のスタブおよびスケルトンファイルを作成するには、rmic を、-poa -iiop オブションを指定して実行します。rmic -poa -iiop コマンドは、引数に 1 つ以上のクラス名をとり、_MyImpl_Tie.class および _MyInterface_Stub.class という形式のクラスファイルを生成します。この例では、リモート実装ファイル HelloImpl.class のクラス名を渡します。

rmic のオプションの詳細は、Solaris オペレーティング環境用 rmic  のマニュアルページまたは Microsoft Windows 用 rmic のマニュアルページを参照してください。

HelloImpl リモートオブジェクト実装のスタブおよびスケルトンを作成するには、次のように rmic を実行します。

    rmic -poa -iiop HelloImpl

上記のコマンドによって、次のファイルが作成されます。

ソースファイルをコンパイルする

ソースファイルをコンパイルするには、次の javac コマンドを実行します。

    javac -d . -classpath . HelloInterface.java HelloServer.java HelloClient.java

このコマンドにより、HelloInterface.classHelloServer.class、および HelloClient.class の各クラスファイルが作成されます。これらのファイルはそれぞれ、リモートインタフェース、サーバー、そしてクライアントアプリケーションです。javac のオプションの詳細は、Solaris 用 javac のマニュアルページまたは Microsoft Windows 用 javac のマニュアルページを参照してください。


ネームサービス、サーバー、およびクライアントアプリケーションの起動

このセクションで実行する作業は次のとおりです。
  1. ネームサービスを起動する
  2. サーバーを起動する
  3. クライアントアプリケーションを実行する

ネームサービスを起動する

この例では、Object Request Broker Daemon (orbd) を使用します。これには、一時ネームサービスと持続ネームサービスの両方が組み込まれており、J2SE 1.4 以降をダウンロードすれば入手できます。

呼び出し側 (クライアント、ピア、またはクライアントアプリケーション) がリモートオブジェクトのメソッドを呼び出すには、呼び出し側はまずリモートオブジェクトへの参照を取得する必要があります。

リモートオブジェクトがサーバーに登録されると、呼び出し側は、そのオブジェクトを名前によって検索して、リモートオブジェクトへの参照を取得できます。そうすれば、そのオブジェクトのメソッドをリモートから呼び出せます。

ネームサービスを起動するには、コマンド行から orbd を実行します。このコマンドからは何の出力もありません。通常、バックグラウンドで実行されます。orbd ツールの詳細は、orbd のマニュアルページを参照してください。

この例の場合、Solaris オペレーティングシステムでは次のコマンドを実行します。

    orbd -ORBInitialPort 1060&

Microsoft Windows オペレーティングシステムでは、次のコマンドを実行します。

    start orbd -ORBInitialPort 1060

このコマンドでは、orbd を実行するポートを指定しなければなりません。この例でポートとして 1060 が選ばれているのは、Solaris オペレーティングシステムでは、プロセスを 1024 より下のポートで開始するユーザーはルートになる必要があるからです。

リモートインタフェースを変更したり、変更または追加されたリモートインタフェースをリモートオブジェクトの実装で使用する場合は、必ずサーバーをいったん停止してから再起動する必要があります。そうしないと、ネームサービスでバインドされるオブジェクト参照の型が、修正されたクラスと一致しなくなります。

サーバーを起動する

端末ウィンドウをもう 1 つ開き、この例のソースファイルが入っているディレクトリに移ります。クライアントを実行するための下記のコマンドは、読みやすくするために複数行に分けてありますが、実際にコマンドを入力するときには改行を入れないでください。このコマンドは、HelloServer サーバーを起動する方法を示しています。もちろん、orbd ツールを起動するときに 1060 以外のポートや localhost 以外のホストを使用した場合には、下記のコマンドの該当する値を、orbd を起動するときに使用した実際の値で置き換えてください。

    java 
      -classpath . 
      -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
      -Djava.naming.provider.url=iiop://localhost:1060 
      HelloServer 

java のオプションについての詳細は、Solaris 用 java のマニュアルページまたは Microsoft Windows 用 java マニュアルページを参照してください。

出力は、次のようになります。

Hello Server: Ready ...

クライアントアプリケーションを実行する

ネームサービスとサーバーを起動したあとは、クライアントアプリケーションを実行することができます。新しい端末ウィンドウから、ソースコードのディレクトリに移り、下記の例のようにしてコマンド行からクライアントアプリケーションを実行します。クライアントを実行するための下記のコマンドは、読みやすくするために複数行に分けてありますが、実際にコマンドを入力するときには改行を入れないでください。もちろん、orbd ツールを起動するときに 1060 以外のポートや localhost 以外のホストを使用した場合には、下記のコマンドの該当する値を、orbd を起動するときに使用した実際の値で置き換えてください。
    java 
      -classpath . 
      -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
      -Djava.naming.provider.url=iiop://localhost:1060 
      HelloClient 
クライアントアプリケーションを実行すると、次のような出力が端末ウィンドウまたはコマンドプロンプトウィンドウに表示されます。
Client: Obtained a ref. to Hello server.

サーバーウィンドウが次のメッセージを返します。

It works! Hello World!!

これでチュートリアルを終わります。さらに複雑なアプリケーションの作成に進む場合は、次に挙げる情報が役に立ちます。


Copyright © 2001-2004 Sun Microsystems, Inc.All Rights Reserved.  Sun