|
ドキュメントの目次 |
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 |
? |