このチュートリアルでは、実装時に持続状態を使用する起動可能なリモートオブジェクトを実装する方法について説明します。このチュートリアルでは、Setup プログラム (「アクティベーションの使用: Setup プログラム」チュートリアルを参照) を使用します。このプログラムは、起動可能なリモートオブジェクトに関する情報を JavaTM Remote Method Invocation (Java RMI) 起動システムデーモン (rmid) に登録し、rmiregistry でそのリモートオブジェクトのスタブをバインドして、クライアントが検索できるようにします。このチュートリアルの前に、Setup プログラムのチュートリアルを読むことをお勧めします。
このチュートリアルでは、次の手順を実行します。
このチュートリアルの実行に必要なファイルは、次のとおりです。
Counter.java - カウント用のリモートインタフェースCounterImpl.java - リモートインタフェースの「起動可能な」実装CounterClient.java - リモートインタフェースを使用するクライアントclient.policy - クライアントのセキュリティーポリシーファイル起動可能なリモートオブジェクトを実装するには、いくつかの基本的な方法があります。このチュートリアルでは、実装時に持続状態を使用する起動可能なリモートオブジェクトを実装する方法について説明します。
リモートオブジェクトは、クライアントが起動可能なリモートオブジェクトのスタブでリモートメソッドを呼び出すときに起動されます。起動可能なリモートオブジェクトのスタブには、リモートオブジェクトの起動識別子と、そのリモートオブジェクトの 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 つのパラメータを取ります。
ActivationID で、起動可能なリモートオブジェクトの識別子です。アプリケーションが起動記述子を rmid に登録すると、rmid はそれに起動識別子を割り当て、この起動識別子が記述子に関連する情報を参照します。リモートオブジェクトが起動されると、この同じ起動識別子 (リモートオブジェクトのスタブにも含まれている) がコンストラクタに渡されます。MarshalledObject で、rmid にあらかじめ登録された初期化データが含まれています。この例では、整列化されたデータは、最新のカウンタ値であるオブジェクトの持続状態を含むファイルの名前です。コンストラクタは、2 つ目のパラメータとして渡された MarshalledObject に含まれるファイル名を取得します。次に、コンストラクタは、ローカルメソッド openFile を呼び出して、そのファイルを開き、現在のカウンタ値を返します。ファイルが存在する場合、openFile メソッドは最後にファイルに保存された値を読み取ります。ファイルが存在しない場合は、新しいファイルを作成して、カウントを 0 に初期化します。次に、コンストラクタは、static メソッド Activatable.exportObject を呼び出し、実装自体 (this)、起動識別子、およびポート番号 0 を渡して、オブジェクトを匿名の TCP ポートでエクスポートする必要があることを示します。この実装では、パラメータとしてコンストラクタに渡された起動識別子は使いませんが、別の実装では、将来の使用に備えて、たとえばオブジェクトを終了するために、起動識別子を保存することもできます。
最後に、クラスは、リモートインタフェースの単一メソッドである increment を実装して、カウントを増分し、そのカウントをファイルに保存して、増分したカウントを返します。writeCount メソッドの実装には弱点があることに注意してください。setLength と writeInt の呼び出しの間にマシンがクラッシュすると、カウンタの値が失われます。わずかな時間の間に発生したクラッシュに対してこの実装をもっと堅牢にすることが、ユーザーに残された課題です。
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 つのアクセス権が必要です。
java.util.PropertyPermission - レジストリ内のスタブの名前を指定するシステムプロパティー examples.activation.name を読み取るjava.net.SocketPermission - レジストリ、起動システム、およびリモートオブジェクトのホストに接続するこの例のソースファイルは、次のようにしてコンパイルできます。
javac -d implDir Counter.java CounterImpl.java javac -d clientDir Counter.java CounterClient.java
implDir は実装のクラスファイルを配置する生成先ディレクトリで、clientDir はクライアントのクラスファイルを配置する生成先ディレクトリです。
Setup プログラムの実行実装段階が完了したら、起動可能なオブジェクトに関する情報を登録して、クライアントが使えるようにする必要があります。Setup プログラムは、「アクティベーションの使用: Setup プログラム」チュートリアルで説明されているように、起動可能なオブジェクトの起動記述子を rmid に登録し、rmiregistry でリモートオブジェクトのスタブをバインドして、クライアントが検索できるようにします。
この例の Setup プログラムを実行するには、Setup プログラムのチュートリアルのセクション「rmid、rmiregistry、および Setup プログラムの起動」を参照してください。rmid、rmiregistry、および Setup プログラム自体の起動方法について説明しています。
Setup チュートリアルの手順に従って rmid と rmiregistry を実行したら、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
次にそれぞれの意味を示します。
Setup プログラムのクラスのルートディレクトリですSetup プログラムのセキュリティーポリシーファイルですdata として登録されているオブジェクトの持続状態が書かれたファイルの名前です前述の各ファイル 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]
次にそれぞれの意味を示します。
注:
Setup プログラムに提供される名前と一致する必要があります。この例では、examples.activation.Counter という名前を使用しました。rmid と rmiregistry は、サーバーのホスト上で稼働している必要があります。サーバーのホストがローカルホストでない場合、host 引数は、それらが稼働しているリモートホストを指定する必要があります。クライアントからの出力は、次のようになります。
Obtained stub from the registry. Invoking increment method... Returned from increment remote call. count = 1