Método de Integración 3 - WrapperListener

Descripción

El Método 3, proporciona la mayor flexibilidad y acceso a todas las características del Wrapper, es también la única que necesita algunos códigos para completar la integración. Este método requiere crear una clase que implemente la interfaz WrapperListener. Una instancia de la clase del usuario es creada y registrada con el WrapperManager.

Mientras que el método 3 tiene características que no estan disponibles en cualquiera de los dos primeros métodos (Method1:WrapperSimpleApp, Method2:WrapperStartStopApp), esto agrega un poco de complejidad. Si dichas características no son requeridas haga uso del Shutdown Hook de esta manera abilitará el uso del método 1 o el implementar una clase de apagado, pueden considerarse como opciones. El método principal de una clase de apagado puede ser muy simple como el llamar un método de apagado en la aplicación.

Este método explica como la aplicación de ejemplo TestWrapper trabaja, el cual viene incluido con el Wrappper.

NOTA

Este documento no cubre la instalación de archivos o los archivos de órdenes del Wrapper, los cuales son usados para iniciar la aplicación. Dichos temas son cubiertos en detalle en las descripciones de los dos primeros Métodos de Integración. (Method1:WrapperSimpleApp, Method2:WrapperStartStopApp).

Instrucciones Detalladas

Clase Principal de la Aplicación

Antes de explicar el proceso en detalle, vamos a empezar con un ejemplo de una clase principal y luego explicar cada uno de sus componentes. El código que se presenta a continuación es una clase simple que implementa la interfaz WrapperListener y contiene un método principal que crea una instancia de la clase e invoca el inicio del WrapperManager. Revise con cuidado conforme vamos paso por paso en la explicación.

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

El Método Principal

El método principal debería ser en casi todos los casos muy simple, tiene la responsabilidad de instanciar una clase que implementa la interfaz WrapperListener y después pasar la instancia con argumentos al método start de la clase WrapperManager. Mientras que esto no es una regla general, el método principal no necesita hacer algo más. El inicio de toda aplicación se debe realizar desde el método 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 );
    }

Constructor

El constructor normalmente debería estar vacío ya que se tiene que completar dentro de las posibilidades del método principal mencionado previamente. En general la regla de no tener que hacer nada hasta que el método start haya sido llamado y entonces continuar.

    private Main()
    {
    }

Iniciar Método WrapperListener

El método start es donde las cosas pasan, este es llamado por el WrapperManager después que ha establecido una conección con el proceso del Wrapper. Una vez que el Wrapper es confirmado que el proceso Java ha sido iniciado exitosamente y que la clase WrapperManager ha sido cargada, solicita que la aplicación del usuario empiece a llamar al método WrapperListener.start.

En muchos aspectos se puede pensar que el método start es un reemplazo del método principal de una aplicación. Sin embargo, existen algunas diferencias que tendrá que mantener en mente. El método start es llamado mientras la aplicación esta en su fase de inicio y la aplicación no será considerada como iniciada hasta que el método start haya regresado.

El Wrapper tiene que ser capaz de decidir cuando una aplicación realmente ha completado su inicio, con el fin de aplazar el inicio de otros procesos que dependen de la aplicación que esté siendo controlada por el Wrapper. Actualmente este es sólo un problema con la versión de Windows, en casos donde otro Servicio de Windows tenga el Wrapper en su lista de dependencias de servicio. Dichas dependencias establecen que el Wrapper debe iniciar antes y detenerse después del servicio dependiente.

En este primer ejemplo, el método start simplmente llama al método principal de otra clase. Recuerde que esto solo funcionará si estamos seguros que el método principal regresará en pocos segundos.

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

El siguiente ejemplo crea una instancia de la clase principal de la aplicación y luego se le ordena que inicie.

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

        return null;
    }

El valor que regresa del método start le da a la aplicación la oportunidad de abortar el proceso de inicio antes de que la aplicación haya en realidad empezado. Esto puede ser importante cuando se esta al pendiente de servicios dependientes. Si esto le preocupa la mejor solución es hacer uso de este Método de Integración. Las clases tanto del Método 1(WrapperSimpleApp) y Método 2(WrapperStartStopApp) llamarán al método principal de la aplicación en un subproceso de segundo plano e informarán que la solicitud ha iniciado con éxito en pocos segundos.

Al recibir un valor de "NULL" significa un inicio exitoso en donde cualquier objeto Integro es usado para indicar el código de salida que el Wrapper debe producir cuando este cierra.

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

Como se estable en la parte de arriba el Wrapper asume que el método "start" regresa después de que la aplicación ha iniciado. Por defecto el Wrapper esperará 30 segundos para que el método haya completado. Este tiempo de espera puede ser establecido usando la propiedad wrapper.startup.timeout pero que no siempre es deseable establecer esa propiedad en un valor grande.

WrapperManager.signalStarting():

Para aplicaciones que toman un considerable tiempo para iniciar, el Wrapper proporciona una manera para que dichas aplicaciones informen sobre su progreso de inicio. En algunos puntos de la fase de inicio, el código del usuario puede llamar periódicamente al método WrapperManager.signalStarting(). Este método permite que el Wrapper este informado que la máquina JVM esta activa y que el proceso de inicio se esta ejecutando de manera normal. El informe se repetirá tantas veces como sea necesario, como resultado se requiere más tiempo y dicho proceso de inicio puede tomar mucho más tiempo respectivamente.

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

Detener Método WrapperListener

El método para detener la aplicación stop es llamado por el Wrapper cuando la máquina JVM necesite ser apagada. Siempre será llamado ya sea que el usuario presione CTRL-C la aplicación será detenida a través del administrador de Servicio de Windows, por un archivo de órdenes, o que sea invocado un código de usuario ya sea System.exit o WrapperManager.stop.

Si hubiese algun código en su aplicación para realizar un apagado limpio, se debería invocar desde este método, en lugar de usar el código WrapperManager.stop. Esto garantiza que dicho código de apagado sea siempre llamado en el momento adecuado. Tenga en cuenta que los actuales Shutdown Hooks seguirán trabajando normalmente.

El método stop es llamado con el código de salida que el Wrapper esta planeando usar para salir. Tiene la opción de que este mismo código regrese o cambiarlo para reflejar algún problem mientras la aplicación esta siendo detenida.

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

En lo que concierne al método start, hay algunas ocasiones cuando el hecho de detener una aplicación puede tomar más tiempo de lo disponible que el tiempo de espera que se tiene predeterminado para detener. En dicho caso tiene la opción de prolongar el tiempo de espera para detener la aplicación, usando la propiedad wrapper.shutdown.timeout.

WrapperManager.signalStopping():

En lugar de cambiar esta propiedad, el Wrapper ofrece una manera para que la aplicación informe sobre su progreso. En ciertos momentos durante la fase de inicio, el código del usuario puede llamar periódicamente al método WrapperManager.signalStopping(). Este método permite que el Wrapper este informado que la máquina JVM esta activa y que el proceso de apagado se esta ejecutando de manera normal. El informe se repetirá las veces que sea necesario, por lo tanto se requiere más tiempo, y como resultado dicho proceso de apagado también tomará más tiempo.

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

Controlar Eventos del Método WrapperListener

El método controlEvent es usado para recibir el control de eventos del sistema. Estos incluyen CTRL-C en todas las plataformas asi como eventos para cuando el usuario termina sesión o cuando la máquina desea ser apagada en Windows.

En la mayoría de los casos, el Wrapper controlará correctamente todos estos eventos y desencadenará el proceso de apagado. Sin embargo, también es posible ejecutar la aplicación Java directamente sin utilizar el Wrapper. Esto se puede hacer para pruebas o cualquier número de razones. En este caso, es la responsabilidad de la aplicación Java de manejar su propio ciclo de vida.

El siguiente ejemplo desencadenará un apagado de la máquina JVM si el usuario presiona CTRL-C, selecciona el cuadro de cierre, cierra la sesión, y/o apaga la máquina, esto solo pasa si no es controlada por el 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.
        }
    }