package er.extensions.appserver; import java.util.HashSet; import java.util.Set; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSNotification; import com.webobjects.foundation.NSNotificationCenter; /** * Use this to register shutdown hooks instead of directly using Runtime.addShutdownHook(). * The net effect is that there will be a specific log file entry AFTER all other shutdown * hooks have completed, notifying a watching user that shutdown of the application has * indeed completed, which says: * <p> * <tt>APPLICATION SHUTDOWN SEQUENCE COMPLETE</tt> * <p> * on a single line. After you see this line in the application's log file, you can be * pretty sure that the process is indeed terminated. This notification works even if * there are no other shutdown hooks registered, if you ensure that this class is * loaded at all, e.g. by calling the no-op {@link #useMe()} method somewhere * ({@link ERXApplication} does this for you if you extend that). * <p> * Usage (e.g. in your Application class constructor): * <pre><code> * new ERXShutdownHook() { * {@literal @}Override * public void hook() { * // do something * } * }; * </code></pre> * * <h3>CAUTION</h3> * You should not use this class when deploying the application as a J2EE servlet as it may interfere with other servlets running in the same VM. * To disable this feature you have to provide the following start parameter to the java VM: * * <code> * -Der.extensions.ERXApplication.enableERXShutdownHook=false * </code> * * @author Maik Musall, maik@selbstdenker.ag * */ public abstract class ERXShutdownHook extends Thread { static final Set<ERXShutdownHook> ALL_HOOKS = new HashSet<>(); public static void initERXShutdownHook() { System.out.println( "WILL ADD SHUTDOWNHOOK" ); Runtime.getRuntime().addShutdownHook( new Thread( "shutdown_complete_message_writer" ) { @Override public void run() { try { synchronized( ALL_HOOKS ) { while( ALL_HOOKS.size() > 0 ) { // Use System.out to minimize dependencies System.out.println( "ShutdownHook waiting for " + ALL_HOOKS.size() + " hook" + (ALL_HOOKS.size() > 1 ? "s" : "") + " to complete" ); ALL_HOOKS.wait(); } if ( ! ERXApplication.erxApplication().getIsTerminating()) { NSNotificationCenter.defaultCenter().postNotification(new NSNotification(ERXApplication.ApplicationWillTerminateNotification, NSKeyValueCoding.NullValue)); } System.out.println( "APPLICATION SHUTDOWN SEQUENCE COMPLETE" ); } } catch( Exception e ) { e.printStackTrace(); } } } ); } private String name; /** * Call this in your app constructor if you have no other shutdown hooks. If you don't call * anything, this class will not be loaded at all and won't work. * * {@link ERXApplication} calls this, so no need to do this if you're extending that. */ public static void useMe() { // do nothing } /** * Construct a new nameless shutdown hook and register it. It is recommended to use named hooks wherever * possible for easier debugging. */ public ERXShutdownHook() { Runtime.getRuntime().addShutdownHook( this ); ALL_HOOKS.add( this ); } /** * Construct a new named shutdown hook and register it. * @param hookName hook name */ public ERXShutdownHook( String hookName ) { super( hookName ); name = hookName; Runtime.getRuntime().addShutdownHook( this ); ALL_HOOKS.add( this ); } @Override public final void run() { try { if( name != null ) System.out.println( "ERXShutdownHook " + name + " launched" ); hook(); if( name != null ) System.out.println( "ERXShutdownHook " + name + " completed" ); synchronized( ALL_HOOKS ) { ALL_HOOKS.remove( this ); ALL_HOOKS.notify(); } } catch( Exception e ) { e.printStackTrace(); } } /** * This is where you implement what is supposed to be run at shutdown time. */ abstract public void hook(); }