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