このチュートリアルでは、おなじみの Hello World プログラムの分散システム版を JavaTM の RMI (Remote Method Invocation、リモートメソッド呼び出し) を使って作成する手順を説明します。このチュートリアルを学習するうちに、関連する多くの疑問に直面することでしょう。それらの解決方法については、RMI FAQ や、RMI ユーザの電子メールアーカイブエイリアスで調べることができます。RMI ユーザの電子メールエイリアスを購読するには、ここをクリックしてください。
分散型の Hello World では、アプレットを使って、そのアプレットのダウンロード元のホスト上で稼動している RMI サーバにリモートメソッド呼び出しを行います。アプレットを実行すると、クライアントのブラウザに、「Hello World!」と表示されます。
このチュートリアルの構成は、次のとおりです。
このチュートリアルの実行に必要なファイルは、次のとおりです。Hello.java
- リモートインタフェース
HelloImpl.java
- examples.hello.Hello
を実装するリモートオブジェクトの実装
HelloApplet.java
- リモートメソッド sayHello
を呼び出すアプレット
hello.html
- アプレットを参照する HTML ページ
examples.hello.HelloImpl
を指します。
このチュートリアルで使うソースコードは、次の形式から選択できます。
examples.hello
、ソースディレクトリは $HOME/mysrc/examples/hello
です。
SolarisTM オペレーティング環境でソースファイル用ディレクトリを作成するには、次のコマンドを実行します。
mkdir -p $HOME/mysrc/examples/helloMicrosoft Windows プラットフォームでは、目的のディレクトリに移動してから、次のように入力します。
mkdir mysrc mkdir mysrc¥examples mkdir mysrc¥examples¥hello
ここで行う作業は 3 つあります。
Remote
インタフェースを実装するクラスのインスタンスです。リモートインタフェースには、ほかの JavaTM 仮想マシン* (JVM) から呼び出すメソッドをすべて宣言します。リモートインタフェースには、次の特性があります。
public
として宣言する必要がある。そうしないと、クライアントがリモートインタフェースと同じパッケージ内にある場合を除いて、リモートインタフェースを実装しているリモートオブジェクトをクライアントがロードしようとした時点でエラーが発生する
java.rmi.Remote
インタフェースを継承する
throws
節内で java.rmi.RemoteException
(または RemoteException
のスーパークラス) を宣言する必要がある
HelloImpl
) ではなく、リモートインタフェースの型 (Hello
) として宣言する必要がある
以下は、リモートインタフェース examples.hello.Hello
のインタフェース定義です。このインタフェースに含まれているメソッドは sayHello
1 つだけで、このメソッドは呼び出し側に文字列を返します。
package examples.hello; import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { String sayHello() throws RemoteException; }リモートメソッド呼び出しは、ローカルメソッド呼び出しとは異なる方法でエラーが発生します。これは、ネットワーク通信上の問題とサーバ上の問題によるものです。このため、リモートメソッドは、
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
をスローする必要がある
java.rmi.RemoteException
が実行時例外ではなくチェックされる例外である理由については、rmi-users 電子メールリストの下記のアーカイブを参照してください。
http://java.sun.com/products/jdk/rmi/archives/3490.html
スーパークラスの引数なしのコンストラクタ
super()
への呼び出しは、省略してもデフォルトで発生しますが、この例では Java 仮想マシン (JVM) がクラスの前にスーパークラスを構築することを明確にするために、この呼び出しを省略せずに含めてあります。各リモートメソッドに実装を提供する
リモートオブジェクトの実装クラスは、リモートインタフェースで指定された各リモートメソッドを実装するコードを含みます。次にsayHello
メソッドの実装例を示します。この例では、呼び出し側に「Hello World!」という文字列が返されます。public String sayHello() throws RemoteException { return "Hello World!"; }リモートメソッドに渡す引数、またはリモートメソッドからの戻り値は、Java プラットフォーム用のどのデータ型であっても構いません。さらに、インタフェースjava.io.Serializable
を実装したオブジェクトであれば、オブジェクト型であっても構いません。java.lang
およびjava.util
内のコア Java クラスの大部分は、Serializable
インタフェースを実装しています。RMI では、次のようになります。クラスは、リモートインタフェースで指定されていないメソッドを定義できますが、これらのメソッドは、サービスを実行する仮想マシン内でしか呼び出すことはできず、リモートから呼び出すことはできません。
- デフォルトでは、ローカルオブジェクトはコピーによって渡される。つまり、
static
またはtransient
とマークされたもの以外は、オブジェクトのすべてのデータメンバ (またはフィールド) がコピーされる。直列化のデフォルト動作を変更する方法については、「Java オブジェクト直列化仕様」を参照- リモートオブジェクトは参照によって渡される。リモートオブジェクトへの参照は、実際にはスタブ (クライアント側でのリモートオブジェクトのプロキシ) への参照である。スタブの詳細については、「Java Remote Method Invocation」を参照。スタブの作成法については、「
rmic
を使ってスタブおよびスケルトンを生成する」で説明するセキュリティマネージャを作成およびインストールする
サーバのmain
メソッドは、まず、RMISecurityManager
または独自に定義したセキュリティマネージャの、作成およびインストールを行う必要があります。例を示します。if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); }セキュリティマネージャは、ロードされたクラスが許可されていない操作を行わないことを保証するもので、必ず実行されなければなりません。セキュリティマネージャが指定されない場合は、ローカルの CLASSPATH 内のクラス以外には、RMI クライアントまたはサーバによるクラスのロードは許可されません。 この例では、すでにクライアントブラウザにインストールされているセキュリティマネージャがアプレットで使われるので、セキュリティマネージャはクライアントにインストールされていません。ただし、クライアントがアプレットではなくアプリケーションの場合は、上記と同じ手順でクライアントにセキュリティマネージャをインストールする必要があります。セキュリティマネージャは、JVM でコードをダウンロードするときに必要になります。RMI クライアントでは、RMI サーバとの通信に必要なすべてのカスタムクラスまたはインタフェース以外に、RMI スタブをダウンロードする必要があります。リモートオブジェクトの 1 つ以上のインスタンスを生成する
サーバのmain
メソッドでは、サービスを提供するリモートオブジェクトの実装のインスタンスを、1 つ以上生成する必要があります。例を示します。HelloImpl obj = new HelloImpl();コンストラクタはリモートオブジェクトをエクスポートします。これは、リモートオブジェクトが作成された時点で、そのリモートオブジェクトは着呼を受け入れる準備ができていることを意味します。リモートオブジェクトを登録する
呼び出し側 (クライアント、ピア、またはアプレット) がリモートオブジェクトのメソッドを呼び出すには、呼び出し側はまずリモートオブジェクトへの参照を取得する必要があります。ブートストラップ用に、RMI システムはリモートオブジェクトのレジストリを提供します。これにより、
//host/objectname
という URL 形式の名前をリモートオブジェクトにバインドできます。この URL 形式のobjectname
は単なる文字列名です。RMI レジストリは、単純なサーバ側のネームサービスで、これによりリモートクライアントは、リモートオブジェクトへの参照を取得できます。通常、RMI レジストリは、RMI クライアントが最初に通信するリモートオブジェクトの場所を特定するためだけに使われます。次に、RMI クライアントが最初に通信したオブジェクトが、ほかのオブジェクトを見つける上で必要なアプリケーション固有のサポートを行います。
たとえば、参照は、別のリモートメソッド呼び出しに渡すパラメータ、またはリモートメソッド呼び出しからの戻り値として取得できます。詳細は、「RMI にファクトリパターンを適用する」を参照してください。
リモートオブジェクトがサーバに登録されると、呼び出し側は、そのオブジェクトを名前によって検索して、リモートオブジェクトへの参照を取得できます。そうすれば、そのオブジェクトのメソッドをリモートから呼び出せます。
たとえば、次のコードは HelloServer という名前をそのリモートオブジェクトへの参照にバインドします。
Naming.rebind("//myhost/HelloServer", obj);
rebind
メソッド呼び出しの引数については、次の点に注意してください。セキュリティ上の理由により、アプリケーションがバインド、またはアンバインドできるのは、同一ホスト上で動作中のレジストリに対してだけです。この制限により、クライアントがサーバのリモートレジストリを削除したり上書きしたりすることはありません。ただし、ルックアップはどのホストからでも可能です。
- 最初のパラメータは、URL 形式の
java.lang.String
で、リモートオブジェクトの位置および名前を表す
- URL 形式の文字列にプロトコルを指定する必要はない
myhost
の値をサーバマシンの名前または IP アドレスに変える必要がある。変えなかった場合は、リモートオブジェクトのホストはデフォルトで現在のホストになる。たとえば、「HelloServer」
は、ローカルホスト上で実行中の、名前HelloServer
にバインドされたリモートオブジェクトを参照する有効な名前の文字列である- オプションで、ポート番号を URL 形式の文字列に提供できる。接続する必要のあるレジストリがデフォルトの 1099 ポート以外のポート上で実行されている場合は、ポート番号を指定する必要がある。 たとえば、
「//myhost:1234/HelloServer」
は、HelloServer
リモートオブジェクトの有効な名前文字列である。この文字列には、myhost
ホスト上で実行され、ポート 1234 で入接続呼を待機している RMI レジストリを使って接続できる
- 2 番目のパラメータは、オブジェクトの実装への参照で、このオブジェクトに対してリモートメソッドが呼び出される
- オブジェクトがエクスポートされると、それ以降 RMI ランタイムは、
obj
引数に指定された実際のリモートオブジェクトの参照の代わりに、リモートオブジェクトのスタブへの参照を使う。クライアントがサーバのリモートオブジェクトのレジストリでルックアップを実行すると、実装に対するスタブのインスタンスが直列化されて返される
この例のアプレットは、アプレットを実行したときに「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.lookup
メソッドは、Naming.rebind
メソッドと同様に、URL 形式の java.lang.String
をとる。この例では、アプレットは getCodeBase
メソッドを getHost
メソッドとともに使用して URL 文字列を構築する。Naming.lookup
は、次の作業を行う
Naming.lookup
への引数として提供されたホスト名およびポート番号を使って、レジストリスタブのインスタンスを構築し、サーバのレジストリに接続する
HelloServer
) を使ってレジストリ上のリモート lookup
メソッドを呼び出す
HelloImpl_Stub
のインスタンスを返す
lookup
メソッドは、リモートオブジェクト (HelloImpl
) のスタブインスタンスを受け取り、CLASSPATH またはアプレットのコードベースからスタブクラス (examples.hello.HelloImpl_Stub
) をロードする
Naming.lookup
は、呼び出し側 (HelloApplet
) にスタブを返す
sayHello
を呼び出す
message
という名前の変数に格納する
paint
メソッドを呼び出す。この結果、文字列 Hello World! がアプレットの描画領域に表示される
Naming.lookup
メソッドにパラメータとして渡される、URL 形式の文字列には、サーバのホスト名が含まれている必要があります。サーバのホスト名が含まれていないと、アプレットのルックアップはデフォルトでクライアントに対して実行されるため、アプレットはローカルシステムにアクセスできず、アプレットのホストとの通信だけに制限されます。このため、AppletSecurityManager
は例外をスローします。
Hello World アプレットを参照する Web ページの HTML コードを次に示します。
<HTML> <title>Hello World</title> <center> <h1>Hello World</h1> </center> <applet codebase="myclasses/" code="examples.hello.HelloApplet" width=500 height=120> </applet> </HTML>次の点に注意してください。
codebase
は、Web ページ自体のロード元ディレクトリの下のディレクトリを指定する。通常は、このように相対パスを使うとよい。たとえば、アプレットの HTML によって参照される (アプレットのクラスファイルがある) codebase
ディレクトリが、その HTML ディレクトリの上のディレクトリにある場合は、相対パス「../」を使う
code
属性は、アプレットの絶対パスによるパッケージ名を指定する。この例では、examples.hello.HelloApplet
となる
code="examples.hello.HelloApplet"
$HOME/mysrc/examples/hello
ディレクトリには 4 つのファイルが存在しています。
Hello.java
- Hello
リモートインタフェースのソースコードが含まれる
HelloImpl.java
- HelloImpl
リモートオブジェクトの実装およびアプレットの RMI サーバのソースコードが含まれる
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 を使うことができます。ただし、同一の物理ファイルシステムにアクセスするクライアントとサーバ間以外で通信することはできません。この場合、システム上に最小限の機能を備えた Web サーバを設定すれば、HTTP URL を使うことができます。このサーバは、ここからダウンロードすることもできます。
ここで行う作業は 4 つあります。
ソースファイルをコンパイルする
コンパイルする前に、生成ファイルの配置ディレクトリ$HOME/public_html/myclasses
および開発ディレクトリ$HOME/mysrc/examples/hello
の両方に、開発マシン上のローカルCLASSPATH
からアクセスできることを確認してください。ソースファイルをコンパイルするには、次の
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
の形式のクラスファイルを生成します。Java 2 SDK, v1.2 以降では、
rmic
はデフォルトで-vcompat
フラグをオンにした状態で実行されます。生成されるスタブおよびスケルトンは、次のオブジェクトへのアクセスをサポートします。JDK 1.1 のクライアントをサポートする必要がない場合は、
- JDK 1.1 のクライアントからの
Activatable
でないユニキャストリモートオブジェクト- 1.2 以降のクライアントからのすべての型のリモートオブジェクト
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
に作成されます。生成されたスタブクラスは、リモートオブジェクト自体とまったく同じリモートインタフェースのセットを実装します。つまり、クライアントは、キャストや型チェックに Java プログラミング言語に組み込まれた演算子を使用することができます。また、Java プラットフォーム用に作成されたリモートオブジェクトでは、真のオブジェクト指向の多相性がサポートされます。
HelloImpl_Stub.class
HelloImpl_Skel.class
HTML ファイルを配置ディレクトリに移す
アプレットを参照する Web ページにクライアントがアクセスできるように、hello.html
ファイルを開発ディレクトリからアプレットのcodebase
ディレクトリに移動する必要があります。例を示します。
mv $HOME/mysrc/examples/hello/hello.html $HOME/public_html/
ランタイムパスを設定する
HelloImpl
サーバの実行時に、サーバのローカルCLASSPATH
から $HOME/public_html/myclasses
ディレクトリにアクセスできることを確認しておいてください。
RMI レジストリを起動する
RMI レジストリは、単純なサーバ側のネームサーバで、これによりリモートクライアントは、リモートオブジェクトへの参照を取得できます。通常、RMI レジストリは、アプリケーションが最初に通信するリモートオブジェクトの場所を特定するためだけに使われます。次いで、アプリケーションが最初に通信したオブジェクトが、ほかのオブジェクトを見つける上で必要なアプリケーション固有のサポートを行います。注:
rmiregistry
を開始する前に、レジストリを実行するシェルまたはウィンドウに CLASSPATH が設定されていないこと、あるいは設定されていても、クライアントにダウンロードするクラスへのパス (リモートオブジェクトの実装クラスのスタブを含む) が含まれていないことを確認してください。
rmiregistry
が、その開始時に CLASSPATH 内でスタブクラスを見つけると、サーバのjava.rmi.server.codebase
プロパティは無視されます。その結果、クライアントは、そのリモートオブジェクトのスタブコードをダウンロードできません。RMI を使ったコードのダウンロードについては、チュートリアルの「RMI の使用による動的なコードのダウンロード」を参照してください。サーバ上でレジストリを開始するには、
rmiregistry
コマンドを実行します。このコマンドからは何の出力もありません。通常、バックグラウンドで実行されます。rmiregistry
の詳細は、Solaris 用のrmiregistry
のマニュアルページまたは Win32 用のrmiregistry
のマニュアルページを参照してください。Solaris オペレーティング環境での例:
rmiregistry &Microsoft Windows 95 システムでの例:
start rmiregistry(start コマンドが使えない場合は
javaw
を使う)デフォルトでは、レジストリはポート番号 1099 で実行されます。別のポート上でレジストリを実行するには、コマンド行でポート番号を指定します。たとえば、Microsoft Windows NT システム上のポート 2001 でレジストリを起動するには、次のようにします。
start rmiregistry 20011099 以外のポートでレジストリを実行している場合は、レジストリを呼び出すときに、
java.rmi.Naming
クラスの URL ベースのメソッドに渡す名前の中でポート番号を指定する必要があります。たとえば、この例題で、レジストリをポート番号 2001 で実行する場合、名前「HelloServer」
をリモートオブジェクト参照にバインドする次の呼び出しが必要になります。Naming.rebind("//myhost:2001/HelloServer", obj);リモートインタフェースを変更したり、変更または追加されたリモートインタフェースをリモートオブジェクトの実装で使用する場合は、必ずレジストリをいったん停止してから再起動する必要があります。そうしないと、レジストリでバインドされたオブジェクト参照の型と修正されたクラスとが一致しなくなります。
サーバを起動する
サーバの起動時に、スタブクラスがレジストリ、次いでクライアントに動的にダウンロードされるように、java.rmi.server.codebase
プロパティが指定されている必要があります。コードベースのプロパティを実装スタブの位置に設定して、サーバを実行します。この例題のコードベースのプロパティはディレクトリを参照するので、ダウンロードされる可能性があるその他すべてのクラスもjava.rmi.server.codebase
によって参照されるディレクトリにインストールしておきます。コードベース設定のコマンド行の例については、チュートリアルの「RMI の使用による動的なコードのダウンロード」も参照してください。各
java.rmi.server
プロパティの説明は、ここをクリックしてください。利用可能なすべてのjava.rmi.activation
プロパティの詳細は、ここをクリックしてください。java
のオプションの詳細は、Solaris 用java
のマニュアルページまたは Win32 用java
マニュアルページを参照してください。サンプルコードの実行に問題がある場合は、RMI とオブジェクト直列化の FAQ を参照してください。注: スタブクラスは、クラスがまだローカルで利用できない場合で、
java.rmi.server.codebase
プロパティがサーバ上のクラスファイルの位置に適切に設定されている場合だけ、クライアントの仮想マシンに動的にダウンロードされます。
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
ファイルを使ってアクセス権を適切に指定する方法については、次のドキュメントを参照してください。
デフォルトの Policy の実装とポリシーファイルの構文
コードベースプロパティは、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 内でスタブを検索したため、別の点にも問題があります。出力は、次のようになります。
アプレットを実行する
レジストリおよびサーバがいったん実行されると、アプレットの実行が可能になります。アプレットの実行は、ブラウザに Web ページをロードするか、または次に示すようにappletviewer
によって行います。appletviewer http://myhost/‾myusrname/hello.html &appletviewer を実行すると、次のような出力が画面に表示されます。
* この Web サイトで使用されている用語「Java 仮想マシン」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。
Copyright © 2001 Sun Microsystems, Inc. All Rights Reserved. コメントの送付先: rmi-comments@java.sun.com |