8 スタブ・インタフェースとスケルトン・インタフェース
このセクションでは、rmicコンパイラによって生成されるスタブとスケルトンが使用するインタフェースとクラスを説明します。
8.1 RemoteStubクラス
java.rmi.server.RemoteStubクラスは、リモート・オブジェクトのスタブで共通のスーパー・クラスです。 スタブ・オブジェクトは代理であり、リモート・オブジェクトの実際の実装が定義するのとまったく同じインタフェースのセットをサポートします。
package java.rmi.server;
public abstract class RemoteStub extends java.rmi.RemoteObject {
protected RemoteStub() {...}
protected RemoteStub(RemoteRef ref) {...}
protected static void setRef(RemoteStub stub, RemoteRef ref) {...}
}
RemoteStubの1つ目のコンストラクタは、リモート参照としてnullをとるスタブを作成します。 2つ目のコンストラクタは、指定されたリモート参照refを引数にとるスタブを作成します。
setRefメソッドは、Java 2 SDK, Standard Edition, v1.2では非推奨です(サポートもされません)。
8.1.1リモート・オブジェクトとスタブ・クラスとの型の等価性
クライアントは、リモート・オブジェクトのクラスが定義するのと厳密に同じセットのリモート・インタフェースを持ったスタブ(代理)オブジェクトと対話します。スタブ・クラスには、オブジェクトの型グラフを構成するクラス階層の非リモート部分は含まれません。 これは、スタブ・クラスが1つ以上のリモート・インタフェースを実装するもっとも細分化された実装クラスから生成されるためです。 たとえば、CがBを継承し、BがAを継承したとして、Bのみがリモート・インタフェースを実装する場合は、スタブが生成されるのはBからであり、Cからではありません。
スタブは、リモート・オブジェクト・クラスと同じリモート・インタフェース群を実装するため、スタブはサーバー・オブジェクトの型のリモート部分と同じ型を持つことになります。 このため、クライアントは、Java言語の組込み演算を利用してリモート・オブジェクトの型チェックや、あるリモート・インタフェースから別のインタフェースへのキャストを行うことができます。
スタブはrmicコンパイラによって生成されます。
8.1.2 final宣言されたObjectメソッドのセマンティックス
次のメソッドはjava.lang.Objectクラスでfinal宣言されているため、どの実装によってもオーバーライドできません。
getClassnotifynotifyAllwait
getClassのデフォルト実装は、Java言語で記述されたすべてのオブジェクト(ローカルとリモート両方)に適用可能です。このため、リモート・オブジェクトの場合でも、特別な実装は必要ありません。 リモート・スタブに使用された場合には、rmicによって生成されたスタブ・オブジェクトの正確な型がgetClassメソッドによってレポートされます。 ただし、スタブの型はリモート・オブジェクトにより実装されたリモート・インタフェースのみを反映したもので、そのオブジェクトのローカル・インタフェースは反映しません。
java.lang.ObjectのwaitとnotifyメソッドはJava言語のスレッド・モデルに則して待機と通知を扱います。 これらのメソッドをリモート・スタブに対して使うことがJavaのスレッド・モデルに違反するわけではありませんが、これらのメソッドは、ローカルなJavaオブジェクトを扱う場合とはセマンティックスが異なります。 特に、これらのメソッドは、クライアントのリモート・オブジェクト(スタブ)へのローカル参照に作用し、リモートにある実際のオブジェクトには作用しません。
8.2 RemoteCallインタフェース
RemoteCallインタフェースは、リモート・オブジェクトのスタブとスケルトンがリモート・オブジェクトへの呼出しを実行するための抽象化です。
ノート: RemoteCallインタフェースは、Java 2 SDK、Standard Edition、v1.2から非推奨になりました。 1.2のスタブ・プロトコルは、このインタフェースを使いません。 Java 2 SDK, Standard Edition, v1.2では、スタブは、パラメータとしてRemoteCallを必要としない新しいinvokeメソッドを使います。
package java.rmi.server;
import java.io.*;
public interface RemoteCall {
ObjectOutput getOutputStream() throws IOException;
void releaseOutputStream() throws IOException;
ObjectInput getInputStream() throws IOException;
void releaseInputStream() throws IOException;
ObjectOutput getResultStream(boolean success)
throws IOException, StreamCorruptedException;
void executeCall() throws Exception;
void done() throws IOException;
}
getOutputStreamメソッドは、スタブが引数を整列化する、またはスケルトンが結果を整列化する先へ出力ストリームを返します。
releaseOutputStreamメソッドは、出力ストリームを解放します。トランスポートによっては、このメソッドはストリーム自体を解放します。
getInputStreamメソッドは、スタブが結果を非整列化する、またはスケルトンが引数を非整列化する先からInputStreamを返します。
releaseInputStreamメソッドは、入力ストリームを解放します。 この結果いくつかのトランスポートは、接続の入力側を早期に解放できます。
getResultStreamメソッドは、出力ストリームを返します(呼出しが成功したかどうかを示すヘッダー情報を書き込んだあと)。 結果ストリームを得ることができるのは、1回のリモート呼出しに対して1回だけです。 successがtrueであれば、転送のため整列化される結果は正常終了であり、それ以外の結果は例外になります。 StreamCorruptedExceptionは、そのリモート呼出しに対しての結果ストリームがすでに得られている場合にスローされます。
executeCallメソッドは、どのような値が与えられてもその値により呼出しを実行します。
doneメソッドは、リモート呼出しが完了したあとのクリーンアップを許可します。
8.3 RemoteRefインタフェース
RemoteRefインタフェースは、リモート・オブジェクトへのハンドルを表します。 それぞれのスタブは、RemoteRefのインスタンスを持ち、そのインスタンスは参照の具体的な表現を含みます。 このリモート参照は、参照されているリモート・オブジェクトへのリモート呼出し実行に使われます。
package java.rmi.server;
public interface RemoteRef extends java.io.Externalizable {
Object invoke(Remote obj,
java.lang.reflect.Method method,
Object[] params,
long opnum)
throws Exception;
RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum,
long hash) throws RemoteException;
void invoke(RemoteCall call) throws Exception;
void done(RemoteCall call) throws RemoteException;
String getRefClass(java.io.ObjectOutput out);
int remoteHashCode();
boolean remoteEquals(RemoteRef obj);
String remoteToString();
}
invoke(Remote,Method,Object[],long)メソッドは、メソッド呼出しをスタブの(obj)リモート参照に委譲します。委譲により、この参照はリモート・ホストへの接続の設定処理を引き受け、methodおよびパラメータparamsの表現を整列化してから、リモート・ホストへのメソッド呼出しを伝えます。 このメソッドは、リモート・ホスト上のリモート・オブジェクトへのメソッド呼出しの結果を返すか、または呼出しが失敗した場合はRemoteExceptionをスローし、リモート呼出しによって例外をスローした場合はアプリケーション・レベルの例外をスローします。 操作番号opnumは、メソッド・シグネチャのハッシュを表し、転送用のメソッドのエンコードに使用することもできます。
opnumパラメータに使用されるメソッド・ハッシュは、64ビット(long)の整数で、米国国立標準技術研究所(NIST)のSecure Hash Algorithm (SHA-1)を使用する特定のバイト・ストリームのメッセージ・ダイジェストの最初の2つの32ビット値から計算されます。 このバイト・ストリームには、java.io.DataOutput.writeUTFメソッドを使用して記述された場合と同様に、リモート・メソッドの名前とそれに続くメソッド記述子からなる文字列が含まれています。メソッド記述子の説明については、『Java仮想マシン仕様』(JVMS)を参照してください。
たとえば、リモート・インタフェースのメソッドが次の名前とシグニチャを持っている場合、
void myRemoteMethod(int count, Object obj, boolean flag)
リモート・メソッドの名前および記述子を含む文字列は、次のようになります。
myRemoteMethod(ILjava/lang/Object;Z)V
64ビット・ハッシュ値は、8バイト・シーケンスのリトルエンディアン構成です。最初の4バイトはメッセージ・ダイジェストの最初の32ビット値(ビッグエンディアンのバイト順)で、後の4バイトはメッセージ・ダイジェストの2番目の32ビット値(ビッグエンディアンのバイト順)です。 たとえば、メッセージ・ダイジェストの最初の2つの32ビット値が0xB0B1B2B3および0xB4B5B6B7の場合、ハッシュ値は0xB7B6B5B4B3B2B1B0になります。
ノート: newCall(RemoteObject,Operation[],int,long)、invoke(RemoteCall)、およびdone(RemoteCall)の各メソッドは、Java 2 SDK、Standard Edition、v1.2から非推奨になりました。 1.2のスタブ・プロトコル・バージョンを使ったrmicによって生成されたスタブは、これらのメソッドを現在は使用しません。 newCall、invoke、およびdoneで構成される呼出しのシーケンスは、Methodオブジェクトをパラメータの1つにとる新しいinvokeメソッドに置き換えられました。
newCallメソッドは、リモート・オブジェクトobj上での新しいリモート・メソッド呼出し用の適切な呼出しオブジェクトを作成します。 操作配列opは、リモート・オブジェクト上で利用可能な複数の操作を含みます。 操作番号opnumは、操作配列のうち、今回のリモート呼出しで使用する特定の操作を指定するためのインデックスです。 インタフェース・ハッシュは、v1.1スタブ・プロトコルを使用するスタブとスケルトン間の互換性維持に使用する64ビット値です。 インタフェース・ハッシュは、SHA-1を使用する特定のバイト・ストリームのメッセージ・ダイジェストの最初の2つの32ビット値から計算されます。 このバイト・ストリームに含まれるデータの書込みは、java.io.DataOutputインタフェースのwriteIntおよびwriteUTFメソッドを使用した場合と同じ方法で行われます。データには次の項目が含まれます。
- (
int)スタブのバージョン番号(常に1) - リモート・メソッドごと、かつ操作番号順:
- (UTF-8)リモート・メソッド名
- (UTF-8)リモート・メソッド記述子(『Java仮想マシン仕様』を参照)
- 宣言された例外ごと、かつバイナリ名の辞書式順:
- (UTF-8)例外クラスの名前
インタフェース・ハッシュ値をメッセージ・ダイジェストから作成する方法は、invokeメソッドで使用するメソッド・ハッシュに関してすでに説明した方法と同じです。
invoke(RemoteCall)メソッドは、リモート呼出しを実行します。invokeは、「ユーザー(user)定義」例外を発生させます。この例外は、スタブではキャッチせずに通過させる必要があります。 リモート呼出しの途中でなんらかの例外が発生した場合、invokeはユーザー定義例外またはRemoteExceptionを発生させる前に接続のクリーンアップを行うべきです。
doneメソッドは、リモート参照に接続のクリーンアップ(または再利用)を許可します。doneが呼び出されるのは、invoke呼出しが正常に行われて(例外を発生させずに)終了し、スタブに戻った場合だけです。
getRefClassメソッドは、直列化されてストリームに出力(out)される参照タイプ内でパッケージ修飾されていないクラス名を返します。
remoteHashCodeメソッドは、リモート・オブジェクトのハッシュ・コードを返します。 同一のリモート・オブジェクトを参照する2つのリモート・オブジェクト・スタブは(リモート・オブジェクトをハッシュ・テーブル上のキーとしてサポートするために)同じハッシュ・コードを持ちます。 RemoteObjectは、hashCodeメソッドへの呼出しをリモート参照のremoteHashCodeメソッドへ転送します。
remoteEqualsメソッドは、2つのリモート・オブジェクトを比較して異同判定します。 2つのリモート・オブジェクトは、両者が同一のリモート・オブジェクトを参照するとき等しいとされます。 たとえば、2つのスタブは両者が同一のリモート・オブジェクトを参照するならば等しいとされます。 RemoteObjectは、equalsメソッドへの呼出しをリモート参照のremoteEqualsメソッドへ転送します。
remoteToStringメソッドは、そのリモート・オブジェクトへの参照を表すStringを返します。
8.4 ServerRefインタフェース
ServerRefインタフェースは、リモート・オブジェクトの実装に対するサーバー側のハンドルを表します。
package java.rmi.server;
public interface ServerRef extends RemoteRef {
RemoteStub exportObject(java.rmi.Remote obj, Object data)
throws java.rmi.RemoteException;
String getClientHost() throws ServerNotActiveException;
}
exportObjectメソッドは、与えられたRemoteオブジェクトの実装objに対するクライアント・スタブ・オブジェクトを探すかあるいは作成します。パラメータdataには、オブジェクトをエクスポートするのに必要となる情報(たとえばポート番号)が含まれます。
getClientHostメソッドは、現在のクライアントのホスト名を返します。 現在、リモート・メソッド呼出しを操作中のスレッドから呼び出された場合には、呼出しを実行中のクライアントのホスト名が返されます。 リモート・メソッドへの呼出しが現在実行中でなければ、ServerNotActiveExceptionが呼び出されます。
8.5 Skeletonインタフェース
Skeletonインタフェースは、rmicコンパイラにより作成されたスケルトンの実装によってだけ使用されます。 リモート・オブジェクトに対するスケルトンとは、実際のリモート・オブジェクトの実装へ呼出しをディスパッチする、サーバー側のエンティティです。
ノート: Skeletonインタフェースは、Java 2 SDK、Standard Edition、v 1.2から非推奨にされました。 rmicスタブ・コンパイラによって生成された1.1の各スケルトン・クラス(およびデフォルトのrmic -vcompatを使用して1.2で生成された、バージョン1.1互換のスケルトン)は、このインタフェースを実装しています。 Java 2 SDK, Standard Edition, v1.2互換のバージョンでは、リモート・メソッド呼出しのディスパッチに、スケルトンが必要なくなりました。 1.2以降のバージョンと互換性のあるスタブを生成するには、オプション-v1.2を指定してrmicコマンドを使用します。
package java.rmi.server;
public interface Skeleton {
void dispatch(Remote obj, RemoteCall call, int opnum, long hash)
throws Exception;
Operation[] getOperations();
}
dispatchメソッドは、callオブジェクトで取得した入力ストリームのすべての引数を非整列化し、実際のリモート・オブジェクトの実装であるobj上のメソッド(操作番号opnumで示される)を呼び出します。そして戻り値をストリームに流すため整列化するか、または呼出し中に例外が発生した場合には例外をスローします。
getOperationsメソッドは、リモート・オブジェクトのメソッドに対する操作記述子が書かれた配列を返します。
8.6 Operationクラス
Operationクラスは、リモート・オブジェクト用のJava言語でのメソッドの記述を保持します。
ノート: Operationインタフェースは、Java 2 SDK、Standard Edition、v1.2から非推奨になりました。 1.2のスタブ・プロトコルは、Operationを引数の1つとしてとる、以前のRemoteRef.invokeを使いません。 Java 2 SDK, Standard Edition, v1.2では、スタブは、パラメータとしてOperationを必要としない新しいinvokeメソッドを使います。
package java.rmi.server;
public class Operation {
public Operation(String op) {...}
public String getOperation() {...}
public String toString() {...}
}
Operationオブジェクトは、多くの場合メソッドのシグニチャ付きで生成されます。
getOperationメソッドは、操作記述子の内容を返します(初期化されたときの値)。
toStringメソッドもまた操作記述子の文字列による表現を返します(一般的にはメソッドのシグニチャ)。