KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > tanukisoftware > wrapper > WrapperStartStopApp


1 package org.tanukisoftware.wrapper;
2
3 /*
4  * Copyright (c) 1999, 2006 Tanuki Software Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of the Java Service Wrapper and associated
8  * documentation files (the "Software"), to deal in the Software
9  * without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sub-license,
11  * and/or sell copies of the Software, and to permit persons to
12  * whom the Software is furnished to do so, subject to the
13  * following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  *
27  *
28  * Portions of the Software have been derived from source code
29  * developed by Silver Egg Technology under the following license:
30  *
31  * Copyright (c) 2001 Silver Egg Technology
32  *
33  * Permission is hereby granted, free of charge, to any person
34  * obtaining a copy of this software and associated documentation
35  * files (the "Software"), to deal in the Software without
36  * restriction, including without limitation the rights to use,
37  * copy, modify, merge, publish, distribute, sub-license, and/or
38  * sell copies of the Software, and to permit persons to whom the
39  * Software is furnished to do so, subject to the following
40  * conditions:
41  *
42  * The above copyright notice and this permission notice shall be
43  * included in all copies or substantial portions of the Software.
44  */

45
46 import java.lang.reflect.InvocationTargetException JavaDoc;
47 import java.lang.reflect.Method JavaDoc;
48 import java.lang.reflect.Modifier JavaDoc;
49
50 /**
51  * By default the WrapperStartStopApp will only wait for 2 seconds for the main
52  * method of the start class to complete. This was done because the main
53  * methods of many applications never return. It is possible to force the
54  * class to wait for the startup main method to complete by defining the
55  * following system property when launching the JVM (defaults to FALSE):
56  * -Dorg.tanukisoftware.wrapper.WrapperStartStopApp.waitForStartMain=TRUE
57  * <p>
58  * Using the waitForStartMain property will cause the startup to wait
59  * indefinitely. This is fine if the main method will always return
60  * within a predefined period of time. But if there is any chance that
61  * it could hang, then the maxStartMainWait property may be a better
62  * option. It allows the 2 second wait time to be overridden. To wait
63  * for up to 5 minutes for the startup main method to complete, set
64  * the property to 300 as follows (defaults to 2 seconds):
65  * -Dorg.tanukisoftware.wrapper.WrapperStartStopApp.maxStartMainWait=300
66  * <p>
67  * It is possible to extend this class but make absolutely sure that any
68  * overridden methods call their super method or the class will fail to
69  * function correctly. Most users will have no need to override this
70  * class.
71  * <p>
72  * NOTE - The main methods of many applications are designed not to
73  * return. In these cases, you must either stick with the default 2 second
74  * startup timeout or specify a slightly longer timeout, using the
75  * maxStartMainWait property, to simulate the amount of time your application
76  * takes to start up.
77  * <p>
78  * WARNING - If the waitForStartMain is specified for an application
79  * whose start method never returns, the Wrapper will appear at first to be
80  * functioning correctly. However the Wrapper will never enter a running
81  * state, this means that the Windows Service Manager and several of the
82  * Wrapper's error recovery mechanisms will not function correctly.
83  *
84  * @author Leif Mortenson <leif@tanukisoftware.com>
85  */

86 public class WrapperStartStopApp
87     implements WrapperListener, Runnable JavaDoc
88 {
89     /**
90      * Application's start main method
91      */

92     private Method JavaDoc m_startMainMethod;
93     
94     /**
95      * Command line arguments to be passed on to the start main method
96      */

97     private String JavaDoc[] m_startMainArgs;
98     
99     /**
100      * Application's stop main method
101      */

102     private Method JavaDoc m_stopMainMethod;
103     
104     /**
105      * Should the stop process force the JVM to exit, or wait for all threads
106      * to die on their own.
107      */

108     private boolean m_stopWait;
109     
110     /**
111      * Command line arguments to be passed on to the stop main method
112      */

113     private String JavaDoc[] m_stopMainArgs;
114     
115     /**
116      * Gets set to true when the thread used to launch the application
117      * actuially starts.
118      */

119     private boolean m_mainStarted;
120     
121     /**
122      * Gets set to true when the thread used to launch the application
123      * completes.
124      */

125     private boolean m_mainComplete;
126     
127     /**
128      * Exit code to be returned if the application fails to start.
129      */

130     private Integer JavaDoc m_mainExitCode;
131     
132     /**
133      * Flag used to signify that the start method has completed.
134      */

135     private boolean m_startComplete;
136     
137     /*---------------------------------------------------------------
138      * Constructors
139      *-------------------------------------------------------------*/

140     protected WrapperStartStopApp( String JavaDoc args[] )
141     {
142         
143         // Initialize the WrapperManager class on startup by referencing it.
144
Class JavaDoc wmClass = WrapperManager.class;
145         
146         // Get the class name of the application
147
if ( args.length < 5 )
148         {
149             System.out.println( "WrapperStartStopApp: Not enough argments. Minimum 5 required." );
150             showUsage();
151             WrapperManager.stop( 1 );
152             return; // Will not get here
153
}
154         
155         
156         // Look for the start main method.
157
m_startMainMethod = getMainMethod( args[0] );
158         // Get the start arguments
159
String JavaDoc[] startArgs = getArgs( args, 1 );
160         
161         
162         // Where do the stop arguments start
163
int stopArgBase = 2 + startArgs.length;
164         if ( args.length < stopArgBase + 3 )
165         {
166             System.out.println( "WrapperStartStopApp: Not enough argments. Minimum 3 after start "
167                 + "arguments." );
168             showUsage();
169             WrapperManager.stop( 1 );
170             return; // Will not get here
171
}
172         // Look for the stop main method.
173
m_stopMainMethod = getMainMethod( args[stopArgBase] );
174         // Get the stopWait flag
175
if ( args[stopArgBase + 1].equalsIgnoreCase( "true" ) )
176         {
177             m_stopWait = true;
178         }
179         else if ( args[stopArgBase + 1].equalsIgnoreCase( "false" ) )
180         {
181             m_stopWait = false;
182         }
183         else
184         {
185             System.out.println( "WrapperStartStopApp: The stop_wait argument must be either true "
186                 + "or false." );
187             showUsage();
188             WrapperManager.stop( 1 );
189             return; // Will not get here
190
}
191         // Get the start arguments
192
m_stopMainArgs = getArgs( args, stopArgBase + 2 );
193         
194         // Start the application. If the JVM was launched from the native
195
// Wrapper then the application will wait for the native Wrapper to
196
// call the application's start method. Otherwise the start method
197
// will be called immediately.
198
WrapperManager.start( this, startArgs );
199         
200         // This thread ends, the WrapperManager will start the application after the Wrapper has
201
// been propperly initialized by calling the start method above.
202
}
203     
204     
205     protected WrapperStartStopApp( Method JavaDoc startMainMethod,
206                                  Method JavaDoc stopMainMethod,
207                                  boolean stopWait,
208                                  String JavaDoc[] stopMainArgs )
209     {
210         m_startMainMethod = startMainMethod;
211         m_stopMainMethod = stopMainMethod;
212         m_stopWait = stopWait;
213         m_stopMainArgs = stopMainArgs;
214     }
215     
216     /*---------------------------------------------------------------
217      * Runnable Methods
218      *-------------------------------------------------------------*/

219     /**
220      * Used to launch the application in a separate thread.
221      */

222     public void run()
223     {
224         // Notify the start method that the thread has been started by the JVM.
225
synchronized( this )
226         {
227             m_mainStarted = true;
228             notifyAll();
229         }
230         
231         Throwable JavaDoc t = null;
232         try
233         {
234             if ( WrapperManager.isDebugEnabled() )
235             {
236                 System.out.println( "WrapperStartStopApp: invoking start main method" );
237             }
238             m_startMainMethod.invoke( null, new Object JavaDoc[] { m_startMainArgs } );
239             if ( WrapperManager.isDebugEnabled() )
240             {
241                 System.out.println( "WrapperStartStopApp: start main method completed" );
242             }
243             
244             synchronized(this)
245             {
246                 // Let the start() method know that the main method returned, in case it is
247
// still waiting.
248
m_mainComplete = true;
249                 this.notifyAll();
250             }
251             
252             return;
253         }
254         catch ( IllegalAccessException JavaDoc e )
255         {
256             t = e;
257         }
258         catch ( IllegalArgumentException JavaDoc e )
259         {
260             t = e;
261         }
262         catch ( InvocationTargetException JavaDoc e )
263         {
264             t = e.getTargetException();
265             if ( t == null )
266             {
267                 t = e;
268             }
269         }
270         
271         // If we get here, then an error was thrown. If this happened quickly
272
// enough, the start method should be allowed to shut things down.
273
System.out.println();
274         System.out.println( "WrapperStartStopApp: Encountered an error running start main: " + t );
275
276         // We should print a stack trace here, because in the case of an
277
// InvocationTargetException, the user needs to know what exception
278
// their app threw.
279
t.printStackTrace();
280
281         synchronized(this)
282         {
283             if ( m_startComplete )
284             {
285                 // Shut down here.
286
WrapperManager.stop( 1 );
287                 return; // Will not get here.
288
}
289             else
290             {
291                 // Let start method handle shutdown.
292
m_mainComplete = true;
293                 m_mainExitCode = new Integer JavaDoc( 1 );
294                 this.notifyAll();
295                 return;
296             }
297         }
298     }
299     
300     /*---------------------------------------------------------------
301      * WrapperListener Methods
302      *-------------------------------------------------------------*/

303     /**
304      * The start method is called when the WrapperManager is signalled by the
305      * native wrapper code that it can start its application. This
306      * method call is expected to return, so a new thread should be launched
307      * if necessary.
308      * If there are any problems, then an Integer should be returned, set to
309      * the desired exit code. If the application should continue,
310      * return null.
311      */

312     public Integer JavaDoc start( String JavaDoc[] args )
313     {
314         // Decide whether or not to wait for the start main method to complete before returning.
315
boolean waitForStartMain = WrapperSystemPropertyUtil.getBooleanProperty(
316             WrapperStartStopApp.class.getName() + ".waitForStartMain", false );
317         int maxStartMainWait = WrapperSystemPropertyUtil.getIntProperty(
318             WrapperStartStopApp.class.getName() + ".maxStartMainWait", 2 );
319         maxStartMainWait = Math.max( 1, maxStartMainWait );
320         
321         // Decide the maximum number of times to loop waiting for the main start method.
322
int maxLoops;
323         if ( waitForStartMain )
324         {
325             maxLoops = Integer.MAX_VALUE;
326             if ( WrapperManager.isDebugEnabled() )
327             {
328                 System.out.println( "WrapperStartStopApp: start(args) Will wait indefinitely "
329                     + "for the main method to complete." );
330             }
331         }
332         else
333         {
334             maxLoops = maxStartMainWait; // 1s loops.
335
if ( WrapperManager.isDebugEnabled() )
336             {
337                 System.out.println( "WrapperStartStopApp: start(args) Will wait up to " + maxLoops
338                     + " seconds for the main method to complete." );
339             }
340         }
341         
342         Thread JavaDoc mainThread = new Thread JavaDoc( this, "WrapperStartStopAppMain" );
343         synchronized(this)
344         {
345             m_startMainArgs = args;
346             mainThread.start();
347             
348             // To avoid problems with the main thread starting slowly on heavily loaded systems,
349
// do not continue until the thread has actually started.
350
while ( !m_mainStarted )
351             {
352                 try
353                 {
354                     this.wait( 1000 );
355                 }
356                 catch ( InterruptedException JavaDoc e )
357                 {
358                     // Continue.
359
}
360             }
361             
362             // Wait for startup main method to complete.
363
int loops = 0;
364             while ( ( loops < maxLoops ) && ( !m_mainComplete ) )
365             {
366                 try
367                 {
368                     this.wait( 1000 );
369                 }
370                 catch ( InterruptedException JavaDoc e )
371                 {
372                     // Continue.
373
}
374                 
375                 if ( !m_mainComplete )
376                 {
377                     // If maxLoops is large then this could take a while. Notify the
378
// WrapperManager that we are still starting so it doesn't give up.
379
WrapperManager.signalStarting( 5000 );
380                 }
381                 
382                 loops++;
383             }
384             
385             // Always set the flag stating that the start method completed. This is needed
386
// so the run method can decide whether or not it needs to be responsible for
387
// shutting down the JVM in the event of an exception thrown by the start main
388
// method.
389
m_startComplete = true;
390             
391             // The main exit code will be null unless an error was thrown by the start
392
// main method.
393
if ( WrapperManager.isDebugEnabled() )
394             {
395                 System.out.println( "WrapperStartStopApp: start(args) end. Main Completed="
396                     + m_mainComplete + ", exitCode=" + m_mainExitCode );
397             }
398             return m_mainExitCode;
399         }
400     }
401     
402     /**
403      * Called when the application is shutting down.
404      */

405     public int stop( int exitCode )
406     {
407         if ( WrapperManager.isDebugEnabled() )
408         {
409             System.out.println( "WrapperStartStopApp: stop(" + exitCode + ")" );
410         }
411         
412         // Execute the main method in the stop class
413
Throwable JavaDoc t = null;
414         try
415         {
416             if ( WrapperManager.isDebugEnabled() )
417             {
418                 System.out.println( "WrapperStartStopApp: invoking stop main method" );
419             }
420             m_stopMainMethod.invoke( null, new Object JavaDoc[] { m_stopMainArgs } );
421             if ( WrapperManager.isDebugEnabled() )
422             {
423                 System.out.println( "WrapperStartStopApp: stop main method completed" );
424             }
425             
426             if ( m_stopWait )
427             {
428                 // This feature exists to make sure the stop process waits for the main
429
// application to fully shutdown. This can only be done by looking for
430
// and counting the number of non-daemon threads still running in the
431
// system.
432

433                 int systemThreadCount = WrapperSystemPropertyUtil.getIntProperty(
434                     WrapperStartStopApp.class.getName() + ".systemThreadCount", 1 );
435                 systemThreadCount = Math.max( 0, systemThreadCount );
436                 
437                 int threadCnt;
438                 while( ( threadCnt = getNonDaemonThreadCount() ) > systemThreadCount )
439                 {
440                     if ( WrapperManager.isDebugEnabled() )
441                     {
442                         System.out.println( "WrapperStartStopApp: stopping. Waiting for "
443                             + ( threadCnt - systemThreadCount ) + " threads to complete." );
444                     }
445                     try
446                     {
447                         Thread.sleep( 1000 );
448                     }
449                     catch ( InterruptedException JavaDoc e )
450                     {
451                     }
452                 }
453             }
454             
455             // Success
456
return exitCode;
457         }
458         catch ( IllegalAccessException JavaDoc e )
459         {
460             t = e;
461         }
462         catch ( IllegalArgumentException JavaDoc e )
463         {
464             t = e;
465         }
466         catch ( InvocationTargetException JavaDoc e )
467         {
468             t = e;
469         }
470         
471         // If we get here, then an error was thrown.
472
System.out.println( "Encountered an error running stop main: " + t );
473
474         // We should print a stack trace here, because in the case of an
475
// InvocationTargetException, the user needs to know what exception
476
// their app threw.
477
t.printStackTrace();
478         
479         // Return a failure exit code
480
return 1;
481     }
482     
483     /**
484      * Called whenever the native wrapper code traps a system control signal
485      * against the Java process. It is up to the callback to take any actions
486      * necessary. Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT,
487      * WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, or
488      * WRAPPER_CTRL_SHUTDOWN_EVENT
489      */

490     public void controlEvent( int event )
491     {
492         if ( ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT )
493             && WrapperManager.isLaunchedAsService() )
494         {
495             // Ignore
496
if ( WrapperManager.isDebugEnabled() )
497             {
498                 System.out.println( "WrapperStartStopApp: controlEvent(" + event + ") Ignored" );
499             }
500         }
501         else
502         {
503             if ( WrapperManager.isDebugEnabled() )
504             {
505                 System.out.println( "WrapperStartStopApp: controlEvent(" + event + ") Stopping" );
506             }
507             WrapperManager.stop( 0 );
508             // Will not get here.
509
}
510     }
511     
512     /*---------------------------------------------------------------
513      * Methods
514      *-------------------------------------------------------------*/

515     /**
516      * Returns a count of all non-daemon threads in the JVM, starting with the top
517      * thread group.
518      *
519      * @return Number of non-daemon threads.
520      */

521     private int getNonDaemonThreadCount()
522     {
523         // Locate the top thread group.
524
ThreadGroup JavaDoc topGroup = Thread.currentThread().getThreadGroup();
525         while ( topGroup.getParent() != null )
526         {
527             topGroup = topGroup.getParent();
528         }
529         
530         // Get a list of all threads. Use an array that is twice the total number of
531
// threads as the number of running threads may be increasing as this runs.
532
Thread JavaDoc[] threads = new Thread JavaDoc[topGroup.activeCount() * 2];
533         topGroup.enumerate( threads, true );
534         
535         // Only count any non daemon threads which are
536
// still alive other than this thread.
537
int liveCount = 0;
538         for ( int i = 0; i < threads.length; i++ )
539         {
540             /*
541             if ( threads[i] != null )
542             {
543                 System.out.println( "Check " + threads[i].getName() + " daemon="
544                     + threads[i].isDaemon() + " alive=" + threads[i].isAlive() );
545             }
546             */

547             if ( ( threads[i] != null ) && threads[i].isAlive() )
548             {
549                 // Do not count this thread.
550
if ( ( Thread.currentThread() != threads[i] ) && ( !threads[i].isDaemon() ) )
551                 {
552                     // Non-Daemon living thread
553
liveCount++;
554                     //System.out.println( " -> Non-Daemon" );
555
}
556             }
557         }
558         //System.out.println( " => liveCount = " + liveCount );
559

560         return liveCount;
561     }
562     
563     /**
564      * Returns the main method of the specified class. If there are any problems,
565      * an error message will be displayed and the Wrapper will be stopped. This
566      * method will only return if it has a valid method.
567      */

568     private Method JavaDoc getMainMethod( String JavaDoc className )
569     {
570         // Look for the start class by name
571
Class JavaDoc mainClass;
572         try
573         {
574             mainClass = Class.forName( className );
575         }
576         catch ( ClassNotFoundException JavaDoc e )
577         {
578             System.out.println( "WrapperStartStopApp: Unable to locate the class " + className
579                 + ": " + e );
580             showUsage();
581             WrapperManager.stop( 1 );
582             return null; // Will not get here
583
}
584         catch ( LinkageError JavaDoc e )
585         {
586             System.out.println( "WrapperStartStopApp: Unable to locate the class " + className
587                 + ": " + e );
588             showUsage();
589             WrapperManager.stop( 1 );
590             return null; // Will not get here
591
}
592         
593         // Look for the start method
594
Method JavaDoc mainMethod;
595         try
596         {
597             // getDeclaredMethod will return any method named main in the specified class,
598
// while getMethod will only return public methods, but it will search up the
599
// inheritance path.
600
mainMethod = mainClass.getMethod( "main", new Class JavaDoc[] { String JavaDoc[].class } );
601         }
602         catch ( NoSuchMethodException JavaDoc e )
603         {
604             System.out.println(
605                 "WrapperStartStopApp: Unable to locate a public static main method in "
606                 + "class " + className + ": " + e );
607             showUsage();
608             WrapperManager.stop( 1 );
609             return null; // Will not get here
610
}
611         catch ( SecurityException JavaDoc e )
612         {
613             System.out.println(
614                 "WrapperStartStopApp: Unable to locate a public static main method in "
615                 + "class " + className + ": " + e );
616             showUsage();
617             WrapperManager.stop( 1 );
618             return null; // Will not get here
619
}
620         
621         // Make sure that the method is public and static
622
int modifiers = mainMethod.getModifiers();
623         if ( !( Modifier.isPublic( modifiers ) && Modifier.isStatic( modifiers ) ) )
624         {
625             System.out.println( "WrapperStartStopApp: The main method in class " + className
626                 + " must be declared public and static." );
627             showUsage();
628             WrapperManager.stop( 1 );
629             return null; // Will not get here
630
}
631         
632         return mainMethod;
633     }
634     
635     private String JavaDoc[] getArgs( String JavaDoc[] args, int argBase )
636     {
637         // The arg at the arg base should be a count of the number of available arguments.
638
int argCount;
639         try
640         {
641             argCount = Integer.parseInt( args[argBase] );
642         }
643         catch ( NumberFormatException JavaDoc e )
644         {
645             System.out.println( "WrapperStartStopApp: Illegal argument count: " + args[argBase] );
646             showUsage();
647             WrapperManager.stop( 1 );
648             return null; // Will not get here
649
}
650         if ( argCount < 0 )
651         {
652             System.out.println( "WrapperStartStopApp: Illegal argument count: " + args[argBase] );
653             showUsage();
654             WrapperManager.stop( 1 );
655             return null; // Will not get here
656
}
657         
658         // Make sure that there are enough arguments in the array.
659
if ( args.length < argBase + 1 + argCount )
660         {
661             System.out.println( "WrapperStartStopApp: Not enough argments. Argument count of "
662                 + argCount + " was specified." );
663             showUsage();
664             WrapperManager.stop( 1 );
665             return null; // Will not get here
666
}
667         
668         // Create the argument array
669
String JavaDoc[] mainArgs = new String JavaDoc[argCount];
670         System.arraycopy( args, argBase + 1, mainArgs, 0, argCount );
671         
672         return mainArgs;
673     }
674     
675     /**
676      * Displays application usage
677      */

678     protected void showUsage()
679     {
680         System.out.println();
681         System.out.println(
682             "WrapperStartStopApp Usage:" );
683         System.out.println(
684             " java org.tanukisoftware.wrapper.WrapperStartStopApp {start_class} {start_arg_count} "
685             + "[start_arguments] {stop_class} {stop_wait} {stop_arg_count} [stop_arguments]" );
686         System.out.println();
687         System.out.println(
688             "Where:" );
689         System.out.println(
690             " start_class: The fully qualified class name to run to start the " );
691         System.out.println(
692             " application." );
693         System.out.println(
694             " start_arg_count: The number of arguments to be passed to the start class's " );
695         System.out.println(
696             " main method." );
697         System.out.println(
698             " start_arguments: The arguments that would normally be passed to the start " );
699         System.out.println(
700             " class application." );
701         System.out.println(
702             " stop_class: The fully qualified class name to run to stop the " );
703         System.out.println(
704             " application." );
705         System.out.println(
706             " stop_wait: When stopping, should the Wrapper wait for all threads to " );
707         System.out.println(
708             " complete before exiting (true/false)." );
709         System.out.println(
710             " stop_arg_count: The number of arguments to be passed to the stop class's " );
711         System.out.println(
712             " main method." );
713         System.out.println(
714             " stop_arguments: The arguments that would normally be passed to the stop " );
715         System.out.println(
716             " class application." );
717     }
718     
719     /*---------------------------------------------------------------
720      * Main Method
721      *-------------------------------------------------------------*/

722     /**
723      * Used to Wrapper enable a standard Java application. This main
724      * expects the first argument to be the class name of the application
725      * to launch. All remaining arguments will be wrapped into a new
726      * argument list and passed to the main method of the specified
727      * application.
728      */

729     public static void main( String JavaDoc args[] )
730     {
731         new WrapperStartStopApp( args );
732     }
733 }
734
735
Popular Tags