KickJava   Java API By Example, From Geeks To Geeks.

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


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.io.DataInputStream JavaDoc;
47 import java.io.File JavaDoc;
48 import java.io.InputStream JavaDoc;
49 import java.io.InterruptedIOException JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.OutputStream JavaDoc;
52 import java.io.PrintStream JavaDoc;
53 import java.lang.reflect.Constructor JavaDoc;
54 import java.lang.reflect.InvocationTargetException JavaDoc;
55 import java.lang.reflect.Method JavaDoc;
56 import java.net.BindException JavaDoc;
57 import java.net.ConnectException JavaDoc;
58 import java.net.InetAddress JavaDoc;
59 import java.net.ServerSocket JavaDoc;
60 import java.net.Socket JavaDoc;
61 import java.net.SocketException JavaDoc;
62 import java.net.UnknownHostException JavaDoc;
63 import java.security.AccessControlException JavaDoc;
64 import java.security.AccessController JavaDoc;
65 import java.security.PrivilegedAction JavaDoc;
66 import java.util.ArrayList JavaDoc;
67 import java.util.Iterator JavaDoc;
68 import java.util.List JavaDoc;
69 import java.util.Properties JavaDoc;
70 import java.util.StringTokenizer JavaDoc;
71
72 import org.tanukisoftware.wrapper.event.WrapperControlEvent;
73 import org.tanukisoftware.wrapper.event.WrapperEvent;
74 import org.tanukisoftware.wrapper.event.WrapperEventListener;
75 import org.tanukisoftware.wrapper.event.WrapperPingEvent;
76 import org.tanukisoftware.wrapper.event.WrapperServiceControlEvent;
77 import org.tanukisoftware.wrapper.event.WrapperTickEvent;
78 import org.tanukisoftware.wrapper.resources.ResourceManager;
79 import org.tanukisoftware.wrapper.security.WrapperEventPermission;
80 import org.tanukisoftware.wrapper.security.WrapperPermission;
81 import org.tanukisoftware.wrapper.security.WrapperServicePermission;
82
83 /**
84  * Handles all communication with the native portion of the Wrapper code.
85  * The native wrapper code will launch Java in a separate process and set
86  * up a server socket which the Java code is expected to open a socket to
87  * on startup. When the server socket is created, a port will be chosen
88  * depending on what is available to the system. This port will then be
89  * passed to the Java process as property named "wrapper.port".
90  *
91  * For security reasons, the native code will only allow connections from
92  * localhost and will expect to receive the key specified in a property
93  * named "wrapper.key".
94  *
95  * This class is implemented as a singleton class.
96  *
97  * Generate JNI Headers with the following command in the build/classes
98  * directory:
99  * javah -jni -classpath ./ org.tanukisoftware.wrapper.WrapperManager
100  *
101  * @author Leif Mortenson <leif@tanukisoftware.com>
102  */

103 public final class WrapperManager
104     implements Runnable JavaDoc
105 {
106     private static final String JavaDoc WRAPPER_CONNECTION_THREAD_NAME = "Wrapper-Connection";
107     
108     private static final int DEFAULT_PORT = 15003;
109     private static final int DEFAULT_SO_TIMEOUT = 10000;
110     private static final int DEFAULT_CPU_TIMEOUT = 10000;
111     
112     /** The number of milliseconds in one tick. Used for internal system
113      * time independent time keeping. */

114     private static final int TICK_MS = 100;
115     private static final int TIMER_FAST_THRESHOLD = 2 * 24 * 3600 * 1000 / TICK_MS; // 2 days.
116
private static final int TIMER_SLOW_THRESHOLD = 2 * 24 * 3600 * 1000 / TICK_MS; // 2 days.
117

118     private static final byte WRAPPER_MSG_START = (byte)100;
119     private static final byte WRAPPER_MSG_STOP = (byte)101;
120     private static final byte WRAPPER_MSG_RESTART = (byte)102;
121     private static final byte WRAPPER_MSG_PING = (byte)103;
122     private static final byte WRAPPER_MSG_STOP_PENDING = (byte)104;
123     private static final byte WRAPPER_MSG_START_PENDING = (byte)105;
124     private static final byte WRAPPER_MSG_STARTED = (byte)106;
125     private static final byte WRAPPER_MSG_STOPPED = (byte)107;
126     private static final byte WRAPPER_MSG_KEY = (byte)110;
127     private static final byte WRAPPER_MSG_BADKEY = (byte)111;
128     private static final byte WRAPPER_MSG_LOW_LOG_LEVEL = (byte)112;
129     private static final byte WRAPPER_MSG_PING_TIMEOUT = (byte)113;
130     private static final byte WRAPPER_MSG_SERVICE_CONTROL_CODE = (byte)114;
131     private static final byte WRAPPER_MSG_PROPERTIES = (byte)115;
132     
133     /** Log commands are actually 116 + the LOG LEVEL. */
134     private static final byte WRAPPER_MSG_LOG = (byte)116;
135     
136     /** Received when the user presses CTRL-C in the console on Windows or UNIX platforms. */
137     public static final int WRAPPER_CTRL_C_EVENT = 200;
138     
139     /** Received when the user clicks on the close button of a Console on Windows. */
140     public static final int WRAPPER_CTRL_CLOSE_EVENT = 201;
141     
142     /** Received when the user logs off of a Windows system. */
143     public static final int WRAPPER_CTRL_LOGOFF_EVENT = 202;
144     
145     /** Received when a Windows system is shutting down. */
146     public static final int WRAPPER_CTRL_SHUTDOWN_EVENT = 203;
147     
148     /** Received when a SIG TERM is received on a UNIX system. */
149     public static final int WRAPPER_CTRL_TERM_EVENT = 204;
150     
151     /** Received when a SIG HUP is received on a UNIX system. */
152     public static final int WRAPPER_CTRL_HUP_EVENT = 205;
153     
154     /** Log message at debug log level. */
155     public static final int WRAPPER_LOG_LEVEL_DEBUG = 1;
156     /** Log message at info log level. */
157     public static final int WRAPPER_LOG_LEVEL_INFO = 2;
158     /** Log message at status log level. */
159     public static final int WRAPPER_LOG_LEVEL_STATUS = 3;
160     /** Log message at warn log level. */
161     public static final int WRAPPER_LOG_LEVEL_WARN = 4;
162     /** Log message at error log level. */
163     public static final int WRAPPER_LOG_LEVEL_ERROR = 5;
164     /** Log message at fatal log level. */
165     public static final int WRAPPER_LOG_LEVEL_FATAL = 6;
166     /** Log message at advice log level. */
167     public static final int WRAPPER_LOG_LEVEL_ADVICE = 7;
168     
169     /** Service Control code which can be sent to start a service. */
170     public static final int SERVICE_CONTROL_CODE_START = 0x10000;
171     
172     /** Service Control code which can be sent or received to stop a service. */
173     public static final int SERVICE_CONTROL_CODE_STOP = 1;
174     
175     /** Service Control code which can be sent to pause a service. */
176     public static final int SERVICE_CONTROL_CODE_PAUSE = 2;
177     
178     /** Service Control code which can be sent to resume a paused service. */
179     public static final int SERVICE_CONTROL_CODE_CONTINUE = 3;
180     
181     /** Service Control code which can be sent to or received interrogate the status of a service. */
182     public static final int SERVICE_CONTROL_CODE_INTERROGATE = 4;
183     
184     /** Service Control code which can be received when the system is shutting down. */
185     public static final int SERVICE_CONTROL_CODE_SHUTDOWN = 5;
186     
187     /** Reference to the original value of System.out. */
188     private static PrintStream JavaDoc m_out;
189     
190     /** Reference to the original value of System.err. */
191     private static PrintStream JavaDoc m_err;
192     
193     /** Flag that will be set to true once a SecurityManager has been detected and tested. */
194     private static boolean m_securityManagerChecked = false;
195     
196     private static boolean m_disposed = false;
197     private static boolean m_started = false;
198     private static WrapperManager m_instance = null;
199     private static Thread JavaDoc m_hook = null;
200     private static boolean m_hookTriggered = false;
201     
202     /* Flag which records when the shutdownJVM method has completed. */
203     private static boolean m_shutdownJVMComplete = false;
204     
205     private static String JavaDoc[] m_args;
206     private static int m_port = DEFAULT_PORT;
207     private static int m_jvmPort;
208     private static int m_jvmPortMin;
209     private static int m_jvmPortMax;
210     private static String JavaDoc m_key;
211     private static int m_soTimeout = DEFAULT_SO_TIMEOUT;
212     private static long m_cpuTimeout = DEFAULT_CPU_TIMEOUT;
213     
214     /** Tick count when the start method completed. */
215     private static int m_startedTicks;
216     
217     /** The lowest configured log level in the Wrapper's configuration. This
218      * is set to a high value by default to disable all logging if the
219      * Wrapper does not register its low level or is not present. */

220     private static int m_lowLogLevel = WRAPPER_LOG_LEVEL_ADVICE + 1;
221     
222     /** The maximum amount of time in ms to allow to pass without the JVM
223      * pinging the server before the JVM is terminated to allow a resynch. */

224     private static int m_pingTimeout = 30000;
225     
226     /** Flag, set when the JVM is launched that is used to remember whether
227      * or not system signals are supposed to be ignored. */

228     private static boolean m_ignoreSignals = false;
229     
230     /** Thread which processes all communications with the native code. */
231     private static Thread JavaDoc m_commRunner;
232     private static boolean m_commRunnerStarted = false;
233     private static Thread JavaDoc m_eventRunner;
234     private static int m_eventRunnerTicks;
235     private static Thread JavaDoc m_startupRunner;
236     
237     /** True if the system time should be used for internal timeouts. */
238     private static boolean m_useSystemTime;
239     
240     /** The threashold of how many ticks the timer can be fast before a
241      * warning is displayed. */

242     private static int m_timerFastThreshold;
243     
244     /** The threashold of how many ticks the timer can be slow before a
245      * warning is displayed. */

246     private static int m_timerSlowThreshold;
247     
248     /**
249      * Bit depth of the currently running JVM. Will be 32 or 64.
250      * A 64-bit JVM means that the system is also 64-bit, but a 32-bit JVM
251      * can be run either on a 32 or 64-bit system.
252      */

253     private static int m_jvmBits;
254     
255     /** An integer which stores the number of ticks since the
256      * JVM was launched. Using an int rather than a long allows the value
257      * to be used without requiring any synchronization. This is only
258      * used if the m_useSystemTime flag is false. */

259     private static volatile int m_ticks;
260     
261     private static WrapperListener m_listener;
262     
263     private static int m_lastPingTicks;
264     private static ServerSocket JavaDoc m_serverSocket;
265     private static Socket JavaDoc m_socket;
266     private static boolean m_shuttingDown = false;
267     private static boolean m_appearHung = false;
268     
269     private static Method JavaDoc m_addShutdownHookMethod = null;
270     private static Method JavaDoc m_removeShutdownHookMethod = null;
271     
272     private static boolean m_service = false;
273     private static boolean m_debug = false;
274     private static int m_jvmId = 0;
275     private static boolean m_stopping = false;
276     private static Thread JavaDoc m_stoppingThread;
277     private static int m_exitCode;
278     private static boolean m_libraryOK = false;
279     private static byte[] m_commandBuffer = new byte[512];
280     
281     /** The contents of the wrapper configuration. */
282     private static WrapperProperties m_properties;
283     
284     /** List of registered WrapperEventListeners and their registered masks. */
285     private static List JavaDoc m_wrapperEventListenerMaskList = new ArrayList JavaDoc();
286     
287     /** Array of registered WrapperEventListeners and their registered masks.
288      * Should not be referenced directly. Access by calling
289      * getWrapperEventListenerMasks(). */

290     private static WrapperEventListenerMask[] m_wrapperEventListenerMasks = null;
291     
292     /** Flag used to tell whether or not WrapperCoreEvents should be produced. */
293     private static boolean m_produceCoreEvents = false;
294     
295     // message resources: eventually these will be split up
296
private static ResourceManager m_res = ResourceManager.getResourceManager();
297     private static ResourceManager m_error = m_res;
298     private static ResourceManager m_warning = m_res;
299     private static ResourceManager m_info = m_res;
300     
301     /*---------------------------------------------------------------
302      * Class Initializer
303      *-------------------------------------------------------------*/

304     /**
305      * When the WrapperManager class is first loaded, it attempts to load the
306      * configuration file specified using the 'wrapper.config' system property.
307      * When the JVM is launched from the Wrapper native code, the
308      * 'wrapper.config' and 'wrapper.key' parameters are specified.
309      * The 'wrapper.key' parameter is a password which is used to verify that
310      * connections are only coming from the native Wrapper which launched the
311      * current JVM.
312      */

313     static
314     {
315         // The wraper.jar must be given AllPermissions if a security manager
316
// has been configured. This is not a problem if one of the standard
317
// Wrapper helper classes is used to launch the JVM.
318
// If however a custom WrapperListener is being implemented then this
319
// class will most likely be loaded by code that is neither part of
320
// the system, nor part of the Wrapper code base. To avoid having
321
// to also give those classes AllPermissions as well, we do all of
322
// initialization in a Privileged block. This means that the code
323
// only requires that the wrapper.jar has been given the required
324
// permissions.
325
AccessController.doPrivileged(
326             new PrivilegedAction JavaDoc() {
327                 public Object JavaDoc run() {
328                     privilegedClassInit();
329                     return null;
330                 }
331             }
332         );
333     }
334     
335     /**
336      * The body of the static initializer is moved into a seperate method so
337      * it can be run as a PrivilegedAction.
338      */

339     private static void privilegedClassInit()
340     {
341         // Store references to the original System.out and System.err
342
// PrintStreams. The WrapperManager will always output to the
343
// original streams so its output will always end up in the
344
// wrapper.log file even if the end user code redirects the
345
// output to another log file.
346
// This is also important to be protect the Wrapper's functionality
347
// from the case where the user PrintStream enters a deadlock state.
348
m_out = System.out;
349         m_err = System.err;
350         
351         // Always create an empty properties object in case we are not running
352
// in the Wrapper or the properties are never sent.
353
m_properties = new WrapperProperties();
354         m_properties.lock();
355         
356         // This must be done before attempting to access any System Properties
357
// as that could cause a SecurityException if it is too strict.
358
checkSecurityManager();
359         
360         // Check for the debug flag
361
m_debug = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.debug", false );
362         
363         if ( m_debug )
364         {
365             m_out.println( "WrapperManager class initialized by thread: "
366                 + Thread.currentThread().getName()
367                 + " Using classloader: " + WrapperManager.class.getClassLoader() );
368         }
369         
370         //
371
// WARNING - The following banner is displayed to inform the user that they
372
// are using the Java Service Wrapper. This banner must remain
373
// clearly visible in the logs of any application making use of
374
// the Wrapper. This includes any applications based on the
375
// wrapper source.
376
// If you are here then you are benefiting from this project,
377
// please have the courtesy to respect its license.
378
m_out.println( "Wrapper (Version " + getVersion() + ") http://wrapper.tanukisoftware.org" );
379         m_out.println( " Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved." );
380         m_out.println();
381         
382         // Check for the jvmID
383
m_jvmId = WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvmid", 1 );
384         if ( m_debug )
385         {
386             m_out.println( "Wrapper Manager: JVM #" + m_jvmId );
387         }
388         
389         // Decide whether this is a 32 or 64 bit version of Java.
390
m_jvmBits = Integer.getInteger( "sun.arch.data.model", -1 ).intValue();
391         if ( m_debug )
392         {
393             if ( m_jvmBits > 0 )
394             {
395                 m_out.println( "Running a " + m_jvmBits + "-bit JVM." );
396             }
397             else
398             {
399                 m_out.println( "The bit depth of this JVM could not be determined." );
400             }
401         }
402         
403         // Initialize the timerTicks to a very high value. This means that we will
404
// always encounter the first rollover (200 * WRAPPER_MS / 1000) seconds
405
// after the Wrapper the starts, which means the rollover will be well
406
// tested.
407
m_ticks = Integer.MAX_VALUE - 200;
408         
409         m_useSystemTime = WrapperSystemPropertyUtil.getBooleanProperty(
410             "wrapper.use_system_time", false );
411         m_timerFastThreshold = WrapperSystemPropertyUtil.getIntProperty(
412             "wrapper.timer_fast_threshold", TIMER_FAST_THRESHOLD ) * 1000 / TICK_MS;
413         m_timerSlowThreshold = WrapperSystemPropertyUtil.getIntProperty(
414             "wrapper.timer_slow_threshold", TIMER_SLOW_THRESHOLD ) * 1000 / TICK_MS;
415         
416         // Check to see if we should register a shutdown hook
417
boolean disableShutdownHook = WrapperSystemPropertyUtil.getBooleanProperty(
418             "wrapper.disable_shutdown_hook", false );
419         
420         // Locate the add and remove shutdown hook methods using reflection so
421
// that this class can be compiled on 1.2.x versions of java.
422
try
423         {
424             m_addShutdownHookMethod =
425                 Runtime JavaDoc.class.getMethod( "addShutdownHook", new Class JavaDoc[] { Thread JavaDoc.class } );
426             m_removeShutdownHookMethod =
427                 Runtime JavaDoc.class.getMethod( "removeShutdownHook", new Class JavaDoc[] { Thread JavaDoc.class } );
428         }
429         catch ( NoSuchMethodException JavaDoc e )
430         {
431             if ( m_debug )
432             {
433                 m_out.println(
434                     "Wrapper Manager: Shutdown hooks not supported by current JVM." );
435             }
436             m_addShutdownHookMethod = null;
437             m_removeShutdownHookMethod = null;
438             disableShutdownHook = true;
439         }
440         
441         // If the shutdown hook is not disabled, then register it.
442
if ( !disableShutdownHook )
443         {
444             if ( m_debug )
445             {
446                 m_out.println( "Wrapper Manager: Registering shutdown hook" );
447             }
448             m_hook = new Thread JavaDoc( "Wrapper-Shutdown-Hook" )
449             {
450                 /**
451                  * Run the shutdown hook. (Triggered by the JVM when it is about to shutdown)
452                  */

453                 public void run()
454                 {
455                     // Stop the Wrapper cleanly.
456
m_hookTriggered = true;
457                     
458                     if ( m_debug )
459                     {
460                         m_out.println( "Wrapper Manager: ShutdownHook started" );
461                     }
462                     
463                     // Let the startup thread die since the shutdown hook is running.
464
m_startupRunner = null;
465                     
466                     // If we are not already stopping, then do so.
467
WrapperManager.stop( 0 );
468                     
469                     if ( m_debug )
470                     {
471                         m_out.println( "Wrapper Manager: ShutdownHook complete" );
472                     }
473                 }
474             };
475             
476             // Actually register the shutdown hook using reflection.
477
try
478             {
479                 m_addShutdownHookMethod.invoke( Runtime.getRuntime(), new Object JavaDoc[] { m_hook } );
480             }
481             catch ( IllegalAccessException JavaDoc e )
482             {
483                 m_out.println( "Wrapper Manager: Unable to register shutdown hook: " + e );
484             }
485             catch ( InvocationTargetException JavaDoc e )
486             {
487                 Throwable JavaDoc t = e.getTargetException();
488                 if ( t == null )
489                 {
490                     t = e;
491                 }
492                 
493                 m_out.println( "Wrapper Manager: Unable to register shutdown hook: " + t );
494             }
495         }
496         
497         // A key is required for the wrapper to work correctly. If it is not
498
// present, then assume that we are not being controlled by the native
499
// wrapper.
500
if ( ( m_key = System.getProperty( "wrapper.key" ) ) == null )
501         {
502             if ( m_debug )
503             {
504                 m_out.println( "Wrapper Manager: Not using wrapper. (key not specified)" );
505             }
506             
507             // The wrapper will not be used, so other values will not be used.
508
m_port = 0;
509             m_jvmPort = 0;
510             m_jvmPortMin = 0;
511             m_jvmPortMax = 0;
512             m_service = false;
513             m_cpuTimeout = 31557600000L; // One Year. Effectively never.
514
}
515         else
516         {
517             if ( m_debug )
518             {
519                 m_out.println( "Wrapper Manager: Using wrapper" );
520             }
521             
522             // A port must have been specified.
523
String JavaDoc sPort;
524             if ( ( sPort = System.getProperty( "wrapper.port" ) ) == null )
525             {
526                 String JavaDoc msg = m_res.format( "MISSING_PORT" );
527                 m_out.println( msg );
528                 throw new ExceptionInInitializerError JavaDoc( msg );
529             }
530             try
531             {
532                 m_port = Integer.parseInt( sPort );
533             }
534             catch ( NumberFormatException JavaDoc e )
535             {
536                 String JavaDoc msg = m_res.format( "BAD_PORT", sPort );
537                 m_out.println( msg );
538                 throw new ExceptionInInitializerError JavaDoc( msg );
539             }
540             
541             m_jvmPort =
542                 WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvm.port", 0 );
543             m_jvmPortMin =
544                 WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvm.port.min", 31000 );
545             m_jvmPortMax =
546                 WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvm.port.max", 31999 );
547             
548             // Check for the ignore signals flag
549
m_ignoreSignals = WrapperSystemPropertyUtil.getBooleanProperty(
550                 "wrapper.ignore_signals", false );
551             
552             // If this is being run as a headless server, then a flag would have been set
553
m_service = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.service", false );
554             
555             // Get the cpuTimeout
556
String JavaDoc sCPUTimeout = System.getProperty( "wrapper.cpu.timeout" );
557             if ( sCPUTimeout == null )
558             {
559                 m_cpuTimeout = DEFAULT_CPU_TIMEOUT;
560             }
561             else
562             {
563                 try
564                 {
565                     m_cpuTimeout = Integer.parseInt( sCPUTimeout ) * 1000L;
566                 }
567                 catch ( NumberFormatException JavaDoc e )
568                 {
569                     String JavaDoc msg = m_res.format( "BAD_CPU_TIMEOUT", sCPUTimeout );
570                     m_out.println( msg );
571                     throw new ExceptionInInitializerError JavaDoc( msg );
572                 }
573             }
574         }
575         
576         // Make sure that the version of the Wrapper is correct.
577
verifyWrapperVersion();
578         
579         // Register the MBeans if configured to do so.
580
if ( WrapperSystemPropertyUtil.getBooleanProperty(
581             WrapperManager.class.getName() + ".mbean", true ) )
582         {
583             registerMBean( new org.tanukisoftware.wrapper.jmx.WrapperManager(),
584                 "org.tanukisoftware.wrapper:type=WrapperManager" );
585         }
586         if ( WrapperSystemPropertyUtil.getBooleanProperty(
587             WrapperManager.class.getName() + ".mbean.testing", false ) )
588         {
589             registerMBean( new org.tanukisoftware.wrapper.jmx.WrapperManagerTesting(),
590                 "org.tanukisoftware.wrapper:type=WrapperManagerTesting" );
591         }
592         
593         // Initialize the native code to trap system signals
594
initializeNativeLibrary();
595         
596         if ( m_libraryOK )
597         {
598             // Make sure that the native library's version is correct.
599
verifyNativeLibraryVersion();
600             
601             // Get the PID of the current JVM from the native library. Be careful as the method
602
// will not exist if the library is old.
603
try
604             {
605                 System.setProperty( "wrapper.java.pid", Integer.toString( nativeGetJavaPID() ) );
606             }
607             catch ( Throwable JavaDoc e )
608             {
609                 if ( m_debug )
610                 {
611                     m_out.println( "Call to nativeGetJavaPID() failed: " + e );
612                 }
613             }
614         }
615         
616         // Start a thread which looks for control events sent to the
617
// process. The thread is also used to keep track of whether
618
// the VM has been getting CPU to avoid invalid timeouts and
619
// to maintain the number of ticks since the JVM was launched.
620
m_eventRunnerTicks = getTicks();
621         m_eventRunner = new Thread JavaDoc( "Wrapper-Control-Event-Monitor" )
622         {
623             public void run()
624             {
625                 if ( m_debug )
626                 {
627                     m_out.println( "Control event monitor thread started." );
628                 }
629                 
630                 try
631                 {
632                     WrapperTickEventImpl tickEvent = new WrapperTickEventImpl();
633                     int lastTickOffset = 0;
634                     boolean first = true;
635                     
636                     while ( !m_shuttingDown )
637                     {
638                         int offsetDiff;
639                         if ( !m_useSystemTime )
640                         {
641                             // Get the tick count based on the system time.
642
int sysTicks = getSystemTicks();
643                             
644                             // Increment the tick counter by 1. This loop takes just slightly
645
// more than the length of a "tick" but it is a good enough
646
// approximation for our purposes. The accuracy of the tick length
647
// falls sharply when the system is under heavly load, but this
648
// has the desired effect as the Wrapper is also much less likely
649
// to encounter false timeouts due to the heavy load.
650
// The ticks field is volatile and a single integer, so it is not
651
// necessary to synchronize this.
652
// When the ticks count reaches the upper limit of the int range,
653
// it is ok to just let it overflow and wrap.
654
m_ticks++;
655                             
656                             // Calculate the offset between the two tick counts.
657
// This will always work due to overflow.
658
int tickOffset = sysTicks - m_ticks;
659                             
660                             // The number we really want is the difference between this tickOffset
661
// and the previous one.
662
offsetDiff = tickOffset - lastTickOffset;
663                             
664                             if ( first )
665                             {
666                                 first = false;
667                             }
668                             else
669                             {
670                                 if ( offsetDiff > m_timerSlowThreshold )
671                                 {
672                                     m_out.println( "The timer fell behind the system clock by "
673                                         + ( offsetDiff * TICK_MS ) + "ms." );
674                                 }
675                                 else if ( offsetDiff < - m_timerFastThreshold )
676                                 {
677                                     m_out.println( "The system clock fell behind the timer by "
678                                         + ( -1 * offsetDiff * TICK_MS ) + "ms." );
679                                 }
680                             }
681                             
682                             // Store this tick offset for the net time through the loop.
683
lastTickOffset = tickOffset;
684                         }
685                         else
686                         {
687                             offsetDiff = 0;
688                         }
689                         
690                         //m_out.println( " UNIX Time: " + Long.toHexString( System.currentTimeMillis() )
691
// + ", ticks=" + Integer.toHexString( getTicks() ) + ", sysTicks="
692
// + Integer.toHexString( getSystemTicks() ) );
693

694                         // Attempt to detect whether or not we are being starved of CPU.
695
// This will only have any effect if the m_useSystemTime flag is
696
// set.
697
int nowTicks = getTicks();
698                         long age = getTickAge( m_eventRunnerTicks, nowTicks );
699                         if ( ( m_cpuTimeout > 0 ) && ( age > m_cpuTimeout ) )
700                         {
701                             m_out.println( "JVM Process has not received any CPU time for "
702                                 + ( age / 1000 ) + " seconds. Extending timeouts." );
703                             
704                             // Make sure that we don't get any ping timeouts in this event
705
m_lastPingTicks = nowTicks;
706                         }
707                         m_eventRunnerTicks = nowTicks;
708                         
709                         // If there are any listeners interrested in core events then fire
710
// off a tick event.
711
if ( m_produceCoreEvents )
712                         {
713                             tickEvent.m_ticks = nowTicks;
714                             tickEvent.m_tickOffset = offsetDiff;
715                             fireWrapperEvent( tickEvent );
716                         }
717                         
718                         if ( m_libraryOK )
719                         {
720                             // Look for a control event in the wrapper library
721
int event = WrapperManager.nativeGetControlEvent();
722                             if ( event != 0 )
723                             {
724                                 WrapperManager.controlEvent( event );
725                             }
726                         }
727                         
728                         // Wait before checking for another control event.
729
try
730                         {
731                             Thread.sleep( TICK_MS );
732                         }
733                         catch ( InterruptedException JavaDoc e )
734                         {
735                         }
736                     }
737                 }
738                 finally
739                 {
740                     if ( m_debug )
741                     {
742                         m_out.println( "Control event monitor thread stopped." );
743                     }
744                 }
745             }
746         };
747         m_eventRunner.setDaemon( true );
748         m_eventRunner.start();
749         
750         // Resolve the system thread count based on the Java Version
751
String JavaDoc fullVersion = System.getProperty( "java.fullversion" );
752         String JavaDoc vendor = System.getProperty( "java.vm.vendor", "" );
753         String JavaDoc os = System.getProperty( "os.name", "" ).toLowerCase();
754         if ( fullVersion == null )
755         {
756             fullVersion = System.getProperty( "java.runtime.version" ) + " "
757                 + System.getProperty( "java.vm.name" );
758         }
759         
760         if ( m_debug )
761         {
762             // Display more JVM infor right after the call initialization of the library.
763
m_out.println( "Java Version : " + fullVersion );
764             m_out.println( "Java VM Vendor : " + vendor );
765             m_out.println();
766         }
767         
768         // This thread will most likely be thread which launches the JVM.
769
// Once this method returns however, the main thread will likely
770
// quit. There will be a slight delay before the Wrapper binary
771
// has a change to send a command to start the application.
772
// During this lag, the JVM may not have any non-daemon threads
773
// running and would exit. To keep it from doing so, start a
774
// simple non-daemon thread which will run until the
775
// WrapperListener.start() method returns or the Wrapper's
776
// shutdown thread has started.
777
m_startupRunner = new Thread JavaDoc( "Wrapper-Startup-Runner" )
778         {
779             public void run()
780             {
781                 if ( m_debug )
782                 {
783                     m_out.println( "Startup runner thread started." );
784                 }
785                 
786                 try
787                 {
788                     while ( m_startupRunner != null )
789                     {
790                         try
791                         {
792                             Thread.sleep( 100 );
793                         }
794                         catch ( InterruptedException JavaDoc e )
795                         {
796                             // Ignore.
797
}
798                     }
799                 }
800                 finally
801                 {
802                     if ( m_debug )
803                     {
804                         m_out.println( "Startup runner thread stopped." );
805                     }
806                 }
807             }
808         };
809         // This thread must not be a daemon thread.
810
m_startupRunner.setDaemon( false );
811         m_startupRunner.start();
812         
813         // Create the singleton
814
m_instance = new WrapperManager();
815     }
816
817     /*---------------------------------------------------------------
818      * Native Methods
819      *-------------------------------------------------------------*/

820     private static native void nativeInit( boolean debug );
821     private static native String JavaDoc nativeGetLibraryVersion();
822     private static native int nativeGetJavaPID();
823     private static native int nativeGetControlEvent();
824     private static native void nativeRequestThreadDump();
825     private static native void accessViolationInner();
826     private static native void nativeSetConsoleTitle( byte[] titleBytes );
827     private static native WrapperUser nativeGetUser( boolean groups );
828     private static native WrapperUser nativeGetInteractiveUser( boolean groups );
829     private static native WrapperWin32Service[] nativeListServices();
830     private static native WrapperWin32Service nativeSendServiceControlCode( byte[] serviceName, int controlCode );
831     
832     /*---------------------------------------------------------------
833      * Methods
834      *-------------------------------------------------------------*/

835     /**
836      * Returns a tick count calculated from the system clock.
837      */

838     private static int getSystemTicks()
839     {
840         // Calculate a tick count using the current system time. The
841
// conversion from a long in ms, to an int in TICK_MS increments
842
// will result in data loss, but the loss of bits and resulting
843
// overflow is expected and Ok.
844
return (int)( System.currentTimeMillis() / TICK_MS );
845     }
846     
847     /**
848      * Returns the number of ticks since the JVM was launched. This
849      * count is not good enough to be used where accuracy is required but
850      * it allows us to implement timeouts in environments where the system
851      * time is modified while the JVM is running.
852      * <p>
853      * An int is used rather than a long so the counter can be implemented
854      * without requiring any synchronization. At the tick resolution, the
855      * tick counter will overflow and wrap (every 6.8 years for 100ms ticks).
856      * This behavior is expected. The getTickAge method should be used
857      * in cases where the difference between two ticks is required.
858      *
859      * Returns the tick count.
860      */

861     private static int getTicks()
862     {
863         if ( m_useSystemTime )
864         {
865             return getSystemTicks();
866         }
867         else
868         {
869             return m_ticks;
870         }
871     }
872     
873     /**
874      * Returns the number of milliseconds that have elapsed between the
875      * start and end counters. This method assumes that both tick counts
876      * were obtained by calling getTicks(). This method will correctly
877      * handle cases where the tick counter has overflowed and reset.
878      *
879      * @param start A base tick count.
880      * @param end An end tick count.
881      *
882      * @return The number of milliseconds that are represented by the
883      * difference between the two specified tick counts.
884      */

885     private static long getTickAge( int start, int end )
886     {
887         // Important to cast the first value so that negative values are correctly
888
// cast to negative long values.
889
return (long)( end - start ) * TICK_MS;
890     }
891     
892     /**
893      * Attempts to load the a native library file.
894      *
895      * @param name Name of the library to load.
896      * @param file Name of the actual library file.
897      *
898      * @return null if the library was successfully loaded, an error message
899      * otherwise.
900      */

901     private static String JavaDoc loadNativeLibrary( String JavaDoc name, String JavaDoc file )
902     {
903         try
904         {
905             System.loadLibrary( name );
906             
907             if ( m_debug )
908             {
909                 m_out.println( "Loaded native library: " + file );
910             }
911             
912             return null;
913         }
914         catch ( UnsatisfiedLinkError JavaDoc e )
915         {
916             if ( m_debug )
917             {
918                 m_out.println( "Loading native library failed: " + file + " Cause: " + e );
919             }
920             String JavaDoc error = e.getMessage();
921             if ( error == null )
922             {
923                 error = e.toString();
924             }
925             return error;
926         }
927         catch ( Throwable JavaDoc e )
928         {
929             if ( m_debug )
930             {
931                 m_out.println( "Loading native library failed: " + file + " Cause: " + e );
932             }
933             String JavaDoc error = e.toString();
934             return error;
935         }
936     }
937     
938     /**
939      * Java 1.5 and above supports the ability to register the WrapperManager
940      * MBean internally.
941      */

942     private static void registerMBean( Object JavaDoc mbean, String JavaDoc name )
943     {
944         Class JavaDoc classManagementFactory;
945         try
946         {
947             classManagementFactory = Class.forName( "java.lang.management.ManagementFactory" );
948         }
949         catch ( ClassNotFoundException JavaDoc e )
950         {
951             if ( m_debug )
952             {
953                 m_out.println( "Registering MBeans not supported by current JVM: " + name );
954             }
955             return;
956         }
957         
958         try
959         {
960             // This code uses reflection so it combiles on older JVMs.
961
// The original code is as follows:
962
// javax.management.MBeanServer mbs =
963
// java.lang.management.ManagementFactory.getPlatformMBeanServer();
964
// javax.management.ObjectName oName = new javax.management.ObjectName( name );
965
// mbs.registerMBean( mbean, oName );
966

967             // The version of the above code using reflection follows.
968
Class JavaDoc classMBeanServer = Class.forName( "javax.management.MBeanServer" );
969             Class JavaDoc classObjectName = Class.forName( "javax.management.ObjectName" );
970             Method JavaDoc methodGetPlatformMBeanServer =
971                 classManagementFactory.getMethod( "getPlatformMBeanServer", null );
972             Constructor JavaDoc constructorObjectName =
973                 classObjectName.getConstructor( new Class JavaDoc[] {String JavaDoc.class} );
974             Method JavaDoc methodRegisterMBean = classMBeanServer.getMethod(
975                 "registerMBean", new Class JavaDoc[] {Object JavaDoc.class, classObjectName} );
976             Object JavaDoc mbs = methodGetPlatformMBeanServer.invoke( null, null );
977             Object JavaDoc oName = constructorObjectName.newInstance( new Object JavaDoc[] {name} );
978             methodRegisterMBean.invoke( mbs, new Object JavaDoc[] {mbean, oName} );
979             
980             if ( m_debug )
981             {
982                 m_out.println( "Registered MBean with Platform MBean Server: " + name );
983             }
984         }
985         catch ( Throwable JavaDoc t )
986         {
987             m_err.println( "Unable to register the " + name + " MBean." );
988             t.printStackTrace();
989         }
990     }
991     
992     /**
993      * Searches for a file on a path.
994      *
995      * @param file File to look for.
996      * @param path Path to be searched.
997      *
998      * @return Reference to thr file object if found, otherwise null.
999      */

1000    private static File JavaDoc locateFileOnPath( String JavaDoc file, String JavaDoc path )
1001    {
1002        // A library path exists but the library was not found on it.
1003
String JavaDoc pathSep = System.getProperty( "path.separator" );
1004        
1005        // Search for the file on the library path to verify that it does not
1006
// exist, it could be some other problem
1007
StringTokenizer JavaDoc st = new StringTokenizer JavaDoc( path, pathSep );
1008        while( st.hasMoreTokens() )
1009        {
1010            File JavaDoc libFile = new File JavaDoc( new File JavaDoc( st.nextToken() ), file );
1011            if ( libFile.exists() )
1012            {
1013                return libFile;
1014            }
1015        }
1016        
1017        return null;
1018    }
1019    
1020    /**
1021     * Generates a detailed native library base name which is made up of the
1022     * base name, the os name, architecture, and the bits of the current JVM,
1023     * not the platform.
1024     *
1025     * @return A detailed native library base name.
1026     */

1027    private static String JavaDoc generateDetailedNativeLibraryBaseName( String JavaDoc baseName,
1028                                                                 int jvmBits,
1029                                                                 boolean universal )
1030    {
1031        // Generate an os name. Most names are used as is, but some are modified.
1032
String JavaDoc os = System.getProperty( "os.name", "" ).toLowerCase();
1033        if ( os.startsWith( "windows" ) )
1034        {
1035            os = "windows";
1036        }
1037        else if ( os.equals( "sunos" ) )
1038        {
1039            os = "solaris";
1040        }
1041        else if ( os.equals( "hp-ux" ) || os.equals( "hp-ux64" ) )
1042        {
1043            os = "hpux";
1044        }
1045        else if ( os.equals( "mac os x" ) )
1046        {
1047            os = "macosx";
1048        }
1049        else if ( os.equals( "unix_sv" ) )
1050        {
1051            os = "unixware";
1052        }
1053        
1054        // Generate an architecture name.
1055
String JavaDoc arch = System.getProperty( "os.arch", "" ).toLowerCase();
1056        if ( universal )
1057        {
1058            arch = "universal";
1059        }
1060        else
1061        {
1062            if ( arch.equals( "amd64" ) || arch.equals( "athlon" ) || arch.equals( "ia32" ) ||
1063                arch.equals( "ia64" ) || arch.equals( "x86_64" ) || arch.equals( "i686" ) ||
1064                arch.equals( "i586" ) || arch.equals( "i486" ) || arch.equals( "i386" ) )
1065            {
1066                arch = "x86";
1067            }
1068            else if ( arch.startsWith( "sparc" ) )
1069            {
1070                arch = "sparc";
1071            }
1072            else if ( arch.equals( "power" ) || arch.equals( "powerpc" ) || arch.equals( "ppc64" ) )
1073            {
1074                arch = "ppc";
1075            }
1076            else if ( arch.equals( "pa_risc" ) || arch.equals( "pa-risc" ) )
1077            {
1078                arch = "parisc";
1079            }
1080        }
1081        
1082        return baseName + "-" + os + "-" + arch + "-" + jvmBits;
1083    }
1084    
1085    /**
1086     * Searches for and then loads the native library. This method will attempt
1087     * locate the wrapper library using one of the following 3 naming
1088     */

1089    private static void initializeNativeLibrary()
1090    {
1091        // Resolve the osname and osarch for the currect system.
1092
String JavaDoc osName = System.getProperty( "os.name" ).toLowerCase();
1093        
1094        // Look for the base name of the library.
1095
String JavaDoc baseName = System.getProperty( "wrapper.native_library" );
1096        if ( baseName == null )
1097        {
1098            // This should only happen if an old version of the Wrapper binary is being used.
1099
m_out.println( "WARNING - The wrapper.native_library system property was not" );
1100            m_out.println( " set. Using the default value, 'wrapper'." );
1101            baseName = "wrapper";
1102        }
1103        String JavaDoc[] detailedNames = new String JavaDoc[4];
1104        if ( m_jvmBits > 0 )
1105        {
1106            detailedNames[0] = generateDetailedNativeLibraryBaseName( baseName, m_jvmBits, false );
1107            if ( osName.startsWith( "mac" ) )
1108            {
1109                detailedNames[1] = generateDetailedNativeLibraryBaseName( baseName, m_jvmBits, true );
1110            }
1111        }
1112        else
1113        {
1114            detailedNames[0] = generateDetailedNativeLibraryBaseName( baseName, 32, false );
1115            detailedNames[1] = generateDetailedNativeLibraryBaseName( baseName, 64, false );
1116            if ( osName.startsWith( "mac" ) )
1117            {
1118                detailedNames[2] = generateDetailedNativeLibraryBaseName( baseName, 32, true );
1119                detailedNames[3] = generateDetailedNativeLibraryBaseName( baseName, 64, true );
1120            }
1121        }
1122        
1123        // Construct brief and detailed native library file names.
1124
String JavaDoc file = System.mapLibraryName( baseName );
1125        String JavaDoc[] detailedFiles = new String JavaDoc[detailedNames.length];
1126        for ( int i = 0; i < detailedNames.length; i++ )
1127        {
1128            if ( detailedNames[i] != null )
1129            {
1130                detailedFiles[i] = System.mapLibraryName( detailedNames[i] );
1131            }
1132        }
1133        
1134        String JavaDoc[] detailedErrors = new String JavaDoc[detailedNames.length];
1135        String JavaDoc baseError = null;
1136        
1137        // Try loading the native library using the detailed name first. If that fails, use
1138
// the brief name.
1139
if ( m_debug )
1140        {
1141            m_out.println( "Load native library. One or more attempts may fail if platform "
1142                + "specific libraries do not exist." );
1143        }
1144        m_libraryOK = false;
1145        for ( int i = 0; i < detailedNames.length; i++ )
1146        {
1147            if ( detailedNames[i] != null )
1148            {
1149                detailedErrors[i] = loadNativeLibrary( detailedNames[i], detailedFiles[i] );
1150                if ( detailedErrors[i] == null )
1151                {
1152                    m_libraryOK = true;
1153                    break;
1154                }
1155            }
1156        }
1157        if ( ( !m_libraryOK ) && ( ( baseError = loadNativeLibrary( baseName, file ) ) == null ) )
1158        {
1159            m_libraryOK = true;
1160        }
1161        if ( m_libraryOK )
1162        {
1163            // The library was loaded correctly, so initialize it.
1164
if ( m_debug )
1165            {
1166                m_out.println( "Calling native initialization method." );
1167            }
1168            nativeInit( m_debug );
1169        }
1170        else
1171        {
1172            // The library could not be loaded, so we want to give the user a useful
1173
// clue as to why not.
1174
String JavaDoc libPath = System.getProperty( "java.library.path" );
1175            m_out.println();
1176            if ( libPath.equals( "" ) )
1177            {
1178                // No library path
1179
m_out.println(
1180                    "WARNING - Unable to load the Wrapper's native library because the" );
1181                m_out.println(
1182                    " java.library.path was set to ''. Please see the" );
1183                m_out.println(
1184                    " documentation for the wrapper.java.library.path " );
1185                m_out.println(
1186                    " configuration property.");
1187            }
1188            else
1189            {
1190                // Attempt to locate the actual files on the path.
1191
String JavaDoc error = null;
1192                File JavaDoc libFile = null;
1193                for ( int i = 0; i < detailedNames.length; i++ )
1194                {
1195                    if ( detailedFiles[i] != null )
1196                    {
1197                        libFile = locateFileOnPath( detailedFiles[i], libPath );
1198                        if ( libFile != null )
1199                        {
1200                            error = detailedErrors[i];
1201                            break;
1202                        }
1203                    }
1204                }
1205                if ( libFile == null )
1206                {
1207                    libFile = locateFileOnPath( file, libPath );
1208                    if ( libFile != null )
1209                    {
1210                        error = baseError;
1211                    }
1212                }
1213                if ( libFile == null )
1214                {
1215                    // The library could not be located on the library path.
1216
m_out.println(
1217                        "WARNING - Unable to load the Wrapper's native library because none of the" );
1218                    m_out.println(
1219                        " following files:" );
1220                    for ( int i = 0; i < detailedNames.length; i++ )
1221                    {
1222                        if ( detailedFiles[i] != null )
1223                        {
1224                            m_out.println(
1225                                " " + detailedFiles[i] );
1226                        }
1227                    }
1228                    m_out.println(
1229                        " " + file );
1230                    m_out.println(
1231                        " could be located on the following java.library.path:" );
1232                    
1233                    String JavaDoc pathSep = System.getProperty( "path.separator" );
1234                    StringTokenizer JavaDoc st = new StringTokenizer JavaDoc( libPath, pathSep );
1235                    while ( st.hasMoreTokens() )
1236                    {
1237                        File JavaDoc pathElement = new File JavaDoc( st.nextToken() );
1238                        m_out.println( " " + pathElement.getAbsolutePath() );
1239                    }
1240                    m_out.println(
1241                        " Please see the documentation for the "
1242                        + "wrapper.java.library.path" );
1243                    m_out.println(
1244                        " configuration property." );
1245                }
1246                else
1247                {
1248                    // The library file was found but could not be loaded for some reason.
1249
m_out.println(
1250                        "WARNING - Unable to load the Wrapper's native library '" + libFile.getName() + "'." );
1251                    m_out.println(
1252                        " The file is located on the path at the following location but" );
1253                    m_out.println(
1254                        " could not be loaded:" );
1255                    m_out.println(
1256                        " " + libFile.getAbsolutePath() );
1257                    m_out.println(
1258                        " Please verify that the file is readable by the current user" );
1259                    m_out.println(
1260                        " and that the file has not been corrupted in any way." );
1261                    m_out.println(
1262                        " One common cause of this problem is running a 32-bit version" );
1263                    m_out.println(
1264                        " of the Wrapper with a 64-bit version of Java, or vica versa." );
1265                    if ( m_jvmBits > 0 )
1266                    {
1267                        m_out.println(
1268                            " This is a " + m_jvmBits + "-bit JVM." );
1269                    }
1270                    else
1271                    {
1272                        m_out.println(
1273                            " The bit depth of this JVM could not be determined." );
1274                    }
1275                    m_out.println(
1276                        " Reported cause:" );
1277                    m_out.println(
1278                        " " + error );
1279                }
1280            }
1281            m_out.println( " System signals will not be handled correctly." );
1282            m_out.println();
1283        }
1284    }
1285    
1286    /**
1287     * Compares the version of the wrapper which launched this JVM with that of
1288     * the jar. If they differ then a Warning message will be displayed. The
1289     * Wrapper application will still be allowed to start.
1290     */

1291    private static void verifyWrapperVersion()
1292    {
1293        // If we are not being controlled by the wrapper then return.
1294
if ( !WrapperManager.isControlledByNativeWrapper() )
1295        {
1296            return;
1297        }
1298        
1299        // Lookup the version from the wrapper. It should have been set as a property
1300
// when the JVM was launched.
1301
String JavaDoc wrapperVersion = System.getProperty( "wrapper.version" );
1302        if ( wrapperVersion == null )
1303        {
1304            wrapperVersion = "unknown";
1305        }
1306        
1307        if ( !WrapperInfo.getVersion().equals( wrapperVersion ) )
1308        {
1309            m_out.println(
1310                "WARNING - The Wrapper jar file currently in use is version \""
1311                + WrapperInfo.getVersion() + "\"" );
1312            m_out.println(
1313                " while the version of the Wrapper which launched this JVM is " );
1314            m_out.println(
1315                " \"" + wrapperVersion + "\"." );
1316            m_out.println(
1317                " The Wrapper may appear to work correctly but some features may" );
1318            m_out.println(
1319                " not function correctly. This configuration has not been tested" );
1320            m_out.println(
1321                " and is not supported." );
1322            m_out.println();
1323        }
1324    }
1325    
1326    /**
1327     * Compares the version of the native library with that of this jar. If
1328     * they differ then a Warning message will be displayed. The Wrapper
1329     * application will still be allowed to start.
1330     */

1331    private static void verifyNativeLibraryVersion()
1332    {
1333        // Request the version from the native library. Be careful as the method
1334
// will not exist if the library is old.
1335
String JavaDoc jniVersion;
1336        try
1337        {
1338            jniVersion = nativeGetLibraryVersion();
1339        }
1340        catch ( Throwable JavaDoc e )
1341        {
1342            if ( m_debug )
1343            {
1344                m_out.println( "Call to nativeGetLibraryVersion() failed: " + e );
1345            }
1346            jniVersion = "unknown";
1347        }
1348        
1349        if ( !WrapperInfo.getVersion().equals( jniVersion ) )
1350        {
1351            m_out.println(
1352                "WARNING - The Wrapper jar file currently in use is version \""
1353                + WrapperInfo.getVersion() + "\"" );
1354            m_out.println(
1355                " while the version of the native library is \"" + jniVersion + "\"." );
1356            m_out.println(
1357                " The Wrapper may appear to work correctly but some features may" );
1358            m_out.println(
1359                " not function correctly. This configuration has not been tested" );
1360            m_out.println(
1361                " and is not supported." );
1362            m_out.println();
1363        }
1364    }
1365    
1366    /**
1367     * Obtain the current version of Wrapper.
1368     *
1369     * @return The version of the Wrapper.
1370     */

1371    public static String JavaDoc getVersion()
1372    {
1373        return WrapperInfo.getVersion();
1374    }
1375    
1376    /**
1377     * Obtain the build time of Wrapper.
1378     *
1379     * @return The time that the Wrapper was built.
1380     */

1381    public static String JavaDoc getBuildTime()
1382    {
1383        return WrapperInfo.getBuildTime();
1384    }
1385    
1386    /**
1387     * Returns the Id of the current JVM. JVM Ids increment from 1 each time
1388     * the wrapper restarts a new one.
1389     *
1390     * @return The Id of the current JVM.
1391     */

1392    public static int getJVMId()
1393    {
1394        return m_jvmId;
1395    }
1396    
1397    /**
1398     * Sets the title of the console in which the Wrapper is running. This
1399     * is currently only supported on Windows platforms.
1400     * <p>
1401     * As an alternative, it is also possible to set the console title from
1402     * within the wrapper.conf file using the wrapper.console.title property.
1403     *
1404     * @param title The new title. The specified string will be encoded
1405     * to a byte array using the default encoding for the
1406     * current platform.
1407     */

1408    public static void setConsoleTitle( String JavaDoc title )
1409    {
1410        SecurityManager JavaDoc sm = System.getSecurityManager();
1411        if ( sm != null )
1412        {
1413            sm.checkPermission( new WrapperPermission( "setConsoleTitle" ) );
1414        }
1415        
1416        if ( m_libraryOK )
1417        {
1418            // Convert the unicode string to a string of bytes using the default
1419
// platform encoding.
1420
byte[] titleBytes = title.getBytes();
1421            
1422            // We need a null terminated string.
1423
byte[] nullTermBytes = new byte[titleBytes.length + 1];
1424            System.arraycopy( titleBytes, 0, nullTermBytes, 0, titleBytes.length );
1425            nullTermBytes[titleBytes.length] = 0;
1426            
1427            nativeSetConsoleTitle( nullTermBytes );
1428        }
1429    }
1430    
1431    /**
1432     * Returns a WrapperUser object which describes the user under which the
1433     * Wrapper is currently running. Additional platform specific information
1434     * can be obtained by casting the object to a platform specific subclass.
1435     * WrapperWin32User, for example.
1436     *
1437     * @param groups True if the user's groups should be returned as well.
1438     * Requesting the groups that a user belongs to increases
1439     * the CPU load required to complete the call.
1440     *
1441     * @return An object describing the current user.
1442     */

1443    public static WrapperUser getUser( boolean groups )
1444    {
1445        SecurityManager JavaDoc sm = System.getSecurityManager();
1446        if ( sm != null )
1447        {
1448            sm.checkPermission( new WrapperPermission( "getUser" ) );
1449        }
1450        
1451        WrapperUser user = null;
1452        if ( m_libraryOK )
1453        {
1454            user = nativeGetUser( groups );
1455        }
1456        return user;
1457    }
1458    
1459    /**
1460     * Returns a WrapperUser object which describes the interactive user whose
1461     * desktop is being interacted with. When a service running on a Windows
1462     * platform has its interactive flag set, this method will return the user
1463     * who is currently logged in. Additional platform specific information
1464     * can be obtained by casting the object to a platform specific subclass.
1465     * WrapperWin32User, for example.
1466     * <p>
1467     * If a user is not currently logged on then this method will return null.
1468     * User code can repeatedly call this method to detect when a user has
1469     * logged in. To detect when a user has logged out, there are two options.
1470     * 1) The user code can continue to call this method until it returns null.
1471     * 2) Or if the WrapperListener method is being implemented, the
1472     * WrapperListener.controlEvent method will receive a WRAPPER_CTRL_LOGOFF_EVENT
1473     * event when the user logs out.
1474     * <p>
1475     * On XP systems, it is possible to switch to another account rather than
1476     * actually logging out. In such a case, the interactive user will be
1477     * the first user that logged in. This will also be the only user with
1478     * which the service will interact. If other users are logged in when the
1479     * interactive user logs out, the service will not automatically switch to
1480     * another logged in user. Rather, the next user to log in will become
1481     * the new user which the service will interact with.
1482     * <p>
1483     * This method will always return NULL on versions of NT prior to Windows
1484     * 2000. This can not be helped as some required functions were not added
1485     * to the windows API until NT version 5.0, also known as Windows 2000.
1486     *
1487     * @param groups True if the user's groups should be returned as well.
1488     * Requesting the groups that a user belongs to increases
1489     * the CPU load required to complete the call.
1490     *
1491     * @return The current interactive user, or null.
1492     */

1493    public static WrapperUser getInteractiveUser( boolean groups )
1494    {
1495        SecurityManager JavaDoc sm = System.getSecurityManager();
1496        if ( sm != null )
1497        {
1498            sm.checkPermission( new WrapperPermission( "getInteractiveUser" ) );
1499        }
1500        
1501        WrapperUser user = null;
1502        if ( m_libraryOK )
1503        {
1504            user = nativeGetInteractiveUser( groups );
1505        }
1506        return user;
1507    }
1508    
1509    /**
1510     * Returns a Properties object containing expanded the contents of the
1511     * configuration file used to launch the Wrapper.
1512     *
1513     * All properties are included so it is possible to define properties
1514     * not used by the Wrapper in the configuration file and have then
1515     * be available in this Properties object.
1516     *
1517     * @return The contents of the Wrapper configuration file.
1518     */

1519    public static Properties JavaDoc getProperties()
1520    {
1521        SecurityManager JavaDoc sm = System.getSecurityManager();
1522        if ( sm != null )
1523        {
1524            sm.checkPermission( new WrapperPermission( "getProperties" ) );
1525        }
1526        
1527        return m_properties;
1528    }
1529    
1530    /**
1531     * Returns the PID of the Wrapper process.
1532     *
1533     * A PID of 0 will be returned if the JVM was launched standalone.
1534     *
1535     * This value can also be obtained using the 'wrapper.pid' system property.
1536     *
1537     * @return The PID of the Wrpper process.
1538     */

1539    public static int getWrapperPID()
1540    {
1541        SecurityManager JavaDoc sm = System.getSecurityManager();
1542        if ( sm != null )
1543        {
1544            sm.checkPermission( new WrapperPermission( "getWrapperPID" ) );
1545        }
1546        
1547        return WrapperSystemPropertyUtil.getIntProperty( "wrapper.pid", 0 );
1548    }
1549    
1550    /**
1551     * Returns the PID of the Java process.
1552     *
1553     * A PID of 0 will be returned if the native library has not been initialized.
1554     *
1555     * This value can also be obtained using the 'wrapper.java.pid' system property.
1556     *
1557     * @return The PID of the Java process.
1558     */

1559    public static int getJavaPID()
1560    {
1561        SecurityManager JavaDoc sm = System.getSecurityManager();
1562        if ( sm != null )
1563        {
1564            sm.checkPermission( new WrapperPermission( "getJavaPID" ) );
1565        }
1566        
1567        return WrapperSystemPropertyUtil.getIntProperty( "wrapper.java.pid", 0 );
1568    }
1569    
1570    /**
1571     * Requests that the current JVM process request a thread dump. This is
1572     * the same as pressing CTRL-BREAK (under Windows) or CTRL-\ (under Unix)
1573     * in the the console in which Java is running. This method does nothing
1574     * if the native library is not loaded.
1575     */

1576    public static void requestThreadDump()
1577    {
1578        SecurityManager JavaDoc sm = System.getSecurityManager();
1579        if ( sm != null )
1580        {
1581            sm.checkPermission( new WrapperPermission( "requestThreadDump" ) );
1582        }
1583        
1584        if ( m_libraryOK )
1585        {
1586            nativeRequestThreadDump();
1587        }
1588        else
1589        {
1590            m_out.println( " wrapper library not loaded." );
1591        }
1592    }
1593    
1594    /**
1595     * (Testing Method) Causes the WrapperManager to go into a state which makes the JVM appear
1596     * to be hung when viewed from the native Wrapper code. Does not have any effect when the
1597     * JVM is not being controlled from the native Wrapper. Useful for testing the Wrapper
1598     * functions.
1599     */

1600    public static void appearHung()
1601    {
1602        SecurityManager JavaDoc sm = System.getSecurityManager();
1603        if ( sm != null )
1604        {
1605            sm.checkPermission( new WrapperPermission( "test.appearHung" ) );
1606        }
1607        
1608        m_out.println( "WARNING: Making JVM appear to be hung..." );
1609        m_appearHung = true;
1610    }
1611    
1612    /**
1613     * (Testing Method) Cause an access violation within the Java code. Useful
1614     * for testing the Wrapper functions. This currently only crashes Sun
1615     * JVMs and takes advantage of Bug #4369043 which does not exist in newer
1616     * JVMs. Use of the accessViolationNative() method is preferred.
1617     */

1618    public static void accessViolation()
1619    {
1620        SecurityManager JavaDoc sm = System.getSecurityManager();
1621        if ( sm != null )
1622        {
1623            sm.checkPermission( new WrapperPermission( "test.accessViolation" ) );
1624        }
1625        
1626        m_out.println( "WARNING: Attempting to cause an access violation..." );
1627        
1628        try
1629        {
1630            Class JavaDoc c = Class.forName( "java.lang.String" );
1631            java.lang.reflect.Method JavaDoc m = c.getDeclaredMethod( null, null );
1632        }
1633        catch( NoSuchMethodException JavaDoc ex )
1634        {
1635            // Correctly did not find method. access_violation attempt failed. Not Sun JVM?
1636
}
1637        catch( Exception JavaDoc ex )
1638        {
1639            if ( ex instanceof NoSuchFieldException JavaDoc )
1640            {
1641                // Can't catch this in a catch because the compiler doesn't think it is being
1642
// thrown. But it is thrown on IBM jvms at least
1643
// Correctly did not find method. access_violation attempt failed. Not Sun JVM?
1644
}
1645            else
1646            {
1647                // Shouldn't get here.
1648
ex.printStackTrace();
1649            }
1650        }
1651        
1652        m_out.println( " Attempt to cause access violation failed. JVM is still alive." );
1653    }
1654
1655    /**
1656     * (Testing Method) Cause an access violation within native JNI code. Useful for testing the
1657     * Wrapper functions. This currently causes the access violation by attempting to write to
1658     * a null pointer.
1659     */

1660    public static void accessViolationNative()
1661    {
1662        SecurityManager JavaDoc sm = System.getSecurityManager();
1663        if ( sm != null )
1664        {
1665            sm.checkPermission( new WrapperPermission( "test.accessViolationNative" ) );
1666        }
1667        
1668        m_out.println( "WARNING: Attempting to cause an access violation..." );
1669        if ( m_libraryOK )
1670        {
1671            accessViolationInner();
1672        
1673            m_out.println( " Attempt to cause access violation failed. "
1674                + "JVM is still alive." );
1675        }
1676        else
1677        {
1678            m_out.println( " wrapper library not loaded." );
1679        }
1680    }
1681        
1682    /**
1683     * Returns true if the JVM was launched by the Wrapper application. False
1684     * if the JVM was launched manually without the Wrapper controlling it.
1685     *
1686     * @return True if the current JVM was launched by the Wrapper.
1687     */

1688    public static boolean isControlledByNativeWrapper()
1689    {
1690        return m_key != null;
1691    }
1692    
1693    /**
1694     * Returns true if the Wrapper was launched as an NT service on Windows or
1695     * as a daemon process on UNIX platforms. False if launched as a console.
1696     * This can be useful if you wish to display a user interface when in
1697     * Console mode. On UNIX platforms, this is not as useful because an
1698     * X display may not be visible even if launched in a console.
1699     *
1700     * @return True if the Wrapper is running as an NT service or daemon
1701     * process.
1702     */

1703    public static boolean isLaunchedAsService()
1704    {
1705        return m_service;
1706    }
1707    
1708    /**
1709     * Returns true if the wrapper.debug property, or any of the logging
1710     * channels are set to DEBUG in the wrapper configuration file. Useful
1711     * for deciding whether or not to output certain information to the
1712     * console.
1713     *
1714     * @return True if the Wrapper is logging any Debug level output.
1715     */

1716    public static boolean isDebugEnabled()
1717    {
1718        return m_debug;
1719    }
1720    
1721    /**
1722     * Start the Java side of the Wrapper code running. This will make it
1723     * possible for the native side of the Wrapper to detect that the Java
1724     * Wrapper is up and running.
1725     * <p>
1726     * This method must be called on startup and then can only be called once
1727     * so there is no reason for any security permission checks on this call.
1728     *
1729     * @param listener The WrapperListener instance which represents the
1730     * application being started.
1731     * @param args The argument list passed to the JVM when it was launched.
1732     */

1733    public static synchronized void start( final WrapperListener listener, final String JavaDoc[] args )
1734    {
1735        // As was done in the static initializer, we need to execute the following
1736
// code in a privileged action so it is not necessary for the calling code
1737
// to have the same privileges as the wrapper jar.
1738
// This is safe because this method can only be called once and that one call
1739
// will presumably be made on JVM startup.
1740
AccessController.doPrivileged(
1741            new PrivilegedAction JavaDoc() {
1742                public Object JavaDoc run() {
1743                    privilegedStart( listener, args );
1744                    return null;
1745                }
1746            }
1747        );
1748    }
1749    
1750    /**
1751     * Called by the start method within a PrivilegedAction.
1752     *
1753     * @param WrapperListener The WrapperListener instance which represents
1754     * the application being started.
1755     * @param args The argument list passed to the JVM when it was launched.
1756     */

1757    private static void privilegedStart( WrapperListener listener, String JavaDoc[] args )
1758    {
1759        // Check the SecurityManager here as it is possible that it was set before this call.
1760
checkSecurityManager();
1761        
1762        // Just in case the user failed to provide an argument list, recover by creating one
1763
// here. This will avoid possible problems down stream.
1764
if ( args == null )
1765        {
1766            args = new String JavaDoc[0];
1767        }
1768        
1769        if ( m_debug )
1770        {
1771            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1772            sb.append( "args[" );
1773            for ( int i = 0; i < args.length; i++ )
1774            {
1775                if ( i > 0 )
1776                {
1777                    sb.append( ", " );
1778                }
1779                sb.append( "\"" );
1780                sb.append( args[i] );
1781                sb.append( "\"" );
1782            }
1783            sb.append( "]" );
1784            
1785            m_out.println( "WrapperManager.start(" + listener + ", " + sb.toString() + ") "
1786                + "called by thread: " + Thread.currentThread().getName() );
1787        }
1788        
1789        // Make sure that the class has not already been disposed.
1790
if ( m_disposed)
1791        {
1792            throw new IllegalStateException JavaDoc( "WrapperManager has already been disposed." );
1793        }
1794        
1795        if ( m_listener != null )
1796        {
1797            throw new IllegalStateException JavaDoc(
1798                "WrapperManager has already been started with a WrapperListener." );
1799        }
1800        if ( listener == null )
1801        {
1802            throw new IllegalStateException JavaDoc( "A WrapperListener must be specified." );
1803        }
1804        m_listener = listener;
1805        
1806        m_args = args;
1807        
1808        startRunner();
1809        
1810        // If this JVM is being controlled by a native wrapper, then we want to
1811
// wait for the command to start. However, if this is a standalone
1812
// JVM, then we want to start now.
1813
if ( !isControlledByNativeWrapper() )
1814        {
1815            startInner();
1816        }
1817    }
1818    
1819    /**
1820     * Tells the native wrapper that the JVM wants to restart, then informs
1821     * all listeners that the JVM is about to shutdown before killing the JVM.
1822     * <p>
1823     * This method will not return.
1824     *
1825     * @throws SecurityException If a SecurityManager is present and the
1826     * calling thread does not have the
1827     * WrapperPermission("restart") permission.
1828     *
1829     * @see WrapperPermission
1830     */

1831    public static void restart()
1832        throws SecurityException JavaDoc
1833    {
1834        SecurityManager JavaDoc sm = System.getSecurityManager();
1835        if ( sm != null )
1836        {
1837            sm.checkPermission( new WrapperPermission( "restart" ) );
1838        }
1839        
1840        if ( m_debug )
1841        {
1842            m_out.println( "WrapperManager.restart() called by thread: "
1843                + Thread.currentThread().getName() );
1844        }
1845        
1846        restartInner();
1847    }
1848    
1849    /**
1850     * Tells the native wrapper that the JVM wants to restart, then informs
1851     * all listeners that the JVM is about to shutdown before killing the JVM.
1852     * <p>
1853     * This method requests that the JVM be restarted but then returns. This
1854     * allows components to initiate a JVM exit and then continue, allowing
1855     * a normal shutdown initiated by the JVM via shutdown hooks. In
1856     * applications which are designed to be shutdown when the user presses
1857     * CTRL-C, this may result in a cleaner shutdown.
1858     *
1859     * @throws SecurityException If a SecurityManager is present and the
1860     * calling thread does not have the
1861     * WrapperPermission("restart") permission.
1862     *
1863     * @see WrapperPermission
1864     */

1865    public static void restartAndReturn()
1866        throws SecurityException JavaDoc
1867    {
1868        SecurityManager JavaDoc sm = System.getSecurityManager();
1869        if ( sm != null )
1870        {
1871            sm.checkPermission( new WrapperPermission( "restart" ) );
1872        }
1873        
1874        synchronized( WrapperManager.class )
1875        {
1876            if ( m_stopping )
1877            {
1878                if ( m_debug )
1879                {
1880                    m_out.println( "WrapperManager.restartAndReturn() called by thread: "
1881                        + Thread.currentThread().getName() + " already stopping." );
1882                }
1883                return;
1884            }
1885            else
1886            {
1887                if ( m_debug )
1888                {
1889                    m_out.println( "WrapperManager.restartAndReturn() called by thread: "
1890                        + Thread.currentThread().getName() );
1891                }
1892            }
1893        }
1894        
1895        
1896        // To make this possible, we have to create a new thread to actually do the shutdown.
1897
Thread JavaDoc restarter = new Thread JavaDoc( "Wrapper-Restarter" )
1898        {
1899            public void run()
1900            {
1901                restartInner();
1902            }
1903        };
1904        restarter.setDaemon( false );
1905        restarter.start();
1906    }
1907    
1908    /**
1909     * Common code used to restart the JVM. It is assumed that the calling
1910     * thread has has passed security checks before this is called.
1911     */

1912    private static void restartInner()
1913    {
1914        boolean stopping;
1915        synchronized( WrapperManager.class )
1916        {
1917            stopping = m_stopping;
1918            if ( !stopping )
1919            {
1920                m_stopping = true;
1921            }
1922        }
1923        
1924        if ( !stopping )
1925        {
1926            if ( !m_commRunnerStarted )
1927            {
1928                startRunner();
1929            }
1930            
1931            // Always send the stop command
1932
sendCommand( WRAPPER_MSG_RESTART, "restart" );
1933        }
1934        
1935        // Give the Wrapper a chance to register the stop command before stopping.
1936
// This avoids any errors thrown by the Wrapper because the JVM died before
1937
// it was expected to.
1938
try
1939        {
1940            Thread.sleep( 1000 );
1941        }
1942        catch ( InterruptedException JavaDoc e )
1943        {
1944        }
1945        
1946        // This is safe because we are already checking for the privilege to restart the JVM
1947
// above. If we get this far then we want the Wrapper to be able to do everything
1948
// necessary to stop the JVM.
1949
AccessController.doPrivileged(
1950            new PrivilegedAction JavaDoc() {
1951                public Object JavaDoc run() {
1952                    privilegedStopInner( 0 );
1953                    return null;
1954                }
1955            }
1956        );
1957    }
1958    
1959    /**
1960     * Tells the native wrapper that the JVM wants to shut down, then informs
1961     * all listeners that the JVM is about to shutdown before killing the JVM.
1962     * <p>
1963     * This method will not return.
1964     *
1965     * @param exitCode The exit code that the Wrapper will return when it exits.
1966     *
1967     * @throws SecurityException If a SecurityManager is present and the
1968     * calling thread does not have the
1969     * WrapperPermission("stop") permission.
1970     *
1971     * @see WrapperPermission
1972     */

1973    public static void stop( final int exitCode )
1974    {
1975        SecurityManager JavaDoc sm = System.getSecurityManager();
1976        if ( sm != null )
1977        {
1978            sm.checkPermission( new WrapperPermission( "stop" ) );
1979        }
1980        
1981        if ( m_debug )
1982        {
1983            m_out.println( "WrapperManager.stop(" + exitCode + ") called by thread: "
1984                + Thread.currentThread().getName() );
1985        }
1986        
1987        stopCommon( exitCode, 1000 );
1988        
1989        // This is safe because we are already checking for the privilege to stop the JVM
1990
// above. If we get this far then we want the Wrapper to be able to do everything
1991
// necessary to stop the JVM.
1992
AccessController.doPrivileged(
1993            new PrivilegedAction JavaDoc() {
1994                public Object JavaDoc run() {
1995                    privilegedStopInner( exitCode );
1996                    return null;
1997                }
1998            }
1999        );
2000    }
2001    
2002    /**
2003     * Tells the native wrapper that the JVM wants to shut down, then informs
2004     * all listeners that the JVM is about to shutdown before killing the JVM.
2005     * <p>
2006     * This method requests that the JVM be shutdown but then returns. This
2007     * allows components to initiate a JVM exit and then continue, allowing
2008     * a normal shutdown initiated by the JVM via shutdown hooks. In
2009     * applications which are designed to be shutdown when the user presses
2010     * CTRL-C, this may result in a cleaner shutdown.
2011     *
2012     * @param exitCode The exit code that the Wrapper will return when it exits.
2013     *
2014     * @throws SecurityException If a SecurityManager is present and the
2015     * calling thread does not have the
2016     * WrapperPermission("stop") permission.
2017     *
2018     * @see WrapperPermission
2019     */

2020    public static void stopAndReturn( final int exitCode )
2021    {
2022        SecurityManager JavaDoc sm = System.getSecurityManager();
2023        if ( sm != null )
2024        {
2025            sm.checkPermission( new WrapperPermission( "stop" ) );
2026        }
2027        
2028        synchronized( WrapperManager.class )
2029        {
2030            if ( m_stopping )
2031            {
2032                if ( m_debug )
2033                {
2034                    m_out.println( "WrapperManager.stopAndReturn(" + exitCode + ") called by thread: "
2035                        + Thread.currentThread().getName() + " already stopping." );
2036                }
2037                return;
2038            }
2039            else
2040            {
2041                if ( m_debug )
2042                {
2043                    m_out.println( "WrapperManager.stopAndReturn(" + exitCode + ") called by thread: "
2044                        + Thread.currentThread().getName() );
2045                }
2046            }
2047        }
2048        
2049        // To make this possible, we have to create a new thread to actually do the shutdown.
2050
Thread JavaDoc stopper = new Thread JavaDoc( "Wrapper-Stopper" )
2051        {
2052            public void run()
2053            {
2054                stopCommon( exitCode, 1000 );
2055                
2056                // This is safe because we are already checking for the privilege to stop the JVM
2057
// above. If we get this far then we want the Wrapper to be able to do everything
2058
// necessary to stop the JVM.
2059
AccessController.doPrivileged(
2060                    new PrivilegedAction JavaDoc() {
2061                        public Object JavaDoc run() {
2062                            privilegedStopInner( exitCode );
2063                            return null;
2064                        }
2065                    }
2066                );
2067            }
2068        };
2069        stopper.setDaemon( false );
2070        stopper.start();
2071    }
2072
2073    /**
2074     * Tells the native wrapper that the JVM wants to shut down and then
2075     * promptly halts. Be careful when using this method as an application
2076     * will not be given a chance to shutdown cleanly.
2077     *
2078     * @param exitCode The exit code that the Wrapper will return when it exits.
2079     *
2080     * @throws SecurityException If a SecurityManager is present and the
2081     * calling thread does not have the
2082     * WrapperPermission("stopImmediate") permission.
2083     *
2084     * @see WrapperPermission
2085     */

2086    public static void stopImmediate( final int exitCode )
2087    {
2088        SecurityManager JavaDoc sm = System.getSecurityManager();
2089        if ( sm != null )
2090        {
2091            sm.checkPermission( new WrapperPermission( "stopImmediate" ) );
2092        }
2093        
2094        if ( m_debug )
2095        {
2096            m_out.println( "WrapperManager.stopImmediate(" + exitCode + ") called by thread: "
2097                + Thread.currentThread().getName() );
2098        }
2099        
2100        stopCommon( exitCode, 250 );
2101        
2102        signalStopped( exitCode );
2103        
2104        // Execute runtime.halt(0) using reflection so this class will
2105
// compile on 1.2.x versions of Java.
2106
Method JavaDoc haltMethod;
2107        try
2108        {
2109            haltMethod =
2110                Runtime JavaDoc.class.getMethod( "halt", new Class JavaDoc[] { Integer.TYPE } );
2111        }
2112        catch ( NoSuchMethodException JavaDoc e )
2113        {
2114            m_out.println( "halt not supported by current JVM." );
2115            haltMethod = null;
2116        }
2117        
2118        if ( haltMethod != null )
2119        {
2120            Runtime JavaDoc runtime = Runtime.getRuntime();
2121            try
2122            {
2123                haltMethod.invoke( runtime, new Object JavaDoc[] { new Integer JavaDoc( exitCode ) } );
2124            }
2125            catch ( IllegalAccessException JavaDoc e )
2126            {
2127                m_out.println( "Unable to call runtime.halt: " + e );
2128            }
2129            catch ( InvocationTargetException JavaDoc e )
2130            {
2131                Throwable JavaDoc t = e.getTargetException();
2132                if ( t == null )
2133                {
2134                    t = e;
2135                }
2136                
2137                m_out.println( "Unable to call runtime.halt: " + t );
2138            }
2139        }
2140        else
2141        {
2142            // Shutdown normally
2143

2144            // This is safe because we are already checking for the privilege to stop the JVM
2145
// above. If we get this far then we want the Wrapper to be able to do everything
2146
// necessary to stop the JVM.
2147
AccessController.doPrivileged(
2148                new PrivilegedAction JavaDoc() {
2149                    public Object JavaDoc run() {
2150                        privilegedStopInner( exitCode );
2151                        return null;
2152                    }
2153                }
2154            );
2155        }
2156    }
2157    
2158    /**
2159     * Signal the native wrapper that the startup is progressing but that more
2160     * time is needed. The Wrapper will extend the startup timeout by the
2161     * specified time.
2162     *
2163     * @param waitHint Additional time in milliseconds.
2164     *
2165     * @throws SecurityException If a SecurityManager is present and the
2166     * calling thread does not have the
2167     * WrapperPermission("signalStarting") permission.
2168     *
2169     * @see WrapperPermission
2170     */

2171    public static void signalStarting( int waitHint )
2172    {
2173        SecurityManager JavaDoc sm = System.getSecurityManager();
2174        if ( sm != null )
2175        {
2176            sm.checkPermission( new WrapperPermission( "signalStarting" ) );
2177        }
2178        
2179        sendCommand( WRAPPER_MSG_START_PENDING, Integer.toString( waitHint ) );
2180    }
2181
2182    /**
2183     * Signal the native wrapper that the shutdown is progressing but that more
2184     * time is needed. The Wrapper will extend the stop timeout by the
2185     * specified time.
2186     *
2187     * @param waitHint Additional time in milliseconds.
2188     *
2189     * @throws SecurityException If a SecurityManager is present and the
2190     * calling thread does not have the
2191     * WrapperPermission("signalStopping") permission.
2192     *
2193     * @see WrapperPermission
2194     */

2195    public static void signalStopping( int waitHint )
2196    {
2197        SecurityManager JavaDoc sm = System.getSecurityManager();
2198        if ( sm != null )
2199        {
2200            sm.checkPermission( new WrapperPermission( "signalStopping" ) );
2201        }
2202        
2203        m_stopping = true;
2204        sendCommand( WRAPPER_MSG_STOP_PENDING, Integer.toString( waitHint ) );
2205    }
2206    
2207    /**
2208     * This method should not normally be called by user code as it is called
2209     * from within the stop and restart methods. However certain applications
2210     * which stop the JVM may need to call this method to let the wrapper code
2211     * know that the shutdown was intentional.
2212     *
2213     * @throws SecurityException If a SecurityManager is present and the
2214     * calling thread does not have the
2215     * WrapperPermission("signalStopped") permission.
2216     *
2217     * @see WrapperPermission
2218     */

2219    public static void signalStopped( int exitCode )
2220    {
2221        SecurityManager JavaDoc sm = System.getSecurityManager();
2222        if ( sm != null )
2223        {
2224            sm.checkPermission( new WrapperPermission( "signalStopped" ) );
2225        }
2226        
2227        m_stopping = true;
2228        sendCommand( WRAPPER_MSG_STOPPED, Integer.toString( exitCode ) );
2229        
2230        // Give the socket time to actuall send the packet to the Wrapper
2231
// as this call is often immediately followed by a halt command.
2232
try
2233        {
2234            Thread.sleep( 250 );
2235        }
2236        catch ( InterruptedException JavaDoc e )
2237        {
2238            // Ignore.
2239
}
2240    }
2241    
2242    /**
2243     * Returns true if the ShutdownHook for the JVM has already been triggered.
2244     * Some code needs to know whether or not the system is shutting down.
2245     *
2246     * @return True if the ShutdownHook for the JVM has already been triggered.
2247     */

2248    public static boolean hasShutdownHookBeenTriggered()
2249    {
2250        return m_hookTriggered;
2251    }
2252    
2253    /**
2254     * Requests that the Wrapper log a message at the specified log level.
2255     * If the JVM is not being managed by the Wrapper then calls to this
2256     * method will be ignored. This method has been optimized to ignore
2257     * messages at a log level which will not be logged given the current
2258     * log levels of the Wrapper.
2259     * <p>
2260     * Log messages will currently by trimmed by the Wrapper at 4k (4096 bytes).
2261     * <p>
2262     * Because of differences in the way console output is collected and
2263     * messages logged via this method, it is expected that interspersed
2264     * console and log messages will not be in the correct order in the
2265     * resulting log file.
2266     * <p>
2267     * This method was added to allow simple logging to the wrapper.log
2268     * file. This is not meant to be a full featured log file and should
2269     * not be used as such. Please look into a logging package for most
2270     * application logging.
2271     *
2272     * @param logLevel The level to log the message at can be one of
2273     * WRAPPER_LOG_LEVEL_DEBUG, WRAPPER_LOG_LEVEL_INFO,
2274     * WRAPPER_LOG_LEVEL_STATUS, WRAPPER_LOG_LEVEL_WARN,
2275     * WRAPPER_LOG_LEVEL_ERROR, or WRAPPER_LOG_LEVEL_FATAL.
2276     * @param message The message to be logged.
2277     *
2278     * @throws SecurityException If a SecurityManager is present and the
2279     * calling thread does not have the
2280     * WrapperPermission("log") permission.
2281     *
2282     * @see WrapperPermission
2283     */

2284    public static void log( int logLevel, String JavaDoc message )
2285    {
2286        SecurityManager JavaDoc sm = System.getSecurityManager();
2287        if ( sm != null )
2288        {
2289            sm.checkPermission( new WrapperPermission( "log" ) );
2290        }
2291        
2292        // Make sure that the logLevel is valid to avoid problems with the
2293
// command sent to the server.
2294

2295        if ( ( logLevel < WRAPPER_LOG_LEVEL_DEBUG ) || ( logLevel > WRAPPER_LOG_LEVEL_ADVICE ) )
2296        {
2297            throw new IllegalArgumentException JavaDoc( "The specified logLevel is not valid." );
2298        }
2299        if ( message == null )
2300        {
2301            throw new IllegalArgumentException JavaDoc( "The message parameter can not be null." );
2302        }
2303        
2304        if ( m_lowLogLevel <= logLevel )
2305        {
2306            sendCommand( (byte)( WRAPPER_MSG_LOG + logLevel ), message );
2307        }
2308    }
2309    
2310    /**
2311     * Returns an array of all registered services. This method is only
2312     * supported on Windows platforms which support services. Calling this
2313     * method on other platforms will result in null being returned.
2314     *
2315     * @return An array of services.
2316     *
2317     * @throws SecurityException If a SecurityManager has not been set in the
2318     * JVM or if the calling code has not been
2319     * granted the WrapperPermission "listServices"
2320     * permission. A SecurityManager is required
2321     * for this operation because this method makes
2322     * it possible to learn a great deal about the
2323     * state of the system.
2324     */

2325    public static WrapperWin32Service[] listServices()
2326        throws SecurityException JavaDoc
2327    {
2328        SecurityManager JavaDoc sm = System.getSecurityManager();
2329        if ( sm == null )
2330        {
2331            throw new SecurityException JavaDoc( "A SecurityManager has not yet been set." );
2332        }
2333        else
2334        {
2335            sm.checkPermission( new WrapperPermission( "listServices" ) );
2336        }
2337        
2338        if ( m_libraryOK )
2339        {
2340            return nativeListServices();
2341        }
2342        else
2343        {
2344            return null;
2345        }
2346    }
2347    
2348    /**
2349     * Sends a service control code to the specified service. The state of the
2350     * service should be tested on return. If the service was not currently
2351     * running then the control code will not be sent.
2352     * <p>
2353     * The control code sent can be one of the system control codes:
2354     * WrapperManager.SERVICE_CONTROL_CODE_START,
2355     * WrapperManager.SERVICE_CONTROL_CODE_STOP,
2356     * WrapperManager.SERVICE_CONTROL_CODE_PAUSE,
2357     * WrapperManager.SERVICE_CONTROL_CODE_CONTINUE, or
2358     * WrapperManager.SERVICE_CONTROL_CODE_INTERROGATE. In addition, user
2359     * defined codes in the range 128-255 can also be sent.
2360     *
2361     * @param serviceName Name of the Windows service which will receive the
2362     * control code.
2363     * @param controlCode The actual control code to be sent. User defined
2364     * control codes should be in the range 128-255.
2365     *
2366     * @return A WrapperWin32Service containing the last known status of the
2367     * service after sending the control code. This will be null if
2368     * the currently platform is not a version of Windows which
2369     * supports services.
2370     *
2371     * @throws WrapperServiceException If there are any problems accessing the
2372     * specified service.
2373     * @throws SecurityException If a SecurityManager has not been set in the
2374     * JVM or if the calling code has not been
2375     * granted the WrapperServicePermission
2376     * permission for the specified service and
2377     * control code. A SecurityManager is required
2378     * for this operation because this method makes
2379     * it possible to control any service on the
2380     * system, which is of course rather dangerous.
2381     */

2382    public static WrapperWin32Service sendServiceControlCode( String JavaDoc serviceName, int controlCode )
2383        throws WrapperServiceException, SecurityException JavaDoc
2384    {
2385        SecurityManager JavaDoc sm = System.getSecurityManager();
2386        if ( sm == null )
2387        {
2388            throw new SecurityException JavaDoc( "A SecurityManager has not yet been set." );
2389        }
2390        else
2391        {
2392            String JavaDoc action;
2393            switch( controlCode )
2394            {
2395            case SERVICE_CONTROL_CODE_START:
2396                action = WrapperServicePermission.ACTION_START;
2397                break;
2398                
2399            case SERVICE_CONTROL_CODE_STOP:
2400                action = WrapperServicePermission.ACTION_STOP;
2401                break;
2402                
2403            case SERVICE_CONTROL_CODE_PAUSE:
2404                action = WrapperServicePermission.ACTION_PAUSE;
2405                break;
2406                
2407            case SERVICE_CONTROL_CODE_CONTINUE:
2408                action = WrapperServicePermission.ACTION_CONTINUE;
2409                break;
2410                
2411            case SERVICE_CONTROL_CODE_INTERROGATE:
2412                action = WrapperServicePermission.ACTION_INTERROGATE;
2413                break;
2414                
2415            default:
2416                action = WrapperServicePermission.ACTION_USER_CODE;
2417                break;
2418            }
2419            
2420            sm.checkPermission( new WrapperServicePermission( serviceName, action ) );
2421        }
2422        
2423        WrapperWin32Service service = null;
2424        if ( m_libraryOK )
2425        {
2426            service = nativeSendServiceControlCode( serviceName.getBytes(), controlCode );
2427        }
2428        return service;
2429    }
2430    
2431    /**
2432     * Adds a WrapperEventListener which will receive WrapperEvents. The
2433     * specific events can be controlled using the mask parameter. This API
2434     * was chosen to allow for additional events in the future.
2435     *
2436     * To avoid future compatibility problems, WrapperEventListeners should
2437     * always test the class of an event before making use of it. This will
2438     * avoid problems caused by new event classes added in future versions
2439     * of the Wrapper.
2440     *
2441     * This method should only be called once for a given WrapperEventListener.
2442     * Build up a single mask to receive events of multiple types.
2443     *
2444     * @param listener WrapperEventListener to be start receiving events.
2445     * @param mask A mask specifying the event types that the listener is
2446     * interrested in receiving. See the WrapperEventListener
2447     * class for a full list of flags. A mask is created by
2448     * combining multiple flags using the binary '|' OR operator.
2449     */

2450    public static void addWrapperEventListener( WrapperEventListener listener, long mask )
2451    {
2452        SecurityManager JavaDoc sm = System.getSecurityManager();
2453        if ( sm != null )
2454        {
2455            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
2456            boolean first = true;
2457            if ( ( mask & WrapperEventListener.EVENT_FLAG_SERVICE ) != 0 )
2458            {
2459                first = false;
2460                sb.append( WrapperEventPermission.EVENT_TYPE_SERVICE );
2461            }
2462            if ( ( mask & WrapperEventListener.EVENT_FLAG_CONTROL ) != 0 )
2463            {
2464                if ( first )
2465                {
2466                    first = false;
2467                }
2468                else
2469                {
2470                    sb.append( "," );
2471                }
2472                sb.append( WrapperEventPermission.EVENT_TYPE_CONTROL );
2473            }
2474            if ( ( mask & WrapperEventListener.EVENT_FLAG_CORE ) != 0 )
2475            {
2476                if ( first )
2477                {
2478                    first = false;
2479                }
2480                else
2481                {
2482                    sb.append( "," );
2483                }
2484                sb.append( WrapperEventPermission.EVENT_TYPE_CORE );
2485            }
2486            sm.checkPermission( new WrapperEventPermission( sb.toString() ) );
2487        }
2488        
2489        synchronized( WrapperManager.class )
2490        {
2491            WrapperEventListenerMask listenerMask = new WrapperEventListenerMask();
2492            listenerMask.m_listener = listener;
2493            listenerMask.m_mask = mask;
2494            
2495            m_wrapperEventListenerMaskList.add( listenerMask );
2496            m_wrapperEventListenerMasks = null;
2497        }
2498        
2499        updateWrapperEventListenerFlags();
2500    }
2501    
2502    /**
2503     * Removes a WrapperEventListener so it will not longer receive WrapperEvents.
2504     *
2505     * @param listener WrapperEventListener to be stop receiving events.
2506     */

2507    public static void removeWrapperEventListener( WrapperEventListener listener )
2508    {
2509        SecurityManager JavaDoc sm = System.getSecurityManager();
2510        if ( sm != null )
2511        {
2512            sm.checkPermission( new WrapperPermission( "removeWrapperEventListener" ) );
2513        }
2514        
2515        synchronized( WrapperManager.class )
2516        {
2517            // Look for the first instance of a given listener in the list.
2518
for ( Iterator JavaDoc iter = m_wrapperEventListenerMaskList.iterator(); iter.hasNext(); )
2519            {
2520                WrapperEventListenerMask listenerMask = (WrapperEventListenerMask)iter.next();
2521                if ( listenerMask.m_listener == listener )
2522                {
2523                    iter.remove();
2524                    m_wrapperEventListenerMasks = null;
2525                    break;
2526                }
2527            }
2528        }
2529        
2530        updateWrapperEventListenerFlags();
2531    }
2532    
2533    /*---------------------------------------------------------------
2534     * Constructors
2535     *-------------------------------------------------------------*/

2536    /**
2537     * This class can not be instantiated.
2538     */

2539    private WrapperManager()
2540    {
2541    }
2542    
2543    /*---------------------------------------------------------------
2544     * Private methods
2545     *-------------------------------------------------------------*/

2546    /**
2547     * Checks for the existence of a SecurityManager and then makes sure that
2548     * the Wrapper jar has been granted AllPermissions. If not then a warning
2549     * will be displayed as this will most likely result in the Wrapper
2550     * failing to function correctly.
2551     *
2552     * This method is called at various points in the startup as it is possible
2553     * and in fact likely that any SecurityManager will be set by user code
2554     * during or shortly after initialization. Once a SecurityManager has
2555     * been located and tested then this method will become a noop.
2556     */

2557    private static void checkSecurityManager()
2558    {
2559        if ( m_securityManagerChecked )
2560        {
2561            return;
2562        }
2563        
2564        SecurityManager JavaDoc securityManager = System.getSecurityManager();
2565        if ( securityManager != null )
2566        {
2567            if ( m_debug )
2568            {
2569                m_out.println(
2570                    "Detected a SecurityManager: " + securityManager.getClass().getName() );
2571            }
2572            
2573            try
2574            {
2575                securityManager.checkPermission( new java.security.AllPermission JavaDoc() );
2576            }
2577            catch ( SecurityException JavaDoc e )
2578            {
2579                m_out.println();
2580                m_out.println(
2581                    "WARNING - Detected that a SecurityManager has been installed but the " );
2582                m_out.println(
2583                    " wrapper.jar has not been granted the java.security.AllPermission" );
2584                m_out.println(
2585                    " permission. This will most likely result in SecurityExceptions" );
2586                m_out.println(
2587                    " being thrown by the Wrapper." );
2588                m_out.println();
2589            }
2590            
2591            // Always set the flag.
2592
m_securityManagerChecked = true;
2593        }
2594    }
2595    
2596    /**
2597     * Returns an array of WrapperEventListenerMask instances which can
2598     * be safely used outside of synchronization.
2599     *
2600     * @return An array of WrapperEventListenerMask instances.
2601     */

2602    private static WrapperEventListenerMask[] getWrapperEventListenerMasks()
2603    {
2604        WrapperEventListenerMask[] listenerMasks = m_wrapperEventListenerMasks;
2605        if ( listenerMasks == null )
2606        {
2607            synchronized( WrapperManager.class )
2608            {
2609                if ( listenerMasks == null )
2610                {
2611                    listenerMasks =
2612                        new WrapperEventListenerMask[m_wrapperEventListenerMaskList.size()];
2613                    m_wrapperEventListenerMaskList.toArray( listenerMasks );
2614                    m_wrapperEventListenerMasks = listenerMasks;
2615                }
2616            }
2617        }
2618        
2619        return listenerMasks;
2620    }
2621    
2622    /**
2623     * Updates the internal flags based on the WrapperEventListeners currently
2624     * registered.
2625     */

2626    private static void updateWrapperEventListenerFlags()
2627    {
2628        boolean core = false;
2629        
2630        WrapperEventListenerMask[] listenerMasks = getWrapperEventListenerMasks();
2631        for ( int i = 0; i < listenerMasks.length; i++ )
2632        {
2633            long mask = listenerMasks[i].m_mask;
2634            
2635            // See whether particular event types are required.
2636
core = core | ( ( mask & WrapperEventListener.EVENT_FLAG_CORE ) != 0 );
2637        }
2638        
2639        m_produceCoreEvents = core;
2640    }
2641    
2642    /**
2643     * Notifies registered listeners that an event has been fired.
2644     *
2645     * @param event Event to notify the listeners of.
2646     */

2647    private static void fireWrapperEvent( WrapperEvent event )
2648    {
2649        long eventMask = event.getFlags();
2650        
2651        WrapperEventListenerMask[] listenerMasks = getWrapperEventListenerMasks();
2652        for ( int i = 0; i < listenerMasks.length; i++ )
2653        {
2654            long listenerMask = listenerMasks[i].m_mask;
2655            
2656            // See if the event should be passed to this listner.
2657
if ( ( listenerMask & eventMask ) != 0 )
2658            {
2659                // The listener wants the event.
2660
WrapperEventListener listener = listenerMasks[i].m_listener;
2661                try
2662                {
2663                    listener.fired( event );
2664                }
2665                catch ( Throwable JavaDoc t )
2666                {
2667                    m_out.println( "Encountered an uncaught exception while notifying "
2668                        + "WrapperEventListener of an event:" );
2669                    t.printStackTrace( m_out );
2670                }
2671            }
2672        }
2673    }
2674    
2675    /**
2676     * Executed code common to the stop and stopImmediate methods.
2677     */

2678    private static void stopCommon( int exitCode, int delay )
2679    {
2680        boolean stopping;
2681        synchronized( WrapperManager.class )
2682        {
2683            stopping = m_stopping;
2684            if ( !stopping )
2685            {
2686                m_stopping = true;
2687            }
2688        }
2689        
2690        if ( !stopping )
2691        {
2692            if ( !m_commRunnerStarted )
2693            {
2694                startRunner();
2695            }
2696            
2697            // Always send the stop command
2698
sendCommand( WRAPPER_MSG_STOP, Integer.toString( exitCode ) );
2699            
2700            // Give the Wrapper a chance to register the stop command before stopping.
2701
// This avoids any errors thrown by the Wrapper because the JVM died before
2702
// it was expected to.
2703
try
2704            {
2705                Thread.sleep( delay );
2706            }
2707            catch ( InterruptedException JavaDoc e )
2708            {
2709            }
2710        }
2711    }
2712    
2713    /**
2714     * Dispose of all resources used by the WrapperManager. Closes the server
2715     * socket which is used to listen for events from the
2716     */

2717    private static void dispose()
2718    {
2719        synchronized( WrapperManager.class )
2720        {
2721            m_disposed = true;
2722            
2723            // Close the open socket if it exists.
2724
closeSocket();
2725            
2726            // Give the Connection Thread a chance to stop itself.
2727
try
2728            {
2729                Thread.sleep( 500 );
2730            }
2731            catch ( InterruptedException JavaDoc e )
2732            {
2733            }
2734        }
2735    }
2736    
2737    /**
2738     * Informs the listener that it should start.
2739     */

2740    private static void startInner()
2741    {
2742        // Set the thread priority back to normal so that any spawned threads
2743
// will use the normal priority
2744
int oldPriority = Thread.currentThread().getPriority();
2745        Thread.currentThread().setPriority( Thread.NORM_PRIORITY );
2746        
2747        // This method can be called from the connection thread which must be a
2748
// daemon thread by design. We need to call the WrapperListener.start method
2749
// from a non-daemon thread. This means that if the current thread is a
2750
// daemon we need to launch a new thread while we wait for the start method
2751
// to return.
2752
if ( m_listener == null )
2753        {
2754            if ( m_debug )
2755            {
2756                m_out.println( "No WrapperListener has been set. Nothing to start." );
2757            }
2758        }
2759        else
2760        {
2761            if ( m_debug )
2762            {
2763                m_out.println( "calling WrapperListener.start()" );
2764            }
2765            
2766            // These arrays aren't pretty, but we need final variables for the inline
2767
// class and this makes it possible to get the values back.
2768
final Integer JavaDoc[] resultF = new Integer JavaDoc[1];
2769            final Throwable JavaDoc[] tF = new Throwable JavaDoc[1];
2770            
2771            if ( Thread.currentThread().isDaemon() )
2772            {
2773                // Start in a dedicated thread.
2774
Thread JavaDoc startRunner = new Thread JavaDoc( "WrapperListener_start_runner" )
2775                {
2776                    public void run()
2777                    {
2778                        if ( m_debug )
2779                        {
2780                           m_out.println( "WrapperListener.start runner thread started." );
2781                        }
2782                        
2783                        try
2784                        {
2785                            // This is user code, so don't trust it.
2786
try
2787                            {
2788                                resultF[0] = m_listener.start( m_args );
2789                            }
2790                            catch ( Throwable JavaDoc t )
2791                            {
2792                                tF[0] = t;
2793                            }
2794                        }
2795                        finally
2796                        {
2797                            if ( m_debug )
2798                            {
2799                               m_out.println( "WrapperListener.start runner thread stopped." );
2800                            }
2801                        }
2802                    }
2803                };
2804                startRunner.setDaemon( false );
2805                startRunner.start();
2806                
2807                // Wait for the start runner to complete.
2808
if ( m_debug )
2809                {
2810                   m_out.println( "Waiting for WrapperListener.start runner thread to complete." );
2811                }
2812                while ( ( startRunner != null ) && ( startRunner.isAlive() ) )
2813                {
2814                    try
2815                    {
2816                        startRunner.join();
2817                        startRunner = null;
2818                    }
2819                    catch ( InterruptedException JavaDoc e )
2820                    {
2821                        // Ignore and keep waiting.
2822
}
2823                }
2824            }
2825            else
2826            {
2827                // Start in line.
2828
// This is user code, so don't trust it.
2829
try
2830                {
2831                    resultF[0] = m_listener.start( m_args );
2832                }
2833                catch ( Throwable JavaDoc t )
2834                {
2835                    tF[0] = t;
2836                }
2837            }
2838            
2839            // Now that we are back in the main thread, handle the results.
2840
if ( tF[0] != null )
2841            {
2842                m_out.println( "Error in WrapperListener.start callback. " + tF[0] );
2843                tF[0].printStackTrace();
2844                // Kill the JVM, but don't tell the wrapper that we want to stop.
2845
// This may be a problem with this instantiation only.
2846
privilegedStopInner( 1 );
2847                // Won't make it here.
2848
return;
2849            }
2850            
2851            if ( m_debug )
2852            {
2853                m_out.println( "returned from WrapperListener.start()" );
2854            }
2855            if ( resultF[0] != null )
2856            {
2857                int exitCode = resultF[0].intValue();
2858                if ( m_debug )
2859                {
2860                    m_out.println(
2861                        "WrapperListener.start() returned an exit code of " + exitCode + "." );
2862                }
2863                
2864                // Signal the native code.
2865
stop( exitCode );
2866                // Won't make it here.
2867
return;
2868            }
2869        }
2870        m_startedTicks = getTicks();
2871        
2872        // Let the startup thread die since the application has been started.
2873
m_startupRunner = null;
2874        
2875        // Check the SecurityManager here as it is possible that it was set in the
2876
// listener's start method.
2877
checkSecurityManager();
2878        
2879        // Crank the priority back up.
2880
Thread.currentThread().setPriority( oldPriority );
2881        
2882        // Signal that the application has started.
2883
signalStarted();
2884    }
2885    
2886    private static void shutdownJVM( int exitCode )
2887    {
2888        if ( m_debug )
2889        {
2890            m_out.println( "shutdownJVM(" + exitCode + ") Thread:" + Thread.currentThread().getName() );
2891        }
2892        
2893        // Do not call System.exit if this is the ShutdownHook
2894
if ( Thread.currentThread() == m_hook )
2895        {
2896            // Signal that the application has stopped and the JVM is about to shutdown.
2897
signalStopped( exitCode );
2898            
2899            // Dispose the wrapper. (If the hook runs, it will do this.)
2900
dispose();
2901            
2902            // This is the shutdown hook, so fall through because things are
2903
// already shutting down.
2904

2905            m_shutdownJVMComplete = true;
2906        }
2907        else
2908        {
2909            // We do not want the ShutdownHook to execute, so unregister it before calling exit.
2910
// It can't be unregistered if it has already fired however. The only way that this
2911
// could happen is if user code calls System.exit from within the listener stop
2912
// method.
2913
if ( ( !m_hookTriggered ) && ( m_hook != null ) )
2914            {
2915                // Remove the shutdown hook using reflection.
2916
try
2917                {
2918                    m_removeShutdownHookMethod.invoke(
2919                        Runtime.getRuntime(), new Object JavaDoc[] { m_hook } );
2920                }
2921                catch ( IllegalAccessException JavaDoc e )
2922                {
2923                    m_out.println( "Wrapper Manager: Unable to unregister shutdown hook: " + e );
2924                }
2925                catch ( InvocationTargetException JavaDoc e )
2926                {
2927                    Throwable JavaDoc t = e.getTargetException();
2928                    if ( t == null )
2929                    {
2930                        t = e;
2931                    }
2932                    
2933                    m_out.println( "Wrapper Manager: Unable to unregister shutdown hook: " + t );
2934                }
2935            }
2936            // Signal that the application has stopped and the JVM is about to shutdown.
2937
signalStopped( exitCode );
2938            
2939            // Dispose the wrapper. (If the hook runs, it will do this.)
2940
dispose();
2941            
2942            if ( m_debug )
2943            {
2944                m_out.println( "calling System.exit(" + exitCode + ")" );
2945            }
2946            m_shutdownJVMComplete = true;
2947            safeSystemExit( exitCode );
2948        }
2949    }
2950    
2951    /**
2952     * A user ran into a JVM bug where a call to System exit was causing an
2953     * IllegalThreadStateException to be thrown. Not sure how widespread
2954     * this problem is. But it is easy to avoid it causing serious problems
2955     * for the wrapper.
2956     */

2957    private static void safeSystemExit( int exitCode )
2958    {
2959        try
2960        {
2961            System.exit( exitCode );
2962        }
2963        catch ( IllegalThreadStateException JavaDoc e )
2964        {
2965            m_out.println( "Wrapper Manager: Attempted System.exit(" + exitCode + ") call failed: " + e.toString() );
2966            m_out.println( " Trying Runtime.halt(" + exitCode + ")" );
2967            Runtime.getRuntime().halt( exitCode );
2968        }
2969    }
2970    
2971    /**
2972     * Informs the listener that the JVM will be shut down.
2973     *
2974     * This should only be called from within a PrivilegedAction or in a
2975     * context that came from a PrivilegedAction.
2976     */

2977    private static void privilegedStopInner( int exitCode )
2978    {
2979        boolean block;
2980        synchronized( WrapperManager.class )
2981        {
2982            // Always set the stopping flag.
2983
m_stopping = true;
2984            
2985            // Only one thread can be allowed to continue.
2986
if ( m_stoppingThread == null )
2987            {
2988                m_stoppingThread = Thread.currentThread();
2989                block = false;
2990            }
2991            else
2992            {
2993                if ( Thread.currentThread() == m_stoppingThread )
2994                {
2995                    throw new IllegalStateException JavaDoc(
2996                        "WrapperManager.stop() can not be called recursively." );
2997                }
2998                
2999                block = true;
3000            }
3001        }
3002        
3003        if ( block )
3004        {
3005            if ( m_debug )
3006            {
3007                m_out.println( "Thread, " + Thread.currentThread().getName()
3008                    + ", waiting for the JVM to exit." );
3009                
3010                if ( Thread.currentThread() == m_hook )
3011                {
3012                    m_out.println( "System.exit appears to have been called from within the" );
3013                    m_out.println( " WrapperListener.stop() method. If possible the application" );
3014                    m_out.println( " should be modified to avoid this behavior." );
3015                    m_out.println( " To avoid a deadlock, this thread will only wait 5 seconds" );
3016                    m_out.println( " for the application to shutdown. This may result in the" );
3017                    m_out.println( " application failing to shutdown completely before the JVM" );
3018                    m_out.println( " exists. Removing the offending System.exit call will" );
3019                    m_out.println( " resolve this." );
3020                }
3021            }
3022            
3023            // This thread needs to be put into an infinite loop until the JVM exits.
3024
// This thread can not be allowed to return to the caller, but another
3025
// thread is already responsible for shutting down the JVM, so this
3026
// one can do nothing but wait.
3027
int loops = 0;
3028            int wait = 50;
3029            while( true )
3030            {
3031                try
3032                {
3033                    Thread.sleep( wait );
3034                }
3035                catch ( InterruptedException JavaDoc e )
3036                {
3037                }
3038                
3039                // If this is the wrapper's shutdown hook then we only want to loop until
3040
// the shutdownJVM method has completed. We will only get into this state
3041
// if user code calls System.exit from within the WrapperListener.stop
3042
// method. Failing to return here will cause the shutdown process to hang.
3043
// If the user code calls System.exit directly in the stop method then the
3044
// m_shutdownJVMComplete flag will never be set. Always time out after
3045
// 5 seconds so the JVM will not hang in such cases.
3046
if ( Thread.currentThread() == m_hook )
3047                {
3048                    if ( m_shutdownJVMComplete || ( loops > 5000 / wait ) )
3049                    {
3050                        if ( !m_shutdownJVMComplete )
3051                        {
3052                            if ( m_debug )
3053                            {
3054                                m_out.println( "Thread, " + Thread.currentThread().getName()
3055                                    + ", continuing after 5 seconds." );
3056                            }
3057                        }
3058                        
3059                        // To keep the wrapper from showing a JVM exited unexpectedly message
3060
// on shutdown, tell the wrapper that we are ready to stop.
3061
// If the WrapperListener.stop method is taking a long time, we will
3062
// also get here. In that case, the Wrapper will still wait for
3063
// the configured exit timeout before killing the JVM process.
3064
// In theory, the shutdown process of an application will only call
3065
// System.exit after the shutdown is complete so this should be Ok.
3066
// Use the exit code from the thread which initiated the call rather
3067
// than this call as that one is the one we really want.
3068
signalStopped( m_exitCode );
3069                        
3070                        return;
3071                    }
3072                }
3073                
3074                loops++;
3075            }
3076        }
3077        
3078        if ( m_debug )
3079        {
3080            m_out.println( "Thread, " + Thread.currentThread().getName()
3081                + ", handling the shutdown process." );
3082        }
3083        m_exitCode = exitCode;
3084        
3085        // Only stop the listener if the app has been started.
3086
int code = exitCode;
3087        if ( m_started )
3088        {
3089            // Set the thread priority back to normal so that any spawned threads
3090
// will use the normal priority
3091
int oldPriority = Thread.currentThread().getPriority();
3092            Thread.currentThread().setPriority( Thread.NORM_PRIORITY );
3093            
3094            // This method can be called from the connection thread which must be a
3095
// daemon thread by design. We need to call the WrapperListener.stop method
3096
// from a non-daemon thread. This means that if the current thread is a
3097
// daemon we need to launch a new thread while we wait for the stop method
3098
// to return.
3099
if ( m_listener == null )
3100            {
3101                if ( m_debug )
3102                {
3103                    m_out.println( "No WrapperListener has been set. Nothing to stop." );
3104                }
3105            }
3106            else
3107            {
3108                if ( m_debug )
3109                {
3110                    m_out.println( "calling listener.stop()" );
3111                }
3112                
3113                if ( Thread.currentThread().isDaemon() )
3114                {
3115                    // This array isn't pretty, but we need final variables for the inline
3116
// class and this makes it possible to get the values back.
3117
final Integer JavaDoc[] codeF = new Integer JavaDoc[] {new Integer JavaDoc(code)};
3118                    
3119                    // Start in a dedicated thread.
3120
Thread JavaDoc stopRunner = new Thread JavaDoc( "WrapperListener_stop_runner" )
3121                    {
3122                        public void run()
3123                        {
3124                            if ( m_debug )
3125                            {
3126                               m_out.println( "WrapperListener.stop runner thread started." );
3127                            }
3128                            
3129                            try
3130                            {
3131                                // This is user code, so don't trust it.
3132
try
3133                                {
3134                                    codeF[0] = new Integer JavaDoc( m_listener.stop( codeF[0].intValue() ) );
3135                                }
3136                                catch ( Throwable JavaDoc t )
3137                                {
3138                                    m_out.println( "Error in WrapperListener.stop callback. " + t );
3139                                    t.printStackTrace();
3140                                }
3141                            }
3142                            finally
3143                            {
3144                                if ( m_debug )
3145                                {
3146                                   m_out.println( "WrapperListener.stop runner thread stopped." );
3147                                }
3148                            }
3149                        }
3150                    };
3151                    stopRunner.setDaemon( false );
3152                    stopRunner.start();
3153                    
3154                    // Wait for the start runner to complete.
3155
if ( m_debug )
3156                    {
3157                       m_out.println( "Waiting for WrapperListener.stop runner thread to complete." );
3158                    }
3159                    while ( ( stopRunner != null ) && ( stopRunner.isAlive() ) )
3160                    {
3161                        try
3162                        {
3163                            stopRunner.join();
3164                            stopRunner = null;
3165                        }
3166                        catch ( InterruptedException JavaDoc e )
3167                        {
3168                            // Ignore and keep waiting.
3169
}
3170                    }
3171                    
3172                    // Get the exit code back from the array.
3173
code = codeF[0].intValue();
3174                }
3175                else
3176                {
3177                    // This is user code, so don't trust it.
3178
try
3179                    {
3180                        code = m_listener.stop( code );
3181                    }
3182                    catch ( Throwable JavaDoc t )
3183                    {
3184                        m_out.println( "Error in WrapperListener.stop callback. " + t );
3185                        t.printStackTrace();
3186                    }
3187                }
3188                if ( m_debug )
3189                {
3190                    m_out.println( "returned from listener.stop() -> " + code );
3191                }
3192            }
3193            
3194            // Crank the priority back up.
3195
Thread.currentThread().setPriority( oldPriority );
3196        }
3197
3198        shutdownJVM( code );
3199    }
3200    
3201    private static void signalStarted()
3202    {
3203        sendCommand( WRAPPER_MSG_STARTED, "" );
3204        m_started = true;
3205    }
3206    
3207    /**
3208     * Called by the native code when a control event is trapped by native code.
3209     * Can have the values: WRAPPER_CTRL_C_EVENT, WRAPPER_CTRL_CLOSE_EVENT,
3210     * WRAPPER_CTRL_LOGOFF_EVENT, WRAPPER_CTRL_SHUTDOWN_EVENT,
3211     * WRAPPER_CTRL_TERM_EVENT, or WRAPPER_CTRL_HUP_EVENT.
3212     */

3213    private static void controlEvent( int event )
3214    {
3215        String JavaDoc eventName;
3216        boolean ignore;
3217        switch( event )
3218        {
3219        case WRAPPER_CTRL_C_EVENT:
3220            eventName = "WRAPPER_CTRL_C_EVENT";
3221            ignore = m_ignoreSignals;
3222            break;
3223        case WRAPPER_CTRL_CLOSE_EVENT:
3224            eventName = "WRAPPER_CTRL_CLOSE_EVENT";
3225            ignore = m_ignoreSignals;
3226            break;
3227        case WRAPPER_CTRL_LOGOFF_EVENT:
3228            eventName = "WRAPPER_CTRL_LOGOFF_EVENT";
3229            ignore = false;
3230            break;
3231        case WRAPPER_CTRL_SHUTDOWN_EVENT:
3232            eventName = "WRAPPER_CTRL_SHUTDOWN_EVENT";
3233            ignore = false;
3234            break;
3235        case WRAPPER_CTRL_TERM_EVENT:
3236            eventName = "WRAPPER_CTRL_TERM_EVENT";
3237            ignore = m_ignoreSignals;
3238            break;
3239        case WRAPPER_CTRL_HUP_EVENT:
3240            eventName = "WRAPPER_CTRL_HUP_EVENT";
3241            ignore = m_ignoreSignals;
3242            break;
3243        default:
3244            eventName = "Unexpected event: " + event;
3245            ignore = false;
3246            break;
3247        }
3248        
3249        WrapperControlEvent controlEvent = new WrapperControlEvent( event, eventName );
3250        if ( ignore )
3251        {
3252            // Preconsume the event if it is set to be ignored, but go ahead and fire it so
3253
// user can can still have the oportunity to recognize it.
3254
controlEvent.consume();
3255        }
3256        fireWrapperEvent( controlEvent );
3257        
3258        if ( !controlEvent.isConsumed() )
3259        {
3260            if ( ignore )
3261            {
3262                if ( m_debug )
3263                {
3264                    m_out.println( "Ignoring control event(" + eventName + ")" );
3265                }
3266            }
3267            else
3268            {
3269                if ( m_debug )
3270                {
3271                    m_out.println( "Processing control event(" + eventName + ")" );
3272                }
3273                
3274                // This is user code, so don't trust it.
3275
if ( m_listener != null )
3276                {
3277                    try
3278                    {
3279                        m_listener.controlEvent( event );
3280                    }
3281                    catch ( Throwable JavaDoc t )
3282                    {
3283                        m_out.println( "Error in WrapperListener.controlEvent callback. " + t );
3284                        t.printStackTrace();
3285                    }
3286                }
3287                else
3288                {
3289                    // A listener was never registered. Always respond by exiting.
3290
// This can happen if the user does not initialize things correctly.
3291
stop( 0 );
3292                }
3293            }
3294        }
3295    }
3296    
3297    /**
3298     * Parses a long tab separated string of properties into an internal
3299     * properties object. Actual tabs are escaped by real tabs.
3300     */

3301    private static char PROPERTY_SEPARATOR = '\t';
3302    private static void readProperties( String JavaDoc rawProps )
3303    {
3304        WrapperProperties properties = new WrapperProperties();
3305        
3306        int len = rawProps.length();
3307        int first = 0;
3308        while ( first < len )
3309        {
3310            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
3311            boolean foundEnd = false;
3312            do
3313            {
3314                int pos = rawProps.indexOf( PROPERTY_SEPARATOR, first );
3315                if ( pos >= 0 )
3316                {
3317                    if ( pos > 0 )
3318                    {
3319                        sb.append( rawProps.substring( first, pos ) );
3320                    }
3321                    if ( pos < len - 1 )
3322                    {
3323                        if ( rawProps.charAt( pos + 1 ) == PROPERTY_SEPARATOR )
3324                        {
3325                            // Two separators in a row, it was escaped.
3326
sb.append( PROPERTY_SEPARATOR );
3327                            first = pos + 2;
3328                        }
3329                        else
3330                        {
3331                            foundEnd = true;
3332                            first = pos + 1;
3333                        }
3334                    }
3335                    else
3336                    {
3337                        foundEnd = true;
3338                        first = pos + 1;
3339                    }
3340                }
3341                else
3342                {
3343                    // No more separators. The rest is the last property.
3344
sb.append( rawProps.substring( first ) );
3345                    foundEnd = true;
3346                    first = len;
3347                }
3348            }
3349            while ( !foundEnd );
3350            
3351            String JavaDoc property = sb.toString();
3352            
3353            // Parse the property.
3354
int pos = property.indexOf( '=' );
3355            if ( pos > 0 )
3356            {
3357                String JavaDoc key = property.substring( 0, pos );
3358                String JavaDoc value;
3359                if ( pos < property.length() - 1 )
3360                {
3361                    value = property.substring( pos + 1 );
3362                }
3363                else
3364                {
3365                    value = "";
3366                }
3367                
3368                properties.setProperty( key, value );
3369            }
3370        }
3371        
3372        // Lock the properties object and store it.
3373
properties.lock();
3374        
3375        m_properties = properties;
3376    }
3377
3378    private static synchronized Socket JavaDoc openSocket()
3379    {
3380        if ( m_debug )
3381        {
3382            m_out.println( "Open socket to wrapper..." + Thread.currentThread().getName() );
3383        }
3384
3385        InetAddress JavaDoc iNetAddress;
3386        try
3387        {
3388            iNetAddress = InetAddress.getByName( "127.0.0.1" );
3389        }
3390        catch ( UnknownHostException JavaDoc e )
3391        {
3392            // This is pretty fatal.
3393
m_out.println( e );
3394            stop( 1 );
3395            return null; //please the compiler
3396
}
3397        
3398        // If the user has specified a specific port to use then we want to try that first.
3399
boolean connected = false;
3400        int tryPort;
3401        boolean fixedPort;
3402        if ( m_jvmPort > 0 )
3403        {
3404            tryPort = m_jvmPort;
3405            fixedPort = true;
3406        }
3407        else
3408        {
3409            tryPort = m_jvmPortMin;
3410            fixedPort = false;
3411        }
3412        
3413        // Loop until we find a port we can connect using.
3414
do
3415        {
3416            try
3417            {
3418                m_socket = new Socket JavaDoc( iNetAddress, m_port, iNetAddress, tryPort );
3419                if ( m_debug )
3420                {
3421                    m_out.println( "Opened Socket from " + tryPort + " to " + m_port );
3422                }
3423                connected = true;
3424                break;
3425            }
3426            catch ( SocketException JavaDoc e )
3427            {
3428                String JavaDoc eMessage = e.getMessage();
3429                
3430                if ( e instanceof ConnectException JavaDoc )
3431                {
3432                    m_out.println( "Failed to connect to the Wrapper at port " + m_port + "." );
3433                    m_out.println( e );
3434                    // This is fatal because there is nobody listening.
3435
m_out.println( "Exiting JVM..." );
3436                    stopImmediate( 1 );
3437                }
3438                else if ( ( e instanceof BindException JavaDoc ) ||
3439                    ( ( eMessage != null ) && ( eMessage.indexOf( "errno: 48" ) >= 0 ) ) )
3440                {
3441                    // Most Java implementations throw a BindException when the port is in use,
3442
// but FreeBSD throws a SocketException with a specific message.
3443

3444                    // This happens if the local port is already in use. In this case, we want
3445
// to loop and try again.
3446
if ( m_debug )
3447                    {
3448                        m_out.println( "Failed attempt to bind using local port " + tryPort );
3449                    }
3450                    
3451                    if ( fixedPort )
3452                    {
3453                        // The last port checked was the fixed port, switch to the dynamic range.
3454
tryPort = m_jvmPortMin;
3455                        fixedPort = false;
3456                    }
3457                    else
3458                    {
3459                        tryPort++;
3460                    }
3461                }
3462                else
3463                {
3464                    // Unexpected exception.
3465
m_out.println( e );
3466                    m_socket = null;
3467                    return null;
3468                }
3469            }
3470            catch ( IOException JavaDoc e )
3471            {
3472                m_out.println( e );
3473                m_socket = null;
3474                return null;
3475            }
3476        }
3477        while ( tryPort <= m_jvmPortMax );
3478        
3479        if ( connected )
3480        {
3481            if ( ( m_jvmPort > 0 ) && ( m_jvmPort != tryPort ) )
3482            {
3483                m_out.println(
3484                    "Port " + m_jvmPort + " already in use, using port " + tryPort + " instead." );
3485            }
3486        }
3487        else
3488        {
3489            if ( m_jvmPortMax > m_jvmPortMin )
3490            {
3491                m_out.println(
3492                    "Failed to connect to the Wrapper at port " + m_port + " by binding to any "
3493                    + "ports in the range " + m_jvmPortMin + " to " + m_jvmPortMax + "." );
3494            }
3495            else
3496            {
3497                m_out.println(
3498                    "Failed to connect to the Wrapper at port " + m_port + " by binding to port "
3499                    + m_jvmPortMin + "." );
3500            }
3501            // This is fatal because there is nobody listening.
3502
m_out.println( "Exiting JVM..." );
3503            stopImmediate( 1 );
3504        }
3505        
3506        // Now that we have a connected socket, continue on to configure it.
3507
try
3508        {
3509            // Turn on the TCP_NODELAY flag. This is very important for speed!!
3510
m_socket.setTcpNoDelay( true );
3511            
3512            // Set the SO_TIMEOUT for the socket (max block time)
3513
if ( m_soTimeout > 0 )
3514            {
3515                m_socket.setSoTimeout( m_soTimeout );
3516            }
3517        }
3518        catch ( IOException JavaDoc e )
3519        {
3520            m_out.println( e );
3521        }
3522        
3523        // Send the key back to the wrapper so that the wrapper can feel safe
3524
// that it is talking to the correct JVM
3525
sendCommand( WRAPPER_MSG_KEY, m_key );
3526            
3527        return m_socket;
3528    }
3529    
3530    private static synchronized void closeSocket()
3531    {
3532        if ( m_socket != null )
3533        {
3534            if ( m_debug )
3535            {
3536                m_out.println( "Closing socket." );
3537            }
3538            
3539            try
3540            {
3541                m_socket.close();
3542            }
3543            catch ( IOException JavaDoc e )
3544            {
3545            }
3546            finally
3547            {
3548                m_socket = null;
3549            }
3550        }
3551    }
3552    
3553    private static String JavaDoc getPacketCodeName(byte code)
3554    {
3555        String JavaDoc name;
3556    
3557        switch (code)
3558        {
3559        case WRAPPER_MSG_START:
3560            name ="START";
3561            break;
3562    
3563        case WRAPPER_MSG_STOP:
3564            name ="STOP";
3565            break;
3566    
3567        case WRAPPER_MSG_RESTART:
3568            name ="RESTART";
3569            break;
3570    
3571        case WRAPPER_MSG_PING:
3572            name ="PING";
3573            break;
3574    
3575        case WRAPPER_MSG_STOP_PENDING:
3576            name ="STOP_PENDING";
3577            break;
3578    
3579        case WRAPPER_MSG_START_PENDING:
3580            name ="START_PENDING";
3581            break;
3582    
3583        case WRAPPER_MSG_STARTED:
3584            name ="STARTED";
3585            break;
3586    
3587        case WRAPPER_MSG_STOPPED:
3588            name ="STOPPED";
3589            break;
3590    
3591        case WRAPPER_MSG_KEY:
3592            name ="KEY";
3593            break;
3594    
3595        case WRAPPER_MSG_BADKEY:
3596            name ="BADKEY";
3597            break;
3598    
3599        case WRAPPER_MSG_LOW_LOG_LEVEL:
3600            name ="LOW_LOG_LEVEL";
3601            break;
3602    
3603        case WRAPPER_MSG_PING_TIMEOUT:
3604            name ="PING_TIMEOUT";
3605            break;
3606    
3607        case WRAPPER_MSG_SERVICE_CONTROL_CODE:
3608            name ="SERVICE_CONTROL_CODE";
3609            break;
3610    
3611        case WRAPPER_MSG_PROPERTIES:
3612            name ="PROPERTIES";
3613            break;
3614    
3615        case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_DEBUG:
3616            name ="LOG(DEBUG)";
3617            break;
3618    
3619        case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_INFO:
3620            name ="LOG(INFO)";
3621            break;
3622    
3623        case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_STATUS:
3624            name ="LOG(STATUS)";
3625            break;
3626    
3627        case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_WARN:
3628            name ="LOG(WARN)";
3629            break;
3630    
3631        case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_ERROR:
3632            name ="LOG(ERROR)";
3633            break;
3634    
3635        case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_FATAL:
3636            name ="LOG(FATAL)";
3637            break;
3638    
3639        case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_ADVICE:
3640            name ="LOG(ADVICE)";
3641            break;
3642    
3643        default:
3644            name = "UNKNOWN(" + code + ")";
3645            break;
3646        }
3647        return name;
3648    }
3649    
3650    private static synchronized void sendCommand( byte code, String JavaDoc message )
3651    {
3652        if ( m_debug )
3653        {
3654            m_out.println( "Send a packet " + getPacketCodeName( code ) + " : " + message );
3655        }
3656        if ( m_appearHung )
3657        {
3658            // The WrapperManager is attempting to make the JVM appear hung, so do nothing
3659
}
3660        else
3661        {
3662            // Make a copy of the reference to make this more thread safe.
3663
Socket JavaDoc socket = m_socket;
3664            if ( socket == null && isControlledByNativeWrapper() && ( !m_stopping ) )
3665            {
3666                // The socket is not currently open, try opening it.
3667
socket = openSocket();
3668            }
3669            
3670            if ( ( code == WRAPPER_MSG_START_PENDING ) || ( code == WRAPPER_MSG_STARTED ) )
3671            {
3672                // Set the last ping time so that the startup process does not time out
3673
// thinking that the JVM has not received a Ping for too long.
3674
m_lastPingTicks = getTicks();
3675            }
3676            
3677            // If the socket is open, then send the command, otherwise just throw it away.
3678
if ( socket != null )
3679            {
3680                try
3681                {
3682                    // It is possible that a logged message is quite large. Expand the size
3683
// of the command buffer if necessary so that it can be included. This
3684
// means that the command buffer will be the size of the largest message.
3685
byte[] messageBytes = message.getBytes();
3686                    if ( m_commandBuffer.length < messageBytes.length + 2 )
3687                    {
3688                        m_commandBuffer = new byte[messageBytes.length + 2];
3689                    }
3690                    
3691                    // Writing the bytes one by one was sometimes causing the first byte to be lost.
3692
// Try to work around this problem by creating a buffer and sending the whole lot
3693
// at once.
3694
m_commandBuffer[0] = code;
3695                    System.arraycopy( messageBytes, 0, m_commandBuffer, 1, messageBytes.length );
3696                    int len = messageBytes.length + 2;
3697                    m_commandBuffer[len - 1] = 0;
3698                    
3699                    OutputStream JavaDoc os = socket.getOutputStream();
3700                    os.write( m_commandBuffer, 0, len );
3701                    os.flush();
3702                }
3703                catch ( IOException JavaDoc e )
3704                {
3705                    m_out.println( e );
3706                    e.printStackTrace();
3707                    closeSocket();
3708                }
3709            }
3710        }
3711    }
3712    
3713    /**
3714     * Loop reading packets from the native side of the Wrapper until the
3715     * connection is closed or the WrapperManager class is disposed.
3716     * Each packet consists of a packet code followed by a null terminated
3717     * string up to 256 characters in length. If the entire packet has not
3718     * yet been received, then it must not be read until the complete packet
3719     * has arived.
3720     */

3721    private static byte[] m_socketReadBuffer = new byte[256];
3722    private static void handleSocket()
3723    {
3724        WrapperPingEvent pingEvent = new WrapperPingEvent();
3725        try
3726        {
3727            if ( m_debug )
3728            {
3729                m_out.println( "handleSocket(" + m_socket + ")" );
3730            }
3731            DataInputStream JavaDoc is = new DataInputStream JavaDoc( m_socket.getInputStream() );
3732            while ( !m_disposed )
3733            {
3734                try
3735                {
3736                    // A Packet code must exist.
3737
byte code = is.readByte();
3738                    
3739                    // Always read from the buffer until a null '\0' is encountered.
3740
byte b;
3741                    int i = 0;
3742                    do
3743                    {
3744                        b = is.readByte();
3745                        if ( b != 0 )
3746                        {
3747                            if ( i >= m_socketReadBuffer.length )
3748                            {
3749                                byte[] tmp = m_socketReadBuffer;
3750                                m_socketReadBuffer = new byte[tmp.length + 256];
3751                                System.arraycopy( tmp, 0, m_socketReadBuffer, 0, tmp.length );
3752                            }
3753                            m_socketReadBuffer[i] = b;
3754                            i++;
3755                        }
3756                    }
3757                    while ( b != 0 );
3758                    
3759                    String JavaDoc msg = new String JavaDoc( m_socketReadBuffer, 0, i );
3760                    
3761                    if ( m_appearHung )
3762                    {
3763                        // The WrapperManager is attempting to make the JVM appear hung,
3764
// so ignore all incoming requests
3765
}
3766                    else
3767                    {
3768                        if ( m_debug )
3769                        {
3770                            String JavaDoc logMsg;
3771                            if ( code == WRAPPER_MSG_PROPERTIES )
3772                            {
3773                                // The property values are very large and distracting in the log.
3774
// Plus if any triggers are defined, then logging them will fire
3775
// the trigger.
3776
logMsg = "(Property Values)";
3777                            }
3778                            else
3779                            {
3780                                logMsg = msg;
3781                            }
3782                            m_out.println( "Received a packet " + getPacketCodeName( code )
3783                                + " : " + logMsg );
3784                        }
3785                        
3786                        // Ok, we got a packet. Do something with it.
3787
switch( code )
3788                        {
3789                        case WRAPPER_MSG_START:
3790                            startInner();
3791                            break;
3792                            
3793                        case WRAPPER_MSG_STOP:
3794                            // Don't do anything if we are already stopping
3795
if ( !m_stopping )
3796                            {
3797                                privilegedStopInner( 0 );
3798                                // Should never get back here.
3799
}
3800                            break;
3801                            
3802                        case WRAPPER_MSG_PING:
3803                            m_lastPingTicks = getTicks();
3804                            sendCommand( WRAPPER_MSG_PING, "ok" );
3805                            
3806                            if ( m_produceCoreEvents )
3807                            {
3808                                fireWrapperEvent( pingEvent );
3809                            }
3810                            
3811                            break;
3812                            
3813                        case WRAPPER_MSG_BADKEY:
3814                            // The key sent to the wrapper was incorrect. We need to shutdown.
3815
m_out.println(
3816                                "Authorization key rejected by Wrapper. Exiting JVM." );
3817                            closeSocket();
3818                            privilegedStopInner( 1 );
3819                            break;
3820                            
3821                        case WRAPPER_MSG_LOW_LOG_LEVEL:
3822                            try
3823                            {
3824                                m_lowLogLevel = Integer.parseInt( msg );
3825                                m_debug = ( m_lowLogLevel <= WRAPPER_LOG_LEVEL_DEBUG );
3826                                if ( m_debug )
3827                                {
3828                                    m_out.println( "Wrapper Manager: LowLogLevel from Wrapper "
3829                                        + "is " + m_lowLogLevel );
3830                                }
3831                            }
3832                            catch ( NumberFormatException JavaDoc e )
3833                            {
3834                                m_out.println( "Encountered an Illegal LowLogLevel from the "
3835                                    + "Wrapper: " + msg );
3836                            }
3837                            break;
3838                            
3839                        case WRAPPER_MSG_PING_TIMEOUT:
3840                            try
3841                            {
3842                                m_pingTimeout = Integer.parseInt( msg ) * 1000;
3843                                if ( m_debug )
3844                                {
3845                                    m_out.println( "PingTimeout from Wrapper is " + m_pingTimeout );
3846                                }
3847                            }
3848                            catch ( NumberFormatException JavaDoc e )
3849                            {
3850                                m_out.println( "Encountered an Illegal PingTimeout from the "
3851                                    + "Wrapper: " + msg );
3852                            }
3853                            
3854                            // Make sure that the so timeout is longer than the ping timeout
3855
if ( m_pingTimeout <= 0 )
3856                            {
3857                                m_socket.setSoTimeout( 0 );
3858                            }
3859                            else if ( m_soTimeout < m_pingTimeout )
3860                            {
3861                                m_socket.setSoTimeout( m_pingTimeout );
3862                            }
3863                            
3864                            break;
3865                            
3866                        case WRAPPER_MSG_SERVICE_CONTROL_CODE:
3867                            try
3868                            {
3869                                int serviceControlCode = Integer.parseInt( msg );
3870                                if ( m_debug )
3871                                {
3872                                    m_out.println( "ServiceControlCode from Wrapper with code "
3873                                        + serviceControlCode );
3874                                }
3875                                WrapperServiceControlEvent event =
3876                                    new WrapperServiceControlEvent( serviceControlCode );
3877                                fireWrapperEvent( event );
3878                            }
3879                            catch ( NumberFormatException JavaDoc e )
3880                            {
3881                                m_out.println( "Encountered an Illegal ServiceControlCode from "
3882                                    + "the Wrapper: " + msg );
3883                            }
3884                            break;
3885                            
3886                        case WRAPPER_MSG_PROPERTIES:
3887                            readProperties( msg );
3888                            break;
3889                            
3890                        default:
3891                            // Ignore unknown messages
3892
m_out.println( "Wrapper code received an unknown packet type: "
3893                                + code );
3894                            break;
3895                        }
3896                    }
3897                }
3898                catch ( InterruptedIOException JavaDoc e )
3899                {
3900                    int nowTicks = getTicks();
3901                    
3902                    // Unless the JVM is shutting dowm we want to show warning messages and maybe exit.
3903
if ( ( m_started ) && ( !m_stopping ) )
3904                    {
3905                        if ( m_debug )
3906                        {
3907                            m_out.println( "Read Timed out. (Last Ping was "
3908                                + getTickAge( m_lastPingTicks, nowTicks ) + " milliseconds ago)" );
3909                        }
3910                        
3911                        if ( !m_appearHung )
3912                        {
3913                            long lastPingAge = getTickAge( m_lastPingTicks, nowTicks );
3914                            long eventRunnerAge = getTickAge( m_eventRunnerTicks, nowTicks );
3915                            
3916                            // We may have timed out because the system was extremely busy or
3917
// suspended. Only restart due to a lack of ping events if the
3918
// event thread has been running.
3919
if ( eventRunnerAge < 10000 )
3920                            {
3921                                // Only perform ping timeout checks if ping timeouts are enabled.
3922
if ( m_pingTimeout > 0 )
3923                                {
3924                                    // How long has it been since we received the last ping
3925
// from the Wrapper?
3926
if ( lastPingAge > m_pingTimeout + 90000 )
3927                                    {
3928                                        // It has been more than the ping timeout + 90 seconds,
3929
// so just give up and kill the JVM
3930
m_out.println(
3931                                            "Wrapper Manager: JVM did not exit. Give up." );
3932                                        safeSystemExit(1);
3933                                    }
3934                                    else if ( lastPingAge > m_pingTimeout )
3935                                    {
3936                                        // It has been more than the ping timeout since the
3937
// JVM was last pinged. Ask to be stopped (and restarted).
3938
m_out.println(
3939                                            "Wrapper Manager: The Wrapper code did not ping the "
3940                                            + "JVM for " + (lastPingAge / 1000) + " seconds. "
3941                                            + "Quit and let the Wrapper resynch.");
3942                                        
3943                                        // Don't do anything if we are already stopping
3944
if ( !m_stopping )
3945                                        {
3946                                            // Always send the stop command
3947
sendCommand( WRAPPER_MSG_RESTART, "restart" );
3948                                            
3949                                            // Give the Wrapper a chance to register the stop
3950
// command before stopping.
3951
// This avoids any errors thrown by the Wrapper because
3952
// the JVM died before it was expected to.
3953
try
3954                                            {
3955                                                Thread.sleep( 1000 );
3956                                            }
3957                                            catch ( InterruptedException JavaDoc e2 )
3958                                            {
3959                                            }
3960                                            
3961                                            privilegedStopInner( 1 );
3962                                        }
3963                                    }
3964                                }
3965                            }
3966                        }
3967                    }
3968                }
3969            }
3970            return;
3971
3972        }
3973        catch ( SocketException JavaDoc e )
3974        {
3975            if ( m_debug )
3976            {
3977                if ( m_socket == null )
3978                {
3979                    // This error happens if the socket is closed while reading:
3980
// java.net.SocketException: Descriptor not a socket: JVM_recv in socket
3981
// input stream read
3982
}
3983                else
3984                {
3985                    m_out.println( "Closed socket: " + e );
3986                }
3987            }
3988            return;
3989        }
3990        catch ( IOException JavaDoc e )
3991        {
3992            // This means that the connection was closed. Allow this to return.
3993
//m_out.println( e );
3994
//e.printStackTrace();
3995
return;
3996        }
3997    }
3998    
3999    private static void startRunner()
4000    {
4001        if ( isControlledByNativeWrapper() )
4002        {
4003            if ( m_commRunner == null )
4004            {
4005                // Create and launch a new thread to manage this connection
4006
m_commRunner = new Thread JavaDoc( m_instance, WRAPPER_CONNECTION_THREAD_NAME );
4007                m_commRunner.setDaemon( true );
4008                m_commRunner.start();
4009            }
4010            
4011            // Wait to give the runner a chance to connect.
4012
synchronized( WrapperManager.class )
4013            {
4014                while ( !m_commRunnerStarted )
4015                {
4016                    try
4017                    {
4018                        WrapperManager.class.wait( 100 );
4019                    }
4020                    catch ( InterruptedException JavaDoc e )
4021                    {
4022                    }
4023                }
4024            }
4025        }
4026        else
4027        {
4028            // Immediately mark the runner as started as it will never be used.
4029
synchronized( WrapperManager.class )
4030            {
4031                m_commRunnerStarted = true;
4032                WrapperManager.class.notifyAll();
4033            }
4034        }
4035    }
4036    
4037    /*---------------------------------------------------------------
4038     * Runnable Methods
4039     *-------------------------------------------------------------*/

4040    public void run()
4041    {
4042        // Make sure that no other threads call this method.
4043
if ( Thread.currentThread() != m_commRunner )
4044        {
4045            throw new IllegalStateException JavaDoc(
4046                "Only the comm runner thread is allowed to call this method." );
4047        }
4048        
4049        if ( m_debug )
4050        {
4051            m_out.println( "Communications runner thread started." );
4052        }
4053        
4054        // This thread needs to have a very high priority so that it never
4055
// gets put behind other threads.
4056
Thread.currentThread().setPriority( Thread.MAX_PRIORITY );
4057        
4058        // Initialize the last ping tick count.
4059
m_lastPingTicks = getTicks();
4060        
4061        boolean gotPortOnce = false;
4062        while ( !m_disposed )
4063        {
4064            try
4065            {
4066                try
4067                {
4068                    openSocket();
4069                    
4070                    // After the socket has been opened the first time, mark the thread as
4071
// started. This must be done here to make sure that exits work correctly
4072
// when called on startup.
4073
if ( !m_commRunnerStarted )
4074                    {
4075                        synchronized( WrapperManager.class )
4076                        {
4077                            m_commRunnerStarted = true;
4078                            WrapperManager.class.notifyAll();
4079                        }
4080                    }
4081                    
4082                    if ( m_socket != null )
4083                    {
4084                        handleSocket();
4085                    }
4086                    else
4087                    {
4088                        // Failed, so wait for just a moment
4089
try
4090                        {
4091                            Thread.sleep( 100 );
4092                        }
4093                        catch ( InterruptedException JavaDoc e )
4094                        {
4095                        }
4096                    }
4097                }
4098                finally
4099                {
4100                    // Always close the socket here.
4101
closeSocket();
4102                }
4103            }
4104            catch ( ThreadDeath JavaDoc td )
4105            {
4106                m_out.println( m_warning.format( "SERVER_DAEMON_KILLED" ) );
4107            }
4108            catch ( Throwable JavaDoc t )
4109            {
4110                if ( !m_shuttingDown )
4111                {
4112                    // Show a stack trace here because this is fairly critical
4113
m_out.println( m_error.format( "SERVER_DAEMON_DIED" ) );
4114                    t.printStackTrace();
4115                }
4116            }
4117        }
4118        
4119        // Make sure that noone is ever left waiting for this thread to start.
4120
synchronized( WrapperManager.class )
4121        {
4122            if ( !m_commRunnerStarted )
4123            {
4124                m_commRunnerStarted = true;
4125                WrapperManager.class.notifyAll();
4126            }
4127        }
4128        
4129        if ( m_debug )
4130        {
4131            m_out.println( m_info.format( "SERVER_DAEMON_SHUT_DOWN" ) );
4132        }
4133    }
4134    
4135    /*---------------------------------------------------------------
4136     * Inner Classes
4137     *-------------------------------------------------------------*/

4138    /**
4139     * Mapping between WrapperEventListeners and their registered masks.
4140     * This is necessary to support the case where the same listener is
4141     * registered more than once. It also makes it possible to reference
4142     * an array of these mappings without synchronization.
4143     */

4144    private static class WrapperEventListenerMask
4145    {
4146        private WrapperEventListener m_listener;
4147        private long m_mask;
4148    }
4149    
4150    private static class WrapperTickEventImpl
4151        extends WrapperTickEvent
4152    {
4153        private int m_ticks;
4154        private int m_tickOffset;
4155        
4156        /**
4157         * Returns the tick count at the point the event is fired.
4158         *
4159         * @return The tick count at the point the event is fired.
4160         */

4161        public int getTicks()
4162        {
4163            return m_ticks;
4164        }
4165        
4166        /**
4167         * Returns the offset between the tick count used by the Wrapper for time
4168         * keeping and the tick count generated directly from the system time.
4169         *
4170         * This will be 0 in most cases. But will be a positive value if the
4171         * system time is ever set back for any reason. It will be a negative
4172         * value if the system time is set forward or if the system is under
4173         * heavy load. If the wrapper.use_system_time property is set to TRUE
4174         * then the Wrapper will be using the system tick count for internal
4175         * timing and this value will always be 0.
4176         *
4177         * @return The tick count offset.
4178         */

4179        public int getTickOffset()
4180        {
4181            return m_tickOffset;
4182        }
4183    }
4184}
4185
4186
Popular Tags