java.rmi.activation.Activatable を継承しないオブジェクトの起動


このチュートリアルでは、既存のクラスを起動可能クラスにする手順を説明します。また、起動可能オブジェクトの作成UnicastRemoteObject を起動可能にする方法を理解するためにも役立ちます。

JavaTM 2 SDK をリリースする前は、UnicastRemoteObject を継承しないクラスのインスタンスには、(1) リモートオブジェクトのインスタンスを生成し、かつ (2) 常時稼動中である、サーバプログラムからアクセスできました。この機能は、static メソッド UnicastRemoteObject.exportObject を呼び出してオブジェクトをエクスポートするコンストラクタを作成することにより実現されていました。 exportObject メソッドは、java.rmi.Remote を継承するインタフェースを実装するオブジェクトを引数にとります。

現在では、java.rmi.activation.Activatable クラスおよび RMI デーモン rmid の導入により、常時ではなく「必要に応じて」作成され、実行されるリモートオブジェクトの実装情報を登録するプログラムの記述が可能になりました。RMI デーモン rmid は、Java Virtual Machine* (JVM) を提供します。 この JVM からほかの JVM インスタンスを生成させることも可能です。UnicastRemoteObject.exportObject の場合と同様に、起動可能オブジェクトを static メソッド Activatable.exportObject を使ってエクスポートすることもできます。 この場合、エクスポートされたオブジェクトはリモートインタフェースを実装する必要があります。


: 以降のチュートリアルで、「起動可能オブジェクトの実装」、「起動可能オブジェクト」、および「実装」と言った場合、すべて、リモートインタフェースを実装する起動可能クラス examples.activation.MyClass を指します。

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

このチュートリアルの実行に必要なファイルは、次のとおりです。

ここでは、クライアントコードも示しますが、実装クラスやセットアップクラスの場合のように、手順を追った説明はしません。これは、起動可能オブジェクトのクライアントコードが、起動可能でないリモートオブジェクトにアクセスするための RMI クライアントコードと同じであるためです。起動は、厳密にはサーバ側での実装によって決定されます。

このチュートリアルで使われているソースコードは、次のファイル形式から選ぶことができます。


リモートインタフェースの作成

リモートで呼び出す各メソッドを記述するインタフェースを作成します。この例のリモートインタフェースは examples.activation.AnotherRemoteInterface です。リモートインタフェースの作成には、3 つのステップがあります。

  1. インタフェースに対し適切なインポートを実行する
  2. java.rmi.Remote を継承する
  3. リモートに呼び出す可能性のあるメソッドをそれぞれ宣言する

ステップ 1:
インタフェースに対し適切なインポートを実行する

import java.rmi.*;

ステップ 2:
java.rmi.Remote を継承する

public interface AnotherRemoteInterface extends Remote {

ステップ 3:
リモートに呼び出す可能性のあるメソッドをそれぞれ宣言する

public String calltheServer(String s) throws RemoteException;


実装クラスの作成

この例の実装クラスは、 examples.activation.MyClass です。Activatable または UnicastRemoteObject を継承しないクラスを起動可能にするには、次の 3 つのステップを実行します。

  1. 実装クラスに対し適切なインポートを実行する
  2. クラス宣言を変更し、java.rmi.Remote を継承するインタフェースをクラスに実装する
  3. 2 つの引数をとる実装クラスのコンストラクタを宣言する
ステップ 1:
実装クラスに対し適切なインポートを実行する
import java.rmi.*;
import java.rmi.activation.*;

ステップ 2:
クラス宣言を変更し、java.rmi.Remote を継承するインタフェースをクラスに実装する

public class MyClass
    implements examples.activation.AnotherRemoteInterface
{
ステップ 3:
2 つの引数をとる新しいコンストラクタを実装クラスに追加する
public MyClass(ActivationID id, MarshalledObject data) 
    throws RemoteException 
{ 
    // Register the object with the activation system 
    // then export it on an anonymous port 
    //
    Activatable.exportObject(this, id, 0); 
} 

「セットアップ」クラスの作成

「セットアップ」クラスの役割は、リモートオブジェクトのインスタンスを生成しない場合でも、起動可能クラスに必要なすべての情報を作成可能にすることです。この例のセットアップクラスは、examples.activation.Setup3 です。

セットアップクラスは、起動可能クラスに関する情報を rmid に渡し、リモート参照 (実行可能クラスのスタブクラスのインスタンス) および識別子 (名前) を rmiregistry に登録します。 そのあと、セットアップクラスは終了できます。セットアップクラスは、7 つのステップで作成します。

  1. セットアップクラスで適切なインポートを実行する
  2. SecurityManager をインストールする
  3. ActivationGroup のインスタンスを生成する
  4. ActivationDesc のインスタンスを生成する
  5. リモートインタフェースのインスタンスを宣言し、rmid に起動記述子を登録する
  6. スタブを rmiregistry の名前にバインドする
  7. セットアップアプリケーションを終了する

ステップ 1:
セットアップクラスで適切なインポートを実行する

import java.rmi.*;
import java.rmi.activation.*
import java.util.Properties;

ステップ 2:
SecurityManager をインストールする

System.setSecurityManager(new RMISecurityManager());

ステップ 3:
ActivationGroup のインスタンスを生成する

: ここでは、例を単純にするために、すべての位置のすべてのユーザにグローバルなアクセス権を与える policy ファイルを使用します。このポリシーファイルは、実稼働環境では使用しないでください。java.security.policy ファイルを使ってアクセス権を適切に指定する方法については、次のドキュメントを参照してください。

セットアップアプリケーションにおける起動グループ記述子の役割は、rmid が適切な既存の JVM にアクセスしたり、起動可能オブジェクト用の新しい JVM を生成したりする際に必要になるすべての情報を提供することです。

: このコードを実際にシステム上で実行するには、ポリシーファイルの位置を、ソースコードに付属するサンプルのポリシーファイルをインストールした絶対パスに変更する必要があります。

// Because of the Java 2 security model, a security policy should 
// be specified for the ActivationGroup VM. The first argument
// to the Properties put method, inherited from Hashtable, is 
// the key and the second is the value 
// 
Properties props = new Properties(); 
props.put("java.security.policy", 
   "/home/rmi_tutorial/activation/policy"); 

ActivationGroupDesc.CommandEnvironment ace = null; 
ActivationGroupDesc exampleGroup = new ActivationGroupDesc(props, ace);

// Once the ActivationGroupDesc has been created, register it 
// with the activation system to obtain its ID
//
ActivationGroupID agi = 
   ActivationGroup.getSystem().registerGroup(exampleGroup);

// Now explicitly create the group
//
ActivationGroup.createGroup(agi, exampleGroup, 0);

ステップ 4:
ActivationDesc のインスタンスを生成する

起動記述子の役割は、rmid が実装クラスの新しいインスタンスの生成に必要とするすべての情報を提供することです。

: このコードをシステム上で実行するには、ファイルの URL の位置を、サンプルソースコードを実際にインストールしたディレクトリの位置に変更する必要があります。

// The "location" URL specifies where the class definition 
// will come from when this object is requested (activated). 
// Don't forget the trailing slash at the end of the URL 
// or your classes won't be found 
// 
String location = "file:/home/rmi_tutorial/activation/"; 

// Create the rest of the parameters that will be passed to 
// the ActivationDesc constructor 
// 
MarshalledObject data = null; 

// The second argument to the ActivationDesc constructor will be used 
// to uniquely identify this class; it's location is relative to the 
// URL-formatted String, location. 
// 
ActivationDesc desc = new ActivationDesc 
    ("examples.activation.MyClass", location, data); 

ステップ 5:
リモートインタフェースのインスタンスを宣言し、rmid に起動記述子を登録する

AnotherRemoteInterface ari =
    (AnotherRemoteInterface)Activatable.register(desc);
System.out.println("Got the stub for MyClass"); 

ステップ 6:
Activatable.register メソッドによって返されたスタブを rmiregistry の名前にバインドする

Naming.rebind("MyClass", ari);
System.out.println("Exported MyClass");

ステップ 7:
セットアップアプリケーションを終了する

System.exit(0);


コードのコンパイルおよび実行

コードのコンパイルおよび実行は、次の 6 つのステップで行います。

  1. リモートインタフェース、実装、クライアント、およびセットアップの各クラスをコンパイルする
  2. 実装クラス上で rmic を実行する
  3. rmiregistry を開始する
  4. 起動デーモン rmid を開始する
  5. セットアッププログラムを実行する
  6. クライアントを実行する

    ステップ 1:
    リモートインタフェース、実装、クライアント、およびセットアップの各クラスをコンパイルする

    % javac -d . AnotherRemoteInterface.java
    % javac -d . MyClass.java
    % javac -d . Client3.java
    % javac -d . Setup3.java
    

    ステップ 2:
    実装クラス上で rmic を実行する

    % rmic -d . examples.activation.MyClass

    ステップ 3:
    rmiregistry を開始する

    % rmiregistry &

    注: rmiregistry を開始する前に、registry を実行するシェルまたはウィンドウに CLASSPATH が設定されていないこと、あるいは設定されていても、クライアントにダウンロードするクラスへのパス (リモートオブジェクトの実装クラスのスタブを含む) が含まれていないことを確認してください。

    rmiregistry が、その開始時に CLASSPATH 内でスタブクラスを見つけると、サーバの java.rmi.server.codebase プロパティは無視されます。 その結果、クライアントは、そのリモートオブジェクトのスタブコードをダウンロードできません。

    ステップ 4:
    起動デーモン rmid を開始する

    % rmid -J-Djava.security.policy=rmid.policy &
    rmid.policy は、rmid のセキュリティポリシーファイル名です。

    注: デフォルトでは、rmid は現在、セキュリティポリシーファイルを要求します。このファイルは、起動グループ用の JVM の起動に、各 ActivationGroupDescriptor の情報を使用できるかどうかを確認するために使用されます。完全な詳細については、rmid の Solaris オペレーティング環境用マニュアルページおよび rmid の Microsoft の Windows プラットフォーム用マニュアルページを参照してください。

    ステップ 5:
    セットアッププログラムを実行する

    コードベースのプロパティを実装スタブの位置に設定して、セットアップを実行します。次の 4 つを同じコマンド行に記述する必要があります。
     

    1. java」コマンド
    2. セキュリティポリシーファイルの位置を指定するプロパティの「名前」=「値」のペア
    3. スタブコードの位置を指定するプロパティ (「-D」から最後の「/」まで空白文字は含めない)
    4. セットアッププログラムの完全指定されたパッケージ名

    空白文字は、java という語の直後に 1 つ、2 つのプロパティの間に 1 つ、および (ブラウザまたは紙上では判別しにくいですが) examples という語の直前に 1 つ必要です。

    % java  -Djava.security.policy=/home/rmi_tutorial/activation/policy  -Djava.rmi.server.codebase=file:/home/rmi_tutorial/activation/  examples.activation.Setup3

    コードベースプロパティは、URL として解釈処理されます。 このため、プロパティは http://aHost/somesource/file:/myDirectory/location/ の形式、またはオペレーティングシステムによっては file:///myDirectory/location/ (file:のあとにスラッシュが 3 つ続く) の形式でなければなりません。

    サンプルコードを実行するには file:サンプルコードを実行するには file:形式の URL の方が使いやすい場合があります。しかし、この場合、サーバにアクセスできるクライアントは、(サーバと同じマシン上で実行したり NFS などの共用ファイルシステムを使用して) サーバと同じファイルシステムにアクセスできるクライアントだけになります。HTTP サーバがまだ稼動していない場合は、米国サン・マイクロシステムズ社またはその関係会社の HTTP サーバからダウンロードすることができます。

    これらのサンプル URL の各文字列には、末尾に「/」があることに注意してください。 java.rmi.server.codebase プロパティで指定する URL では、実装がクラス定義を適切に解釈処理 (検索) するために、末尾のスラッシュが必要です。コマンド行からの java.rmi.server.codebase プロパティの設定についての詳細は、java.rmi.server.codebase プロパティを使用した動的なコードのダウンロードについてのチュートリアルを参照してください。

    プロパティ上の末尾のスラッシュを忘れたり、ソース位置にクラスファイルが見つからない (ダウンロード可能ではない) 場合、またはプロパティ名の入力を間違えた場合は、java.lang.ClassNotFoundException がスローされます。この例外は、リモートオブジェクトを rmiregistry にバインドしようとした場合、または最初のクライアントがそのオブジェクトのスタブにアクセスしようとした場合にスローされます。後者の場合は、rmiregistry が CLASSPATH 内でスタブを検索するため、さらに問題が発生する場合があります。

    サーバ側の出力は、次のようになります。

          Got the stub for MyClass 
          Exported MyClass 
    
    ステップ 6:
    クライアントを実行する

    クライアントプログラムへの引数は、実装サーバのホスト名で、この場合は vector です。

    % java -Djava.security.policy=/home/rmi_tutorial/activation/policy 
    examples.activation.Client3 vector

    クライアント側の出力は次のようになります。

          Got a remote reference to the class MyClass
          Called the remote method
          Result: Watson are you there?  I'm here!

    別の方法

    これまでの処理を別の方法で行うには、リモートインタフェースを実装する「アダプタ」クラスを作成します。 リモートインタフェースは、セットアッププログラムによって rmid およびレジストリに登録されます。 そのあと、「アダプタ」クラスによってオブジェクトのインスタンスが作成され、そのインスタンスにリモートメソッドが転送されます。この方法には、元のリモートでないクラスを変更する必要がないという利点があります。

    リモートでない元のクラスが次のようであるとします。

      package examples.activation; 
    
      public class MyNonRemoteClass { 
        
          private String result = null; 
        
          // Here's the original class, which concatenates two strings 
          // 
          public String calltheServer(String takeThis) { 
    
    	  result = takeThis + "I'm here!"; 
    	  return result; 
          } 
    
      } 
    

    同じインタフェースである examples.activation.AnotherRemoteInterface を記述して、リモートに呼び出すメソッドを記述します。

      package examples.activation; 
    
      import java.rmi.*; 
    
      public interface AnotherRemoteInterface extends Remote { 
        
          public String calltheServer(String s) throws RemoteException; 
    
      } 
    

    ただし、使用できるソースコードが MyNonRemoteClass.java にない場合もあるため、MyNonRemoteClass.java を編集するのではなく、新しいクラス examples.activation.MyNonRemoteClassAdapter を作成します。 この新しいクラスは、(イベントアダプタと同様に) 指定したインタフェースを実装し、適切な動作を実行します。前述の方法と MyNonRemoteClassAdapter を使用する方法との相違点は、MyNonRemoteClassAdapter がそのコンストラクタで MyNonRemoteClass のインスタンスを生成し、MyNonRemoteClass.calltheServer メソッドを呼び出すことです。

      package examples.activation; 
    
      import java.rmi.*; 
      import java.rmi.activation.*; 
    
      public class MyNonRemoteClassAdapter implements 
          examples.activation.AnotherRemoteInterface 
      { 
          private String result = null; 
          private MyNonRemoteClass mnrc; 
    
          // The constructor for activation and export; this  
          // constructor is called by the method 
          // ActivationInstantiator.newInstance during 
          // activation, to construct the object. 
          // 
          public MyNonRemoteClassAdapter(ActivationID id, 
    	  MarshalledObject data) throws RemoteException 
          { 
    	  // Register the object with the activation system 
    	  // then export it on an anonymous port 
    	  // 
    	  Activatable.exportObject(this, id, 0); 
    
    	  // Create an instance of the class MyNonRemoteClass 
    	  // 
    	  mnrc = new MyNonRemoteClass(); 
          } 
    
          // Define the method declared in AnotherRemoteInterface 
          // to accept a String, modify it, and return it to  
          // the client
          //
          public String calltheServer(String takeThis) 
    	  throws RemoteException 
          { 
    	  // Rather than modify the String here, forward 
    	  // it on to the non-remote object implementation 
    	  // 
    	  result = mnrc.calltheServer(takeThis); 
    	  return result; 
          } 
    
      } 
    

    rmic を実行するクラスおよびセットアッププログラムによって参照されるクラスは、アダプタクラスです。セットアッププログラム examples.activation.Setup3alt は、次のようになります。

      package examples.activation; 
    
      import java.rmi.*; 
      import java.rmi.activation.*; 
      import java.util.Properties; 
    
      public class Setup3alt { 
    
          // This class registers information about the MyClass 
          // class with rmid and the rmiregistry 
          // 
          public static void main(String[] args) throws Exception { 
        
    	  System.setSecurityManager(new RMISecurityManager()); 
        
    	  AnotherRemoteInterface ari; 
    
    	  // Don't forget the trailing slash at the end of the URL 
    	  // or your classes won't be found 
    	  // 
    	  String location = "file:/home/rmi_tutorial/activation/"; 
    
    	  // Create the rest of the parameters that will be passed to 
    	  // the ActivationDesc constructor 
    	  // 
    	  MarshalledObject data = null; 
    
    	  // The second argument to the ActivationDesc constructor  
    	  // will be used to uniquely identify this class; it's  
    	  // location is relative to theURL-formatted String, location. 
    	  // 
    	  ActivationDesc desc = new ActivationDesc 
    	      ("examples.activation.MyNonRemoteClassAdapter", 
    	      location, data); 
    	 
    	  ari = (AnotherRemoteInterface)Activatable.register(desc); 
    	  System.out.println("Got the stub for MyNonRemoteClassAdapter"); 
    
    	  // Bind the stub to a name in the registry running on 1099 
    	  // 
    	  Naming.rebind("MyNonRemoteClassAdapter", ari); 
    	  System.out.println("Exported MyNonRemoteClassAdapter"); 
    
    	  System.exit(0); 
          } 
    
      }
    
    
    * この Web サイトで使用されている用語「Java Virtual Machine」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。
    Copyright © 1999 Sun Microsystems, Inc. All Rights Reserved.
    コメントの送付先: rmi-comments@java.sun.com 
    Sun