RMIアプリケーションは、多くの場合、サーバーとクライアントという2つのプログラムから構成されます。 一般的なサーバー・アプリケーションは、数多くのリモート・オブジェクトを作成し、それらのリモート・オブジェクトへの参照をアクセス可能にして、クライアントがそれらのリモート・オブジェクトのメソッドを呼び出すのを待ちます。 一般的なクライアント・アプリケーションは、サーバー内の1つまたは複数のリモート・オブジェクトへのリモート参照を取得し、それらのオブジェクトのメソッドを呼び出します。 RMIは、サーバーおよびクライアント間で通信や情報の受け渡しを行うメカニズムを提供します。 このようなアプリケーションは、分散オブジェクト・アプリケーションと呼ばれることがあります。
分散オブジェクト・アプリケーションで必要とされる処理は、次のとおりです。
リモート・オブジェクトの検索
アプリケーションは、リモート・オブジェクトへの参照を取得するために、2つのメカニズムのうちの1つを使うことができます。 アプリケーションは、リモート・オブジェクトをRMIの単純な名前付け機能であるrmiregistry
に登録するか、あるいは通常の操作の一部としてリモート・オブジェクト参照を渡したり、返したりできます。
リモート・オブジェクトとの通信
リモート・オブジェクト間の通信の詳細は、RMIによって処理されます。プログラマからは、リモート通信が標準的なメソッド呼出しに見えます。
パラメータまたは戻り値として渡されるオブジェクトのクラス・バイトコードのロード
RMIでは、呼出し側はリモート・オブジェクトにオブジェクトを渡すことができるため、RMIは、データ転送の場合と同様に、オブジェクトのコードのロードにも必要なメカニズムを提供します。
次の図は、リモート・オブジェクトへの参照の取得にレジストリを使用するRMI分散アプリケーションを示しています。 サーバーは、リモート・オブジェクトに名前を関連付けるために、レジストリを呼び出します。 クライアントは、サーバーのレジストリ内の名前によってリモート・オブジェクトを見つけ、そのオブジェクトのメソッドを呼び出します。 この図は、RMIシステムで、既存のWebサーバーを使って、サーバーからクライアント、およびクライアントからサーバーに、必要に応じてオブジェクト用のJava言語内のクラスのバイト・コードをロードする様子も示しています。 RMIでは、Javaプラットフォームのサポートする任意のURLプロトコル(HTTP、FTP、ファイルなど)を使って、クラス・バイトコードがロードされます。
この図は、レジストリを使用してリモート・オブジェクトへの参照を取得するRMI分散アプリケーションを示しています .
Java SEプラットフォームの分散オブジェクト・モデルにおけるリモート・オブジェクトとは、別のJava仮想マシン(別のホスト上にある場合もある)からそのメソッドを呼び出すことができるオブジェクトです。 このタイプのオブジェクトは、1つまたは複数のリモート・インタフェースにより記述され、リモート・オブジェクトのメソッドを宣言するJavaプログラミング言語で書かれたインタフェースです。
リモート・メソッド呼び出し (RMI)とは、リモート・オブジェクト上にあるリモート・インタフェースのメソッドを呼び出すアクションのことです。 この操作でもっとも重要なのは、リモート・オブジェクトを呼び出すための構文が、ローカル・オブジェクトのメソッド呼出しの場合と同じであることです。
Java SEプラットフォームの分散オブジェクト・モデルは、Java SEプラットフォームのオブジェクト・モデルと次の点でよく似ています。
リモート・オブジェクトへの参照を引数として渡すことができ、メソッド(ローカルであれ、リモートであれ)呼出しの結果は戻り値として返される。
リモート・オブジェクトはリモート・インタフェースの実装がサポートする任意の型にキャストすることができる。このときJava言語に組み込まれた型変換構文に従う。
組込み演算子instanceof
により、リモート・オブジェクトがサポートするリモート・インタフェースをテストすることができる。
Java SEプラットフォームの分散オブジェクトはJava SEプラットフォームのオブジェクト・モデルと次の点で異なります。
リモート・オブジェクトのクライアントは、リモート・インタフェースに働きかけるのであって、これらのインタフェースの実装クラスに働きかけることはない。
リモート・メソッド呼出しに対するリモートでない引数と、リモートでない戻り値は参照渡しではなくコピー渡しになる。 この理由は、オブジェクトへの参照は単一の仮想マシン上でのみ有効なため。
リモート・オブジェクトは参照で渡され、実際のリモート実装がコピーされて渡されるのではない。
java.lang.Object
クラスで定義されるメソッドのいくつかのセマンティックスはリモート・オブジェクト用に特殊化されている。
リモート・オブジェクト呼出しの失敗モードは、その性質として、ローカル・オブジェクト呼出しの失敗モードよりも複雑になる。クライアントはリモート・メソッド実行時には、起こり得る追加的な例外処理を取り扱わなければならない。
RMIシステムのリモート動作を決定するインタフェースとクラスは、java.rmiパッケージの階層で定義されます。 次の図は、このうちいくつかのインタフェースとクラスの関係を示します。
これらのインタフェースとクラスのいくつかの関係
java.rmi.Remote
インタフェースRMIにおいて、リモート・インタフェースは、リモートJava仮想マシンから呼び出される可能性があるメソッドのセットを宣言するインタフェースです。 リモート・インタフェースは、次の要件を満たしている必要があります。
リモート・インタフェースが、少なくともjava.rmi.Remote
インタフェースを直接または間接的に継承する。
リモート・インタフェース内またはそのスーパー・インタフェースの各メソッド宣言が、次のリモート・メソッド宣言の要件を満たしている
リモート・メソッド宣言のthrows節に、アプリケーション固有の例外のほかに、例外java.rmi.RemoteException
(またはjava.io.IOException
やjava.lang.Exception
などのjava.rmi.RemoteExceptionのスーパー・クラスの1つ)が含まれている。ただし、アプリケーション固有の例外は、java.rmi.RemoteException
を継承する必要はない。
リモート・メソッド宣言で、パラメータまたは戻り値として宣言された(パラメータ・リストで直接宣言されたか、パラメータの非リモート・オブジェクト内に埋め込まれた)リモート・オブジェクトは、リモート・インタフェースの実装クラスとしてではなく、リモート・インタフェースとして宣言される必要がある。
java.rmi.Remote
インタフェースは、メソッドを定義しないマーカー・インタフェースです。
public interface Remote {}
リモート・インタフェースは、少なくとも、java.rmi.Remote
インタフェース(またはjava.rmi.Remote
を継承する別のリモート・インタフェース)を継承する必要があります。 ただし、次の条件下では、リモート・インタフェースは、非リモート・インタフェースを継承する場合があります。
たとえば、次のインタフェースBankAccount
は、銀行口座にアクセスするためのリモート・インタフェースを定義しています。 ここには、口座への預金、差引残高の取得、および口座からの引出しを行うためのリモート・メソッドが含まれています。
public interface BankAccount extends java.rmi.Remote {
public void deposit(float amount)
throws java.rmi.RemoteException;
public void withdraw(float amount)
throws OverdrawnException, java.rmi.RemoteException;
public float getBalance()
throws java.rmi.RemoteException;
}
次の例では、有効なリモート・インタフェースBeta
は、リモート・メソッドおよびjava.rmi.Remote
インタフェースを持つ非リモート・インタフェースAlpha
を継承しています。
public interface Alpha {
public final String okay = "constants are okay too";
public Object foo(Object obj)
throws java.rmi.RemoteException;
public void bar() throws java.io.IOException;
public int baz() throws java.lang.Exception;
}
public interface Beta extends Alpha, java.rmi.Remote {
public void ping() throws java.rmi.RemoteException;
}
RemoteException
クラスjava.rmi.RemoteException
クラスは、リモート・メソッド呼出しの間にRMIランタイムがスローする例外のスーパー・クラスです。 RMIシステムを使うアプリケーションの安定性を保つために、リモート・インタフェースで宣言される各リモート・メソッドはthrows節の中でjava.rmi.RemoteException
(またはjava.io.IOException
やjava.lang.Exception
などのjava.rmi.RemoteExceptionのスーパー・クラス)を指定する必要があります。
例外java.rmi.RemoteException
は、リモート・メソッド呼出しがなんらかの理由で失敗したときにスローされます。 リモート・メソッド呼出しは、次のような理由で失敗することがあります。
RemoteException
クラスは、チェック例外(リモート・メソッドの呼出し側が処理する必要があり、コンパイラによってチェックされる例外)で、RuntimeException
ではありません。
RemoteObject
クラスとそのサブクラスRMIサーバー関数はjava.rmi.server.RemoteObject
とそのサブクラス、java.rmi.server.RemoteServer
とjava.rmi.server.UnicastRemoteObject
、およびjava.rmi.activation.Activatable
が提供します。
java.rmi.server.RemoteObject
クラスは、リモート・オブジェクトを扱えるjava.lang.Object
のメソッドhashCode
、equals
およびtoString
の実装を提供する。
リモート・オブジェクトを作成してエクスポートする(リモート・クライアントが利用できるようにする)メソッドは、UnicastRemoteObject
クラスおよびActivatable
クラスによって提供される。 サブクラスは、サーバーが単純なリモート・オブジェクトであるか、または起動可能なリモート・オブジェクト(呼出しにより実行されるオブジェクト)であるかなどの、リモート参照のセマンティックスを識別する。
java.rmi.server.UnicastRemoteObject
クラスは、サーバー・プロセスの活動中にだけ参照が有効な単一(ユニキャスト)リモート・オブジェクトを定義する。
java.rmi.activation.Activatable
クラスは、リモート・メソッドの呼出し時に実行を開始し、必要に応じて自身を終了することができるactivatableリモート・オブジェクトを定義する抽象クラスである。
リモート・インタフェースを実装するクラスの一般的な規約は、次のとおりです。
java.rmi.server.UnicastRemoteObject
を拡張することによりjava.rmi.server.RemoteObject
やjava.rmi.server.RemoteServer
クラスが提供するリモート動作を継承する。たとえば、次に示すクラスBankAcctImpl
はBankAccount
リモート・インタフェースを実装し、java.rmi.server.UnicastRemoteObject
クラスを拡張しています。
package mypackage;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class BankAccountImpl
extends UnicastRemoteObject
implements BankAccount
{
private float balance = 0.0;
public BankAccountImpl(float initialBalance)
throws RemoteException
{
balance = initialBalance;
}
public void deposit(float amount) throws RemoteException {
...
}
public void withdraw(float amount) throws OverdrawnException,
RemoteException {
...
}
public float getBalance() throws RemoteException {
...
}
}
ノート:
必要に応じて、リモート・インタフェースを実装するクラスはjava.rmi.server.UnicastRemoteObject
以外のいくつかのクラスを拡張できます。 ただし実装クラスは、オブジェクトのエクスポート(UnicastRemoteObject
コンストラクタによって処理される)、およびjava.lang.Object
クラスから継承されるhashCode
、equals
、およびtoString
メソッドの正しいリモート・セマンティクスの実装(必要な場合)に対する責任を負わなければなりません。
ソケット・ファクトリを指定せずにリモート・オブジェクトをエクスポートするか、RMIClientSocketFactory
およびRMIServerSocketFactory
型のパラメータを含まないバージョンのUnicastRemoteObject.exportObject
メソッドまたはUnicastRemoteObject
コンストラクタを使用してオブジェクトをエクスポートすると、リモート・オブジェクトはすべてのローカル・アドレスにエクスポートされます。 リモート・オブジェクトを特定のアドレスにエクスポートするには、"RMIソケット・ファクトリ"の項を参照してください。
リモート・オブジェクトへの引数、リモート・オブジェクトからの戻り値は、直列化可能なオブジェクトであればどれでも可能です。 これにはプリミティブ型、リモート・オブジェクト、java.io.Serializable
インタフェースを実装した非リモート・オブジェクトが含まれます。 クラスを直列化可能にする方法の詳細は、"Javaオブジェクト直列化仕様。"を参照してください ローカルでは入手できないパラメータまたは戻り値のクラスは、RMIシステムによって動的にダウンロードされます。 パラメータ、戻り値および例外の読取り時にRMIがパラメータをダウンロードし、値クラスを戻す方法の詳細は、"動的クラス・ローディング"の項を参照してください。
リモート・メソッド呼出しのパラメータとして渡されたり、リモート・メソッド呼出しの結果として戻される非リモート・オブジェクトは、コピーにより渡されます。つまり、非リモート・オブジェクトは、Java SEプラットフォームのオブジェクト直列化メカニズムを使って直列化できます。
したがって、リモート・メソッド呼出しで非リモート・オブジェクトが返されるとき、非リモート・オブジェクトの内容は、リモート・オブジェクトの呼出しが呼び出される前にコピーされます。
リモート・メソッド呼び出しから非リモート・オブジェクトが戻されるときは、呼出し側の仮想マシンに新規オブジェクトが作成されます。
リモート・メソッド呼出しで、エクスポートされたリモート・オブジェクトをパラメータまたは戻り値として渡す場合には、そのリモート・オブジェクトのスタブが代わりに渡されます。 エクスポートされていないリモート・オブジェクトは、スタブ・インスタンスに置き換えられません。 パラメータとして渡されたリモート・オブジェクトは、リモート・インタフェースだけを実装できます。
単一のリモート・メソッド呼出しで、あるオブジェクトへの2つの参照が、あるJVMから別のJVMへパラメータ(または戻り値)として渡され、それらの参照が送信側のJVM上の同一オブジェクトを参照している場合、これらの参照は、受信側のJVM上のオブジェクトの1つのコピーも参照します。 さらに一般的に説明すると、単一のリモート・メソッド呼出し内では、RMIシステムは、呼出し内でパラメータとして渡されたり、戻り値として返されるオブジェクト間の参照の整合性を維持します。
リモート・メソッド呼出しで、あるJVMから別のJVMにオブジェクトを送信する際に、RMIシステムは呼出しストリーム内のクラス記述子にクラスの情報(URL)を注釈として付け、受信側でクラスをロードできるようにします。 リモート・メソッド呼出し中には、必要に応じて随時、クラスをダウンロードする必要があります。
リモート呼出し先に渡すパラメータを直列化するために、RMI呼出し内のパラメータは、java.io.ObjectOutputStream
クラスのサブクラスであるストリームに書き込まれます。 ObjectOutputStream
サブクラスは、replaceObject
メソッドをオーバーライドして、エクスポートされた各リモート・オブジェクトを対応するスタブ・インスタンスで置き換えます。 オブジェクトであるパラメータは、ObjectOutputStream
のwriteObject
メソッドを使ってストリームに書き込まれます。 ObjectOutputStream
は、writeObject
メソッドによってストリームに書き込まれた各オブジェクト(書き込まれたオブジェクトによって参照されているオブジェクトも含む)の、replaceObject
メソッドを呼び出します。 RMIのサブクラスObjectOutputStream
のreplaceObject
メソッドの戻り値は、次のとおりです。
replaceObject
に渡されたオブジェクトがjava.rmi.Remote
のインスタンスであり、そのオブジェクトがRMIランタイムにエクスポートされた場合、リモート・オブジェクトのスタブが返される。 オブジェクトがjava.rmi.Remote
のインスタンスであり、そのオブジェクトがRMIランタイムにエクスポートされない場合、replaceObject
はオブジェクト自体を返す。 リモート・オブジェクトのスタブは、java.rmi.server.RemoteObject.toStub
メソッドへの呼出しによって取得される。
replaceObject
に渡されたオブジェクトがjava.rmi.Remote
のインスタンスでない場合は、単にそのオブジェクトが返される。
RMIのサブクラスObjectOutputStream
もannotateClass
メソッドを実装し、クラスが呼出し側でダウンロードできるように呼出しストリームにクラスの場所についての注釈を付けます。 annotateClass
の使用方法の詳細は、"動的クラス・ローディング"の項を参照してください。
どのパラメータも同一のObjectOutputStream
に書き込まれるため、呼出し側で同じオブジェクトを参照している参照は、受信側でも同じオブジェクトのコピーを参照します。 受信側では、パラメータは同一のObjectInputStream
によって読み取られます。
オブジェクトの書込み用のObjectOutputStream
(および読取り用のObjectInputStream
)のその他のデフォルトの動作は、パラメータの引き渡しで維持されます。 たとえば、オブジェクトの書込み時のwriteReplace
の呼び出し、およびオブジェクトの読取り時のreadResolve
の呼出しは、RMIのパラメータ整列化および非整列化ストリームで優先されます。
戻り値(または例外)も上記のRMIでのパラメータの引き渡しと同じ方法でObjectOutputStream
サブクラスに書き込まれ、パラメータの転送と同じ方法で置き換えられます。
単純ブートストラップ・ネーム・サーバーがリモート・オブジェクトに対する名前付き参照を保存するために準備されています。 リモート・オブジェクト参照は、java.rmi.Naming
クラスのURLをベースとするメソッドを使って保存できます。
クライアントがリモート・オブジェクトのメソッドを呼び出すときには、クライアントはまずそのオブジェクトに対する参照を取得する必要があります。 リモート・オブジェクトに対する参照は通常、メソッド呼出しのパラメータまたは戻り値として得られます。 RMIシステムは単純ブートストラップ・ネーム・サーバーを提供して指定されたホストのリモート・オブジェクトを得られるようにしています。 java.rmi.Naming
クラスは、URL (Uniform Resource Locator)ベースのメソッドを提供して特定のホストとポートにおける名前とオブジェクトのペアのルックアップ、バインド、リバインド、アンバインドおよびリストを可能にしています。
Copyright © 1997, 2017, Oracle and/or its affiliates. All rights reserved.
目次|前|次