チュートリアル: 入門: RMI-IIOPの使用法


このチュートリアルでは、おなじみの「Hello World」プログラムの分散システム版を、JavaのRMI (Remote Method Invocation、リモート・メソッド呼び出し)をInternet Inter-ORB Protocol (IIOP)経由で使用して作成する手順を説明します。RMI-IIOPは、Java RMIにCORBA (Common Object Request Broker Architecture)機能を追加することにより、標準規格に基づいた相互運用性と接続性を、ほかの多くのプログラミング言語およびプラットフォームに提供します。RMI-IIOPを利用すると、Webベースの分散Javaアプリケーションから、Object Management Groupによって定義された業界標準であるIIOPを使用しているリモート・ネットワーク・サービス上の操作を、透過的に呼び出すことができます。ランタイム・コンポーネントには、IIOP通信を使った分散コンピューティング用のJava ORBが含まれています。

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

分散アプリケーションを作成するためのその他のオプションは次のとおりです。


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

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

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

  1. ソース・ファイルを作成する手順
  2. 例のコンパイル手順
  3. 例の実行手順

手順のアイコンチュートリアルの各手順はこの記号によって示します。


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

このセクションで行うタスクは3つあります。

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

ソース・ファイルを作成する、またはHelloRMIIIOP.zipをダウンロードして解凍するには、次の手順に従います。

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

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

この例では、すべてのソース・ファイルを同じディレクトリ(たとえば、$HOME/mysrc/helloWorld)内に作成します。

手順のアイコンHelloInterface.javaファイルを作成します。次のコードは、リモート・インタフェースHelloInterfaceのインタフェース定義です。この定義には、ただ1つのメソッドsayHelloが含まれています。


//HelloInterface.java
import java.rmi.Remote;

public interface HelloInterface extends java.rmi.Remote {
   public void sayHello( String from ) throws java.rmi.RemoteException;
}
リモート・メソッド呼出しでは、ローカル・メソッド呼出しとは異なる方法でエラーが発生することがあります。これは、ネットワーク通信上の問題や、サーバーの問題がエラーの原因になるからです。このため、リモート・メソッドは、通信上の障害を、java.rmi.RemoteExceptionをスローすることによって報告します。

実装クラスを作成する

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

手順のアイコンHelloImpl.javaファイルを作成します。このファイルのコードは次のとおりです。ソース・コードを紹介したあと、上記の各ステップについて説明します。

//HelloImpl.java
import javax.rmi.PortableRemoteObject;

public class HelloImpl extends PortableRemoteObject implements HelloInterface {
   public HelloImpl() throws java.rmi.RemoteException {
       super();     // invoke rmi linking and remote object initialization
   }

   public void sayHello( String from ) throws java.rmi.RemoteException {
       System.out.println( "Hello from " + from + "!!" );
       System.out.flush();
   }
}

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

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(); 
  }
次の点に注意してください。

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

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

リモート・オブジェクトの実装クラスは、リモート・インタフェースで指定された各リモート・メソッドを実装するコードを含みます。次にsayHello()メソッドの実装例を示します。この例では、呼出し側に「Hello from MARS!!」という文字列が返されます。
   public void sayHello( String from ) throws java.rmi.RemoteException {
       System.out.println( "Hello from " + from + "!!");
       System.out.flush();
   }

リモート・メソッドに渡す引数、またはリモート・メソッドからの戻り値は、Javaプラットフォーム用のどのデータ型であってもかまいません。さらに、インタフェースjava.io.Serializableを実装したオブジェクトであれば、オブジェクト型であってもかまいません。java.langおよびjava.util内のコア・クラスの大部分は、Serializableインタフェースを実装しています。RMIでは:

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

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

この例では、mainメソッドはHelloServer.javaの一部として含まれており、次の処理を実行します。

手順のアイコンHelloServer.javaファイルを作成します。このファイルのソース・コードは次のとおりです。ソース・コードを紹介したあと、上記の各ステップについて説明します。

//HelloServer.java
import javax.naming.InitialContext;
import javax.naming.Context;


public class HelloServer {
    public static void main(String[] args) {
        try {
            // Step 1: Instantiate the Hello servant
            HelloImpl helloRef = new HelloImpl();

            // Step 2: Publish the reference in the Naming Service 
            // using JNDI API
            Context initialNamingContext = new InitialContext();
            initialNamingContext.rebind("HelloService", helloRef );

            System.out.println("Hello Server: Ready...");

         } catch (Exception e) {
            System.out.println("Trouble: " + e);
            e.printStackTrace();
         } 
     }
}

リモート・オブジェクトのインスタンスを作成する

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

オブジェクト参照を公開する

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

リモート・オブジェクトがサーバーに登録されたあとは、呼出し側は、オブジェクトを名前で検索し(ネーム・サービスを利用する)、リモート・オブジェクトへの参照を取得してはじめて、そのオブジェクトのメソッドをリモートから呼び出せるようになります。この例では、Object Request Broker Daemon (Solaris、LinuxまたはMac OS X用またはWindows用orbd)の一部であるネーム・サービスを使用します。

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

            // Step 2: Publish the reference in the Naming Service 
            // using JNDI API
            Context initialNamingContext = new InitialContext();
            initialNamingContext.rebind("HelloService", helloRef );

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

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

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

手順のアイコンHelloClient.javaファイルを作成します。クライアント・アプリケーションのソース・コードは、次のとおりです。

//HelloClient.java
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import javax.rmi.*;
import java.util.Vector;
import javax.naming.NamingException;
import javax.naming.InitialContext;
import javax.naming.Context;

public class HelloClient {

    public static void  main( String args[] ) {
        Context ic;
        Object objref;
        HelloInterface hi;

        try {
            ic = new InitialContext();
         
        // STEP 1: Get the Object reference from the Name Service
        // using JNDI call.
            objref = ic.lookup("HelloService");
            System.out.println("Client: Obtained a ref. to Hello server.");

        // STEP 2: Narrow the object reference to the concrete type and
        // invoke the method.
            hi = (HelloInterface) PortableRemoteObject.narrow(
                objref, HelloInterface.class);
            hi.sayHello( " MARS " );

        } catch( Exception e ) {
            System.err.println( "Exception " + e + "Caught" );
            e.printStackTrace( );
            return;
        }
    }
}

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


プログラム例のコンパイル

この例のソース・コードは、これで完成しました。ディレクトリには、次の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コンパイラを、-iiopオプションを指定して実行します。rmic -iiopコマンドは、引数に1つ以上のクラス名をとり、_HelloImpl_Tie.classおよび_HelloInterface_Stub.classという形式のクラス・ファイルを生成します。この例では、リモート実装ファイルHelloImpl.classのクラス名を渡します。

rmicのオプションの詳細は、Solaris、LinuxまたはMac OS X用rmicのマニュアル・ページまたはWindows用rmicのマニュアル・ページを参照してください。

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

    rmic -iiop HelloImpl

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

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

手順のアイコン次の方法で、すべてのソース・ファイルをコンパイルします。

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

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


プログラム例の実行

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

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

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

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

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

手順のアイコンネーム・サービスを起動するには、コマンド行からorbdを実行します。

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

    orbd -ORBInitialPort 1050&

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

    start orbd -ORBInitialPort 1050

orbdを実行するポートを指定する必要があります。Solarisオペレーティング環境では、1024より番号の小さいポート上でプロセスを開始するにはスーパー・ユーザーになる必要があるため、この例では、ポート1050が選択されています。orbdツールの詳細は、orbdのマニュアル・ページ(Solaris、LinuxまたはMac OS X用またはWindows用)を参照してください。

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

サーバーを起動する

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

手順のアイコン次のようにして、Helloサーバーを起動します。

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

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

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

Hello Server: Ready ...

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

ネーム・サービスとサーバーを起動したあとは、クライアント・アプリケーションを実行することができます。新しい端末ウィンドウから、ソース・コードのディレクトリに移り、下記の例のようにしてコマンド行からクライアント・アプリケーションを実行します。クライアントを実行するための下記のコマンドは、読みやすくするために複数行に分けてありますが、実際にコマンドを入力するときには改行を入れないでください。orbdツールを起動するときに1050以外のポートやlocalhost以外のホストを使用した場合には、下記のコマンドの該当する値を、orbdを起動するときに使用した実際の値で置き換えてください。

手順のアイコン次のようにして、クライアント・アプリケーションを起動します。

    java 
      -classpath . 
      -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
      -Djava.naming.provider.url=iiop://localhost:1050 
      HelloClient 
クライアント・アプリケーションを実行すると、次のような出力がクライアント・ウィンドウの画面に表示されます。
Client: Obtained a ref. to Hello server.

サーバー・ウィンドウに次のメッセージが表示されます。

Hello from  MARS 

ORBDおよびHelloサーバーは、明示的に停止しないかぎり継続して実行されます。Solaris、LinuxまたはMac OS Xでは、これらのプロセスを停止する場合に、端末ウィンドウからpkill orbdおよびpkill HelloServerコマンドを実行します。Windowsの場合は、プロンプト・ウィンドウ内でCtrl+Cと入力します。

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


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