方法3 - WrapperListener インテグレーション

概要

他の方法ではコーディングを必要としませんが、この方法3では、唯一、手順の中で少しコーディングを加える必要があるもので、 最も柔軟に、 かつWrapperの特徴を最大限に活用したものと言えるでしょう。 この方法3は、[WrapperListener] インターフェイスを実装するクラス作成にまで踏み込みます。 ユーザークラスのインスタンスが作成され、WrapperManagerに登録されます。

この方法3は、前述の2つの方法 (方法1:[WrapperSimpleApp]方法2:[WrapperStartStopApp]) とは 共には利用できないという難点がありますが、複雑さにも十分に耐えうるのも最大の特徴です。 この方法3は、スタートアップやシャットダウンのプロセス同様に、ユーザーコードで要求を受けたり、 直接的にシステム コントロール イベントへ 応答を返したり、ユーザーコードの実行を許可する唯一の方法です。

もし、コーディングして機能を追加する必要がない場合には、方法1(WrapperSimpleApp)シャットダウンフックを実装すること、 あるいは、[シャットダウン]クラスを実装することは、必要であれば追加すればいい程度のオプションとして考えれば良いでしょう。 [シャットダウン]クラスのメインメソッドは、アプリケーションのシャットダウンメソッドを呼び出すのと同様に、シンプルです。

このセクションでは、Wrapperに同梱されているTestWrapperサンプルアプリケーションを、 どのように動かすのか、例にあげて説明します。

注意

ここの説明には、Wrapperやアプリケーションの起動に使うスクリプトのインストール方法は含まれていません。 インストール方法の具体的な説明は、インテグレーション 方法1(WrapperSimpleApp)方法2(WrapperStartStopApp) を参照してください。

操作方法の詳細

アプリケーションのメインクラス

プロセスの説明に深く入る前に、メインクラスの例を取り上げて進めながら、 各コンポーネントについて説明をしていきます。 下記のコードは、シンプルなクラスで、 [WrapperListener] インターフェイスを実装しており、 WrapperManager上でクラスを作成し、スタートを呼び出すメインメソッドを含んでいます。 以下では、説明を加えながら、ステップbyステップで進んでいきます。

import org.tanukisoftware.wrapper.WrapperManager;
import org.tanukisoftware.wrapper.WrapperListener;

public class Main
    implements WrapperListener
{
    private MyApp m_app;

    /*---------------------------------------------------------------
     * Constructors
     *-------------------------------------------------------------*/
    private Main()
    {
    }

    /*---------------------------------------------------------------
     * WrapperListener Methods
     *-------------------------------------------------------------*/
    /**
     * The start method is called when the WrapperManager is signaled by the 
     *    native Wrapper code that it can start its application.  This
     *    method call is expected to return, so a new thread should be launched
     *    if necessary.
     *
     * @param args List of arguments used to initialize the application.
     *
     * @return Any error code if the application should exit on completion
     *         of the start method.  If there were no problems then this
     *         method should return null.
     */
    public Integer start( String[] args )
    {
        m_app = new MyApp( args );
        m_app.start();

        return null;
    }

    /**
     * Called when the application is shutting down.  The Wrapper assumes that
     *  this method will return fairly quickly.  If the shutdown code code
     *  could potentially take a long time, then WrapperManager.signalStopping()
     *  should be called to extend the timeout period.  If for some reason,
     *  the stop method can not return, then it must call
     *  WrapperManager.stopped() to avoid warning messages from the Wrapper.
     *
     * @param exitCode The suggested exit code that will be returned to the OS
     *                 when the JVM exits.
     *
     * @return The exit code to actually return to the OS.  In most cases, this
     *         should just be the value of exitCode, however the user code has
     *         the option of changing the exit code if there are any problems
     *         during shutdown.
     */
    public int stop( int exitCode )
    {
        m_app.stop();
        
        return exitCode;
    }
    
    /**
     * Called whenever the native Wrapper code traps a system control signal
     *  against the Java process.  It is up to the callback to take any actions
     *  necessary.  Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT, 
     *    WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, or 
     *    WRAPPER_CTRL_SHUTDOWN_EVENT
     *
     * @param event The system control signal.
     */
    public void controlEvent( int event )
    {
        if ( ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT )
                && ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) )
        {
                // Ignore
        }
        else
        {
                WrapperManager.stop( 0 );
                // Will not get here.
        }
    }
    
    /*---------------------------------------------------------------
     * Main Method
     *-------------------------------------------------------------*/
    public static void main( String[] args )
    {
        // Start the application.  If the JVM was launched from the native
        //  Wrapper then the application will wait for the native Wrapper to
        //  call the application's start method.  Otherwise the start method
        //  will be called immediately.
        WrapperManager.start( new Main(), args );
    }
}

メインメソッド

メインメソッドは、ほとんどのケースでは、非常にシンプルなはずです。 [WrapperListener] インターフェイスを実装するクラスを作成し、 [WrapperManager] クラスの [start]メソッドへの引数に応じてインスタンスを引き渡す、 ジョブがあります。厳密なルールがない場合には、一般的に、メインメソッドは他に何もしないはずです。 全てのアプリケーション初期化は、[start]メソッド内から実行されるはずです。

    public static void main( String[] args )
    {
        // Start the application.  If the JVM was launched from the native
        //  Wrapper then the application will wait for the native Wrapper to
        //  call the application's start method.  Otherwise the start method
        //  will be called immediately.
        WrapperManager.start( new Main(), args );
    }

コンストラクタ

コンストラクタは、上記のメインメソッドの範囲内から実行されなければならないため、 たいてい、空であるはずです。 一般的に、[start]メソッドが呼び出されるまで何もしないルールの場合には、下記のとおりです。

    private Main()
    {
    }

WrapperListener スタートメソッド

start]メソッドとは、物事がスタートする始めのところです。 Wrapperプロセスとの接続を確立した後、WrapperManagerが[start]メソッドを呼び出します。 一旦、Wrapperが、Javaプロセスが正常に起動したことや、 [WrapperManager] クラスがロード(読み込み)されたことを確認すると、次に、 [WrapperListener.start] メソッドを呼び出すことで、 ユーザーアプリケーションがスタートするようにリクエストを出します。

多くの場合、[start]メソッドは、アプリケーションのメインメソッドを置きかえると考えられるはずです。 しかしながら、覚えておかなければならない、いくつか異なることがあります。 アプリケーションがスタートアップの段階の中にあるとき、[start]メソッドが呼び出され、 [start]メソッドが応答を返すまで、アプリケーションがスタートしたものとして解釈されません。

Wrapperによってコントロールされているアプリケーションに依存している他のプロセスの起動をdeferするために、 いつアプリケーションが実際にスタートアップを完了したのかタイミングを、Wrapperが把握する必要があります。 現在のところ、他のWindowsサービスが、サービス依存関係のリスト上に、Wrapperを持っているケースで、 これが、Wrapper現バージョンの唯一の問題です。 その依存関係によれば、依存サービスの前にWrapperが開始され、 依存サービスの後にWrapperが停止されなければならない、ということです。

ここでの最初の例では、[start]メソッドが単純に他のクラスのメインメソッドを呼び出します。 覚えておいてください、 これは、メインメソッドが数秒以内に応答を返してくると、確実に分かっているならば、の場合に、動きます。

    public Integer start( String[] args )
    {
        MyApp.main( args );
        
        return null;
    }

次の例では、アプリケーションのメインクラスを作成して、スタートアップするようにしています。

    public Integer start( String[] args )
    {
        m_app = new MyApp( args );
        m_app.start();

        return null;
    }

start]メソッドの戻り値により、アプリケーションが実際に正式に開始する前に、 アプリケーションが、スタートアップ処理を中断できるチャンスを持てるようにしてあります。 これは、依存サービスが関係するところでは大切でしょう。 もし、そのような関連があるなら、この方法3(WrapperListener)によるインテグレーション方法を使わなければなりません。 方法1(WrapperSimpleApp)方法2(WrapperStartStopApp) ヘルパークラスの両方では、 バックグランドスレッドで、アプリケーションのメインメソッドを呼び出し、 アプリケーションが数秒で正常にスタートしたことを報告します。

終了時にWrapperが返してくる「終了(exit)」コードを指し示すために 「Integer」(インテージャー)オブジェクトが使われるところでは、 「NULL値」のある戻り値は、スタートアップの成功を意味します。

    public Integer start( String[] args )
    {
        m_app = new MyApp( args );
        m_app.start();
        if ( m_app.isOk() )
        {
                return null;
        }
        else
        {
                System.out.println( "MyApp startup failed." );
                return new Integer( 1 );
        }
    }

上記のように、アプリケーションがスタートした後、 Wrapperは[start]メソッドが応答を返した、と解釈をします。 Wrapperのデフォルトでは、[start]メソッドの確立に、30秒間、待ちます。 このタイムアウトは [wrapper.startup.timeout] プロパティを使って設定しますが、大きい設定値が常に望ましいわけではありません。

WrapperManager.signalStarting():

スタートアップの所要時間が変速的であるアプリケーション用に、 Wrapperでは、アプリケーションの進捗状態をレポートする手法を用意しています。 スタートアップ中の何箇所かのポイントで、ユーザーコードが [WrapperManager.signalStarting()] メソッドを定期的に呼び出します。 この手法により、Wrapperへ「JVMが生きている」 「アプリケーションのスタートアップがうまく進んでいる」ことを報告しながら、 アプリケーションの正常な動作を確信することができます。 ただし、そのレポート回数分だけ、たくさん呼び出すことになるため、 アプリケーションのスタートアップに要する時間数がさらに長くなると思われます。

    public Integer start( String[] args )
    {
        m_app = new MyApp( args );
        
        WrapperManager.signalStarting( 60000 );
        // Do something that takes a while
        
        WrapperManager.signalStarting( 60000 );
        // Do something else that also may take a while
        
        return null;
    }

注意

もし、利用しているスタートメソッドが適宜にタイミングよく返してこない場合、 Wrapperは次のようなメッセージでタイムアウトします:

Startup failed: Timed out waiting for signal from JVM.

もし、これが発生する場合には、このセクションを再度、お読みください。

WrapperListener ストップメソッド

JVMがシャットダウンする必要がある時にはいつでも、Wrapperによって[stop]メソッドが呼び出されます。 ユーザーが[CTRL]+[C]のキー操作をした場合や、 Windowsサービスマネージャー経由やスクリプトからの指示でアプリケーションが停止される場合や、 あるいは、ユーザーコードが[System.exit] または[WrapperManager.stop] を呼び出した場合など、 いずれに場合にでも、Wrapperによって[stop]メソッドが呼び出されます。

もし、キレイなシャットダウンを実現するために、アプリケーション中に何かコードがあるならば、 あなたのシャットダウンコードに[WrapperManager.stop]を呼び出させるよりも、 むしろ、このメソッドの中から、呼び出されるべきです。 これは、そのシャットダウンコードが常に正しいタイミングで呼び出されることを保証しているからです。 既存のシャットダウンフックが、いつものように動き続けていることに注目してください。

stop]メソッドは、Wrapperが終了を予定している、「終了(exit)」コードと共に呼び出されます。 この同じ「終了(exit)」コードを返すか、あるいは、 アプリケーションを停止する際の問題を反映するように「終了(exit)」コードを変更するか、 どちらか選択してください。

    public int stop( int exitCode )
    {
        m_app.stop();
        
        return exitCode;
    }

その[start]メソッドのため、デフォルトの「stop」タイムアウトで有効な時間よりも、 アプリケーション停止動作に要する時間数が長くなるかもしれないことも多々あります。 そのようなケースでは、 [wrapper.shutdown.timeout] プロパティを使って、「stop」タイムアウト時間を延長するという、選択オプションがあります。

WrapperManager.signalStopping():

あるいは、そのプロパティを変更しなくても、 Wrapperでは、アプリケーションの進捗状態をレポートする手法を用意しています。 シャットダウン中の何箇所かのポイントで、ユーザーコードが [WrapperManager.signalStopping()] メソッドを定期的に呼び出します。 この手法により、Wrapperへ「JVMが生きている」 「アプリケーションのシャットダウンがうまく進んでいる」ことを報告しながら、 アプリケーションの正常な動作を確信することができます。 ただし、そのレポート回数分だけ、たくさん呼び出すことになるため、 アプリケーションのシャットダウンに要する時間数がさらに長くなると思われます。

    public int stop( int exitCode )
    {
        WrapperManager.signalStopping( 60000 );
        // Do some cleanup that takes a while
                        
        WrapperManager.signalStopping( 60000 );
        // Do some more cleanup that takes a while
        
        return exitCode;
    }

WrapperListener controlEvent メソッド

controlEvent]メソッドは、 システムからコントロールイベントを受け取るために使われます。 これらは、Windows上で、「ユーザーがログオフするとき」、 あるいは「マシンがシャットダウンするとき」などのイベント同様に、 全てのプラットフォーム上での[CTRL]+[C]のキー操作イベントを含みます。

ほとんどの場合、Wrapperはこれらの全てのイベントを正しく取り扱い、 シャットダウンプロセスのキッカケを引き起こします。 Wrapperを使わずに、直接にJavaアプリケーションを動かすこともまた可能ですが、 これは、十分にテストを試みた上で、あるいは、何か特別な理由がある場合にそうするべきであり、 この場合、何が起ころうとも、 それは己のライフサイクル(稼働状況)を管理するJavaアプリケーション自身の責任になります。

次の例では、ユーザーが [CTRL]+[C]のキー操作や、 [閉じる]ボックスをクリックしたり、 ログアウトしたり、マシンをシャットダウンする場合に、JVMのシャットダウンを引き起こしますが、 Wrapperによってコントロールされていない状況の場合です。

    public void controlEvent( int event )
    {
        if ( ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT )
                && ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) )
        {
                // Ignore
        }
        else
        {
                WrapperManager.stop( 0 );
                // Will not get here.
        }
    }