アクティベーションの使用: 持続性

このチュートリアルでは、実装時に持続状態を使用する起動可能なリモート・オブジェクトを実装する方法について説明します。このチュートリアルでは、Setupプログラム(「アクティベーションの使用: Setupプログラム」チュートリアルを参照)を使用します。このプログラムは、起動可能なリモート・オブジェクトに関する情報をJava Remote Method Invocation (Java RMI)起動システム・デーモン(rmid)に登録し、rmiregistryでそのリモート・オブジェクトのスタブをバインドして、クライアントが検索できるようにします。この前に、そのチュートリアルを読むことをお薦めします。

このチュートリアルでは、次の手順を実行します。

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


起動可能なリモート・オブジェクトの実装

起動可能なリモート・オブジェクトを実装するには、いくつかの基本的な方法があります。このチュートリアルでは、実装時に持続状態を使用する起動可能なリモート・オブジェクトを実装する方法について説明します。

リモート・オブジェクトは、クライアントが起動可能なリモート・オブジェクトのスタブでリモート・メソッドを呼び出すときに起動されます。起動可能なリモート・オブジェクトのスタブには、リモート・オブジェクトの起動識別子と、そのリモート・オブジェクトのJava RMI起動システム・デーモン(rmid)にコンタクトする方法に関する情報が含まれます。スタブは、リモート・オブジェクトの最新のアドレス(つまり、ホスト/ポート)に接続できない場合、リモート・オブジェクトのアクティベータ(rmid)にコンタクトしてそのオブジェクトを起動します。rmidは、起動要求を受け取ると、リモート・オブジェクトの起動グループがまだ実行されていない場合は、そのグループ(またはコンテナ)の仮想マシン(VM)を起動します。次に、rmidは、グループにそのリモート・オブジェクトのインスタンスを作成するように要求します。グループは、リモート・オブジェクトを構築すると、そのリモート・オブジェクトのスタブをrmidに返します。次に、rmidは実際のスタブを起動スタブに返して、起動スタブが将来そのリモート・オブジェクトにコンタクトする方法についての情報を更新できるようにします。

このアクティベーションを行う前に、アプリケーションでは、使用する必要のある起動可能なリモート・オブジェクトに関する情報を登録する必要があります。次の別個のチュートリアルでは、リモート・オブジェクトの起動に必要な情報と、その情報をrmidに登録する方法について説明します。

この例では、リモート・インタフェースであるCounterを単一のincrement操作で定義します。CounterImpl実装クラスは、increment操作の結果をファイルに持続的に保存します。そのため、increment操作の結果は、オブジェクトの起動/終了のサイクルを越えて存在し続けます。また、マシンのリブート後やクラッシュ後でも存在します。この処理をすべて行うには、CounterImplオブジェクトが起動している場合、このオブジェクトにはカウンタ値を読み取って保存するのに使用するファイル名が必要です。ユーザーは、このファイル名を「初期化データ」としてMarshalledObject内に含む起動記述子を登録できます。オブジェクトの起動グループは、グループが起動時にオブジェクトを構築するときに、このあらかじめ登録された整列化データ(ファイル名)をオブジェクトのコンストラクタに渡します。

リモート・インタフェースexamples.activation.Counterは次のように定義されます。

package examples.activation;

import java.rmi.Remote;
import java.io.IOException;

public interface Counter extends Remote {

    int increment() throws IOException;
}

起動可能なリモート・オブジェクトのexamples.activation.CounterImpl実装クラスは次のとおりです。

package examples.activation; 

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.rmi.MarshalledObject;
import java.rmi.activation.Activatable;
import java.rmi.activation.ActivationID;

public class CounterImpl implements Counter {
    private RandomAccessFile raf;
    private int count;
    private final Object countLock = new Object();
    
    public CounterImpl(ActivationID id, MarshalledObject data) 
        throws Exception
    {
        if (data != null) {
            String filename = (String) data.get();
            synchronized (countLock) {
                count = openFile(filename);
            }
            System.err.println("count upon activation = " + count);
        }

        Activatable.exportObject(this, id, 0);
    }

    private int openFile(String filename) throws IOException {

        if (filename != null && !filename.equals("")) {
            File file = new File(filename);
            boolean fileExists = file.exists();
            raf = new RandomAccessFile(file, "rws");
            return (fileExists) ? raf.readInt() : writeCount(0);
            
        } else {
            throw new IOException("invalid filename");
        }
    }

    private int writeCount(int value) throws IOException {
        raf.setLength(0);
        raf.writeInt(value);
        return value;
    }
            
    public int increment() throws IOException {
        synchronized (countLock) {
            return writeCount(++count);
        }
    }
}

CounterImplクラスは、Counterリモート・インタフェースを実装しますが、どのクラスも拡張しません。

このクラスは、起動グループが起動プロセス中にインスタンスを構築するために呼び出す特別な「起動」コンストラクタを宣言します。この特別なコンストラクタは、次の2つのパラメータを取ります。

コンストラクタは、2つ目のパラメータとして渡されたMarshalledObjectに含まれるファイル名を取得します。次に、コンストラクタは、ローカル・メソッドopenFileを呼び出して、そのファイルを開き、現在のカウンタ値を返します。ファイルが存在する場合、openFileメソッドは最後にファイルに保存された値を読み取ります。そうでない場合は、新しいファイルを作成して、カウントを0に初期化します。次に、コンストラクタは、staticメソッドActivatable.exportObjectを呼び出し、実装自体(this)、起動識別子およびポート番号0を渡して、オブジェクトを匿名のTCPポートでエクスポートする必要があることを示します。この実装では、パラメータとしてコンストラクタに渡された起動識別子は使いませんが、別の実装では、将来の使用に備えて、たとえばオブジェクトを終了するために、起動識別子を保存することもできます。

最後に、クラスは、リモート・インタフェースの単一メソッドであるincrementを実装して、カウントを増分し、そのカウントをファイルに保存して、増分したカウントを返します。writeCountメソッドの実装には弱点があることに注意してください。setLengthwriteIntの呼出しの間にマシンがクラッシュすると、カウンタの値が失われます。わずかな時間の間に発生したクラッシュに対してこの実装をもっと堅牢にすることが、ユーザーに残された課題です。

クライアントの実装

CounterClientプログラムは、オプションの第1引数として提供されたホストのレジストリ内のリモート・オブジェクトのスタブ(リモート・インタフェースCounterを実装するスタブ)を検索して、そのスタブのincrementメソッドを呼び出し、結果を表示します。レジストリから獲得したスタブでこのクライアントがリモート・メソッドを呼び出すと、リモート・オブジェクトが起動されていない場合にはリモート・オブジェクトが起動されます。

プログラムのソースは次のとおりです。

package examples.activation; 

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class CounterClient {

    public static void main(String args[])  throws Exception {

        String hostname = "localhost";
        if (args.length < 1) {
            System.err.println(
                "usage: java [options] examples.activation.CounterClient [host]");
            System.exit(1);
        } else {
            hostname = args[0];
        }

        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }

        String name = System.getProperty("examples.activation.name");
        Registry registry = LocateRegistry.getRegistry(hostname);
        Counter stub = (Counter) registry.lookup(name);
        System.err.println("Obtained stub from the registry.");

        System.err.println("Invoking increment method...");
        int count = stub.increment();
        System.err.println("Returned from increment remote call.");
        System.err.println("count = " + count);
    }
}

このプログラムを次のように実行します。

java -cp clientDir                               \
     -Djava.security.policy=client.policy        \
     -Dexamples.activation.client.codebase=clientCodebase   \
     -Dexamples.activation.name=name             \
     examples.activation.CounterClient [host]

次にそれぞれの意味を示します。

注: このプログラムを実行する前に、rmidがそのデフォルト・ポートで稼働し、rmiregistryがそのデフォルト・ポート(両方ともリモート・ホスト上)で稼働している必要があります。

アクティベーションの例として、適切なアクセス権を付与するclient.policyファイルの例を次に示します。

grant codeBase "${examples.activation.client.codebase}" {

    // permissions to read system properties required by the client
    permission java.util.PropertyPermission "examples.activation.name","read";

    // permission to connect to the registry, activation system, and remote host
    permission java.net.SocketPermission "*:1024-","connect";
};

アクセス権が付与されるコード・ベースは、クライアントのクラスの場所を指定するファイルURLです。このファイルURLは、examples.activation.client.codebaseシステム・プロパティの値で、クライアント・プログラムの実行時に定義されます。クライアントには2つのアクセス権が必要です。

ソース・ファイルのコンパイル

この例のソース・ファイルは、次のようにしてコンパイルできます。

javac -d implDir Counter.java CounterImpl.java 
javac -d clientDir Counter.java CounterClient.java

implDirは実装のクラス・ファイルを配置する生成先ディレクトリで、clientDirはクライアントのクラス・ファイルを配置する生成先ディレクトリです。

Setupプログラムの実行

実装段階が完了したら、起動可能なオブジェクトに関する情報を登録して、クライアントが使えるようにする必要があります。Setupプログラムは、「アクティベーションの使用: Setupプログラム」チュートリアルで説明されているように、起動可能なオブジェクトの起動記述子をrmidに登録し、rmiregistryでリモート・オブジェクトのスタブをバインドして、クライアントが検索できるようにします。

この例のSetupプログラムを実行するには、Setupプログラムのチュートリアルのセクションrmidrmiregistry、およびSetupプログラムの起動」を参照してください。rmidrmiregistry、およびSetupプログラム自体の起動方法について説明しています。

Setupチュートリアルの手順に従ってrmidrmiregistryを実行したら、Setupプログラムを実行して、examples.activation.CounterImplクラスを実装する起動可能なオブジェクトの起動記述子を登録する必要があります。次のコマンド行では、使用する各コード・ベースの適切なファイルURLを指定してSetupプログラムを実行します。

java -cp setupDir:implDir                       \
     -Djava.security.policy=setup.policy                      \
     -Djava.rmi.server.codebase=file:/implDir/                \
     -Dexamples.activation.setup.codebase=file:/setupDir/     \
     -Dexamples.activation.impl.codebase=file:/impDir/        \
     -Dexamples.activation.name=examples.activation.Counter                 \
     -Dexamples.activation.policy=group.policy                \
     -Dexamples.activation.file=file                          \
         examples.activation.Setup examples.activation.CounterImpl

次にそれぞれの意味を示します。

前述の各ファイルURLには、必須の末尾のスラッシュがあることに注意してください。このチュートリアルに適したグループとセットアップ・ポリシー・ファイルの例は、Setupのチュートリアルに示されていますが、次にも示しておきます。

Setupプログラムからの出力は、次のようになります。

Activation group descriptor registered.
Activation descriptor registered.
Stub bound in registry.

クライアントの実行

正常にCounterImpl実装の起動記述子を登録したら、クライアント・プログラムを実行できるようになります。クライアント・プログラムは、最初の実行時に起動可能なオブジェクトを起動します。

次のコマンド行は、クライアント・コード・ベースのファイルURLを指定してクライアント・プログラムを実行する方法を示しています。

java -cp clientDir                                              \
     -Djava.security.policy=client.policy                       \
     -Dexamples.activation.client.codebase=file:/clientDir/     \
     -Dexamples.activation.name=examples.activation.Counter                   \
     examples.activation.CounterClient [host]

次にそれぞれの意味を示します。

注:

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

Obtained stub from the registry.
Invoking increment method...
Returned from increment remote call.
count = 1

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