Method 3 - WrapperListener Integration

Overview

The Method 3, while providing the most flexibility and access to all of the Wrapper's features, is also the only one which requires some coding to complete the integration. This method involves creating a class which implements the WrapperListener interface. An instance of the user class is then instantiated and registered with the WrapperManager.

While this method provides features that are not available with either of the first two methods ( Method1:WrapperSimpleApp, Method2:WrapperStartStopApp ), it does add some complexity. If the additional features are not required, implementing a Shutdown Hook to enable the use of Method 1, or implementing a shutdown class should be considered as options. The main method of a shutdown class can be as simple as just calling a shutdown method in the application.

This Method 3 will explain how the TestWrapper Example Application which is shipped with the Wrapper works.

NOTE

This document will not cover the installation of the Wrapper files or scripts which will be used to launch the application. Both of these subjects are covered in detail in the descriptions of the first two Integration Methods (Method1:WrapperSimpleApp, Method2:WrapperStartStopApp).

Detailed Instructions

The Application Main Class

Before explaining the process in too much detail, we will start with an example main class and then explain each of its components. The code below is a simple class which implements the WrapperListener interface and contains a main method which instantiates the class and calls start on the WrapperManager. Give it a brief once over, we go through it step by step below.

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 );
    }
}

The Main Method

The main method should in most cases be extremely simple. It has the job of instantiating a class which implements WrapperListener interface and then passing that instance along with any arguments to the start method of the WrapperManager class. While it is not a strict rule, in general, the main method should do nothing else. All application initialization should be performed from within the start 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 );
    }

Constructor

The constructor should usually be empty as it has to complete within the scope of the main method above. In general, the rule of doing nothing until the start method has been called should be followed.

    private Main()
    {
    }

WrapperListener start Method

The start method is where things start to happen. This is called by the WrapperManager after it has established a connection with the Wrapper process. Once the Wrapper has confirmed that the Java process has been successfully launched and that the WrapperManager class has been loaded, it will request that the user application be started by calling the WrapperListener.start method.

In many ways, the start method can be thought of as replacing an application's main method. There are some differences that you will have to keep in mind however. The start method is called while the application is in its startup phase and the application will not be considered to have started until the start method has returned.

The Wrapper needs to be able to tell when an application has actually completed its startup in order to defer the launching of other processes which depend on the application being controlled by the Wrapper. This is currently only an issue with the Windows version, in cases where another Windows Service has the Wrapper on its list of service dependencies. Those dependencies state that the Wrapper must be started before and stopped after the dependent Service.

In this first example, the start method simply calls the main method of another class. Remember, this will only work if we know for sure that the main method will return within a few seconds.

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

This next example instantiates an application's main class and then tells it to start up.

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

        return null;
    }

The return value of the start method gives the application a chance to abort the startup process before the application has actually been officially started. This can be important where dependent services are concerned. If you have such concerns then you must use this Integration Method. Both the method1 (WrapperSimpleApp) and method2 (WrapperStartStopApp) helper classes call the application's main method in a background thread and report that the application has successfully started within a few seconds.

A return value of "NULL" indicates a successful startup, where as any Integer object is used to indicate the exit code which the Wrapper should return when it quits.

    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 );
        }
    }

As stated above, the Wrapper assumes that the start method returns after the application has started. By default, the Wrapper will wait for 30 seconds for the start method to complete. This timeout can be set using the wrapper.startup.timeout property, but it is not always desirable to set that property to a large value.

WrapperManager.signalStarting():

For applications which take a variable amount of time to start up, the Wrapper provides a way for the application to report on its progress. At various points during the startup phase, user code can periodically call WrapperManager.signalStarting(). This method lets the Wrapper know that the JVM is alive and the application's startup is going well. The report will be repeated as many times as needed. Therefore, that additional time is required, and it may take much longer time on the startup.

    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;
    }

NOTE

If your start method does not return in a timely manner, the Wrapper will timeout with a message like the following:

Startup failed: Timed out waiting for signal from JVM.

If this happens, please reread this section.

WrapperListener stop Method

The stop method is called by the Wrapper whenever the JVM needs to be shutdown. It will always be called, whether a user presses CTRL-C, the application is stopped via the Windows Service manager or from a script, or user code calls System.exit or WrapperManager.stop.

If there is some code in your application to perform a clean shutdown, it should be called from within this method, rather than having your shutdown code call WrapperManager.stop. This will guarantee that the shutdown code is always called at the correct time. Be aware that existing Shutdown Hooks will continue to work as always.

The stop method is called with the exit code that the Wrapper is planning on exiting with. You have the choice of returning this same exit code, or changing the exit code to reflect a problem while stopping the application.

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

As with the start method. There are times when the act of stopping an application may take longer than the time available in the default stop timeout. In such a case, you have the option of extending the stop timeout using the wrapper.shutdown.timeout property.

WrapperManager.signalStopping():

Rather than changing this property, the Wrapper provides a way for the application to report on its progress. At various points during the startup phase, user code can periodically call WrapperManager.signalStopping(). This method lets the Wrapper know that the JVM is alive and the application's shutdown is going well. The report will be repeated as many times as needed. Therefore, that additional time is required, and it may take much longer time on the shutdown.

    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 Method

The controlEvent method is used to receive control events from the system. These include CTRL-C on all platforms, as well as events for when the user logs off or the machine wants to shutdown, under Windows.

In most cases, the Wrapper will correctly handle all of these events and trigger the shutdown process. However, it is also possible to run the Java application directly without using the Wrapper. This can be done for testing or any number of reasons. In this case, it is the responsibility of the Java application to handle its own life-cycle.

The following example will trigger a shutdown of the JVM if the user presses CTRL-C, hits the close box, logs out, or shuts down the machine. But only if not controlled by the 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.
        }
    }