|  | 
 | ドキュメントの目次 | 
Solaris オペレーティングシステム (Solaris OS) では、インターネットサービスデーモンの inetd が、システムブート時にサービスを起動する代替手段になります。インターネットの標準サービスに対するサーバプロセスであるこのデーモンを、必要に応じてサービスを起動するように構成することができます。インターネットサービスデーモンの詳細については、Solaris OS の inetd(1M) のマニュアルページを参照してください。
J2SE 5.0 リリースでは、inetd を構成して、必要に応じて Java RMI サービスを起動することができます。ただし、アプリケーションとその構成要素のサービスが inetd から起動されるようにするには、アプリケーションで特別な技法を使う必要があります。まず、サービスプログラムで、inetd から継承される I/O ソケットを使用できるようにエクスポートされる、ローカルレジストリを作成する必要があります。次に、この特別にエクスポートされたレジストリ内にサービスのプロキシをバインドして、クライアントがサービスを検索できるようにします。このサービスプログラムの完成後、inetd を構成することができます。この構成によって、クライアントがサービスのローカルレジストリに接続して名前でサービスを検索するときに、このプログラムが起動されます。
このチュートリアルでは、まず特別にエクスポートされるローカルレジストリを使用したサービスプログラムを構築する方法を説明します。その結果、クライアントがサービスのローカルレジストリに接続されるときに inetd からサービスを起動することができるようになります。
次に、サービスプログラムを起動するための、inetd の構成方法を説明します。inetd に使用される /etc/inetd.conf および /etc/services の 2 つの構成ファイルには、それぞれエントリを追加する必要があります。これらのファイルを編集するには、サービスが実行されるマシンへのルートアクセス権が必要です。
inetd を再構成したあとで、正しく機能するかどうかを確認するためにテストする必要があります。
このチュートリアルでは、次の手順を実行します。
サーバ側の実装には、次のソースファイルを使います。
ServiceInterface.java:サービス用リモートインタフェース
InitializeRegistry.java:継承チャネルを使用してレジストリを作成/エクスポートするユーティリティ
Server.java:サービスプログラムServiceInterfaceインタフェースで、単一の引数messageをとり、かつメッセージが受信されたという確認応答を返すように指定された単一メソッドsendMessage付きのリモートインタフェースを定義します。
InitializeRegistryクラスで、static ユーティリティメソッドのinitializeWithInheritedChannelを定義します。このメソッドは、レジストリを作成してエクスポートし (継承チャネルがある場合にはそれを使用)、クライアントが検索できるように、そのレジストリ内でリモートサービスのプロキシをバインドします。
Serverサービスプログラムで、ServiceInterfaceインタフェースを実装し、サービスの実行用に static メソッドmainを定義します。static メソッドmainで、次の操作が実行されます。
- サービスプログラムが
inetdから起動された場合にエラー出力が失われることを防ぐために、System.errの出力をファイルにリダイレクトする- ローカルレジストリのポート番号を示すオプションの引数を解析する
- 匿名ポートにサーバを作成し、エクスポートする
- サービスのプロキシ、レジストリ内のサービスの名前、およびプログラムが
inetdではなくコマンド行から実行された場合にはポート番号も指定して、InitializeRegistry.initializeWithInheritedChannelユーティリティメソッドを呼び出す
readyメッセージを出力する- 待機する
実装の中でもっとも興味深い部分は、
initializeWithInheritedChannelユーティリティメソッド内にあります。このメソッドでは、仮想マシンを起動したプロセスから継承されたチャネル (たとえばjava.nio.channels.SocketChannelまたはjava.nio.channels.ServerSocketChannel) をアプリケーションが取得できるようにする、System.inheritedChannelメソッドを使います。この継承チャネルは、SocketChannelの場合は単一の着信接続を行うため、ServerSocketChannelの場合は複数の着信接続を受け入れるために使用できます。このようにして、inetdによって起動されたアプリケーションは、inetdから継承されたSocketChannelまたはServerSocketChannelを取得することができます。
initializeWithInheritedChannelユーティリティメソッドは、継承チャネルの取得のためにまずSystem.inheritedChannelメソッドを呼び出します。継承チャネルは、nullまたはServerSocketChannelまたはIOExceptionをスローするメソッドのいずれかである必要があります。継承チャネルが
nullの場合、継承されたチャネルがないことを示します。つまり、プログラムはコマンド行から実行されました。この場合、initializeWithInheritedChannelメソッドは指定されたポート (ゼロ以外) のレジストリを単純にエクスポートし、そのレジストリ内の指定されたサービスプロキシをバインドします。継承チャネルが
ServerSocketChannelインスタンスの場合は、プログラムがinetdから起動されています。その場合、initializeWithInheritedChannelメソッドはRMIServerSocketFactory付きでレジストリをエクスポートします。このファクトリのcreateServerSocketメソッドは、指定されたプロキシがレジストリ内にバインドされるまで、継承されたServerSocketChannelからの要求の受け入れを遅らせるために、ServerSocketを返します。プログラムが
inetdから起動された場合は、サービスのプロキシがローカルレジストリ内にバインドされるまで、レジストリは、継承されたServerSocket上の着信接続を何も受け入れることができないということが重要な点です。サービスがレジストリ内にバインドされる前に接続が受け入れられた場合、クライアントは、サービスプロキシの検索をしようとしてjava.rmi.NotBoundExceptionを受け取ることがあります。サービスがレジストリ内にバインドされるまで要求の受け取りを遅らせる
ServerSocketの実装方法の詳細については、InitializeRegistryクラスに定義済みの入れ子にされた、private クラスDelayedAcceptServerSocketを参照してください。次のようにして、サービスプログラムをコンパイルします。
classDir は、この例のクラスパスです。% javac -d classDir ServiceInterface.java InitializeRegistry.java Server.javaJ2SE 5.0 より前のリリース上で実行されるクライアントからサービスにアクセスする必要がある場合は、
rmicを使用してリモートサービス用のスタブを作成する必要があります。次の 3 つの項で、サービスプログラムを起動するように
inetdを設定する方法を説明します。
/etc/inetd.conf の構成
/etc/inetd.conf構成ファイルには、inetdがソケット経由で要求を受け取ったときに起動されるサービス用のエントリが含まれています。この構成ファイルの形式についての詳細は、Solaris OS のinetd.conf(4)のマニュアルページを参照してください。サービスプログラムを起動するように
inetdを構成するには、次のエントリを/etc/inetd.conf構成ファイルに追加します (マシンへのルートアクセス権が必要)。jreHome はインストール済みの JRE へのパス、classpath はこの例へのクラスパスです。example-server stream tcp wait nobody jreHome/bin/java \ java -classpath classpath example.inetd.Server
nobody以外のユーザとしてプログラムを実行する必要がある場合は、nobodyを、プログラムを実行する必要のあるユーザ ID に置き換えてください。
/etc/services の構成次に、サービスを参照する名前の
example-serverを、/etc/services構成ファイル内にサービスとして指定する必要があります。この構成ファイルの形式についての詳細は、Solaris OS のservices(4)のマニュアルページを参照してください。サービスとして
example-serverを指定するには、次のエントリを/etc/services構成ファイルに追加します (マシンへのルートアクセス権が必要)。port は、サービスのローカルレジストリ用のポート番号です。example-server port/tcp
inetd に新しい構成を読ませるここまでで構成ファイルが変更されたので、
inetdは新しい構成を読む必要があります。その結果、構成されたサービスに対応する適切なポートで要求を待機できるようになります。ただし最初に、サービスプログラムがまだ実行されていないことを確認する必要があります。これを行うには、次のコマンドを実行します。
上記コマンドによってサービスプログラムのために実行されている% ps -ef | grep example.inetd.Serverjavaプロセスに関する情報が表示されない場合は、プログラムが実行されていないことになります。情報が表示された場合は、作業を続行する前に、まずそのプログラムを終了させる必要があります。次に、
inetdが新しい構成を読む必要があります。inetdに構成を読ませるには、実行中のinetdプロセスにハングアップの信号を送信する必要があります。まず、次のコマンドを実行して、実行中のinetdプロセスのプロセス ID を調べます。このコマンドによって、次のように表示されます。% ps -ef | grep inetdこの例のroot 171 1 0 Sep 30 ?0:02 /usr/sbin/inetd -sinetdのプロセス ID は、171です。これで、次のコマンドにプロセス ID を指定して実行すると (ルートアクセス権が必要)、inetdプロセスにハングアップ信号を送信することができます。これで、クライアントが上記のように構成されたポートに接続しようとしたときにサービスプログラムを起動するための設定が、% kill -HUP 171inetdに対してすべて行われました。
inetdが正しく構成されたことをテストするために、上記のように構成されたポート上のレジストリ内でサービスを検索し、そのサービス上でメソッドを呼び出す単純なクライアントプログラムを実行することができます。構成が正しい場合は、検出したサービスのローカルレジストリに接続しようとすることで、inetdがサービスプログラムを起動します。次に、サービスを検索してそのサービス上でメッセージを送信するメソッドを呼び出す、単純なプログラムを示します。このプログラムは、3 つのコマンド行引数、つまりサービスのローカルレジストリのホストおよびポート番号、サービスに送信するメッセージをとります。
package example.inetd; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class Client { public static void main(String[] args) throws Exception { int port = 0; String host = ""; String message = ""; if (args.length > 2) { host = args[0]; try { port = Integer.parseInt(args[1]); if (port == 0) { goodbye("nonzero port argument required", null); } } catch (NumberFormatException e) { goodbye("malformed port argument", e); } message = args[2]; } else { usage(); } Registry registry = LocateRegistry.getRegistry(host, port); ServiceInterface proxy = (ServiceInterface) registry.lookup("ServiceInterface"); System.out.println("sending message:" + message); System.out.println("received message from proxy: " + proxy.sendMessage(message)); System.out.println("done."); } private static void goodbye(String message, Exception e) { System.err.println("Client:" + message + (e != null ? ": " : "")); if (e != null) { e.printStackTrace( ); } System.exit(1); } private static void usage() { System.err.println("Client <host> <port> <message>"); System.exit(1); } }クライアント用ソースのすべて (コメントも含めて) は、次のとおりです。
ServiceInterface.java:サービス用リモートインタフェース
Client.java:クライアントプログラム次のようにして、このプログラムをコンパイルし、実行します。
classDir はこの例のクラスパス、host はサービスがそこで実行されるように構成されたホスト、port は% javac -d classDir ServiceInterface.java Client.java % java -classpath classDir example.inetd.Client host port "message"/etc/servicesファイル内でサービス用に構成されたポート、message はサービスに送信される文字列です。クライアントプログラムがメッセージを送信し、サービスプログラムからメッセージを受信したことが表示されれば、サービスプログラムが
inetdから正しく起動されたことになります。クライアントプログラムがハングアップするか例外のトレースを出力した場合は、サービスプログラムによって作成された出力をチェックしてください。サービスプログラムは、
System.errに書き出されたすべての出力をjava.io.tmpdirプロパティで指定されたディレクトリ内のファイルにリダイレクトします。通常このディレクトリは、Solaris OS の/var/tmpです。この出力ファイルの接頭辞は「example-server-err」、接尾辞は「.tmp」です。このファイル名には、ファイル名を一意にするための文字 (通常は数字) も含めます。サービスプログラムが
inetdから正しく起動された場合、出力ファイル内のテキストは 「ready」であり、警告またはエラーメッセージは含まれません。ファイルが存在しない場合は、「
ready」メッセージがファイル内にないか、その他のエラー出力がファイル内に存在するので、構成を再チェックしてください。inetd構成の変更を終えたときには、inetdにハングアップ信号を送信して変更済みの構成が再読み込みされるようにしてください。 また、その前に起動されたすべてのプロセスを終了させることも忘れないでください。
| Copyright © 2004 Sun Microsystems, Inc.All Rights Reserved. コメントの送付先: rmi-comments@java.sun.com |  ? |