リモートメソッド呼び出し (RMI) とはリモートオブジェクト上にあるリモートインタフェースのメソッドを呼び出す操作のことです。このとき最も重要なのはリモートオブジェクトを呼び出すためのシンタックスがローカルオブジェクト呼び出しと同じであることです。
instanceof 演算子によりリモートオブジェクトがサポートするリモートインタフェースをテストすることができる。
Java 分散オブジェクトは Java オブジェクトモデルと次の点で異なります。
Object クラスで定義されるメソッドのいくつかのセマンティクスはリモートオブジェクト用に特化されている。
java.rmi と java.rmi.server パッケージで定義されます。次の図はこれらのインタフェースとクラスの関係を示します。
java.rmi.remote インタフェースを拡張します。Remote インタフェースフェースはここに示すように、メソッドを定義しません。
public interface Remote {}
public interface BankAccount
extends Remote
{
public void deposit (float amount)
throws java.rmi.RemoteException;
public void withdraw (float amount)
throws OverdrawnException, java.rmi.RemoteException;
public float balance()
throws java.rmi.RemoteException;
}
java.rmi.RemoteException を宣言しなければならない。
java.rmi.RemoteException クラスは RMI ランタイムがスローすることができる全ての例外のスーパークラスです。RMI システムを使うアプリケーションの安定性を保つために、リモートインタフェースで宣言される各メソッドは throws 節の中で
java.rmi.RemoteException を指定しなければなりません。
java.rmi.RemoteException はリモートメソッド呼び出しが失敗(ネットワーキングに失敗した場合や目的とするサーバーへコールが到達しなかったとき)したときにスローされます。これによりリモート呼び出しを実行するアプリケーションはリモート例外にどう対処すればよいかを決定することができます。
java.rmi.server.RemoteObject とそのサブクラス、java.rmi.server.RemoteServer と java.rmi.server.UnicastRemoteObject が提供します。
java.rmi.server.RemoteObject クラスは、hashCode,
equals と toString メソッドの実装により Object のリモートセマンティクスを提供する。
java.rmi.server.RemoteServer により抽象化されたものが提供され、具体化されたものがそのサブクラスにより提供される。
サブクラスはリモート参照のセマンティクスの判定(例えばサーバーは単一オブジェクトなのか、または複数の場所の通信を必要とする複製オブジェクトなのか)を行う。
java.rmi.server.UnicastRemoteObject クラスは、サーバープロセスが活動中にのみ参照が有効になる singleton (ユニキャスト) リモートオブジェクトを定義する。
java.rmi.server.UnicastRemoteObject を拡張することにより java.rmi.server.RemoteObject や java.rmi.server.RemoteServer クラスが提供するリモート動作を継承する。
BankAcctImpl クラスを定義し、その中で BankAccount リモートインタフェースを実装して java.rmi.server.UnicastRemoteObject クラスを拡張しています。:
package my_package;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class BankAccountImpl
extends UnicastRemoteObject
implements BankAccount
{
public void deposit (float amount) throws RemoteException {
...
}
public void withdraw (float amount) throws OverdrawnException,
RemoteException {
...
}
public float balance() throws RemoteException {
...
}
}
java.rmi.server.RemoteServer 以外のいくつかのクラスを拡張することもできることに注意してください。
しかし、この場合実装クラスはその Object クラスから継承した hashCode、equals、および toString が正しいリモートセマンティクスに従っていることに対して責任を負わなければなりません。
スタブはリモートオブジェクトクラスと同じセットのリモートインタフェースを実装しますから、Java システム側から見ると、スタブはサーバーオブジェクトの型グラフのリモート部分と同じ型を持つことになります。従って、クライアントは Java の組み込み演算によってリモートオブジェクトの型チェックをおこない、一つのリモートインタフェースから別なインタフェースへのキャストを行うことができます。
スタブは rmic コンパイラにより作成されます。
java.io.Serializable インタフェースを実装した非リモート Java オブジェクトが含まれます。
クラスを直列化可能にする方法の詳細については Java の「オブジェクト直列化仕様 (Object Serialization Specification)」を参照してください。アプレットの場合には、引数や返り値のクラスがローカルには得られないときは
AppletClassLoader によって動的にロードされます。 アプリケーションの場合には、これらのクラスはアプリケーションをロードしたクラスローダーにより行われます。このクラスローダーはデフォルトクラスローダー(ローカルなクラスパスを用いる)か RMIClassLoader(サーバーの codebase を用いる)のいずれかです。
いくつかのクラスは、例えばセキュリティ上の理由により、(直列化ができないため)渡されるのが禁止されます。 この場合にはリモートメソッド呼び出しは例外発生により失敗します。
リモートメソッド呼び出しの引数、および呼び出しの結果戻される返り値としての非リモートオブジェクトはコピーにより渡されます。
つまり、非リモートオブジェクトがリモートメソッド呼び出しに現れると、非リモートオブジェクトの内容はリモートオブジェクトの呼び出しがコールされる前にコピーされます。デフォルトでは static ではなく、transient でないフィールドだけがコピーの対象になります。
同様にして、リモートメソッド呼び出しから非リモートオブジェクトが戻されるときは、呼び出し側の仮想マシンに新規オブジェクトが作成されます。
java.rmi.RemoteException を含むため、呼び出し側はアプリケーションに固有の例外に加えてこれに備えておく必要があります。
リモートメソッド呼び出し中に java.rmi.RemoteException
がスローされたとき、クライアント側には呼び出しの結果、エラーが発生するのは呼び出しの前なのか、実行中なのか、呼び出し完了後なのか等々についての情報がないかもしれません。
このため、リモートインタフェースとその中で宣言される呼び出し側メソッドはこれらのエラーに関するセマンティクスに留意して設計するべきです。
Object クラスのデフォルト実装による
equals、 hashCode、および
toString メソッドはリモートオブジェクトに対して適切ではありません。
このため、java.rmi.server.RemoteObject
クラスがこれらのメソッドに対するリモートオブジェクトに適したセマンティクスの実装を提供しています。
この方法により、リモートから利用できなければならない全てのオブジェクトは java.rmi.server.RemoteObject を拡張することができます(典型的には java.rmi.server.UnicastRemoteObject を介して間接的に拡張します)。
equals と hashCode は
java.rmi.server.RemoteObject
クラスでオーバーライドされます。
java.rmi.server.RemoteObject
クラスの実装による equals メソッドは2つのオブジェクト参照が等しいかどうか(オブジェクトの内容が等しいかではなく)を決定する。
この理由は、内容が等しいかを判定するためにはリモートメソッド呼び出しが必要であり、equals のシグネチャはリモート例外がスローされるのを許さないからである。
java.rmi.server.RemoteObject
クラスの実装による hashCode メソッドは同じ下層からなるリモートオブジェクトへの全てのリモート参照にたいして同じ値を返す(同じオブジェクトへの参照は同じとみなされる)。
toString メソッドはオブジェクトへの参照を表現した文字列を返すために定義されています。
文字列の内容は参照のタイプに固有のものです。 singleton (ユニキャスト)
オブジェクトの現在の実装はトランスポートレイヤに固有なオブジェクトの情報(ホスト名やポート番号)とオブジェクト識別子を含みます。複製オブジェクトへの参照はさらに多くの情報を含みます。
java.lang.Cloneable インタフェースをサポートしているときのみ可能です。リモートオブジェクトはこのインタフェースを実装しませんが、
clone メソッドは実装していますからサブクラスが Cloneable を実装する必要があるならばリモートクラスは正しく動作します。
クライアントスタブは final として宣言され、 clone を実装しません。
このためスタブのクローン生成はローカルな操作であり、クライアントが新規にリモートオブジェクトを生成するために使うことはできません。
RemoteObject のサブクラス)は必要に応じて自分自身のクリーンアップのために finalize を使うことができます。例えば、
finalize を使ってオブジェクトサーバーを不活性化することができます。
Object クラスにおいて final 宣言され、オーバーライドはできません:
getClass
notify
notifyAll
wait
getClass のデフォルト実装は全て(ローカルとリモート両方)の Java オブジェクトに適用可能です。
リモートオブジェクトに使われた場合には、getClass
メソッドが生成されたスタブオブジェクトの正確な型をレポートしてくれます。
ただし、この型はオブジェクトにより実装されたリモートインタフェースのみを反映したもので、そのローカルインタフェースではないことに注意してください。
Object の wait と
notify メソッドは
Java 言語のスレッドモデルに即して待ちと通知を取り扱います。これらのメソッドをリモートオブジェクトに対して使うのは Java のスレッドモデルに違反する訳ではありませんが、これらのメソッドはローカルな Java オブジェクトを扱う場合とはセマンティクスが異なります。
特に、これらのメソッドはクライアントのリモートオブジェクト(スタブ)に対するローカルな参照に作用し、リモートにある実際のオブジェクトに作用する訳ではありません。
java.rmi.Naming クラスの URL をベースとするメソッドを使って保存することができます。
クライアントがリモートオブジェクトのメソッドを呼び出すときには、クライアントはまずそのオブジェクトに対する参照を得る必要があります。
リモートオブジェクトに対する参照は通常メソッドコールの返り値として得られます。
RMI システムは単純ブートストラップネームサーバーを提供して指定されたホストのリモートオブジェクトを得られるようにしています。
java.rmi.Naming
クラスは URL(Uniform Resource Lacator) ベースのメソッドを提供して特定のホストとポートにおける名前-オブジェクト対のルックアップ、バインド、リバインド、アンバインドそしてリストを可能にしています。
次に示すのは、リモートオブジェクトのルックアップとバインドを実行する例です(例外処理は省略)。
BankAccount acct = new BankAcctImpl(); String url = "rmi://java.Sun.COM/account"; // bind url to remote object java.rmi.Naming.bind(url, acct); ... // lookup account acct = (BankAccount)java.rmi.Naming.lookup(url);