概要

ほとんどのアプリケーションや開発ツールで言えることですが、 始めるのに一番良いのは、通常、シンプルな「Hello World!」例です。 通常の Java アプリケーション「Hello World」は開始して、「Hello World」を出力してから終了します。 これは、サービスの動作やモニタリング(監視)ツールの実力を見せつけるほど、さほど興味深いものではありませんが、 Java Service Wrapper の場合、インストールして Windows サービス、あるいは UNIX デーモンとして動作を続けるというこの一例をご覧いただく必要があります。

このページでは、弊社から提供している「Hello World Server」サンプルアプリケーションをご覧いただき、 開発プロセスや仕組みをご理解いただくことで、日常にお役に立てることでしょう。 「Java アプリケーションを Windows サービスとして動かすには?」ページでは、 この一例をご覧いただきながら、Java Service Wrapper を活用して、 素早くこのサンプルアプリケーションをインストールして Windows サービスとして動かす設定の手順をご案内しています。

この「Hello World」サンプルアプリケーションは、デーモンスレッドを起動し、 ポート 9000 番でリッスン待機し、クライアントから telnet で接続するようにシンプルテキストベースのクライアント受信待ちにします。 その後、入力出口が見つかるまで入力を単純にエコーし、その時点でその特定の接続が閉じられます。 ある特定の接続が閉じられる時点で「exit」を受信するまで、 いかなる入力もシンプルに繰り返します。

これは典型的な「Hello World」のサンプルよりも少し複雑ですが、 基本となるサーバークライアントの設計モデルどおりに、 クライアントプログラムとインタラクティブ(対話型)に通信したり、 サーバーアプリケーションを簡単に動作させることができるようになります。

ソースコード例

弊社のサンプルアプリケーションをキレイに保ち、キレイに動作させるために、 空のディレクトリーを用意して作業を開始してください。 そのディレクトリーを「%HELLO_HOME%」と呼びます。 そのディレクトリーの中に、 「lib\classes」ディレクトリーと 「src\java」ディレクトリーを作成してください。 以下のような構造になっているはずです。

ディレクトリー構造
%HELLO_HOME%\
  lib\
    classes\
  src\
    java\

次に、「src\java」ディレクトリー内に 「HelloWorldServer.java」というファイル名で一つファイルを作成してください。 そのファイルをテキストエディターで開いて、 以下のソース(テキスト)をコピーしてそのまま貼り付けて、ファイルを保存してください。

ファイル名「HelloWorldServer.java」
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class HelloWorldServer
    implements Runnable
{
    private final Socket m_socket;
    private final int m_num;

    HelloWorldServer( Socket socket, int num )
    {
        m_socket = socket;
        m_num = num;
        
        Thread handler = new Thread( this, "handler-" + m_num );
        handler.start();
    }
    
    public void run()
    {
        try
        {
            try
            {
                System.out.println( m_num + " Connected." );
                BufferedReader in = new BufferedReader( new InputStreamReader( m_socket.getInputStream() ) );
                OutputStreamWriter out = new OutputStreamWriter( m_socket.getOutputStream() );
                out.write( "Welcome connection #" + m_num + "\n\r" );
                out.flush();
                
                while ( true )
                {
                    String line = in.readLine();
                    if ( line == null )
                    {
                        System.out.println( m_num + " Closed." );
                        return;
                    }
                    else
                    {
                        System.out.println( m_num + " Read: " + line );
                        if ( line.equals( "exit" ) )
                        {
                            System.out.println( m_num + " Closing Connection." );
                            return;
                        }
                        //else if ( line.equals( "crash" ) )
                        //{
                        //    System.out.println( m_num + " Simulating a crash of the Server..." );
                        //    Runtime.getRuntime().halt(0);
                        //}
                        else
                        {
                            System.out.println( m_num + " Write: echo " + line );
                            out.write( "echo " + line + "\n\r" );
                            out.flush();
                        }
                    }
                }
            }
            finally
            {
                m_socket.close();
            }
        }
        catch ( IOException e )
        {
            System.out.println( m_num + " Error: " + e.toString() );
        }
    }
    
    public static void main( String[] args )
        throws Exception
    {
        int port = 9000;
        if ( args.length > 0 )
        {
            port = Integer.parseInt( args[0] );
        }
        System.out.println( "Accepting connections on port: " + port );
        int nextNum = 1;
        ServerSocket serverSocket = new ServerSocket( port );
        while ( true )
        {
            Socket socket = serverSocket.accept();
            HelloWorldServer hw = new HelloWorldServer( socket, nextNum++ );
        }
    }
}

アプリケーションがスタートアップし、いかなるソースからでもリモート接続の受け入れ用にポート 9000 でリッスン待機を開始します。 それぞれ新しい接続が受け入れられると、それが閉じられるまで、その接続との全通信が行えるようにハンドラースレッドが作成されます。

そのハンドラースレッドが、任意のテキスト行を読み込み、クライアントにエコーバックします。 もしテキストの一行に「exit」の一語だけが含まれていた場合、 クライアント接続が閉じられます。

サンプル例を構築する

サンプル例を構築する方法は簡単な作業です。 Windows のコマンドプロンプト(UNIX ではシェル)を開き、「cd」コマンドを利用して、 上記で作成した「%HELLO_HOME% ディレクトリー」を開きます。

以下のコマンドを使って、このサンプル例をコンパイルします。

サンプル例をコンパイルする:
javac -d lib\classes src\java\HelloWorldServer.java

もし「javac コマンドが見つかりませんでした」というエラーが出た場合、 システムに Java SDK がインストールされていることを確認してください。 もし問題なければ、他に何もエラーが出ることなく、コンパイル作業が完了して、元のプロンプト状態に戻るはずです。

そのクラスファイルが作成されたことを確認してください。

クラスファイルの確認:
dir lib\classes

無事に「HelloWorldServer サンプル例」の作成が完了しました。

サンプル例を実行する

サンプル例を実行するには、「%HELLO_HOME%」ディレクトリー内から以下のコマンドを実行します。

例を実行する:
java -classpath lib\classes HelloWorldServer

全てが正常に動作しているならば、以下のように表示されます。

サンプル出力例:
Accepting connections on port: 9000

クライアントとして接続する

クライアントをサーバーに接続させて試してみましょう。 2回目のコマンドプロンプトを立ち上げて、telnet を使って接続を始めましょう。

Telnet での接続:
telnet localhost 9000

一旦、接続されると、接続番号を示す応答を受信するはずです。 それから、いかなるテキストも入力してエンターキーで、自分へエコーバックされます。

telnet セッションの例:
Welcome connection #1
Test
echo Test
Hello there world!
echo Hello there world!

サーバーとの対話を終了する際には、一行に「exit」だけを入力してエンターキーで 接続を閉じることができます。

弊社のサンプルサーバーでは、どのコンピューターからでも telnet の接続を受信することができます。 もし接続に問題があった場合、まずファイヤーウォールの設定で通信がブロックされていないか確認してください。

サーバーをクラッシュしてみる

上記のサンプル例は、シンプルなスタンドアローン Java サーバーとして優れていますが、 Java Service Wrapper のサポート力で、サンプルサーバーがどれだけ安定しているかの凄さをお見せしたいと思います。 御社の重要な基幹システム用にも信頼高くご利用いただけることを体感いただけることと思います。

まず開始するには、新しく「crash」(クラッシュ)コマンドを追加して、 接続されたクライアントが JVM をクラッシュできるようにしましょう。 JVM のよく知られた様々なバグを利用して、実際のクラッシュを引き起こすことができるのですが、 それらは、一部のプラットフォーム上で特定の Java バージョンで再現できるものです。 例えば、キレイに正常なシャットダウンをさせずに、一瞬で JVM を落とすために、 「Runtime.getRuntime().halt(0)」をコールして、 クラッシュのシミュレーションをお見せします。

HelloWorldServer.java」ソースファイルをエディターで開いて、 以下のようにソースを書き換えます。

HelloWorldServer.java (書き換え)
    ...
    System.out.println( m_num + " Read: " + line );
    if ( line.equals( "exit" ) )
    {
        System.out.println( m_num + " Closing Connection." );
        return;
    }
    else if ( line.equals( "crash" ) )
    {
        System.out.println( m_num + " Simulating a crash of the Server..." );
        Runtime.getRuntime().halt(0);
    }
    else
    {
        System.out.println( m_num + " Write: echo " + line );
        out.write( "echo " + line + "\n\r" );
        out.flush();
    }
    ...

上記と同じ手順で、このサンプル例を再びコンパイルしましょう。

では、サーバーへ接続して、コマンド「crash」を送信してみましょう。 弊社のシンプルなサンプルサーバーは、もちろんクラッシュして落ちますので、 もはや接続は有効ではなく、telnet からサーバーを利用することもできません。 明らかに、もしこれが重要な基幹サーバーであったなら、ユーザーが不便に陥り、 そのシステム管理者は緊急連絡を受けることでしょう。

この問題を解決するには、「Java アプリケーションを Windows サービスとして動かすには?」 ページを参照して、この単純なサンプルサーバーを Windows サービスまたは UNIX デーモンとして動かす方法を参照してください。 これにより、例のクラッシュやその他の多くの問題から自動的に回復できるようになります。