Methode 3 - WrapperListener-Integration

Übersicht

Die Methode 3 ist, wobei sie auch die größte Flexibilität und Zugang zu allen Wrapper-Features anbietet, die einzige Methode, die etwas Programmieraufwand erfordert, um die Integration abzuschließen. Diese Methode beinhaltet das Erstellen einer Klasse, die die WrapperListener-Schnittstelle implementiert. Eine Instanz der User-Klasse wird dann mit dem WrapperManager realisiert und registriert.

Während diese Methode Features anbietet, die mit einer der ersten beiden Methoden nicht verfügbar sind (Method1:WrapperSimpleApp, Method2:WrapperStartStopApp), fügt es auch etwas Komplexität hinzu. Wenn Sie die zusätzlichen Features nicht benötigen, kann auch die Implementierung eines Shutdown-Hooks Sinn machen, um die Nutzung der Methode 1 zu aktivieren oder die Implementierung einer Shutdown-Klasse als Option in Betracht zu ziehen. Die Hauptmethode einer Shutdown-Klasse kann so einfach wie das Aufrufen einer Shutdown-Methode in der Anwendung sein.

Diese Methode 3 erklärt, wie die TestWrapper-Beispielanwendung, die mit dem Wrapper ausgeliefert wird, funktioniert.

NOTE

Dieses Dokument behandelt nicht die Installation der Wrapper-Dateien oder Wrapper-Skripts, die für den Start der Anwendung genutzt werden. Diese beiden Themen werden im Detail in den Beschreibungen der ersten beiden Integrationsmethoden behandelt: (Method1:WrapperSimpleApp, Method2:WrapperStartStopApp).

Detaillierte Anleitungen

Die Anwendung Main-Class

Bevor wir den Prozess zu sehr im Detail besprechen, starten wir mit einem Beispiel Main-Class und erklären dann jede ihrer Komponenten. Der Programmcode unten ist eine einfache Klasse, die die WrapperListener-Schnittstelle implementiert und eine Hauptmethode enthält, die die Klasse realisiert und via WrapperManager aufruft. Hier ein kurzer Überblick; wir behandeln dies Schritt für Schritt weiter unten.

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

Die Hauptmethode

Die Hauptmethode sollte in den meisten Fällen extrem einfach gestaltet sein. Sie hat die Aufgabe, eine Klasse zu realisieren, die die WrapperListener-Schnittstelle implementiert und dann diese Instanz zusammen mit allen Argumenten an die start-Methode der WrapperManager-Klasse übergibt. Auch wenn keine strenge Regel gilt, sollte die Hauptmethode im Allgemeinen nichts Anderes tun. Die gesamte Anwendungsinitialisierung sollte komplett innerhalb der start-Methode realisiert werden.

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

Konstruktor

Der Konstruktor sollte gewöhnlich "leer" sein, da er innerhalb des Bereichs der oben genannten main-Methode abgearbeitet wird. Im Allgemeinen sollte man sich an die Regel halten, zu warten bis die start-Methode aufgerufen wird.

    private Main()
    {
    }

WrapperListener start-Methode

Die start-Methode ist meistens der Beginn aller Dinge. Diese wird vom WrapperManager aufgerufen, nachdem dieser eine Verbindung mit dem Wrapper-Prozess hergestellt hat. Sobald der Wrapper bestätigt hat, dass der Java-Prozess erfolgreich gestartet und die WrapperManager-Klasse geladen wurde, wird es den Anwendungsstart des Users durch den Aufruf der WrapperListener.start-Methode anfordern.

In vielerlei Hinsicht kann die start-Methode als ein Ersatz für die Main-Methode der Anwendung angesehen werden. Es gibt jedoch ein paar Unterschiede, über die Sie Bescheid wissen sollten. Die start-Methode wird aufgerufen, während die Anwendung sich in seiner Startphase befindet und die Anwendung wird zuerst dann als gestartet angesehen werden, sobald die start-Methode einen Rückgabewert geliefert hat.

Der Wrapper muss imstande sein, darüber zu informieren, sobald die Anwendung tatsächlich ihren Startup-Vorgang beendet hat, damit das Starten anderer abhängiger Prozesse, die von der vom Wrapper kontrollierten Anwendung abhängig sind, zurückgestellt werden kann. Dies ist aktuell nur ein Problem mit der Windows Version; in Fällen, in denen ein anderer Windows Dienst den Wrapper auf seiner Liste von Dienstabhängigkeiten hat. Diese Abhängigkeiten müssen vor bzw. nach dem abhängigen Dienst gestartet werde.

In diesem ersten Beispiel ruft die start-Methode einfach die Main-Methode einer anderen Klasse auf. Bitte erinnern Sie sich daran, dass das nur funktionieren wird, wenn wir sicher wissen, dass die Main-Methode innerhalb weniger Sekunden einen Rückgabewert liefert.

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

Dieses nächste Beispiel erstellt eine Instanz und sagt dieser, dass sie starten soll. Es realisiert die main-Klasse einer Anwendung.

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

        return null;
    }

Der Rückgabewert der start-Methode gibt der Anwendung eine Möglichkeit, den Startvorgang abzubrechen, bevor die Anwendung tatsächlich offiziell gestartet wurde. Dies kann wichtig sein, wo abhängige Dienste betroffen sind. Wenn Sie solche Bedenken haben, dann müssen Sie die Integrationsmethode nutzen. Beide die method1-(WrapperSimpleApp) und method2-(WrapperStartStopApp)-Helper-Klassen rufen beide die Main-Methode einer Anwendung in einem Hintergrund-Thread auf und melden zurück, dass die Anwendung erfolgreich innerhalb weniger Sekunden gestartet wurde.

Ein Rückgabewert von "NULL" zeigt einen erfolgreichen Startvorgang an, wobei ein Integer-Objekt genutzt wird, um den Exit-Code anzugeben, den der Wrapper zurückgeben sollte, wenn er sich beendet.

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

Wie oben angegeben nimmt der Wrapper an, dass die start-Methode einen Rückgabewert liefert, nachdem die Anwendung gestartet wurde. Standardmäßig wartet der Wrapper für 30 Sekunden, damit sich die start-Methode beenden kann. Diese Timeout-Zeit kann durch Nutzung der wrapper.startup.timeout Eigenschaft eingestellt werden, aber es ist nicht immer wünschenswert, diese Eigenschaft auf einen großen Wert festzulegen.

WrapperManager.signalStarting():

Für Anwendungen, die unterschiedliche Zeitdauer zum Starten brauchen, bietet der Wrapper einen Weg für die Anwendung an, seinen Fortschritt mitzuteilen. Zu verschiednenen Zeitpunkten des Startvorgangs kann die Anwendung regelmäßig WrapperManager.signalStarting() aufrufen. Diese Methode informiert den Wrapper darüber, dass die JVM noch aktiv ist und das Starten der Anwendung gut verläuft. Die Meldung wird so viele Male wie notwendig wiederholt. Das kann beim Starten zusätzliche Zeit in Anspruch nehmen.

    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

Wenn Ihre Startmethode nicht zeitnah einen Rückgabewert liefert, kann der Wrapper einen Timeout mit einer Nachricht wie folgt erhalten:

Startup failed: Timed out waiting for signal from JVM.

Wenn dies geschieht, lesen Sie bitte noch mal diesen Abschnitt.

WrapperListener-stop-Methode

Die stop-Methode wird durch den Wrapper immer dann aufgerufen, wenn die JVM beendet werden muss. Sie wird immer dann aufgerufen, wenn ein User STRG-C drückt, die Anwendung via dem Windows Dienstmanager oder von einem Skript beendet wird oder eine Anwendung System.exit oder WrapperManager.stop aufruft.

Wenn es Code in Ihrer Anwendung gibt, um ein korrektes Beenden zu gewährleisten, sollte dieser Code sich innerhalb der Methode befinden, statt einen Aufruf eines eigenen Beenden-Codes zu haben WrapperManager.stop. Dies stellt sicher, dass der Beenden-Code stets zur rechten Zeit aufgerufen wird. Seien Sie sich bewusst, dass vorhandene Shutdown-Hooks weiterhin wie gewohnt funktionieren.

Die stop-Methode wird via des Exit-Codes, mit dem Wrapper sich beenden tut, aufgerufen. Sie haben die Wahl, den gleichen Exit-Code zurückzugeben oder den Exit-Code so zu ändern, dass dieser ein Problem wiedergibt, wenn Sie die Anwendung beenden.

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

Analog zu der start-Methode. Es gibt Zeiten, zu denen der Vorgang des Beendens einer Anwendung mehr Zeit als die standardmäßig verfügbare Timeout-Zeit beanspruchen kann. In so einem Fall haben Sie die Option die Timeout-Zeit für das Beenden durch Nutzung der wrapper.shutdown.timeout Eigenschaft zu erhöhen.

WrapperManager.signalStopping():

Statt diese Eigenschaft zu ändern, bietet der Wrapper einen Weg, um der Anwendung zu ermöglichen, ihren Fortschritt mitzuteilen. Zu verschiedenen Zeitpunkten während der Startphase kann die Anwendung regelmäßig die Methode WrapperManager.signalStopping() aufrufen. Diese Methode informiert den Wrapper darüber, dass die JVM noch läuft und der Beendigungsvorgang für die Anwendung planmäßig verläuft. Die Mitteilung wird so viele Male wie notwendig wiederholt. Daher kann zusätzliche Zeit erforderlich sein und das Beenden deutlich länger dauern.

    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-Methode

Die controlEvent-Methode wird genutzt, um Kontrollereignisse vom System zu erhalten. Diese beinhalten STRG-C auf allen Plattformen, sowohl Ereignisse für Zeiten, wann der User sich abmeldet oder die Maschine unter Windows herunterfahren möchte.

In den meisten Fällen wird der Wrapper mit allen diesen Ereignissen korrekt umgehen und den Beenden-Prozess ausführen können. Jedoch ist es auch möglich, die Java-Anwendung direkt ohne Nutzung des Wrappers auszuführen. Dies kann aus Testzwecken oder aufgrund einer Vielzahl von Gründen getan werden. In diesem Fall liegt es im Verantwortungsbereich der Java-Anwendung, selbst dafür zu sorgen.

Das folgende Beispiel löst das Beenden der JVM aus, wenn der Benutzer STRG-C drückt, auf das Schließen-Kästchen klickt, sich ausloggt oder die Maschine herunterfährt. Aber nur dann, wenn die Anwendung nicht vom Wrapper kontrolliert wird.

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