Méthode 3 - Intégration WrapperListener

Vue d'ensemble

La Méthode 3, tout en offrant la plus grande flexibilité et l'accès à toutes les fonctionnalités du Wrapper, est également la seule qui nécessite un peu de programmation pour terminer l'intégration. Cette méthode consiste à créer une classe qui implémente l'interface WrapperListener. Une instance de la classe utilisateur est ensuite instanciée et enregistrée auprès du WrapperManager.

Bien que cette méthode offre des fonctionnalités qui ne sont pas disponibles avec l'une ou l'autre des deux premières méthodes ( Method1:WrapperSimpleApp, Method2:WrapperStartStopApp ), elle ajoute une certaine complexité. Si les fonctionnalités supplémentaires ne sont pas nécessaires, l'implémentation d'un Shutdown Hook pour permettre l'utilisation de la Méthode 1, ou l'implémentation d'une classe d'arrêt, devrait être envisagée comme options. La méthode main d'une classe shutdown peut être aussi simple que d'appeler simplement une méthode d'arrêt dans l'application.

Cette Méthode 3 expliquera le fonctionnement de l'application d'exemple TestWrapper fournie avec le Wrapper.

NOTE

Ce document ne couvrira pas l'installation des fichiers du Wrapper ni des scripts qui seront utilisés pour lancer l'application. Ces deux sujets sont traités en détail dans les descriptions des deux premières méthodes d'intégration (Method1:WrapperSimpleApp, Method2:WrapperStartStopApp).

Instructions détaillées

La classe principale de l'application

Avant d'expliquer le processus trop en détail, nous commencerons par un exemple de classe principale, puis nous expliquerons chacun de ses composants. Le code ci-dessous est une classe simple qui implémente l'interface WrapperListener et contient une méthode main qui instancie la classe et appelle start sur le WrapperManager. Parcourez-le rapidement, nous l'examinerons étape par étape ci-dessous.

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

La méthode main

La méthode main devrait dans la plupart des cas être extrêmement simple. Elle a pour rôle d'instancier une classe qui implémente l'interface WrapperListener puis de transmettre cette instance ainsi que tous les arguments à la méthode start de la classe WrapperManager. Bien qu'il ne s'agisse pas d'une règle stricte, en général, la méthode main ne devrait rien faire d'autre. Toute l'initialisation de l'application devrait être effectuée depuis la méthode 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 );
    }

Constructeur

Le constructeur devrait généralement être vide car il doit se terminer dans le cadre de la méthode main ci-dessus. En général, la règle de ne rien faire jusqu'à ce que la méthode start ait été appelée devrait être suivie.

    private Main()
    {
    }

Méthode start de WrapperListener

La méthode start est l'endroit où les choses commencent à se produire. Elle est appelée par le WrapperManager après qu'il a établi une connexion avec le processus Wrapper. Une fois que le Wrapper a confirmé que le processus Java a été lancé avec succès et que la classe WrapperManager a été chargée, il demandera que l'application utilisateur soit démarrée en appelant la méthode WrapperListener.start.

À bien des égards, la méthode start peut être considérée comme remplaçant la méthode main d'une application. Il y a cependant quelques différences que vous devrez garder à l'esprit. La méthode start est appelée pendant que l'application est dans sa phase de démarrage et l'application ne sera pas considérée comme ayant démarré tant que la méthode start n'aura pas retourné.

Le Wrapper doit pouvoir déterminer quand une application a réellement terminé son démarrage afin de différer le lancement d'autres processus qui dépendent de l'application contrôlée par le Wrapper. Ceci n'est actuellement un problème que pour la version Windows, dans les cas où un autre service Windows a le Wrapper dans sa liste de dépendances de services. Ces dépendances stipulent que le Wrapper doit être démarré avant et arrêté après le service dépendant.

Dans ce premier exemple, la méthode start appelle simplement la méthode main d'une autre classe. N'oubliez pas que cela ne fonctionnera que si nous savons avec certitude que la méthode main retournera en quelques secondes.

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

L'exemple suivant instancie la classe principale d'une application, puis lui demande de démarrer.

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

        return null;
    }

La valeur de retour de la méthode start donne à l'application une chance d'interrompre le processus de démarrage avant que l'application n'ait réellement été officiellement démarrée. Cela peut être important lorsqu'il s'agit de services dépendants. Si vous avez de telles préoccupations, vous devez alors utiliser cette méthode d'intégration. Les deux classes d'aide method1 (WrapperSimpleApp) et method2 (WrapperStartStopApp) appellent la méthode main de l'application dans un thread d'arrière-plan et signalent que l'application a démarré avec succès en quelques secondes.

Une valeur de retour de « NULL » indique un démarrage réussi, tandis que tout objet Integer est utilisé pour indiquer le code de sortie que le Wrapper devrait retourner lorsqu'il se termine.

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

Comme indiqué ci-dessus, le Wrapper suppose que la méthode start retourne après que l'application a démarré. Par défaut, le Wrapper attendra 30 secondes que la méthode start se termine. Ce délai d'attente peut être défini à l'aide de la propriété wrapper.startup.timeout, mais il n'est pas toujours souhaitable de définir cette propriété à une valeur élevée.

WrapperManager.signalStarting() :

Pour les applications dont le temps de démarrage est variable, le Wrapper fournit un moyen pour l'application de rendre compte de sa progression. À différents moments de la phase de démarrage, le code utilisateur peut appeler périodiquement WrapperManager.signalStarting(). Cette méthode informe le Wrapper que la JVM est en vie et que le démarrage de l'application se déroule bien. Le rapport sera répété autant de fois que nécessaire. Par conséquent, ce temps supplémentaire est requis, et le démarrage peut prendre beaucoup plus de temps.

    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

Si votre méthode start ne retourne pas en temps voulu, le Wrapper déclenchera un timeout avec un message comme le suivant :

Startup failed: Timed out waiting for signal from JVM.

Si cela se produit, veuillez relire cette section.

Méthode stop de WrapperListener

La méthode stop est appelée par le Wrapper chaque fois que la JVM doit être arrêtée. Elle sera toujours appelée, qu'un utilisateur appuie sur CTRL-C, que l'application soit arrêtée via le gestionnaire de services Windows ou depuis un script, ou que le code utilisateur appelle System.exit ou WrapperManager.stop.

S'il existe du code dans votre application pour effectuer un arrêt propre, il devrait être appelé depuis cette méthode, plutôt que de faire appeler WrapperManager.stop par votre code d'arrêt. Cela garantira que le code d'arrêt est toujours appelé au bon moment. Sachez que les Shutdown Hooks existants continueront de fonctionner comme toujours.

La méthode stop est appelée avec le code de sortie avec lequel le Wrapper prévoit de se terminer. Vous avez le choix de retourner ce même code de sortie, ou de modifier le code de sortie pour refléter un problème lors de l'arrêt de l'application.

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

Comme pour la méthode start. Il arrive que l'arrêt d'une application prenne plus de temps que le délai disponible dans le timeout d'arrêt par défaut. Dans ce cas, vous avez la possibilité d'étendre le timeout d'arrêt à l'aide de la propriété wrapper.shutdown.timeout.

WrapperManager.signalStopping() :

Plutôt que de modifier cette propriété, le Wrapper fournit un moyen pour l'application de rendre compte de sa progression. À différents moments de la phase d'arrêt, le code utilisateur peut appeler périodiquement WrapperManager.signalStopping(). Cette méthode informe le Wrapper que la JVM est en vie et que l'arrêt de l'application se déroule bien. Le rapport sera répété autant de fois que nécessaire. Par conséquent, ce temps supplémentaire est requis, et l'arrêt peut prendre beaucoup plus de temps.

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

Méthode controlEvent de WrapperListener

La méthode controlEvent est utilisée pour recevoir les événements de contrôle du système. Ceux-ci incluent CTRL-C sur toutes les plates-formes, ainsi que les événements lorsque l'utilisateur se déconnecte ou que la machine veut s'arrêter, sous Windows.

Dans la plupart des cas, le Wrapper gérera correctement tous ces événements et déclenchera le processus d'arrêt. Cependant, il est également possible d'exécuter l'application Java directement sans utiliser le Wrapper. Cela peut être fait pour des tests ou pour un certain nombre de raisons. Dans ce cas, c'est à l'application Java de gérer son propre cycle de vie.

L'exemple suivant déclenchera un arrêt de la JVM si l'utilisateur appuie sur CTRL-C, clique sur la case de fermeture, se déconnecte ou arrête la machine. Mais uniquement si elle n'est pas contrôlée par le 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.
        }
    }