| 
ドキュメントの目次 | 
Solaris オペレーティングシステム (Solaris OS) では、インターネットサービスデーモンの inetd が、システムブート時にサービスを起動する代替手段になります。インターネットの標準サービスに対するサーバープロセスであるこのデーモンを、必要に応じてサービスを起動するように構成することができます。インターネットサービスデーモンの詳細については、Solaris OS の inetd(1M) のマニュアルページを参照してください。
inetd は、JavaTM Remote Method Invocation (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を使用してレジストリをエクスポートします。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構成の変更を終えたときには、inetdにハングアップ信号を送信して変更済みの構成が再読み込みされるようにしてください。ファイルが存在しない場合は、「
ready」メッセージがファイル内にないか、その他のエラー出力がファイル内に存在するので、構成を再チェックしてください。inetd構成の変更を終えたときには、inetdにハングアップ信号を送信して変更済みの構成が再読み込みされるようにしてください。また、その前に起動されたすべてのプロセスを終了させることも忘れないでください。
| 
Copyright© 2006 Sun Microsystems, Inc.All Rights Reserved. コメントの送付先:rmi-comments@java.sun.com  | 
 Java Software  |