この章ではおなじみの Hello World プログラムの分散システム版を Java の RMI (Remote Method Invocation) を使って作成する手順をステップを追って説明します。
分散バージョンの Hello World はアプレットを使ってサーバのリモートメソッドを呼び出し、ここからプログラムをダウンロードして "Hello World" というメッセージを取り出します。このアプレットが走るとクライアント側にメッセージが表示されます。
これを完成させるには、次の3つのレッスンを実行しなければなりません。
examples.hello、そしてルートディレクトリを $HOME/jdk1.1/mysrc/examples/hello とします。
mkdir $HOME/jdk1.1/mysrc/examples/hello
リモートオブジェクトであることをはっきりさせるために、オブジェクトはリモートインタフェースを実装しますが、このインタフェースは次の特徴を持っています。
java.rmi.Remote インタフェースを拡張する。
java.rmi.RemoteException を宣言しなければならない。
sayHello 、を一つだけ含み、呼び出し側に文字列を返します。
package examples.hello;
public interface Hello extends java.rmi.Remote {
String sayHello() throws java.rmi.RemoteException;
}
HelloImpl.java ファイルは Hello World サーバのためのコードを含んでいます。このコードは上に説明した6つのステップにしたがって記述されています。
package examples.hello;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl
extends UnicastRemoteObject
implements Hello
{
private String name;
public HelloImpl(String s) throws RemoteException {
super();
name = s;
}
public String sayHello() throws RemoteException {
return "Hello World!";
}
public static void main(String args[])
{
// Create and install a security manager
System.setSecurityManager(new RMISecurityManager());
try {
HelloImpl obj = new HelloImpl("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();
}
}
}
HelloImpl です。実装クラスは自分が実装する一つ以上のリモートインタフェースを指定します。オプションとして、自分が拡張するリモートサーバを指定します。この例では java.rmi.server.UnicastRemoteObject がこれにあたります。 HelloImpl クラス宣言を次に示します。
public class HelloImpl implements Hello extends java.rmi.server.UnicastRemoteObject
UnicastRemoteObject を拡張するということは HelloImpl クラスを使って単一の(複製されたものでない)リモートオブジェクトを作成し、そのオブジェクトは通信のために RMI のデフォルトソケットをベースとするトランスポートを使うことを示します。 リモートオブジェクトをリモートでないクラスから拡張する場合には、メソッド UnicastRemoteObject.exportObject を呼び出すことにより陽にリモートオブジェクトをエクスポートしなければなりません。
次に示すのは HelloImpl クラスのコンストラクタで、private な文字列変数 name をリモートオブジェクトの名前で初期化します。
private String name;
public HelloImpl(String s) throws java.rmi.RemoteException {
super();
name = s;
}
super を呼び出すと java.rmi.server.UnicastRemoteObject の引数を持たないコンストラクタが呼び出され、無名ポート上にあるリモートオブジェクトに対する外部からの呼び出しに応じてそのリモートオブジェクトを "export(エクスポート)" する。
java.rmi.RemoteException をスローしなければならない。
super の引数無しコンストラクタが呼び出されますが、この例では Java がクラス作成前にスーパークラスを作成することを明示するために省略せずに載せてあります。
一例として次に sayHello メソッドの実装例を示します。この例では文字列 "Hello World!" を呼び出し側に返します。
public String sayHello() throws RemoteException {
return "Hello World!";
}
java.io.Serializable を実装するものであればオブジェクトであってもかまいません。java.lang と java.util のコアとなるほとんどの Java クラスは Serializable インタフェースを実装しています。
RMISecurityManager かまたは自分自身で定義したものです。例えば、
System.setSecurityManager(new RMISecurityManager());
HelloImpl obj = new HelloImpl("HelloServer");
ブートストラッピングの場合には、RMI システムが URL を基礎とするレジストリを与え、これにより //host/objectname の形式の URL を単なる文字列で指定されたリモートオブジェクトに結び付けることができます。いったんリモートオブジェクトがサーバに登録されると、呼び出し側はオブジェクトを名前で探し、リモートオブジェクトへの参照を得ることができ、そのオブジェクトのメソッドをリモートから呼び出すことができるようになります。
例えば、次のコードは HelloServer という名前のリモートオブジェクトの URL をリモートオブジェクトへの参照と結び付けるものです。
Naming.rebind("//myhost/HelloServer", obj);
package examples.hello;
import java.awt.*;
import java.rmi.*;
public class HelloApplet extends java.applet.Applet {
String message = "";
public void init() {
try {
Hello 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);
}
}
AppletSecurityManager が例外処理にスローしてしまい、その代わりにアプレットホストのみとの交信ができなくなってしまいます。
<HTML> <title>Hello World</title> <center> <h1>Hello World</h1> </center> The message from the HelloServer is: <p> <applet codebase="../.." code="examples.hello.HelloApplet" width=500 height=120> </applet> </HTML>
codebase="../.."この例での codebase は web ページ自体がそこからロードされるディレクトリの2段上のレベルを示している。このように相対パスを使うのは良い方法である。
examples.hello.HelloApplet となる。
$HOME/jdk1.1/mysrc/hello ディレクトリに下には4つのファイルが存在します。
Hello.java, このファイルには Hello リモートインタフェースのソースコードが書かれている。
HelloImpl.java, このファイルには HelloImpl リモートオブジェクトを実装するためのソースコードが書かれており、hello World アプレットのサーバになる。
HelloApplet.java, これはアプレットのソースコードである。
index.html, これは Hello World アプレットを参照する web ページである。
javac や rmic コンパイラを使用するときは、その結果生成するクラスファイルをどのディレクトリに置くかを指定しなければなりません。アプレットの場合には全てのファイルをそのアプレットの codebase ディレクトリに置かなければなりません。この例では $HOME/public_html/codebase になります。
Web サーバの中には、"http://host/‾username/" の形式で構成された HTTP URL によってユーザの public_html ディレクトリにアクセスできるものがあります。もし Web サーバがこの規約をサポートしていなければ "file://home/username/public_html" の形式のファイル URL を使うことになります。
$HOME/public_html/codebase と開発ディレクトリ $HOME/jdk1.1/mysrc/examples/hello がローカル CLASSPATH によって開発マシン上で明確に定義されていることを確認してください。Java ソースファイルをコンパイルするには、次の形式の javac コマンドを実行します。:
javac -d $HOME/public_html/codebase Hello.java HelloImpl.java HelloApplet.java
examples/hello (もし、まだ存在していなければ) をディレクトリ $HOME/public_html/codebase の下に作成します。このコマンドは今作成したディレクトリにファイル Hello.class, HelloImpl.class, and HelloApplet.class を書き込みます。 これらのファイルはそれぞれリモートインタフェース、サーバ、そしてアプレットです。
rmic コンパイラには一つ以上のクラス名を入力として与え、出力として myImpl_Skel.class と myImpl_Stub.class の形式のクラスファイルが作られます。例えば、HelloImpl リモートオブジェクト実装のスタブとスケルトンを作成するには次の形式で rmic を実行します。
rmic -d $HOME/public_html/codebase examples.hello.HelloImpl
-d オプションはコンパイルされたスタブとスケルトンファイルが置かれるルートディレクトリを示します。したがって、上のコマンドを実行すると次のファイルがディレクトリ $HOME/public_html/codebase/examples/hello の下に作成されます。
HelloImpl_Stub.class
HelloImpl_Skel.class
mv $HOME/jdk1.1/mysrc/examples/hello/index.html $HOME/public_html/codebase/examples/hello
HOME/public_html/codebase ディレクトリがサーバのローカル CLASSPATH で認識できることを確認しておいてください。
サーバ上でレジストリをスタートされるためには rmiregistry コマンドを実行します。このコマンドは何も出力せず、一般にバックグランドで実行されます。Windows95、および Windows NT では次のようになります。
start rmiregistry
Solaris 上では次のようになります。
rmiregistry &
start rmiregistry 2001
Naming.rebind("//myhost:2001/HelloServer", obj);
<PARAM name="url" value="//myhost:2001/HelloServer">
java.rmi.server.codebase プロパティが正しく指定され、サーバが作成するリモートオブジェクトへの参照がスタブクラスをクライアントへ動的にダウンロードするための URL を含んでいなければなりません。次のコマンドは、このプロパティを含んで HelloImpl サーバをスタートさせる方法を示します。
java -Djava.rmi.server.codebase=http://myhost/‾myusrname/codebase/ examples.hello.HelloImpl &
codebase URL の最後の / は省略できません。スタブクラスはそれがすでにローカルで利用できる状態になっていない場合に限り、クライアントの仮想マシンへ動的にロードされます。
appletviewer http://myhost/‾myusrname/codebase/examples/hello/index.html &