MarshalledObject を使った持続データの作成


起動に関するチュートリアルについて、このチュートリアルから読み始めることはお勧めしません。このチュートリアルは、起動に関する 3 つの導入チュートリアルのうち、すでに 1 つ以上を読み終えていることを想定しています。

UnicastRemoteObject の場合は、実装クラスにコマンド行引数を渡すのは容易です。 これは、引数を受け取るサーバプログラムが、リモートオブジェクトの実装が有効な間ずっと実行されているからです。しかし、起動可能オブジェクトの場合、セットアップクラスは、起動記述子を RMI デーモンに登録し、スタブを rmiregistry に登録したあと、ただちに終了する可能性があります。

MarshalledObject クラスは、実装クラスのファイルに値をハードコーディングするのではなく、rmid に登録された ActivationDesc を使って持続 (初期化) データの引き渡しを可能にする柔軟な機構を提供します。


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

このチュートリアルでは、セットアップクラス examples.activation.Setup4 は 2 つの処理を行います。

この例では、persistentObjectStore.ser ファイルが存在する場合は、起動可能オブジェクトの実装は、そのファイルの持続データを使って初期化されます。ファイルが存在しない場合は、起動可能オブジェクトは、クライアントが最初にデータを送信する場合のように、自分自身を初期化します。

クライアントプログラム examples.activation.Client4 は、起動可能オブジェクトにトランザクションデータのようなベクトルを渡し、そのデータを実装オブジェクトのベクトルに追加します。クライアントが実装を呼び出してトランザクションデータを追加するたびに、起動可能な実装は MarshalledObject によって指定されたファイルに実装の状態を格納 (ベクトルを記述) します。

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

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

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

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


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

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

  1. インタフェースに対し適切なインポートを実行する
  2. java.rmi.Remote を継承する
  3. リモートに呼び出す可能性のあるメソッドをそれぞれ宣言する
ステップ 1:
インタフェースに対し適切なインポートを実行する
import java.rmi.*;
import java.util.Vector; 
ステップ 2:
java.rmi.Remote を継承する
public interface YetAnotherRemoteInterface extends Remote {
ステップ 3:
リモートに呼び出す可能性のあるメソッドをそれぞれ宣言する
public Vector calltheServer(Vector v) throws RemoteException;

実装クラスの作成

この例の実装クラスは、examples.activation.MyPersistentClass です。MarshalledObject を使用する、起動可能な実装クラスを作成するには、次の 5 つのステップを実行します。

  1. 実装クラスに対し適切なインポートを実行する
  2. java.rmi.activation.Activatable からクラスを継承する
  3. 2 つの引数をとる実装クラスのコンストラクタを宣言する
  4. MarshalledObject を使用するメソッドを記述して、オブジェクトのデータ状態を保存および復元する
  5. リモートインタフェースメソッドを実装する

ステップ 1:
実装クラスに対し適切なインポートを実行する
import java.io.*;
import java.rmi.*;
import java.rmi.activation.*;
import java.util.Vector; 

ステップ 2:
java.rmi.activation.Activatable からクラスを継承する

public class MyPersistentClass extends Activatable
    implements examples.activation.YetAnotherRemoteInterface {
ステップ 3:
2 つの引数をとる実装クラスのコンストラクタを宣言する

この例のコンストラクタでは、スーパークラスのコンストラクタへの通常の呼び出しのほかに、MarshalledObject を使用して持続データを格納するファイル名を指定します。ファイルが存在する場合は、そのファイルを使ってこのオブジェクトの変数である transactions という名の Vector が初期化されます。ファイルオブジェクトが存在しない場合は、ベクトルは手動で初期化します。ファイルの読み取りにエラーが発生した場合は、オブジェクトの構築は失敗します。

private Vector transactions;
private File holder;

public MyPersistentClass(ActivationID id, MarshalledObject data)
    throws RemoteException, ClassNotFoundException, java.io.IOException {

    // Register the object with the activation system
    // then export it on an anonymous port
    super(id, 0);

    // Extract the File object from the MarshalledObject that was
    // passed to the constructor
    //
    holder = (File)data.get();

    if (holder.exists()) {
        // Use the MarshalledObject to restore my state
	//
	this.restoreState();
    } else {
	transactions = new Vector(1,1);
	transactions.addElement("Initializing transaction vector");
    }

}

ステップ 4:
MarshalledObject を使用するメソッドを記述して、オブジェクトのデータ状態を保存および復元する

// If the MarshalledObject that was passed to the constructor was
// a file, then use it to recover the vector of transaction data
//
private void restoreState() throws IOException, ClassNotFoundException {
    File f  = holder;
    FileInputStream fis = new FileInputStream(f);
    ObjectInputStream ois = new ObjectInputStream(fis);
    transactions = (Vector)ois.readObject();
    ois.close();
}

private void saveState() {
    try {
	File f  = holder;
	FileOutputStream fos = new FileOutputStream(f);
	ObjectOutputStream oos = new ObjectOutputStream(fos);
	oos.writeObject(getTransactions());
	oos.close();
    } catch (Exception e) {
	throw new RuntimeException("Error saving vector of data");
    }
}

ステップ 5:
リモートインタフェースメソッドを実装する

クライアントから渡されたベクトルの各要素をオブジェクトのインスタンスに追加し、更新されたベクトルをファイルに保存します。

public Vector calltheServer(Vector v) throws RemoteException {

     int limit = v.size();
     for (int i = 0; i
< limit; i++) {
         transactions.addElement(v.elementAt(i));
     }

     // Save this object's data out to file
     //
     this.saveState();
     return transactions;
}

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

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

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

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

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

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

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

System.setSecurityManager(new RMISecurityManager());

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

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

セットアップアプリケーションにおける起動グループ記述子の役割は、rmid が適切な既存の Java TM 仮想マシン* (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);

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

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

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

// 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/";

// Pass the file that we want to persist to as the Marshalled
// object
MarshalledObject data =  new MarshalledObject (new File(
    "/home/rmi_tutorial/activation/persistentObjectStore.ser"));

// The location 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
    (agi, "examples.activation.ActivatableImplementation",
      location, data);

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

YetAnotherRemoteInterface yari =
    (YetAnotherRemoteInterface)Activatable.register(desc);
System.out.println("Got the stub for MyPersistentClass");

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

Naming.rebind("MyPersistentClass", yari);
System.out.println("Exported MyPersistentClass");

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

System.exit(0);

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

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

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

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

% javac -d . YetAnotherRemoteInterface.java
% javac -d . MyPersistentClass.java
% javac -d . Client4.java
% javac -d . Setup4.java

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

% rmic -d . examples.activation.MyPersistentClass

ステップ 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.Setup4

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

サンプルコードを実行するには 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 MyPersistentClass
     Exported MyPersistentClass
ステップ 6:
クライアントを実行する

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

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

この実装に対してクライアントが最初に実行されたときの出力は、次のようになります。

	Got a remote reference to the class MyPersistentClass
        Called the remote method
	Result:
	Initializing transaction vector
	Deposited money
	Withdrew money
	Transferred money from Savings
	Check cleared
	Point-of-sale charge at grocery store 
この実装に対してクライアントが 2 回目に実行されたときの出力には、さらに 5 つの「トランザクション」が含まれ、次のようになります。
        Got a remote reference to the class MyPersistentClass
	Called the remote method
	Result:
	Initializing transaction vector
	Deposited money
	Withdrew money
	Transferred money from Savings
	Check cleared
	Point-of-sale charge at grocery store
	Deposited money
	Withdrew money
	Transferred money from Savings
	Check cleared
	Point-of-sale charge at grocery store
以降、クライアント呼び出しごとに persistentObjectStore.ser ファイルのサイズが増えていきます。 * この Web サイトで使用されている用語「Java 仮想マシン」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。
Copyright © 1999 Sun Microsystems, Inc. All Rights Reserved. 
コメントの送付先: rmi-comments@java.sun.com 
Sun