このチュートリアルでは、おなじみの Hello World プログラムの分散システム版を Java の RMI (Remote Method Invocation, リモートメソッド呼び出し) を使って作成する手順を説明します。このチュートリアルを学習するうちに、関連する多くの疑問に直面することでしょう。それらの解決方法については、RMI FAQ や、RMI ユーザのエイリアスの電子メールアーカイブで調べることができます。RMI ユーザの電子メールエイリアスを購読するには、ここをクリックしてください。
分散型の Hello World では、アプレットを使って、そのダウンロード元のサーバへリモートメソッド呼び出しを行い、メッセージ「Hello World!」を取得します。アプレットを実行すると、クライアントのブラウザに、「Hello World!」と表示されます。
このチュートリアルの構成は、次のとおりです。
このチュートリアルには、次のファイルが必要です。Hello.java
HelloImpl.java
。このファイルは examples.hello.Hello
を実装する
sayHello
を呼び出す Java アプレット HelloApplet.java
hello.html
の HTML コード
examples.hello.HelloImpl
を指します。
このチュートリアル内で使われているソースコードは、次のファイル形式から選ぶことができます。
examples.hello
、ソースディレクトリは $HOME/mysrc/examples/hello
です。
Solaris 上でソースファイル用ディレクトリを作成するには、次のコマンドを実行します。
mkdir -p $HOME/mysrc/examples/hello
Windows プラットフォームでは、目的のディレクトリに移動してから、次のように入力します。
mkdir mysrc
mkdir mysrc¥examples
mkdir mysrc¥examples¥hello
このセクションで行う作業は 3 つあります。
Java では、リモートオブジェクトは、リモートインタフェースを実装するクラスのインスタンスです。リモートインタフェースは、リモートで呼び出す各メソッドを宣言します。リモートインタフェースには、次のような特徴があります。リモートメソッド呼び出しは、ローカルメソッド呼び出しとは異なる方法でエラーが発生します。これは、ネットワーク通信上の問題およびサーバ上の問題がエラーの原因となるためです。このため、リモートメソッドは、
- リモートインタフェースは、public として宣言する必要がある。そうでないと、クライアントがリモートインタフェースと同じパッケージ内にない限り、クライアントがリモートインタフェースを実装しているモートオブジェクトをロードしようとするとエラーが発生する
- リモートインタフェースは、
java.rmi.Remote
インタフェースを継承する- 各メソッドは、アプリケーション固有の例外のほかに、throws 節内で
java.rmi.RemoteException
(またはRemoteException
のスーパークラス) を宣言する必要がある- 引数か戻り値として (直接またはローカルオブジェクト内に埋め込まれて) 渡されるリモートオブジェクトのデータ型は、実装クラス (例:
HelloImpl
) ではなく、リモートインタフェースの型 (Hello
) として宣言する必要がある以下は、リモートインタフェース
examples.hello.Hello
のインタフェース定義です。このインタフェースには、メソッドsayHello
が 1 つだけ含まれ、呼び出し側に文字列を返します。
java.rmi.RemoteException
をスローすることにより通信エラーを報告します。分散システム上のエラーおよび復元の詳細については、「A Note on Distributed Computing」を参照してください。リモートオブジェクトの実装クラスは、少くとも次の条件を備えていなければなりません。
この場合、「サーバ」クラスは、リモートオブジェクトの実装のインスタンスを生成し、そのインスタンスをrmiregistry
内の名前にバインドするmain
メソッドを持ちます。このmain
メソッドは、実装クラス内に含まれる場合も、まったく別のクラスに含まれる場合もあります。この例では、
main
メソッドは、examples.hello.HelloImpl
の一部です。サーバプログラムが行う事柄は、以下のとおりです。HelloImpl.java:
のソースをもとにして、上記の 6 つの各ステップを説明します。
package examples.hello; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements Hello { public HelloImpl() throws RemoteException { super(); } public String sayHello() { return "Hello World!"; }
public static void main(String args[]) {
// Create and install a security manager
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
HelloImpl obj = new HelloImpl();
// Bind this object instance to the name "HelloServer"
Naming.rebind("//myhost/HelloServer", obj);
System.out.println("HelloServer bound in registry");
} catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
}
リモートインタフェースを実装するリモートサービスを利用するクライアントプログラムを記述する
Java では、クラスがインタフェースの実装を宣言すると、クラスとコンパイラの間で契約が結ばれます。この契約によって、クラスは、実装するインタフェース内で宣言された各メソッドシグニチャーに対して、メソッドの本体または定義を提供することを約束します。インタフェースメソッドは、暗黙のうちにpublic
およびabstract
に設定されているため、実装クラスが契約を果たさない場合、クラスは定義に基づきabstract
になります。そのクラスがabstract
として宣言されていない場合は、コンパイラによってそのことが指摘されます。この例の実装クラスは、
examples.hello.HelloImpl
です。このクラスは、どのリモートインタフェースを実装するのかを宣言します。以下は、HelloImpl
クラスの宣言です。実装クラスはリモートクラスを継承できます。この例では、リモートクラスは、
public class HelloImpl extends UnicastRemoteObject implements Hello
java.rmi.server.UnicastRemoteObject
です。UnicastRemoteObject
を継承することにより、HelloImpl
クラスから以下の機能を持つリモートオブジェクトを作成できます。常時稼動するのではなく、クライアントの要求時に起動 (作成) できるリモートオブジェクトが必要な場合は、このチュートリアルを終了したあとで、「リモートオブジェクトの起動」を参照してください。また、RMI がデフォルトで使う TCP ソケットではなく、独自の通信プロトコルを使う方法については、「カスタム RMI ソケットファクトリの作成」で学ぶことができます。
- 通信に RMI のデフォルトソケットをベースとするトランスポートを使用する
- 常時稼動している
リモートオブジェクトのコンストラクタを定義する
リモートクラスのコンストラクタが提供する機能は、リモートでないクラスのコンストラクタが提供する機能と同じで、新しく作成されたクラスのインスタンス変数を初期化して、そのコンストラクタを呼び出したプログラムにクラスのインスタンスを返します。さらに、リモートオブジェクトのインスタンスは「エクスポート」される必要があります。リモートオブジェクトをエクスポートすると、そのオブジェクトは、匿名ポート上のリモートオブジェクトへの着信呼び出しを監視することによって、着信するリモートメソッド要求を受け入れることができるようになります。
java.rmi.server.UnicastRemoteObject
またはjava.rmi.activation.Activatable
を継承すると、クラスは作成時に自動的にエクスポートされます。
UnicastRemoteObject
またはActivatable
以外のクラスからリモートオブジェクトを継承する場合は、クラスのコンストラクタ (または適切な別の初期化メソッド) からUnicastRemoteObject.exportObject
メソッドまたはActivatable.exportObject
メソッドを呼び出すことにより、明示的にリモートオブジェクトをエクスポートする必要があります。オブジェクトのエクスポートは、
java.rmi.RemoteException
をスローする可能性があるため、コンストラクタがほかに何も行わない場合でも、RemoteException
をスローするコンストラクタを定義する必要があります。コンストラクタを定義しなかった場合は、javac
は、次のエラーメッセージを生成します。復習: リモートオブジェクトの実装クラスが行う必要のある事柄は、以下のとおりです。HelloImpl.java:13: Exception java.rmi.RemoteException must be caught, or it must be declared in the throws clause of this method.
super();
^
1 error
以下に、
- リモートインタフェースを実装する
- オブジェクトをエクスポートして、着信するリモートメソッド呼び出しを受け入れ可能にする
- 最低でも、
java.rmi.RemoteException
をスローするようにコンストラクタを定義するexamples.hello.HelloImpl
クラスのコンストラクタを示します。次の点に注意してください。
public HelloImpl() throws RemoteException {
super();
}
スーパークラスの引数なしのコンストラクタ
super
メソッド呼び出しは、リモートオブジェクトをエクスポートするjava.rmi.server.UnicastRemoteObject
の引数なしのコンストラクタを呼び出す- 通信リソースが利用できない場合は、RMI がリモートオブジェクトを構築中にエクスポートしようとすると失敗する可能性があるため、コンストラクタは
java.rmi.RemoteException
をスローする必要があるsuper()
への呼び出しは、省略してもデフォルトで発生しますが、この例では Java VM がクラスの前にスーパークラスを構築することを明確にするために、この呼び出しを省略せずに含めてあります。各リモートメソッドに実装を提供する
リモートオブジェクトの実装クラスは、リモートインタフェースで指定された各リモートメソッドを実装するコードを含みます。たとえば、次にsayHello
メソッドの実装例を示します。この例では、呼び出し側に「Hello World!」という文字列が返されます。リモートメソッドに渡す引数、またはリモートメソッドからの戻り値は、Java の持つどの変数型でも構いません。また、インタフェース
public String sayHello() throws RemoteException { return "Hello World!"; }
java.io.Serializable
を実装するオブジェクトであれば、どのオブジェクトであっても構いません。java.lang
およびjava.util
のほとんどのコア Java クラスは、Serializable
インタフェースを実装しています。クラスは、リモートインタフェースで指定されていないメソッドを定義できますが、これらのメソッドは、サービスを実行する Virtual Machine 内でしか呼び出すことはできず、リモートから呼び出すことはできません。
- デフォルトでは、ローカルオブジェクトはコピーによって渡される。つまり、
static
またはtransient
であるもの以外は、オブジェクトのすべてのデータメンバ (またはフィールド) がコピーされる。デフォルトの直列化動作を変更する方法については、「Java オブジェクト直列化仕様」を参照- リモートオブジェクトは参照によって渡される。リモートオブジェクトへの参照は、実際には、リモートオブジェクトに対するクライアント側のプロキシである、スタブへの参照である。スタブの詳細は、「Java リモートメソッド呼び出し仕様」で説明する。スタブの作成法については、このセクションの「
rmic
を使ってスタブまたはスケルトンを生成する」で説明するセキュリティマネージャを作成およびインストールする
サーバのmain
メソッドは、まず最初にセキュリティマネージャの作成とインストールを行う必要があります。セキュリティマネージャは、RMISecurityManager
か、または独自に定義したものを使用します。以下に例を示します。セキュリティマネージャは、ロードされたクラスが許可されていない操作を行わないことを保証するもので、必ず実行されなければなりません。セキュリティマネージャが指定されない場合は、ローカルの CLASSPATH 内のクラス以外には、RMI クライアントまたはサーバによるクラスのロードは許可されません。
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
リモートオブジェクトの 1 つ以上のインスタンスを生成する
サーバのmain
メソッドは、サービスを提供するリモートオブジェクトの実装の、1 つ以上のインスタンスを生成する必要があります。次に例を示します。コンストラクタはリモートオブジェクトをエクスポートします。これは、リモートオブジェクトが作成されると、そのリモートオブジェクトは着信する呼び出しを受け入れる準備ができることを意味します。
HelloImpl obj = new HelloImpl();
リモートオブジェクトを登録する
呼び出し側 (クライアント、ピア、またはアプレット) がリモートオブジェクトのメソッドを呼び出すには、呼び出し側はまずリモートオブジェクトへの参照を取得する必要があります。ブートストラップ用に、RMI システムはリモートオブジェクトのレジストリを提供します。これにより、
//host/objectname
という URL 形式の名前をリモートオブジェクトにバインドできます。この URL 形式の名前のobjectname
の単なる文字列名です。RMI レジストリは、単純なサーバ側のネームサーバです。RMI レジストリにより、リモートクライアントはリモートオブジェクトへの参照を取得できます。通常、RMI レジストリは、RMI クライアントが最初に通信するリモートオブジェクトの場所を特定するためだけに使われます。次に、RMI クライアントが最初に通信したオブジェクトが、ほかのオブジェクトを見つける上で必要なアプリケーション固有のサポートを行います。
たとえば、参照は、別のリモートメソッド呼び出しに渡すパラメータ、またはリモートメソッド呼び出しからの戻り値として取得できます。詳細は、「RMI にファクトリパターンを適用する」を参照してください。
いったん、リモートオブジェクトがサーバに登録されると、呼び出し側は、オブジェクトを名前で探して、リモートオブジェクトへの参照を得ることができ、そのオブジェクトのメソッドをリモートから呼び出せるようになります。
たとえば、次のコードは HelloServer という名前をそのリモートオブジェクトへの参照にバインドします。
Naming.rebind("//myhost/HelloServer", obj);
rebind
メソッド呼び出しの引数については、次の点に注意してください。セキュリティ上の理由により、アプリケーションがバインド、またはアンバインドできるのは、同一ホスト上で動作中のレジストリに対してだけです。この制限により、クライアントがサーバのリモートレジストリを削除したり上書きしたりすることはありません。ただし、ルックアップはどのホストからでも可能です。
- 最初のパラメータは、URL 形式の
java.lang.String
で、リモートオブジェクトの位置および名前を表す
myhost
の値をサーバマシンの名前または IP アドレスに変える必要がある。ホストが URL から省略された場合は、デフォルトで現在のホストになるので、URL にプロトコルを指定する必要はない。たとえば、HelloServer
などとする- オプションで、ポート番号を URL に提供できる。たとえば、
//myhost:1234/HelloServer
のようにする。ポートのデフォルトは 1099 である。ポート番号を指定する必要があるのは、サーバがデフォルトの 1099 以外のポートでレジストリを作成する場合だけである- 2 番目のパラメータは、リモートメソッドが呼び出されるオブジェクトの実装の参照である
- 2 番目のパラメータは、リモートメソッドが呼び出されるオブジェクトの実装の参照である
obj
引数が指定する実際のリモートオブジェクトへの参照の代りに使う。HelloImpl
のインスタンスのようなリモート実装オブジェクトは、それらが作成された Virtual Machine を離れることはない。このため、クライアントがサーバのリモートオブジェクトのレジストリでルックアップを行なった場合は、その実装のスタブを含むオブジェクトが返される分散システム版 Hello World のアプレット部分は、Hello World! という文字列を得るために
sayHello
メソッドをリモートから呼び出します。実際にアプレットを実行すると、この文字列が表示されます。次にこのアプレットのコードを示します。
package examples.hello;
import java.applet.Applet;
import java.awt.Graphics;
import java.rmi.Naming;
import java.rmi.RemoteException;
public class HelloApplet extends Applet {
String message = "blank";
// "obj" is the identifier that we'll use to refer
// to the remote object that implements the "Hello"
// interface
Hello obj = null;
public void init() {
try {
obj = (Hello)Naming.lookup("//" +
getCodeBase().getHost() + "/HelloServer");
message = obj.sayHello();
} catch (Exception e) {
System.out.println("HelloApplet exception: " +
e.getMessage());
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawString(message, 25, 50);
}
}
- アプレットは、最初にサーバホストの
rmiregistry
からリモートオブジェクトの実装 (HelloServer として公示されている) への参照を取得する。Naming.rebind
メソッドは、Naming.lookup
メソッドと同様に、URL 形式のjava.lang.String
をとる。この例では、アプレットはgetCodeBase
メソッドをgetHost
メソッドとともに使用して URL 文字列を構築する。Naming.lookup
は、次の作業を行う
Naming.lookup
への引数として提供されたホスト名およびポート番号を使って、レジストリスタブのインスタンスを構築し、サーバのレジストリに接続する- レジストリスタブから、URL の名前要素 (
HelloServer
) を使ってレジストリ上のリモートlookup
メソッドを呼び出す
- レジストリは、その名前にバインドされた
HelloImpl_Stub
のインスタンスを返す- リモートオブジェクトの実装 (
HelloImpl
) のスタブインスタンスを受け取り、CLASSPATH またはスタブのコードベースからスタブクラス (examples.hello.HelloImpl_Stub
) をロードするNaming.lookup
は、呼び出し側 (HelloApplet
) にスタブを返す- アプレットは、サーバのリモートオブジェクトの
sayHello
メソッドを呼び出す
- RMI は、応答文字列 Hello World! を直列化して返す
- RMI は、直列化された文字列を復元し、
message
という名前の変数に格納する
- アプレットは、
paint
メソッドを呼び出す。この結果、文字列 Hello World! がアプレットの描画領域に表示されるNaming.lookup
メソッドにパラメータとして渡される、構築された URL 文字列には、サーバのホスト名が含まれている必要があります。サーバのホスト名が含まれていないと、アプレットのルックアップはデフォルトでクライアントに対して実行されるため、アプレットはローカルシステムにアクセスできず、アプレットのホストとの通信だけに制限されます。このため、AppletSecurityManager
は例外をスローします。Hello World アプレットを参照する Web ページの HTML コードを次に示します。
次の点に注意してください。
<HTML>
<title>Hello World</title>
<center> <h1>Hello World</h1> </center>
The message from the HelloServer is:
<p>
<applet codebase="myclasses/"
code="examples.hello.HelloApplet"
width=500 height=120>
</applet>
</HTML>
- クラスのダウンロード元のマシン上で HTTP サーバが実行されていなければならない
- この例のコードベースは、Web ページ自体のロード元ディレクトリの下のディレクトリを指定する。通常は、このように相対パスを使うとよい。たとえば、アプレットの HTML によって参照される (アプレットのクラスファイルがある)
codebase
ディレクトリが、その HTML ディレクトリの上のディレクトリにある場合は、「../」のような相対パスを使うことができる- アプレットの
code
属性は、アプレットの絶対パスによるパッケージ名を指定する。この例では、examples.hello.HelloApplet
となる
code="examples.hello.HelloApplet"
$HOME/mysrc/examples/hello
ディレクトリには 4 つのファイルが存在しています。
Hello.java
- Hello
リモートインタフェースのソースコードを含む
HelloImpl.java
- HelloImpl
リモートオブジェクトの実装である Hello World アプレットのサーバのソースコード
HelloApplet.java
- アプレットのソースコード
hello.html
- Hello World アプレットを参照する Web ページ
.java
この節では、.class
ファイルを作成します。次に rmic
コンパイラを実行してスタブとスケルトンを作成します。スタブとは、リモートオブジェクトのクライアント側のプロキシのことで、RMI 呼び出しをサーバ側のディスパッチャーに転送します。次に、ディスパッチャーが、呼び出しを実際のリモートオブジェクトの実装に転送します。
javac
や rmic
コンパイラを使うときは、生成されるクラスファイルをどのディレクトリに置くかを指定しなければなりません。アプレットの場合には、すべてのファイルをアプレットのコードベースディレクトリに置く必要があります。この例では、コードベースディレクトリは $HOME/public_html/myclasses
になります。
Web サーバの中には、http://host/‾username/ の形式で構成された HTTP URL 経由でユーザの public_html ディレクトリにアクセスできるものがあります。Web サーバがこの規約をサポートしていない場合、file://home/username/public_html の形式でファイル URL を使うことになります。
ここで行う作業は 4 つあります。
$HOME/public_html/myclasses
および開発ディレクトリ $HOME/mysrc/examples/hello
の両方に、開発マシン上のローカル CLASSPATH
からアクセスできることを確認してください。
Java ソースファイルをコンパイルするには、次の javac
コマンドを実行します。
javac -d $HOME/public_html/myclasses
Hello.java HelloImpl.java HelloApplet.java
examples/hello
をディレクトリ $HOME/public_html/myclasses
内に作成します (まだ存在していない場合)。次に、このコマンドは、examples/hello
ディレクトリにファイル Hello.class
、HelloImpl.class
、および HelloApplet.class
を書き込みます。これらのファイルはそれぞれ、リモートインタフェース、実装、アプレットです。javac
オプションの詳細は、Solaris 用 javac
のマニュアルページまたは Win32 用 javac
のマニュアルページを参照してください。
rmic
を使ってスタブまたはスケルトンを生成するmy.package.MyImpl
のようなリモートオブジェクトの実装を含んだ、コンパイルするクラスファイルの絶対パスによるパッケージ名を指定して、rmic
を実行します。rmic
コマンドは、引数に 1 つ以上のクラス名をとり、MyImpl_Skel.class
および MyImpl_Stub.class
の形式のクラスファイルを生成します。
JDK 1.2 では、rmic
はデフォルトで -vcompat
フラグをオンにした状態で実行されます。生成されるスタブおよびスケルトンは、次のオブジェクトへのアクセスをサポートします。
Activatable
でないユニキャストリモートオブジェクト
rmic
に -v1.2
オプションを指定して実行しても構いません。rmic
オプションの詳細については、Solaris 用 rmic
のマニュアルページまたは Win32 用 rmic
のマニュアルページを参照してください。
たとえば、HelloImpl リモートオブジェクトの実装のスタブおよびスケルトンを作成するには、次のように rmic
を実行します。
rmic -d $HOME/public_html/myclasses
examples.hello.HelloImpl
-d
オプションは、コンパイルされたスタブおよびスケルトンクラスファイルが置かれるルートディレクトリを示します。このため、上のコマンドを実行すると、次のファイルがディレクトリ $HOME/public_html/myclasses/examples/hello
に作成されます。
HelloImpl_Stub.class
HelloImpl_Skel.class
hello.html
ファイルを開発ディレクトリからアプレットの codebase
ディレクトリに移動する必要があります。たとえば次のコマンドを実行します。
mv $HOME/mysrc/examples/hello/hello.html $HOME/public_html/
HelloImpl
サーバの実行時に、サーバのローカル CLASSPATH
から $HOME/public_html/codebase
ディレクトリにアクセスできることを確認しておいてください。RMI レジストリを起動する
RMI レジストリは、単純なサーバ側のネームサーバで、これによりリモートクライアントは、リモートオブジェクトへの参照を取得できます。通常、RMI レジストリは、アプリケーションが最初に通信するリモートオブジェクトの場所を特定するためだけに使われます。次いで、アプリケーションが最初に通信したオブジェクトが、ほかのオブジェクトを見つける上で必要なアプリケーション固有のサポートを行います。注: rmiregistry を起動する前に、
registry
を実行するシェルとウィンドウのどちらにも、CLASSPATH が設定されていない、または設定されていても、クライアントにダウンロードするクラス (リモートオブジェクトの実装クラスのスタブを含む) へのパスが含まれていないことを確認してください。
rmiregistry
を起動し、rmiregistry
が CLASSPATH 内にスタブクラスを見つけた場合は、サーバのjava.rmi.server.codebase
プロパティは無視されます。その結果、クライアントは、そのリモートオブジェクトのスタブコードをダウンロードできません。サーバ上でレジストリを開始するには、
rmiregistry
コマンドを実行します。このコマンドは、何も出力せず、通常はバックグラウンドで実行されます。rmiregistry
の詳細については、Solaris 用rmiregistry
のマニュアルページまたは Win32 用rmiregistry
のマニュアルページを参照してください。
たとえば、Solaris 上では次のようになります。
rmiregistry &
たとえば、Windows 95 または Windows NT 上では次のようになります。
(start コマンドが使えない場合は
start rmiregistry
javaw
を使う)デフォルトでは、レジストリはポート番号 1099 で実行されます。別のポート上でレジストリを実行するには、コマンド行でポート番号を指定します。たとえば、Windows NT システム上のポート 2001 でレジストリを起動するには、次のようにします。
デフォルト以外のポートでレジストリを実行している場合は、レジストリを呼び出すときに、
start rmiregistry 2001
java.rmi.Naming
クラスの URL ベースのメソッドに渡す名前の中でポート番号を指定する必要があります。たとえば、例題 Hello World で、レジストリをポート番号 2001 で実行する場合、HelloServer の URL をリモートオブジェクト参照にバインドする次の呼び出しが必要になります。リモートインタフェースを変更したり、変更または追加されたリモートインタフェースをリモートオブジェクトの実装で使用する場合は、必ずレジストリをいったん停止してから再起動する必要があります。そうしないと、レジストリでバインドされたオブジェクト参照の型と修正されたクラスとが一致しなくなります。
Naming.rebind("//myhost:2001/HelloServer", obj);
サーバを起動する
サーバの起動時に、スタブクラスがレジストリ、次いでクライアントに動的にダウンロードされるように、java.rmi.server.codebase
プロパティが指定されている必要があります。コードベースプロパティを実装スタブの位置に設定して、サーバを実行します。コードベースプロパティは、単一のディレクトリしか参照できないので、ダウンロードする可能性のあるほかのクラスも、java.rmi.server.codebase
が参照するディレクトリにインストールしておいてください。各
java.rmi.server
プロパティの説明は、ここをクリックしてください。利用可能なすべてのjava.rmi.activation
プロパティの詳細は、ここをクリックしてください。java
オプションの詳細は、Solaris 用java
のマニュアルページまたは Win32 用java
マニュアルページを参照してください。サンプルコードの実行に問題がある場合は、RMI とオブジェクト直列化の FAQ を参照してください。注: スタブクラスは、クラスがまだローカルで利用できない場合、および
java.rmi.server.codebase
プロパティがサーバ上のクラスファイルの位置に適切に設定されている場合だけ、クライアントの Virtual Machine に動的にダウンロードされます。
java
コマンド、プロパティの「名前」=「値」の 2 つのペア (codebase
プロパティには「-D」から最後の「/」まで空白文字がないことに注意)、および絶対パスで指定されたサーバプログラムのパッケージ名の 4 つをこの順序で同じコマンド行に記述する必要があります。 空白文字は、java
という語の直後に 1 つと、2 つのプロパティの間に 1 つと、(ブラウザまたは紙上では見にくいのですが)examples
という語の直前に 1 つ必要です。次のコマンドは、java.rmi.server.codebase
およびjava.security.policy
プロパティを指定して、HelloImpl
サーバを開始する方法を示します。
java -Djava.rmi.server.codebase=http://myhost/‾myusrname/myclasses/ -Djava.security.policy=$HOME/mysrc/policy examples.hello.HelloImpl
このコードをシステム上で実行するには、policy
ファイルの位置を、サンプルソースコードをインストールしたシステム上のディレクトリの位置に変更する必要があります。 注: ここでは、例を単純にするために、任意の場所にいる任意のユーザにグローバルなアクセス権を与える policy ファイルを使っています。このポリシーファイルは、実際の開発環境では使わないでください。java.security.policy
ファイルを使ってアクセス権を適切に指定する方法については、次のドキュメントを参照してください。
http://java.sun.com/products/jdk/1.2/docs/guide/security/PolicyFiles.html
http://java.sun.com/products/jdk/1.2/docs/guide/security/permissions.html
コードベースプロパティは、URL として解釈処理されます。このため、プロパティは「
http://aHost/somesource/
」か「file:/myDirectory/location/
」の形式、またはオペレーティングシステムによってはfile:///myDirectory/location/
("file:" のあとにスラッシュが 3 つ) の形式で指定する必要があります。
これらのサンプル URL の各文字列には、末尾に「/」があることに注意してください。実装がクラス定義を適切に解釈処理 (検索) するためには、java.rmi.server.codebase
プロパティで指定する URL に末尾のスラッシュを記述する必要があります。
codebase
プロパティ上の末尾のスラッシュを忘れたり、ソースファイルで指定された位置にクラスファイルが見つからない (ダウンロード可能でない) 場合、またはプロパティ名を間違って入力した場合は、java.lang.ClassNotFoundException がスローされます。この例外は、リモートオブジェクトをrmiregistry
にバインドしようとした場合、または最初のクライアントがそのオブジェクトのスタブにアクセスしようとした場合にスローされます。後者の場合は、rmiregistry
が CLASSPATH 内でスタブを検索したため、別の点にも問題があります。出力は、次のようになります。
HelloServer bound in registry
アプレットを実行する
レジストリおよびサーバがいったん実行されると、アプレットの実行が可能になります。アプレットの実行は、ブラウザに Web ページをロードするか、または次に示すようにappletviewer
によって行います。appletviewer を実行すると、次のような出力が画面に表示されます。
appletviewer http://myhost/‾myusrname/hello.html &