0001: package org.tanukisoftware.wrapper;
0002:
0003: /*
0004: * Copyright (c) 1999, 2006 Tanuki Software Inc.
0005: *
0006: * Permission is hereby granted, free of charge, to any person
0007: * obtaining a copy of the Java Service Wrapper and associated
0008: * documentation files (the "Software"), to deal in the Software
0009: * without restriction, including without limitation the rights
0010: * to use, copy, modify, merge, publish, distribute, sub-license,
0011: * and/or sell copies of the Software, and to permit persons to
0012: * whom the Software is furnished to do so, subject to the
0013: * following conditions:
0014: *
0015: * The above copyright notice and this permission notice shall be
0016: * included in all copies or substantial portions of the Software.
0017: *
0018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0019: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
0020: * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0021: * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
0022: * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
0023: * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0024: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0025: * OTHER DEALINGS IN THE SOFTWARE.
0026: *
0027: *
0028: * Portions of the Software have been derived from source code
0029: * developed by Silver Egg Technology under the following license:
0030: *
0031: * Copyright (c) 2001 Silver Egg Technology
0032: *
0033: * Permission is hereby granted, free of charge, to any person
0034: * obtaining a copy of this software and associated documentation
0035: * files (the "Software"), to deal in the Software without
0036: * restriction, including without limitation the rights to use,
0037: * copy, modify, merge, publish, distribute, sub-license, and/or
0038: * sell copies of the Software, and to permit persons to whom the
0039: * Software is furnished to do so, subject to the following
0040: * conditions:
0041: *
0042: * The above copyright notice and this permission notice shall be
0043: * included in all copies or substantial portions of the Software.
0044: */
0045:
0046: import java.io.DataInputStream;
0047: import java.io.File;
0048: import java.io.InputStream;
0049: import java.io.InterruptedIOException;
0050: import java.io.IOException;
0051: import java.io.OutputStream;
0052: import java.io.PrintStream;
0053: import java.lang.reflect.Constructor;
0054: import java.lang.reflect.InvocationTargetException;
0055: import java.lang.reflect.Method;
0056: import java.net.BindException;
0057: import java.net.ConnectException;
0058: import java.net.InetAddress;
0059: import java.net.ServerSocket;
0060: import java.net.Socket;
0061: import java.net.SocketException;
0062: import java.net.UnknownHostException;
0063: import java.security.AccessControlException;
0064: import java.security.AccessController;
0065: import java.security.PrivilegedAction;
0066: import java.util.ArrayList;
0067: import java.util.Iterator;
0068: import java.util.List;
0069: import java.util.Properties;
0070: import java.util.StringTokenizer;
0071:
0072: import org.tanukisoftware.wrapper.event.WrapperControlEvent;
0073: import org.tanukisoftware.wrapper.event.WrapperEvent;
0074: import org.tanukisoftware.wrapper.event.WrapperEventListener;
0075: import org.tanukisoftware.wrapper.event.WrapperPingEvent;
0076: import org.tanukisoftware.wrapper.event.WrapperServiceControlEvent;
0077: import org.tanukisoftware.wrapper.event.WrapperTickEvent;
0078: import org.tanukisoftware.wrapper.resources.ResourceManager;
0079: import org.tanukisoftware.wrapper.security.WrapperEventPermission;
0080: import org.tanukisoftware.wrapper.security.WrapperPermission;
0081: import org.tanukisoftware.wrapper.security.WrapperServicePermission;
0082:
0083: /**
0084: * Handles all communication with the native portion of the Wrapper code.
0085: * The native wrapper code will launch Java in a separate process and set
0086: * up a server socket which the Java code is expected to open a socket to
0087: * on startup. When the server socket is created, a port will be chosen
0088: * depending on what is available to the system. This port will then be
0089: * passed to the Java process as property named "wrapper.port".
0090: *
0091: * For security reasons, the native code will only allow connections from
0092: * localhost and will expect to receive the key specified in a property
0093: * named "wrapper.key".
0094: *
0095: * This class is implemented as a singleton class.
0096: *
0097: * Generate JNI Headers with the following command in the build/classes
0098: * directory:
0099: * javah -jni -classpath ./ org.tanukisoftware.wrapper.WrapperManager
0100: *
0101: * @author Leif Mortenson <leif@tanukisoftware.com>
0102: */
0103: public final class WrapperManager implements Runnable {
0104: private static final String WRAPPER_CONNECTION_THREAD_NAME = "Wrapper-Connection";
0105:
0106: private static final int DEFAULT_PORT = 15003;
0107: private static final int DEFAULT_SO_TIMEOUT = 10000;
0108: private static final int DEFAULT_CPU_TIMEOUT = 10000;
0109:
0110: /** The number of milliseconds in one tick. Used for internal system
0111: * time independent time keeping. */
0112: private static final int TICK_MS = 100;
0113: private static final int TIMER_FAST_THRESHOLD = 2 * 24 * 3600
0114: * 1000 / TICK_MS; // 2 days.
0115: private static final int TIMER_SLOW_THRESHOLD = 2 * 24 * 3600
0116: * 1000 / TICK_MS; // 2 days.
0117:
0118: private static final byte WRAPPER_MSG_START = (byte) 100;
0119: private static final byte WRAPPER_MSG_STOP = (byte) 101;
0120: private static final byte WRAPPER_MSG_RESTART = (byte) 102;
0121: private static final byte WRAPPER_MSG_PING = (byte) 103;
0122: private static final byte WRAPPER_MSG_STOP_PENDING = (byte) 104;
0123: private static final byte WRAPPER_MSG_START_PENDING = (byte) 105;
0124: private static final byte WRAPPER_MSG_STARTED = (byte) 106;
0125: private static final byte WRAPPER_MSG_STOPPED = (byte) 107;
0126: private static final byte WRAPPER_MSG_KEY = (byte) 110;
0127: private static final byte WRAPPER_MSG_BADKEY = (byte) 111;
0128: private static final byte WRAPPER_MSG_LOW_LOG_LEVEL = (byte) 112;
0129: private static final byte WRAPPER_MSG_PING_TIMEOUT = (byte) 113;
0130: private static final byte WRAPPER_MSG_SERVICE_CONTROL_CODE = (byte) 114;
0131: private static final byte WRAPPER_MSG_PROPERTIES = (byte) 115;
0132:
0133: /** Log commands are actually 116 + the LOG LEVEL. */
0134: private static final byte WRAPPER_MSG_LOG = (byte) 116;
0135:
0136: /** Received when the user presses CTRL-C in the console on Windows or UNIX platforms. */
0137: public static final int WRAPPER_CTRL_C_EVENT = 200;
0138:
0139: /** Received when the user clicks on the close button of a Console on Windows. */
0140: public static final int WRAPPER_CTRL_CLOSE_EVENT = 201;
0141:
0142: /** Received when the user logs off of a Windows system. */
0143: public static final int WRAPPER_CTRL_LOGOFF_EVENT = 202;
0144:
0145: /** Received when a Windows system is shutting down. */
0146: public static final int WRAPPER_CTRL_SHUTDOWN_EVENT = 203;
0147:
0148: /** Received when a SIG TERM is received on a UNIX system. */
0149: public static final int WRAPPER_CTRL_TERM_EVENT = 204;
0150:
0151: /** Received when a SIG HUP is received on a UNIX system. */
0152: public static final int WRAPPER_CTRL_HUP_EVENT = 205;
0153:
0154: /** Log message at debug log level. */
0155: public static final int WRAPPER_LOG_LEVEL_DEBUG = 1;
0156: /** Log message at info log level. */
0157: public static final int WRAPPER_LOG_LEVEL_INFO = 2;
0158: /** Log message at status log level. */
0159: public static final int WRAPPER_LOG_LEVEL_STATUS = 3;
0160: /** Log message at warn log level. */
0161: public static final int WRAPPER_LOG_LEVEL_WARN = 4;
0162: /** Log message at error log level. */
0163: public static final int WRAPPER_LOG_LEVEL_ERROR = 5;
0164: /** Log message at fatal log level. */
0165: public static final int WRAPPER_LOG_LEVEL_FATAL = 6;
0166: /** Log message at advice log level. */
0167: public static final int WRAPPER_LOG_LEVEL_ADVICE = 7;
0168:
0169: /** Service Control code which can be sent to start a service. */
0170: public static final int SERVICE_CONTROL_CODE_START = 0x10000;
0171:
0172: /** Service Control code which can be sent or received to stop a service. */
0173: public static final int SERVICE_CONTROL_CODE_STOP = 1;
0174:
0175: /** Service Control code which can be sent to pause a service. */
0176: public static final int SERVICE_CONTROL_CODE_PAUSE = 2;
0177:
0178: /** Service Control code which can be sent to resume a paused service. */
0179: public static final int SERVICE_CONTROL_CODE_CONTINUE = 3;
0180:
0181: /** Service Control code which can be sent to or received interrogate the status of a service. */
0182: public static final int SERVICE_CONTROL_CODE_INTERROGATE = 4;
0183:
0184: /** Service Control code which can be received when the system is shutting down. */
0185: public static final int SERVICE_CONTROL_CODE_SHUTDOWN = 5;
0186:
0187: /** Reference to the original value of System.out. */
0188: private static PrintStream m_out;
0189:
0190: /** Reference to the original value of System.err. */
0191: private static PrintStream m_err;
0192:
0193: /** Flag that will be set to true once a SecurityManager has been detected and tested. */
0194: private static boolean m_securityManagerChecked = false;
0195:
0196: private static boolean m_disposed = false;
0197: private static boolean m_started = false;
0198: private static WrapperManager m_instance = null;
0199: private static Thread m_hook = null;
0200: private static boolean m_hookTriggered = false;
0201:
0202: /* Flag which records when the shutdownJVM method has completed. */
0203: private static boolean m_shutdownJVMComplete = false;
0204:
0205: private static String[] m_args;
0206: private static int m_port = DEFAULT_PORT;
0207: private static int m_jvmPort;
0208: private static int m_jvmPortMin;
0209: private static int m_jvmPortMax;
0210: private static String m_key;
0211: private static int m_soTimeout = DEFAULT_SO_TIMEOUT;
0212: private static long m_cpuTimeout = DEFAULT_CPU_TIMEOUT;
0213:
0214: /** Tick count when the start method completed. */
0215: private static int m_startedTicks;
0216:
0217: /** The lowest configured log level in the Wrapper's configuration. This
0218: * is set to a high value by default to disable all logging if the
0219: * Wrapper does not register its low level or is not present. */
0220: private static int m_lowLogLevel = WRAPPER_LOG_LEVEL_ADVICE + 1;
0221:
0222: /** The maximum amount of time in ms to allow to pass without the JVM
0223: * pinging the server before the JVM is terminated to allow a resynch. */
0224: private static int m_pingTimeout = 30000;
0225:
0226: /** Flag, set when the JVM is launched that is used to remember whether
0227: * or not system signals are supposed to be ignored. */
0228: private static boolean m_ignoreSignals = false;
0229:
0230: /** Thread which processes all communications with the native code. */
0231: private static Thread m_commRunner;
0232: private static boolean m_commRunnerStarted = false;
0233: private static Thread m_eventRunner;
0234: private static int m_eventRunnerTicks;
0235: private static Thread m_startupRunner;
0236:
0237: /** True if the system time should be used for internal timeouts. */
0238: private static boolean m_useSystemTime;
0239:
0240: /** The threashold of how many ticks the timer can be fast before a
0241: * warning is displayed. */
0242: private static int m_timerFastThreshold;
0243:
0244: /** The threashold of how many ticks the timer can be slow before a
0245: * warning is displayed. */
0246: private static int m_timerSlowThreshold;
0247:
0248: /**
0249: * Bit depth of the currently running JVM. Will be 32 or 64.
0250: * A 64-bit JVM means that the system is also 64-bit, but a 32-bit JVM
0251: * can be run either on a 32 or 64-bit system.
0252: */
0253: private static int m_jvmBits;
0254:
0255: /** An integer which stores the number of ticks since the
0256: * JVM was launched. Using an int rather than a long allows the value
0257: * to be used without requiring any synchronization. This is only
0258: * used if the m_useSystemTime flag is false. */
0259: private static volatile int m_ticks;
0260:
0261: private static WrapperListener m_listener;
0262:
0263: private static int m_lastPingTicks;
0264: private static ServerSocket m_serverSocket;
0265: private static Socket m_socket;
0266: private static boolean m_shuttingDown = false;
0267: private static boolean m_appearHung = false;
0268:
0269: private static Method m_addShutdownHookMethod = null;
0270: private static Method m_removeShutdownHookMethod = null;
0271:
0272: private static boolean m_service = false;
0273: private static boolean m_debug = false;
0274: private static int m_jvmId = 0;
0275: private static boolean m_stopping = false;
0276: private static Thread m_stoppingThread;
0277: private static int m_exitCode;
0278: private static boolean m_libraryOK = false;
0279: private static byte[] m_commandBuffer = new byte[512];
0280:
0281: /** The contents of the wrapper configuration. */
0282: private static WrapperProperties m_properties;
0283:
0284: /** List of registered WrapperEventListeners and their registered masks. */
0285: private static List m_wrapperEventListenerMaskList = new ArrayList();
0286:
0287: /** Array of registered WrapperEventListeners and their registered masks.
0288: * Should not be referenced directly. Access by calling
0289: * getWrapperEventListenerMasks(). */
0290: private static WrapperEventListenerMask[] m_wrapperEventListenerMasks = null;
0291:
0292: /** Flag used to tell whether or not WrapperCoreEvents should be produced. */
0293: private static boolean m_produceCoreEvents = false;
0294:
0295: // message resources: eventually these will be split up
0296: private static ResourceManager m_res = ResourceManager
0297: .getResourceManager();
0298: private static ResourceManager m_error = m_res;
0299: private static ResourceManager m_warning = m_res;
0300: private static ResourceManager m_info = m_res;
0301:
0302: /*---------------------------------------------------------------
0303: * Class Initializer
0304: *-------------------------------------------------------------*/
0305: /**
0306: * When the WrapperManager class is first loaded, it attempts to load the
0307: * configuration file specified using the 'wrapper.config' system property.
0308: * When the JVM is launched from the Wrapper native code, the
0309: * 'wrapper.config' and 'wrapper.key' parameters are specified.
0310: * The 'wrapper.key' parameter is a password which is used to verify that
0311: * connections are only coming from the native Wrapper which launched the
0312: * current JVM.
0313: */
0314: static {
0315: // The wraper.jar must be given AllPermissions if a security manager
0316: // has been configured. This is not a problem if one of the standard
0317: // Wrapper helper classes is used to launch the JVM.
0318: // If however a custom WrapperListener is being implemented then this
0319: // class will most likely be loaded by code that is neither part of
0320: // the system, nor part of the Wrapper code base. To avoid having
0321: // to also give those classes AllPermissions as well, we do all of
0322: // initialization in a Privileged block. This means that the code
0323: // only requires that the wrapper.jar has been given the required
0324: // permissions.
0325: AccessController.doPrivileged(new PrivilegedAction() {
0326: public Object run() {
0327: privilegedClassInit();
0328: return null;
0329: }
0330: });
0331: }
0332:
0333: /**
0334: * The body of the static initializer is moved into a seperate method so
0335: * it can be run as a PrivilegedAction.
0336: */
0337: private static void privilegedClassInit() {
0338: // Store references to the original System.out and System.err
0339: // PrintStreams. The WrapperManager will always output to the
0340: // original streams so its output will always end up in the
0341: // wrapper.log file even if the end user code redirects the
0342: // output to another log file.
0343: // This is also important to be protect the Wrapper's functionality
0344: // from the case where the user PrintStream enters a deadlock state.
0345: m_out = System.out;
0346: m_err = System.err;
0347:
0348: // Always create an empty properties object in case we are not running
0349: // in the Wrapper or the properties are never sent.
0350: m_properties = new WrapperProperties();
0351: m_properties.lock();
0352:
0353: // This must be done before attempting to access any System Properties
0354: // as that could cause a SecurityException if it is too strict.
0355: checkSecurityManager();
0356:
0357: // Check for the debug flag
0358: m_debug = WrapperSystemPropertyUtil.getBooleanProperty(
0359: "wrapper.debug", false);
0360:
0361: if (m_debug) {
0362: m_out
0363: .println("WrapperManager class initialized by thread: "
0364: + Thread.currentThread().getName()
0365: + " Using classloader: "
0366: + WrapperManager.class.getClassLoader());
0367: }
0368:
0369: //
0370: // WARNING - The following banner is displayed to inform the user that they
0371: // are using the Java Service Wrapper. This banner must remain
0372: // clearly visible in the logs of any application making use of
0373: // the Wrapper. This includes any applications based on the
0374: // wrapper source.
0375: // If you are here then you are benefiting from this project,
0376: // please have the courtesy to respect its license.
0377: m_out.println("Wrapper (Version " + getVersion()
0378: + ") http://wrapper.tanukisoftware.org");
0379: m_out
0380: .println(" Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved.");
0381: m_out.println();
0382:
0383: // Check for the jvmID
0384: m_jvmId = WrapperSystemPropertyUtil.getIntProperty(
0385: "wrapper.jvmid", 1);
0386: if (m_debug) {
0387: m_out.println("Wrapper Manager: JVM #" + m_jvmId);
0388: }
0389:
0390: // Decide whether this is a 32 or 64 bit version of Java.
0391: m_jvmBits = Integer.getInteger("sun.arch.data.model", -1)
0392: .intValue();
0393: if (m_debug) {
0394: if (m_jvmBits > 0) {
0395: m_out.println("Running a " + m_jvmBits + "-bit JVM.");
0396: } else {
0397: m_out
0398: .println("The bit depth of this JVM could not be determined.");
0399: }
0400: }
0401:
0402: // Initialize the timerTicks to a very high value. This means that we will
0403: // always encounter the first rollover (200 * WRAPPER_MS / 1000) seconds
0404: // after the Wrapper the starts, which means the rollover will be well
0405: // tested.
0406: m_ticks = Integer.MAX_VALUE - 200;
0407:
0408: m_useSystemTime = WrapperSystemPropertyUtil.getBooleanProperty(
0409: "wrapper.use_system_time", false);
0410: m_timerFastThreshold = WrapperSystemPropertyUtil
0411: .getIntProperty("wrapper.timer_fast_threshold",
0412: TIMER_FAST_THRESHOLD)
0413: * 1000 / TICK_MS;
0414: m_timerSlowThreshold = WrapperSystemPropertyUtil
0415: .getIntProperty("wrapper.timer_slow_threshold",
0416: TIMER_SLOW_THRESHOLD)
0417: * 1000 / TICK_MS;
0418:
0419: // Check to see if we should register a shutdown hook
0420: boolean disableShutdownHook = WrapperSystemPropertyUtil
0421: .getBooleanProperty("wrapper.disable_shutdown_hook",
0422: false);
0423:
0424: // Locate the add and remove shutdown hook methods using reflection so
0425: // that this class can be compiled on 1.2.x versions of java.
0426: try {
0427: m_addShutdownHookMethod = Runtime.class.getMethod(
0428: "addShutdownHook", new Class[] { Thread.class });
0429: m_removeShutdownHookMethod = Runtime.class.getMethod(
0430: "removeShutdownHook", new Class[] { Thread.class });
0431: } catch (NoSuchMethodException e) {
0432: if (m_debug) {
0433: m_out
0434: .println("Wrapper Manager: Shutdown hooks not supported by current JVM.");
0435: }
0436: m_addShutdownHookMethod = null;
0437: m_removeShutdownHookMethod = null;
0438: disableShutdownHook = true;
0439: }
0440:
0441: // If the shutdown hook is not disabled, then register it.
0442: if (!disableShutdownHook) {
0443: if (m_debug) {
0444: m_out
0445: .println("Wrapper Manager: Registering shutdown hook");
0446: }
0447: m_hook = new Thread("Wrapper-Shutdown-Hook") {
0448: /**
0449: * Run the shutdown hook. (Triggered by the JVM when it is about to shutdown)
0450: */
0451: public void run() {
0452: // Stop the Wrapper cleanly.
0453: m_hookTriggered = true;
0454:
0455: if (m_debug) {
0456: m_out
0457: .println("Wrapper Manager: ShutdownHook started");
0458: }
0459:
0460: // Let the startup thread die since the shutdown hook is running.
0461: m_startupRunner = null;
0462:
0463: // If we are not already stopping, then do so.
0464: WrapperManager.stop(0);
0465:
0466: if (m_debug) {
0467: m_out
0468: .println("Wrapper Manager: ShutdownHook complete");
0469: }
0470: }
0471: };
0472:
0473: // Actually register the shutdown hook using reflection.
0474: try {
0475: m_addShutdownHookMethod.invoke(Runtime.getRuntime(),
0476: new Object[] { m_hook });
0477: } catch (IllegalAccessException e) {
0478: m_out
0479: .println("Wrapper Manager: Unable to register shutdown hook: "
0480: + e);
0481: } catch (InvocationTargetException e) {
0482: Throwable t = e.getTargetException();
0483: if (t == null) {
0484: t = e;
0485: }
0486:
0487: m_out
0488: .println("Wrapper Manager: Unable to register shutdown hook: "
0489: + t);
0490: }
0491: }
0492:
0493: // A key is required for the wrapper to work correctly. If it is not
0494: // present, then assume that we are not being controlled by the native
0495: // wrapper.
0496: if ((m_key = System.getProperty("wrapper.key")) == null) {
0497: if (m_debug) {
0498: m_out
0499: .println("Wrapper Manager: Not using wrapper. (key not specified)");
0500: }
0501:
0502: // The wrapper will not be used, so other values will not be used.
0503: m_port = 0;
0504: m_jvmPort = 0;
0505: m_jvmPortMin = 0;
0506: m_jvmPortMax = 0;
0507: m_service = false;
0508: m_cpuTimeout = 31557600000L; // One Year. Effectively never.
0509: } else {
0510: if (m_debug) {
0511: m_out.println("Wrapper Manager: Using wrapper");
0512: }
0513:
0514: // A port must have been specified.
0515: String sPort;
0516: if ((sPort = System.getProperty("wrapper.port")) == null) {
0517: String msg = m_res.format("MISSING_PORT");
0518: m_out.println(msg);
0519: throw new ExceptionInInitializerError(msg);
0520: }
0521: try {
0522: m_port = Integer.parseInt(sPort);
0523: } catch (NumberFormatException e) {
0524: String msg = m_res.format("BAD_PORT", sPort);
0525: m_out.println(msg);
0526: throw new ExceptionInInitializerError(msg);
0527: }
0528:
0529: m_jvmPort = WrapperSystemPropertyUtil.getIntProperty(
0530: "wrapper.jvm.port", 0);
0531: m_jvmPortMin = WrapperSystemPropertyUtil.getIntProperty(
0532: "wrapper.jvm.port.min", 31000);
0533: m_jvmPortMax = WrapperSystemPropertyUtil.getIntProperty(
0534: "wrapper.jvm.port.max", 31999);
0535:
0536: // Check for the ignore signals flag
0537: m_ignoreSignals = WrapperSystemPropertyUtil
0538: .getBooleanProperty("wrapper.ignore_signals", false);
0539:
0540: // If this is being run as a headless server, then a flag would have been set
0541: m_service = WrapperSystemPropertyUtil.getBooleanProperty(
0542: "wrapper.service", false);
0543:
0544: // Get the cpuTimeout
0545: String sCPUTimeout = System
0546: .getProperty("wrapper.cpu.timeout");
0547: if (sCPUTimeout == null) {
0548: m_cpuTimeout = DEFAULT_CPU_TIMEOUT;
0549: } else {
0550: try {
0551: m_cpuTimeout = Integer.parseInt(sCPUTimeout) * 1000L;
0552: } catch (NumberFormatException e) {
0553: String msg = m_res.format("BAD_CPU_TIMEOUT",
0554: sCPUTimeout);
0555: m_out.println(msg);
0556: throw new ExceptionInInitializerError(msg);
0557: }
0558: }
0559: }
0560:
0561: // Make sure that the version of the Wrapper is correct.
0562: verifyWrapperVersion();
0563:
0564: // Register the MBeans if configured to do so.
0565: if (WrapperSystemPropertyUtil.getBooleanProperty(
0566: WrapperManager.class.getName() + ".mbean", true)) {
0567: registerMBean(
0568: new org.tanukisoftware.wrapper.jmx.WrapperManager(),
0569: "org.tanukisoftware.wrapper:type=WrapperManager");
0570: }
0571: if (WrapperSystemPropertyUtil.getBooleanProperty(
0572: WrapperManager.class.getName() + ".mbean.testing",
0573: false)) {
0574: registerMBean(
0575: new org.tanukisoftware.wrapper.jmx.WrapperManagerTesting(),
0576: "org.tanukisoftware.wrapper:type=WrapperManagerTesting");
0577: }
0578:
0579: // Initialize the native code to trap system signals
0580: initializeNativeLibrary();
0581:
0582: if (m_libraryOK) {
0583: // Make sure that the native library's version is correct.
0584: verifyNativeLibraryVersion();
0585:
0586: // Get the PID of the current JVM from the native library. Be careful as the method
0587: // will not exist if the library is old.
0588: try {
0589: System.setProperty("wrapper.java.pid", Integer
0590: .toString(nativeGetJavaPID()));
0591: } catch (Throwable e) {
0592: if (m_debug) {
0593: m_out.println("Call to nativeGetJavaPID() failed: "
0594: + e);
0595: }
0596: }
0597: }
0598:
0599: // Start a thread which looks for control events sent to the
0600: // process. The thread is also used to keep track of whether
0601: // the VM has been getting CPU to avoid invalid timeouts and
0602: // to maintain the number of ticks since the JVM was launched.
0603: m_eventRunnerTicks = getTicks();
0604: m_eventRunner = new Thread("Wrapper-Control-Event-Monitor") {
0605: public void run() {
0606: if (m_debug) {
0607: m_out
0608: .println("Control event monitor thread started.");
0609: }
0610:
0611: try {
0612: WrapperTickEventImpl tickEvent = new WrapperTickEventImpl();
0613: int lastTickOffset = 0;
0614: boolean first = true;
0615:
0616: while (!m_shuttingDown) {
0617: int offsetDiff;
0618: if (!m_useSystemTime) {
0619: // Get the tick count based on the system time.
0620: int sysTicks = getSystemTicks();
0621:
0622: // Increment the tick counter by 1. This loop takes just slightly
0623: // more than the length of a "tick" but it is a good enough
0624: // approximation for our purposes. The accuracy of the tick length
0625: // falls sharply when the system is under heavly load, but this
0626: // has the desired effect as the Wrapper is also much less likely
0627: // to encounter false timeouts due to the heavy load.
0628: // The ticks field is volatile and a single integer, so it is not
0629: // necessary to synchronize this.
0630: // When the ticks count reaches the upper limit of the int range,
0631: // it is ok to just let it overflow and wrap.
0632: m_ticks++;
0633:
0634: // Calculate the offset between the two tick counts.
0635: // This will always work due to overflow.
0636: int tickOffset = sysTicks - m_ticks;
0637:
0638: // The number we really want is the difference between this tickOffset
0639: // and the previous one.
0640: offsetDiff = tickOffset - lastTickOffset;
0641:
0642: if (first) {
0643: first = false;
0644: } else {
0645: if (offsetDiff > m_timerSlowThreshold) {
0646: m_out
0647: .println("The timer fell behind the system clock by "
0648: + (offsetDiff * TICK_MS)
0649: + "ms.");
0650: } else if (offsetDiff < -m_timerFastThreshold) {
0651: m_out
0652: .println("The system clock fell behind the timer by "
0653: + (-1 * offsetDiff * TICK_MS)
0654: + "ms.");
0655: }
0656: }
0657:
0658: // Store this tick offset for the net time through the loop.
0659: lastTickOffset = tickOffset;
0660: } else {
0661: offsetDiff = 0;
0662: }
0663:
0664: //m_out.println( " UNIX Time: " + Long.toHexString( System.currentTimeMillis() )
0665: // + ", ticks=" + Integer.toHexString( getTicks() ) + ", sysTicks="
0666: // + Integer.toHexString( getSystemTicks() ) );
0667:
0668: // Attempt to detect whether or not we are being starved of CPU.
0669: // This will only have any effect if the m_useSystemTime flag is
0670: // set.
0671: int nowTicks = getTicks();
0672: long age = getTickAge(m_eventRunnerTicks,
0673: nowTicks);
0674: if ((m_cpuTimeout > 0) && (age > m_cpuTimeout)) {
0675: m_out
0676: .println("JVM Process has not received any CPU time for "
0677: + (age / 1000)
0678: + " seconds. Extending timeouts.");
0679:
0680: // Make sure that we don't get any ping timeouts in this event
0681: m_lastPingTicks = nowTicks;
0682: }
0683: m_eventRunnerTicks = nowTicks;
0684:
0685: // If there are any listeners interrested in core events then fire
0686: // off a tick event.
0687: if (m_produceCoreEvents) {
0688: tickEvent.m_ticks = nowTicks;
0689: tickEvent.m_tickOffset = offsetDiff;
0690: fireWrapperEvent(tickEvent);
0691: }
0692:
0693: if (m_libraryOK) {
0694: // Look for a control event in the wrapper library
0695: int event = WrapperManager
0696: .nativeGetControlEvent();
0697: if (event != 0) {
0698: WrapperManager.controlEvent(event);
0699: }
0700: }
0701:
0702: // Wait before checking for another control event.
0703: try {
0704: Thread.sleep(TICK_MS);
0705: } catch (InterruptedException e) {
0706: }
0707: }
0708: } finally {
0709: if (m_debug) {
0710: m_out
0711: .println("Control event monitor thread stopped.");
0712: }
0713: }
0714: }
0715: };
0716: m_eventRunner.setDaemon(true);
0717: m_eventRunner.start();
0718:
0719: // Resolve the system thread count based on the Java Version
0720: String fullVersion = System.getProperty("java.fullversion");
0721: String vendor = System.getProperty("java.vm.vendor", "");
0722: String os = System.getProperty("os.name", "").toLowerCase();
0723: if (fullVersion == null) {
0724: fullVersion = System.getProperty("java.runtime.version")
0725: + " " + System.getProperty("java.vm.name");
0726: }
0727:
0728: if (m_debug) {
0729: // Display more JVM infor right after the call initialization of the library.
0730: m_out.println("Java Version : " + fullVersion);
0731: m_out.println("Java VM Vendor : " + vendor);
0732: m_out.println();
0733: }
0734:
0735: // This thread will most likely be thread which launches the JVM.
0736: // Once this method returns however, the main thread will likely
0737: // quit. There will be a slight delay before the Wrapper binary
0738: // has a change to send a command to start the application.
0739: // During this lag, the JVM may not have any non-daemon threads
0740: // running and would exit. To keep it from doing so, start a
0741: // simple non-daemon thread which will run until the
0742: // WrapperListener.start() method returns or the Wrapper's
0743: // shutdown thread has started.
0744: m_startupRunner = new Thread("Wrapper-Startup-Runner") {
0745: public void run() {
0746: if (m_debug) {
0747: m_out.println("Startup runner thread started.");
0748: }
0749:
0750: try {
0751: while (m_startupRunner != null) {
0752: try {
0753: Thread.sleep(100);
0754: } catch (InterruptedException e) {
0755: // Ignore.
0756: }
0757: }
0758: } finally {
0759: if (m_debug) {
0760: m_out.println("Startup runner thread stopped.");
0761: }
0762: }
0763: }
0764: };
0765: // This thread must not be a daemon thread.
0766: m_startupRunner.setDaemon(false);
0767: m_startupRunner.start();
0768:
0769: // Create the singleton
0770: m_instance = new WrapperManager();
0771: }
0772:
0773: /*---------------------------------------------------------------
0774: * Native Methods
0775: *-------------------------------------------------------------*/
0776: private static native void nativeInit(boolean debug);
0777:
0778: private static native String nativeGetLibraryVersion();
0779:
0780: private static native int nativeGetJavaPID();
0781:
0782: private static native int nativeGetControlEvent();
0783:
0784: private static native void nativeRequestThreadDump();
0785:
0786: private static native void accessViolationInner();
0787:
0788: private static native void nativeSetConsoleTitle(byte[] titleBytes);
0789:
0790: private static native WrapperUser nativeGetUser(boolean groups);
0791:
0792: private static native WrapperUser nativeGetInteractiveUser(
0793: boolean groups);
0794:
0795: private static native WrapperWin32Service[] nativeListServices();
0796:
0797: private static native WrapperWin32Service nativeSendServiceControlCode(
0798: byte[] serviceName, int controlCode);
0799:
0800: /*---------------------------------------------------------------
0801: * Methods
0802: *-------------------------------------------------------------*/
0803: /**
0804: * Returns a tick count calculated from the system clock.
0805: */
0806: private static int getSystemTicks() {
0807: // Calculate a tick count using the current system time. The
0808: // conversion from a long in ms, to an int in TICK_MS increments
0809: // will result in data loss, but the loss of bits and resulting
0810: // overflow is expected and Ok.
0811: return (int) (System.currentTimeMillis() / TICK_MS);
0812: }
0813:
0814: /**
0815: * Returns the number of ticks since the JVM was launched. This
0816: * count is not good enough to be used where accuracy is required but
0817: * it allows us to implement timeouts in environments where the system
0818: * time is modified while the JVM is running.
0819: * <p>
0820: * An int is used rather than a long so the counter can be implemented
0821: * without requiring any synchronization. At the tick resolution, the
0822: * tick counter will overflow and wrap (every 6.8 years for 100ms ticks).
0823: * This behavior is expected. The getTickAge method should be used
0824: * in cases where the difference between two ticks is required.
0825: *
0826: * Returns the tick count.
0827: */
0828: private static int getTicks() {
0829: if (m_useSystemTime) {
0830: return getSystemTicks();
0831: } else {
0832: return m_ticks;
0833: }
0834: }
0835:
0836: /**
0837: * Returns the number of milliseconds that have elapsed between the
0838: * start and end counters. This method assumes that both tick counts
0839: * were obtained by calling getTicks(). This method will correctly
0840: * handle cases where the tick counter has overflowed and reset.
0841: *
0842: * @param start A base tick count.
0843: * @param end An end tick count.
0844: *
0845: * @return The number of milliseconds that are represented by the
0846: * difference between the two specified tick counts.
0847: */
0848: private static long getTickAge(int start, int end) {
0849: // Important to cast the first value so that negative values are correctly
0850: // cast to negative long values.
0851: return (long) (end - start) * TICK_MS;
0852: }
0853:
0854: /**
0855: * Attempts to load the a native library file.
0856: *
0857: * @param name Name of the library to load.
0858: * @param file Name of the actual library file.
0859: *
0860: * @return null if the library was successfully loaded, an error message
0861: * otherwise.
0862: */
0863: private static String loadNativeLibrary(String name, String file) {
0864: try {
0865: System.loadLibrary(name);
0866:
0867: if (m_debug) {
0868: m_out.println("Loaded native library: " + file);
0869: }
0870:
0871: return null;
0872: } catch (UnsatisfiedLinkError e) {
0873: if (m_debug) {
0874: m_out.println("Loading native library failed: " + file
0875: + " Cause: " + e);
0876: }
0877: String error = e.getMessage();
0878: if (error == null) {
0879: error = e.toString();
0880: }
0881: return error;
0882: } catch (Throwable e) {
0883: if (m_debug) {
0884: m_out.println("Loading native library failed: " + file
0885: + " Cause: " + e);
0886: }
0887: String error = e.toString();
0888: return error;
0889: }
0890: }
0891:
0892: /**
0893: * Java 1.5 and above supports the ability to register the WrapperManager
0894: * MBean internally.
0895: */
0896: private static void registerMBean(Object mbean, String name) {
0897: Class classManagementFactory;
0898: try {
0899: classManagementFactory = Class
0900: .forName("java.lang.management.ManagementFactory");
0901: } catch (ClassNotFoundException e) {
0902: if (m_debug) {
0903: m_out
0904: .println("Registering MBeans not supported by current JVM: "
0905: + name);
0906: }
0907: return;
0908: }
0909:
0910: try {
0911: // This code uses reflection so it combiles on older JVMs.
0912: // The original code is as follows:
0913: // javax.management.MBeanServer mbs =
0914: // java.lang.management.ManagementFactory.getPlatformMBeanServer();
0915: // javax.management.ObjectName oName = new javax.management.ObjectName( name );
0916: // mbs.registerMBean( mbean, oName );
0917:
0918: // The version of the above code using reflection follows.
0919: Class classMBeanServer = Class
0920: .forName("javax.management.MBeanServer");
0921: Class classObjectName = Class
0922: .forName("javax.management.ObjectName");
0923: Method methodGetPlatformMBeanServer = classManagementFactory
0924: .getMethod("getPlatformMBeanServer", null);
0925: Constructor constructorObjectName = classObjectName
0926: .getConstructor(new Class[] { String.class });
0927: Method methodRegisterMBean = classMBeanServer.getMethod(
0928: "registerMBean", new Class[] { Object.class,
0929: classObjectName });
0930: Object mbs = methodGetPlatformMBeanServer
0931: .invoke(null, null);
0932: Object oName = constructorObjectName
0933: .newInstance(new Object[] { name });
0934: methodRegisterMBean.invoke(mbs,
0935: new Object[] { mbean, oName });
0936:
0937: if (m_debug) {
0938: m_out
0939: .println("Registered MBean with Platform MBean Server: "
0940: + name);
0941: }
0942: } catch (Throwable t) {
0943: m_err.println("Unable to register the " + name + " MBean.");
0944: t.printStackTrace();
0945: }
0946: }
0947:
0948: /**
0949: * Searches for a file on a path.
0950: *
0951: * @param file File to look for.
0952: * @param path Path to be searched.
0953: *
0954: * @return Reference to thr file object if found, otherwise null.
0955: */
0956: private static File locateFileOnPath(String file, String path) {
0957: // A library path exists but the library was not found on it.
0958: String pathSep = System.getProperty("path.separator");
0959:
0960: // Search for the file on the library path to verify that it does not
0961: // exist, it could be some other problem
0962: StringTokenizer st = new StringTokenizer(path, pathSep);
0963: while (st.hasMoreTokens()) {
0964: File libFile = new File(new File(st.nextToken()), file);
0965: if (libFile.exists()) {
0966: return libFile;
0967: }
0968: }
0969:
0970: return null;
0971: }
0972:
0973: /**
0974: * Generates a detailed native library base name which is made up of the
0975: * base name, the os name, architecture, and the bits of the current JVM,
0976: * not the platform.
0977: *
0978: * @return A detailed native library base name.
0979: */
0980: private static String generateDetailedNativeLibraryBaseName(
0981: String baseName, int jvmBits, boolean universal) {
0982: // Generate an os name. Most names are used as is, but some are modified.
0983: String os = System.getProperty("os.name", "").toLowerCase();
0984: if (os.startsWith("windows")) {
0985: os = "windows";
0986: } else if (os.equals("sunos")) {
0987: os = "solaris";
0988: } else if (os.equals("hp-ux") || os.equals("hp-ux64")) {
0989: os = "hpux";
0990: } else if (os.equals("mac os x")) {
0991: os = "macosx";
0992: } else if (os.equals("unix_sv")) {
0993: os = "unixware";
0994: }
0995:
0996: // Generate an architecture name.
0997: String arch = System.getProperty("os.arch", "").toLowerCase();
0998: if (universal) {
0999: arch = "universal";
1000: } else {
1001: if (arch.equals("amd64") || arch.equals("athlon")
1002: || arch.equals("ia32") || arch.equals("ia64")
1003: || arch.equals("x86_64") || arch.equals("i686")
1004: || arch.equals("i586") || arch.equals("i486")
1005: || arch.equals("i386")) {
1006: arch = "x86";
1007: } else if (arch.startsWith("sparc")) {
1008: arch = "sparc";
1009: } else if (arch.equals("power") || arch.equals("powerpc")
1010: || arch.equals("ppc64")) {
1011: arch = "ppc";
1012: } else if (arch.equals("pa_risc") || arch.equals("pa-risc")) {
1013: arch = "parisc";
1014: }
1015: }
1016:
1017: return baseName + "-" + os + "-" + arch + "-" + jvmBits;
1018: }
1019:
1020: /**
1021: * Searches for and then loads the native library. This method will attempt
1022: * locate the wrapper library using one of the following 3 naming
1023: */
1024: private static void initializeNativeLibrary() {
1025: // Resolve the osname and osarch for the currect system.
1026: String osName = System.getProperty("os.name").toLowerCase();
1027:
1028: // Look for the base name of the library.
1029: String baseName = System.getProperty("wrapper.native_library");
1030: if (baseName == null) {
1031: // This should only happen if an old version of the Wrapper binary is being used.
1032: m_out
1033: .println("WARNING - The wrapper.native_library system property was not");
1034: m_out
1035: .println(" set. Using the default value, 'wrapper'.");
1036: baseName = "wrapper";
1037: }
1038: String[] detailedNames = new String[4];
1039: if (m_jvmBits > 0) {
1040: detailedNames[0] = generateDetailedNativeLibraryBaseName(
1041: baseName, m_jvmBits, false);
1042: if (osName.startsWith("mac")) {
1043: detailedNames[1] = generateDetailedNativeLibraryBaseName(
1044: baseName, m_jvmBits, true);
1045: }
1046: } else {
1047: detailedNames[0] = generateDetailedNativeLibraryBaseName(
1048: baseName, 32, false);
1049: detailedNames[1] = generateDetailedNativeLibraryBaseName(
1050: baseName, 64, false);
1051: if (osName.startsWith("mac")) {
1052: detailedNames[2] = generateDetailedNativeLibraryBaseName(
1053: baseName, 32, true);
1054: detailedNames[3] = generateDetailedNativeLibraryBaseName(
1055: baseName, 64, true);
1056: }
1057: }
1058:
1059: // Construct brief and detailed native library file names.
1060: String file = System.mapLibraryName(baseName);
1061: String[] detailedFiles = new String[detailedNames.length];
1062: for (int i = 0; i < detailedNames.length; i++) {
1063: if (detailedNames[i] != null) {
1064: detailedFiles[i] = System
1065: .mapLibraryName(detailedNames[i]);
1066: }
1067: }
1068:
1069: String[] detailedErrors = new String[detailedNames.length];
1070: String baseError = null;
1071:
1072: // Try loading the native library using the detailed name first. If that fails, use
1073: // the brief name.
1074: if (m_debug) {
1075: m_out
1076: .println("Load native library. One or more attempts may fail if platform "
1077: + "specific libraries do not exist.");
1078: }
1079: m_libraryOK = false;
1080: for (int i = 0; i < detailedNames.length; i++) {
1081: if (detailedNames[i] != null) {
1082: detailedErrors[i] = loadNativeLibrary(detailedNames[i],
1083: detailedFiles[i]);
1084: if (detailedErrors[i] == null) {
1085: m_libraryOK = true;
1086: break;
1087: }
1088: }
1089: }
1090: if ((!m_libraryOK)
1091: && ((baseError = loadNativeLibrary(baseName, file)) == null)) {
1092: m_libraryOK = true;
1093: }
1094: if (m_libraryOK) {
1095: // The library was loaded correctly, so initialize it.
1096: if (m_debug) {
1097: m_out.println("Calling native initialization method.");
1098: }
1099: nativeInit(m_debug);
1100: } else {
1101: // The library could not be loaded, so we want to give the user a useful
1102: // clue as to why not.
1103: String libPath = System.getProperty("java.library.path");
1104: m_out.println();
1105: if (libPath.equals("")) {
1106: // No library path
1107: m_out
1108: .println("WARNING - Unable to load the Wrapper's native library because the");
1109: m_out
1110: .println(" java.library.path was set to ''. Please see the");
1111: m_out
1112: .println(" documentation for the wrapper.java.library.path ");
1113: m_out.println(" configuration property.");
1114: } else {
1115: // Attempt to locate the actual files on the path.
1116: String error = null;
1117: File libFile = null;
1118: for (int i = 0; i < detailedNames.length; i++) {
1119: if (detailedFiles[i] != null) {
1120: libFile = locateFileOnPath(detailedFiles[i],
1121: libPath);
1122: if (libFile != null) {
1123: error = detailedErrors[i];
1124: break;
1125: }
1126: }
1127: }
1128: if (libFile == null) {
1129: libFile = locateFileOnPath(file, libPath);
1130: if (libFile != null) {
1131: error = baseError;
1132: }
1133: }
1134: if (libFile == null) {
1135: // The library could not be located on the library path.
1136: m_out
1137: .println("WARNING - Unable to load the Wrapper's native library because none of the");
1138: m_out.println(" following files:");
1139: for (int i = 0; i < detailedNames.length; i++) {
1140: if (detailedFiles[i] != null) {
1141: m_out.println(" "
1142: + detailedFiles[i]);
1143: }
1144: }
1145: m_out.println(" " + file);
1146: m_out
1147: .println(" could be located on the following java.library.path:");
1148:
1149: String pathSep = System
1150: .getProperty("path.separator");
1151: StringTokenizer st = new StringTokenizer(libPath,
1152: pathSep);
1153: while (st.hasMoreTokens()) {
1154: File pathElement = new File(st.nextToken());
1155: m_out.println(" "
1156: + pathElement.getAbsolutePath());
1157: }
1158: m_out
1159: .println(" Please see the documentation for the "
1160: + "wrapper.java.library.path");
1161: m_out.println(" configuration property.");
1162: } else {
1163: // The library file was found but could not be loaded for some reason.
1164: m_out
1165: .println("WARNING - Unable to load the Wrapper's native library '"
1166: + libFile.getName() + "'.");
1167: m_out
1168: .println(" The file is located on the path at the following location but");
1169: m_out.println(" could not be loaded:");
1170: m_out.println(" "
1171: + libFile.getAbsolutePath());
1172: m_out
1173: .println(" Please verify that the file is readable by the current user");
1174: m_out
1175: .println(" and that the file has not been corrupted in any way.");
1176: m_out
1177: .println(" One common cause of this problem is running a 32-bit version");
1178: m_out
1179: .println(" of the Wrapper with a 64-bit version of Java, or vica versa.");
1180: if (m_jvmBits > 0) {
1181: m_out.println(" This is a "
1182: + m_jvmBits + "-bit JVM.");
1183: } else {
1184: m_out
1185: .println(" The bit depth of this JVM could not be determined.");
1186: }
1187: m_out.println(" Reported cause:");
1188: m_out.println(" " + error);
1189: }
1190: }
1191: m_out
1192: .println(" System signals will not be handled correctly.");
1193: m_out.println();
1194: }
1195: }
1196:
1197: /**
1198: * Compares the version of the wrapper which launched this JVM with that of
1199: * the jar. If they differ then a Warning message will be displayed. The
1200: * Wrapper application will still be allowed to start.
1201: */
1202: private static void verifyWrapperVersion() {
1203: // If we are not being controlled by the wrapper then return.
1204: if (!WrapperManager.isControlledByNativeWrapper()) {
1205: return;
1206: }
1207:
1208: // Lookup the version from the wrapper. It should have been set as a property
1209: // when the JVM was launched.
1210: String wrapperVersion = System.getProperty("wrapper.version");
1211: if (wrapperVersion == null) {
1212: wrapperVersion = "unknown";
1213: }
1214:
1215: if (!WrapperInfo.getVersion().equals(wrapperVersion)) {
1216: m_out
1217: .println("WARNING - The Wrapper jar file currently in use is version \""
1218: + WrapperInfo.getVersion() + "\"");
1219: m_out
1220: .println(" while the version of the Wrapper which launched this JVM is ");
1221: m_out.println(" \"" + wrapperVersion + "\".");
1222: m_out
1223: .println(" The Wrapper may appear to work correctly but some features may");
1224: m_out
1225: .println(" not function correctly. This configuration has not been tested");
1226: m_out.println(" and is not supported.");
1227: m_out.println();
1228: }
1229: }
1230:
1231: /**
1232: * Compares the version of the native library with that of this jar. If
1233: * they differ then a Warning message will be displayed. The Wrapper
1234: * application will still be allowed to start.
1235: */
1236: private static void verifyNativeLibraryVersion() {
1237: // Request the version from the native library. Be careful as the method
1238: // will not exist if the library is old.
1239: String jniVersion;
1240: try {
1241: jniVersion = nativeGetLibraryVersion();
1242: } catch (Throwable e) {
1243: if (m_debug) {
1244: m_out
1245: .println("Call to nativeGetLibraryVersion() failed: "
1246: + e);
1247: }
1248: jniVersion = "unknown";
1249: }
1250:
1251: if (!WrapperInfo.getVersion().equals(jniVersion)) {
1252: m_out
1253: .println("WARNING - The Wrapper jar file currently in use is version \""
1254: + WrapperInfo.getVersion() + "\"");
1255: m_out
1256: .println(" while the version of the native library is \""
1257: + jniVersion + "\".");
1258: m_out
1259: .println(" The Wrapper may appear to work correctly but some features may");
1260: m_out
1261: .println(" not function correctly. This configuration has not been tested");
1262: m_out.println(" and is not supported.");
1263: m_out.println();
1264: }
1265: }
1266:
1267: /**
1268: * Obtain the current version of Wrapper.
1269: *
1270: * @return The version of the Wrapper.
1271: */
1272: public static String getVersion() {
1273: return WrapperInfo.getVersion();
1274: }
1275:
1276: /**
1277: * Obtain the build time of Wrapper.
1278: *
1279: * @return The time that the Wrapper was built.
1280: */
1281: public static String getBuildTime() {
1282: return WrapperInfo.getBuildTime();
1283: }
1284:
1285: /**
1286: * Returns the Id of the current JVM. JVM Ids increment from 1 each time
1287: * the wrapper restarts a new one.
1288: *
1289: * @return The Id of the current JVM.
1290: */
1291: public static int getJVMId() {
1292: return m_jvmId;
1293: }
1294:
1295: /**
1296: * Sets the title of the console in which the Wrapper is running. This
1297: * is currently only supported on Windows platforms.
1298: * <p>
1299: * As an alternative, it is also possible to set the console title from
1300: * within the wrapper.conf file using the wrapper.console.title property.
1301: *
1302: * @param title The new title. The specified string will be encoded
1303: * to a byte array using the default encoding for the
1304: * current platform.
1305: */
1306: public static void setConsoleTitle(String title) {
1307: SecurityManager sm = System.getSecurityManager();
1308: if (sm != null) {
1309: sm
1310: .checkPermission(new WrapperPermission(
1311: "setConsoleTitle"));
1312: }
1313:
1314: if (m_libraryOK) {
1315: // Convert the unicode string to a string of bytes using the default
1316: // platform encoding.
1317: byte[] titleBytes = title.getBytes();
1318:
1319: // We need a null terminated string.
1320: byte[] nullTermBytes = new byte[titleBytes.length + 1];
1321: System.arraycopy(titleBytes, 0, nullTermBytes, 0,
1322: titleBytes.length);
1323: nullTermBytes[titleBytes.length] = 0;
1324:
1325: nativeSetConsoleTitle(nullTermBytes);
1326: }
1327: }
1328:
1329: /**
1330: * Returns a WrapperUser object which describes the user under which the
1331: * Wrapper is currently running. Additional platform specific information
1332: * can be obtained by casting the object to a platform specific subclass.
1333: * WrapperWin32User, for example.
1334: *
1335: * @param groups True if the user's groups should be returned as well.
1336: * Requesting the groups that a user belongs to increases
1337: * the CPU load required to complete the call.
1338: *
1339: * @return An object describing the current user.
1340: */
1341: public static WrapperUser getUser(boolean groups) {
1342: SecurityManager sm = System.getSecurityManager();
1343: if (sm != null) {
1344: sm.checkPermission(new WrapperPermission("getUser"));
1345: }
1346:
1347: WrapperUser user = null;
1348: if (m_libraryOK) {
1349: user = nativeGetUser(groups);
1350: }
1351: return user;
1352: }
1353:
1354: /**
1355: * Returns a WrapperUser object which describes the interactive user whose
1356: * desktop is being interacted with. When a service running on a Windows
1357: * platform has its interactive flag set, this method will return the user
1358: * who is currently logged in. Additional platform specific information
1359: * can be obtained by casting the object to a platform specific subclass.
1360: * WrapperWin32User, for example.
1361: * <p>
1362: * If a user is not currently logged on then this method will return null.
1363: * User code can repeatedly call this method to detect when a user has
1364: * logged in. To detect when a user has logged out, there are two options.
1365: * 1) The user code can continue to call this method until it returns null.
1366: * 2) Or if the WrapperListener method is being implemented, the
1367: * WrapperListener.controlEvent method will receive a WRAPPER_CTRL_LOGOFF_EVENT
1368: * event when the user logs out.
1369: * <p>
1370: * On XP systems, it is possible to switch to another account rather than
1371: * actually logging out. In such a case, the interactive user will be
1372: * the first user that logged in. This will also be the only user with
1373: * which the service will interact. If other users are logged in when the
1374: * interactive user logs out, the service will not automatically switch to
1375: * another logged in user. Rather, the next user to log in will become
1376: * the new user which the service will interact with.
1377: * <p>
1378: * This method will always return NULL on versions of NT prior to Windows
1379: * 2000. This can not be helped as some required functions were not added
1380: * to the windows API until NT version 5.0, also known as Windows 2000.
1381: *
1382: * @param groups True if the user's groups should be returned as well.
1383: * Requesting the groups that a user belongs to increases
1384: * the CPU load required to complete the call.
1385: *
1386: * @return The current interactive user, or null.
1387: */
1388: public static WrapperUser getInteractiveUser(boolean groups) {
1389: SecurityManager sm = System.getSecurityManager();
1390: if (sm != null) {
1391: sm.checkPermission(new WrapperPermission(
1392: "getInteractiveUser"));
1393: }
1394:
1395: WrapperUser user = null;
1396: if (m_libraryOK) {
1397: user = nativeGetInteractiveUser(groups);
1398: }
1399: return user;
1400: }
1401:
1402: /**
1403: * Returns a Properties object containing expanded the contents of the
1404: * configuration file used to launch the Wrapper.
1405: *
1406: * All properties are included so it is possible to define properties
1407: * not used by the Wrapper in the configuration file and have then
1408: * be available in this Properties object.
1409: *
1410: * @return The contents of the Wrapper configuration file.
1411: */
1412: public static Properties getProperties() {
1413: SecurityManager sm = System.getSecurityManager();
1414: if (sm != null) {
1415: sm.checkPermission(new WrapperPermission("getProperties"));
1416: }
1417:
1418: return m_properties;
1419: }
1420:
1421: /**
1422: * Returns the PID of the Wrapper process.
1423: *
1424: * A PID of 0 will be returned if the JVM was launched standalone.
1425: *
1426: * This value can also be obtained using the 'wrapper.pid' system property.
1427: *
1428: * @return The PID of the Wrpper process.
1429: */
1430: public static int getWrapperPID() {
1431: SecurityManager sm = System.getSecurityManager();
1432: if (sm != null) {
1433: sm.checkPermission(new WrapperPermission("getWrapperPID"));
1434: }
1435:
1436: return WrapperSystemPropertyUtil.getIntProperty("wrapper.pid",
1437: 0);
1438: }
1439:
1440: /**
1441: * Returns the PID of the Java process.
1442: *
1443: * A PID of 0 will be returned if the native library has not been initialized.
1444: *
1445: * This value can also be obtained using the 'wrapper.java.pid' system property.
1446: *
1447: * @return The PID of the Java process.
1448: */
1449: public static int getJavaPID() {
1450: SecurityManager sm = System.getSecurityManager();
1451: if (sm != null) {
1452: sm.checkPermission(new WrapperPermission("getJavaPID"));
1453: }
1454:
1455: return WrapperSystemPropertyUtil.getIntProperty(
1456: "wrapper.java.pid", 0);
1457: }
1458:
1459: /**
1460: * Requests that the current JVM process request a thread dump. This is
1461: * the same as pressing CTRL-BREAK (under Windows) or CTRL-\ (under Unix)
1462: * in the the console in which Java is running. This method does nothing
1463: * if the native library is not loaded.
1464: */
1465: public static void requestThreadDump() {
1466: SecurityManager sm = System.getSecurityManager();
1467: if (sm != null) {
1468: sm.checkPermission(new WrapperPermission(
1469: "requestThreadDump"));
1470: }
1471:
1472: if (m_libraryOK) {
1473: nativeRequestThreadDump();
1474: } else {
1475: m_out.println(" wrapper library not loaded.");
1476: }
1477: }
1478:
1479: /**
1480: * (Testing Method) Causes the WrapperManager to go into a state which makes the JVM appear
1481: * to be hung when viewed from the native Wrapper code. Does not have any effect when the
1482: * JVM is not being controlled from the native Wrapper. Useful for testing the Wrapper
1483: * functions.
1484: */
1485: public static void appearHung() {
1486: SecurityManager sm = System.getSecurityManager();
1487: if (sm != null) {
1488: sm
1489: .checkPermission(new WrapperPermission(
1490: "test.appearHung"));
1491: }
1492:
1493: m_out.println("WARNING: Making JVM appear to be hung...");
1494: m_appearHung = true;
1495: }
1496:
1497: /**
1498: * (Testing Method) Cause an access violation within the Java code. Useful
1499: * for testing the Wrapper functions. This currently only crashes Sun
1500: * JVMs and takes advantage of Bug #4369043 which does not exist in newer
1501: * JVMs. Use of the accessViolationNative() method is preferred.
1502: */
1503: public static void accessViolation() {
1504: SecurityManager sm = System.getSecurityManager();
1505: if (sm != null) {
1506: sm.checkPermission(new WrapperPermission(
1507: "test.accessViolation"));
1508: }
1509:
1510: m_out
1511: .println("WARNING: Attempting to cause an access violation...");
1512:
1513: try {
1514: Class c = Class.forName("java.lang.String");
1515: java.lang.reflect.Method m = c
1516: .getDeclaredMethod(null, null);
1517: } catch (NoSuchMethodException ex) {
1518: // Correctly did not find method. access_violation attempt failed. Not Sun JVM?
1519: } catch (Exception ex) {
1520: if (ex instanceof NoSuchFieldException) {
1521: // Can't catch this in a catch because the compiler doesn't think it is being
1522: // thrown. But it is thrown on IBM jvms at least
1523: // Correctly did not find method. access_violation attempt failed. Not Sun JVM?
1524: } else {
1525: // Shouldn't get here.
1526: ex.printStackTrace();
1527: }
1528: }
1529:
1530: m_out
1531: .println(" Attempt to cause access violation failed. JVM is still alive.");
1532: }
1533:
1534: /**
1535: * (Testing Method) Cause an access violation within native JNI code. Useful for testing the
1536: * Wrapper functions. This currently causes the access violation by attempting to write to
1537: * a null pointer.
1538: */
1539: public static void accessViolationNative() {
1540: SecurityManager sm = System.getSecurityManager();
1541: if (sm != null) {
1542: sm.checkPermission(new WrapperPermission(
1543: "test.accessViolationNative"));
1544: }
1545:
1546: m_out
1547: .println("WARNING: Attempting to cause an access violation...");
1548: if (m_libraryOK) {
1549: accessViolationInner();
1550:
1551: m_out
1552: .println(" Attempt to cause access violation failed. "
1553: + "JVM is still alive.");
1554: } else {
1555: m_out.println(" wrapper library not loaded.");
1556: }
1557: }
1558:
1559: /**
1560: * Returns true if the JVM was launched by the Wrapper application. False
1561: * if the JVM was launched manually without the Wrapper controlling it.
1562: *
1563: * @return True if the current JVM was launched by the Wrapper.
1564: */
1565: public static boolean isControlledByNativeWrapper() {
1566: return m_key != null;
1567: }
1568:
1569: /**
1570: * Returns true if the Wrapper was launched as an NT service on Windows or
1571: * as a daemon process on UNIX platforms. False if launched as a console.
1572: * This can be useful if you wish to display a user interface when in
1573: * Console mode. On UNIX platforms, this is not as useful because an
1574: * X display may not be visible even if launched in a console.
1575: *
1576: * @return True if the Wrapper is running as an NT service or daemon
1577: * process.
1578: */
1579: public static boolean isLaunchedAsService() {
1580: return m_service;
1581: }
1582:
1583: /**
1584: * Returns true if the wrapper.debug property, or any of the logging
1585: * channels are set to DEBUG in the wrapper configuration file. Useful
1586: * for deciding whether or not to output certain information to the
1587: * console.
1588: *
1589: * @return True if the Wrapper is logging any Debug level output.
1590: */
1591: public static boolean isDebugEnabled() {
1592: return m_debug;
1593: }
1594:
1595: /**
1596: * Start the Java side of the Wrapper code running. This will make it
1597: * possible for the native side of the Wrapper to detect that the Java
1598: * Wrapper is up and running.
1599: * <p>
1600: * This method must be called on startup and then can only be called once
1601: * so there is no reason for any security permission checks on this call.
1602: *
1603: * @param listener The WrapperListener instance which represents the
1604: * application being started.
1605: * @param args The argument list passed to the JVM when it was launched.
1606: */
1607: public static synchronized void start(
1608: final WrapperListener listener, final String[] args) {
1609: // As was done in the static initializer, we need to execute the following
1610: // code in a privileged action so it is not necessary for the calling code
1611: // to have the same privileges as the wrapper jar.
1612: // This is safe because this method can only be called once and that one call
1613: // will presumably be made on JVM startup.
1614: AccessController.doPrivileged(new PrivilegedAction() {
1615: public Object run() {
1616: privilegedStart(listener, args);
1617: return null;
1618: }
1619: });
1620: }
1621:
1622: /**
1623: * Called by the start method within a PrivilegedAction.
1624: *
1625: * @param WrapperListener The WrapperListener instance which represents
1626: * the application being started.
1627: * @param args The argument list passed to the JVM when it was launched.
1628: */
1629: private static void privilegedStart(WrapperListener listener,
1630: String[] args) {
1631: // Check the SecurityManager here as it is possible that it was set before this call.
1632: checkSecurityManager();
1633:
1634: // Just in case the user failed to provide an argument list, recover by creating one
1635: // here. This will avoid possible problems down stream.
1636: if (args == null) {
1637: args = new String[0];
1638: }
1639:
1640: if (m_debug) {
1641: StringBuffer sb = new StringBuffer();
1642: sb.append("args[");
1643: for (int i = 0; i < args.length; i++) {
1644: if (i > 0) {
1645: sb.append(", ");
1646: }
1647: sb.append("\"");
1648: sb.append(args[i]);
1649: sb.append("\"");
1650: }
1651: sb.append("]");
1652:
1653: m_out.println("WrapperManager.start(" + listener + ", "
1654: + sb.toString() + ") " + "called by thread: "
1655: + Thread.currentThread().getName());
1656: }
1657:
1658: // Make sure that the class has not already been disposed.
1659: if (m_disposed) {
1660: throw new IllegalStateException(
1661: "WrapperManager has already been disposed.");
1662: }
1663:
1664: if (m_listener != null) {
1665: throw new IllegalStateException(
1666: "WrapperManager has already been started with a WrapperListener.");
1667: }
1668: if (listener == null) {
1669: throw new IllegalStateException(
1670: "A WrapperListener must be specified.");
1671: }
1672: m_listener = listener;
1673:
1674: m_args = args;
1675:
1676: startRunner();
1677:
1678: // If this JVM is being controlled by a native wrapper, then we want to
1679: // wait for the command to start. However, if this is a standalone
1680: // JVM, then we want to start now.
1681: if (!isControlledByNativeWrapper()) {
1682: startInner();
1683: }
1684: }
1685:
1686: /**
1687: * Tells the native wrapper that the JVM wants to restart, then informs
1688: * all listeners that the JVM is about to shutdown before killing the JVM.
1689: * <p>
1690: * This method will not return.
1691: *
1692: * @throws SecurityException If a SecurityManager is present and the
1693: * calling thread does not have the
1694: * WrapperPermission("restart") permission.
1695: *
1696: * @see WrapperPermission
1697: */
1698: public static void restart() throws SecurityException {
1699: SecurityManager sm = System.getSecurityManager();
1700: if (sm != null) {
1701: sm.checkPermission(new WrapperPermission("restart"));
1702: }
1703:
1704: if (m_debug) {
1705: m_out.println("WrapperManager.restart() called by thread: "
1706: + Thread.currentThread().getName());
1707: }
1708:
1709: restartInner();
1710: }
1711:
1712: /**
1713: * Tells the native wrapper that the JVM wants to restart, then informs
1714: * all listeners that the JVM is about to shutdown before killing the JVM.
1715: * <p>
1716: * This method requests that the JVM be restarted but then returns. This
1717: * allows components to initiate a JVM exit and then continue, allowing
1718: * a normal shutdown initiated by the JVM via shutdown hooks. In
1719: * applications which are designed to be shutdown when the user presses
1720: * CTRL-C, this may result in a cleaner shutdown.
1721: *
1722: * @throws SecurityException If a SecurityManager is present and the
1723: * calling thread does not have the
1724: * WrapperPermission("restart") permission.
1725: *
1726: * @see WrapperPermission
1727: */
1728: public static void restartAndReturn() throws SecurityException {
1729: SecurityManager sm = System.getSecurityManager();
1730: if (sm != null) {
1731: sm.checkPermission(new WrapperPermission("restart"));
1732: }
1733:
1734: synchronized (WrapperManager.class) {
1735: if (m_stopping) {
1736: if (m_debug) {
1737: m_out
1738: .println("WrapperManager.restartAndReturn() called by thread: "
1739: + Thread.currentThread().getName()
1740: + " already stopping.");
1741: }
1742: return;
1743: } else {
1744: if (m_debug) {
1745: m_out
1746: .println("WrapperManager.restartAndReturn() called by thread: "
1747: + Thread.currentThread().getName());
1748: }
1749: }
1750: }
1751:
1752: // To make this possible, we have to create a new thread to actually do the shutdown.
1753: Thread restarter = new Thread("Wrapper-Restarter") {
1754: public void run() {
1755: restartInner();
1756: }
1757: };
1758: restarter.setDaemon(false);
1759: restarter.start();
1760: }
1761:
1762: /**
1763: * Common code used to restart the JVM. It is assumed that the calling
1764: * thread has has passed security checks before this is called.
1765: */
1766: private static void restartInner() {
1767: boolean stopping;
1768: synchronized (WrapperManager.class) {
1769: stopping = m_stopping;
1770: if (!stopping) {
1771: m_stopping = true;
1772: }
1773: }
1774:
1775: if (!stopping) {
1776: if (!m_commRunnerStarted) {
1777: startRunner();
1778: }
1779:
1780: // Always send the stop command
1781: sendCommand(WRAPPER_MSG_RESTART, "restart");
1782: }
1783:
1784: // Give the Wrapper a chance to register the stop command before stopping.
1785: // This avoids any errors thrown by the Wrapper because the JVM died before
1786: // it was expected to.
1787: try {
1788: Thread.sleep(1000);
1789: } catch (InterruptedException e) {
1790: }
1791:
1792: // This is safe because we are already checking for the privilege to restart the JVM
1793: // above. If we get this far then we want the Wrapper to be able to do everything
1794: // necessary to stop the JVM.
1795: AccessController.doPrivileged(new PrivilegedAction() {
1796: public Object run() {
1797: privilegedStopInner(0);
1798: return null;
1799: }
1800: });
1801: }
1802:
1803: /**
1804: * Tells the native wrapper that the JVM wants to shut down, then informs
1805: * all listeners that the JVM is about to shutdown before killing the JVM.
1806: * <p>
1807: * This method will not return.
1808: *
1809: * @param exitCode The exit code that the Wrapper will return when it exits.
1810: *
1811: * @throws SecurityException If a SecurityManager is present and the
1812: * calling thread does not have the
1813: * WrapperPermission("stop") permission.
1814: *
1815: * @see WrapperPermission
1816: */
1817: public static void stop(final int exitCode) {
1818: SecurityManager sm = System.getSecurityManager();
1819: if (sm != null) {
1820: sm.checkPermission(new WrapperPermission("stop"));
1821: }
1822:
1823: if (m_debug) {
1824: m_out.println("WrapperManager.stop(" + exitCode
1825: + ") called by thread: "
1826: + Thread.currentThread().getName());
1827: }
1828:
1829: stopCommon(exitCode, 1000);
1830:
1831: // This is safe because we are already checking for the privilege to stop the JVM
1832: // above. If we get this far then we want the Wrapper to be able to do everything
1833: // necessary to stop the JVM.
1834: AccessController.doPrivileged(new PrivilegedAction() {
1835: public Object run() {
1836: privilegedStopInner(exitCode);
1837: return null;
1838: }
1839: });
1840: }
1841:
1842: /**
1843: * Tells the native wrapper that the JVM wants to shut down, then informs
1844: * all listeners that the JVM is about to shutdown before killing the JVM.
1845: * <p>
1846: * This method requests that the JVM be shutdown but then returns. This
1847: * allows components to initiate a JVM exit and then continue, allowing
1848: * a normal shutdown initiated by the JVM via shutdown hooks. In
1849: * applications which are designed to be shutdown when the user presses
1850: * CTRL-C, this may result in a cleaner shutdown.
1851: *
1852: * @param exitCode The exit code that the Wrapper will return when it exits.
1853: *
1854: * @throws SecurityException If a SecurityManager is present and the
1855: * calling thread does not have the
1856: * WrapperPermission("stop") permission.
1857: *
1858: * @see WrapperPermission
1859: */
1860: public static void stopAndReturn(final int exitCode) {
1861: SecurityManager sm = System.getSecurityManager();
1862: if (sm != null) {
1863: sm.checkPermission(new WrapperPermission("stop"));
1864: }
1865:
1866: synchronized (WrapperManager.class) {
1867: if (m_stopping) {
1868: if (m_debug) {
1869: m_out.println("WrapperManager.stopAndReturn("
1870: + exitCode + ") called by thread: "
1871: + Thread.currentThread().getName()
1872: + " already stopping.");
1873: }
1874: return;
1875: } else {
1876: if (m_debug) {
1877: m_out.println("WrapperManager.stopAndReturn("
1878: + exitCode + ") called by thread: "
1879: + Thread.currentThread().getName());
1880: }
1881: }
1882: }
1883:
1884: // To make this possible, we have to create a new thread to actually do the shutdown.
1885: Thread stopper = new Thread("Wrapper-Stopper") {
1886: public void run() {
1887: stopCommon(exitCode, 1000);
1888:
1889: // This is safe because we are already checking for the privilege to stop the JVM
1890: // above. If we get this far then we want the Wrapper to be able to do everything
1891: // necessary to stop the JVM.
1892: AccessController.doPrivileged(new PrivilegedAction() {
1893: public Object run() {
1894: privilegedStopInner(exitCode);
1895: return null;
1896: }
1897: });
1898: }
1899: };
1900: stopper.setDaemon(false);
1901: stopper.start();
1902: }
1903:
1904: /**
1905: * Tells the native wrapper that the JVM wants to shut down and then
1906: * promptly halts. Be careful when using this method as an application
1907: * will not be given a chance to shutdown cleanly.
1908: *
1909: * @param exitCode The exit code that the Wrapper will return when it exits.
1910: *
1911: * @throws SecurityException If a SecurityManager is present and the
1912: * calling thread does not have the
1913: * WrapperPermission("stopImmediate") permission.
1914: *
1915: * @see WrapperPermission
1916: */
1917: public static void stopImmediate(final int exitCode) {
1918: SecurityManager sm = System.getSecurityManager();
1919: if (sm != null) {
1920: sm.checkPermission(new WrapperPermission("stopImmediate"));
1921: }
1922:
1923: if (m_debug) {
1924: m_out.println("WrapperManager.stopImmediate(" + exitCode
1925: + ") called by thread: "
1926: + Thread.currentThread().getName());
1927: }
1928:
1929: stopCommon(exitCode, 250);
1930:
1931: signalStopped(exitCode);
1932:
1933: // Execute runtime.halt(0) using reflection so this class will
1934: // compile on 1.2.x versions of Java.
1935: Method haltMethod;
1936: try {
1937: haltMethod = Runtime.class.getMethod("halt",
1938: new Class[] { Integer.TYPE });
1939: } catch (NoSuchMethodException e) {
1940: m_out.println("halt not supported by current JVM.");
1941: haltMethod = null;
1942: }
1943:
1944: if (haltMethod != null) {
1945: Runtime runtime = Runtime.getRuntime();
1946: try {
1947: haltMethod.invoke(runtime, new Object[] { new Integer(
1948: exitCode) });
1949: } catch (IllegalAccessException e) {
1950: m_out.println("Unable to call runtime.halt: " + e);
1951: } catch (InvocationTargetException e) {
1952: Throwable t = e.getTargetException();
1953: if (t == null) {
1954: t = e;
1955: }
1956:
1957: m_out.println("Unable to call runtime.halt: " + t);
1958: }
1959: } else {
1960: // Shutdown normally
1961:
1962: // This is safe because we are already checking for the privilege to stop the JVM
1963: // above. If we get this far then we want the Wrapper to be able to do everything
1964: // necessary to stop the JVM.
1965: AccessController.doPrivileged(new PrivilegedAction() {
1966: public Object run() {
1967: privilegedStopInner(exitCode);
1968: return null;
1969: }
1970: });
1971: }
1972: }
1973:
1974: /**
1975: * Signal the native wrapper that the startup is progressing but that more
1976: * time is needed. The Wrapper will extend the startup timeout by the
1977: * specified time.
1978: *
1979: * @param waitHint Additional time in milliseconds.
1980: *
1981: * @throws SecurityException If a SecurityManager is present and the
1982: * calling thread does not have the
1983: * WrapperPermission("signalStarting") permission.
1984: *
1985: * @see WrapperPermission
1986: */
1987: public static void signalStarting(int waitHint) {
1988: SecurityManager sm = System.getSecurityManager();
1989: if (sm != null) {
1990: sm.checkPermission(new WrapperPermission("signalStarting"));
1991: }
1992:
1993: sendCommand(WRAPPER_MSG_START_PENDING, Integer
1994: .toString(waitHint));
1995: }
1996:
1997: /**
1998: * Signal the native wrapper that the shutdown is progressing but that more
1999: * time is needed. The Wrapper will extend the stop timeout by the
2000: * specified time.
2001: *
2002: * @param waitHint Additional time in milliseconds.
2003: *
2004: * @throws SecurityException If a SecurityManager is present and the
2005: * calling thread does not have the
2006: * WrapperPermission("signalStopping") permission.
2007: *
2008: * @see WrapperPermission
2009: */
2010: public static void signalStopping(int waitHint) {
2011: SecurityManager sm = System.getSecurityManager();
2012: if (sm != null) {
2013: sm.checkPermission(new WrapperPermission("signalStopping"));
2014: }
2015:
2016: m_stopping = true;
2017: sendCommand(WRAPPER_MSG_STOP_PENDING, Integer
2018: .toString(waitHint));
2019: }
2020:
2021: /**
2022: * This method should not normally be called by user code as it is called
2023: * from within the stop and restart methods. However certain applications
2024: * which stop the JVM may need to call this method to let the wrapper code
2025: * know that the shutdown was intentional.
2026: *
2027: * @throws SecurityException If a SecurityManager is present and the
2028: * calling thread does not have the
2029: * WrapperPermission("signalStopped") permission.
2030: *
2031: * @see WrapperPermission
2032: */
2033: public static void signalStopped(int exitCode) {
2034: SecurityManager sm = System.getSecurityManager();
2035: if (sm != null) {
2036: sm.checkPermission(new WrapperPermission("signalStopped"));
2037: }
2038:
2039: m_stopping = true;
2040: sendCommand(WRAPPER_MSG_STOPPED, Integer.toString(exitCode));
2041:
2042: // Give the socket time to actuall send the packet to the Wrapper
2043: // as this call is often immediately followed by a halt command.
2044: try {
2045: Thread.sleep(250);
2046: } catch (InterruptedException e) {
2047: // Ignore.
2048: }
2049: }
2050:
2051: /**
2052: * Returns true if the ShutdownHook for the JVM has already been triggered.
2053: * Some code needs to know whether or not the system is shutting down.
2054: *
2055: * @return True if the ShutdownHook for the JVM has already been triggered.
2056: */
2057: public static boolean hasShutdownHookBeenTriggered() {
2058: return m_hookTriggered;
2059: }
2060:
2061: /**
2062: * Requests that the Wrapper log a message at the specified log level.
2063: * If the JVM is not being managed by the Wrapper then calls to this
2064: * method will be ignored. This method has been optimized to ignore
2065: * messages at a log level which will not be logged given the current
2066: * log levels of the Wrapper.
2067: * <p>
2068: * Log messages will currently by trimmed by the Wrapper at 4k (4096 bytes).
2069: * <p>
2070: * Because of differences in the way console output is collected and
2071: * messages logged via this method, it is expected that interspersed
2072: * console and log messages will not be in the correct order in the
2073: * resulting log file.
2074: * <p>
2075: * This method was added to allow simple logging to the wrapper.log
2076: * file. This is not meant to be a full featured log file and should
2077: * not be used as such. Please look into a logging package for most
2078: * application logging.
2079: *
2080: * @param logLevel The level to log the message at can be one of
2081: * WRAPPER_LOG_LEVEL_DEBUG, WRAPPER_LOG_LEVEL_INFO,
2082: * WRAPPER_LOG_LEVEL_STATUS, WRAPPER_LOG_LEVEL_WARN,
2083: * WRAPPER_LOG_LEVEL_ERROR, or WRAPPER_LOG_LEVEL_FATAL.
2084: * @param message The message to be logged.
2085: *
2086: * @throws SecurityException If a SecurityManager is present and the
2087: * calling thread does not have the
2088: * WrapperPermission("log") permission.
2089: *
2090: * @see WrapperPermission
2091: */
2092: public static void log(int logLevel, String message) {
2093: SecurityManager sm = System.getSecurityManager();
2094: if (sm != null) {
2095: sm.checkPermission(new WrapperPermission("log"));
2096: }
2097:
2098: // Make sure that the logLevel is valid to avoid problems with the
2099: // command sent to the server.
2100:
2101: if ((logLevel < WRAPPER_LOG_LEVEL_DEBUG)
2102: || (logLevel > WRAPPER_LOG_LEVEL_ADVICE)) {
2103: throw new IllegalArgumentException(
2104: "The specified logLevel is not valid.");
2105: }
2106: if (message == null) {
2107: throw new IllegalArgumentException(
2108: "The message parameter can not be null.");
2109: }
2110:
2111: if (m_lowLogLevel <= logLevel) {
2112: sendCommand((byte) (WRAPPER_MSG_LOG + logLevel), message);
2113: }
2114: }
2115:
2116: /**
2117: * Returns an array of all registered services. This method is only
2118: * supported on Windows platforms which support services. Calling this
2119: * method on other platforms will result in null being returned.
2120: *
2121: * @return An array of services.
2122: *
2123: * @throws SecurityException If a SecurityManager has not been set in the
2124: * JVM or if the calling code has not been
2125: * granted the WrapperPermission "listServices"
2126: * permission. A SecurityManager is required
2127: * for this operation because this method makes
2128: * it possible to learn a great deal about the
2129: * state of the system.
2130: */
2131: public static WrapperWin32Service[] listServices()
2132: throws SecurityException {
2133: SecurityManager sm = System.getSecurityManager();
2134: if (sm == null) {
2135: throw new SecurityException(
2136: "A SecurityManager has not yet been set.");
2137: } else {
2138: sm.checkPermission(new WrapperPermission("listServices"));
2139: }
2140:
2141: if (m_libraryOK) {
2142: return nativeListServices();
2143: } else {
2144: return null;
2145: }
2146: }
2147:
2148: /**
2149: * Sends a service control code to the specified service. The state of the
2150: * service should be tested on return. If the service was not currently
2151: * running then the control code will not be sent.
2152: * <p>
2153: * The control code sent can be one of the system control codes:
2154: * WrapperManager.SERVICE_CONTROL_CODE_START,
2155: * WrapperManager.SERVICE_CONTROL_CODE_STOP,
2156: * WrapperManager.SERVICE_CONTROL_CODE_PAUSE,
2157: * WrapperManager.SERVICE_CONTROL_CODE_CONTINUE, or
2158: * WrapperManager.SERVICE_CONTROL_CODE_INTERROGATE. In addition, user
2159: * defined codes in the range 128-255 can also be sent.
2160: *
2161: * @param serviceName Name of the Windows service which will receive the
2162: * control code.
2163: * @param controlCode The actual control code to be sent. User defined
2164: * control codes should be in the range 128-255.
2165: *
2166: * @return A WrapperWin32Service containing the last known status of the
2167: * service after sending the control code. This will be null if
2168: * the currently platform is not a version of Windows which
2169: * supports services.
2170: *
2171: * @throws WrapperServiceException If there are any problems accessing the
2172: * specified service.
2173: * @throws SecurityException If a SecurityManager has not been set in the
2174: * JVM or if the calling code has not been
2175: * granted the WrapperServicePermission
2176: * permission for the specified service and
2177: * control code. A SecurityManager is required
2178: * for this operation because this method makes
2179: * it possible to control any service on the
2180: * system, which is of course rather dangerous.
2181: */
2182: public static WrapperWin32Service sendServiceControlCode(
2183: String serviceName, int controlCode)
2184: throws WrapperServiceException, SecurityException {
2185: SecurityManager sm = System.getSecurityManager();
2186: if (sm == null) {
2187: throw new SecurityException(
2188: "A SecurityManager has not yet been set.");
2189: } else {
2190: String action;
2191: switch (controlCode) {
2192: case SERVICE_CONTROL_CODE_START:
2193: action = WrapperServicePermission.ACTION_START;
2194: break;
2195:
2196: case SERVICE_CONTROL_CODE_STOP:
2197: action = WrapperServicePermission.ACTION_STOP;
2198: break;
2199:
2200: case SERVICE_CONTROL_CODE_PAUSE:
2201: action = WrapperServicePermission.ACTION_PAUSE;
2202: break;
2203:
2204: case SERVICE_CONTROL_CODE_CONTINUE:
2205: action = WrapperServicePermission.ACTION_CONTINUE;
2206: break;
2207:
2208: case SERVICE_CONTROL_CODE_INTERROGATE:
2209: action = WrapperServicePermission.ACTION_INTERROGATE;
2210: break;
2211:
2212: default:
2213: action = WrapperServicePermission.ACTION_USER_CODE;
2214: break;
2215: }
2216:
2217: sm.checkPermission(new WrapperServicePermission(
2218: serviceName, action));
2219: }
2220:
2221: WrapperWin32Service service = null;
2222: if (m_libraryOK) {
2223: service = nativeSendServiceControlCode(serviceName
2224: .getBytes(), controlCode);
2225: }
2226: return service;
2227: }
2228:
2229: /**
2230: * Adds a WrapperEventListener which will receive WrapperEvents. The
2231: * specific events can be controlled using the mask parameter. This API
2232: * was chosen to allow for additional events in the future.
2233: *
2234: * To avoid future compatibility problems, WrapperEventListeners should
2235: * always test the class of an event before making use of it. This will
2236: * avoid problems caused by new event classes added in future versions
2237: * of the Wrapper.
2238: *
2239: * This method should only be called once for a given WrapperEventListener.
2240: * Build up a single mask to receive events of multiple types.
2241: *
2242: * @param listener WrapperEventListener to be start receiving events.
2243: * @param mask A mask specifying the event types that the listener is
2244: * interrested in receiving. See the WrapperEventListener
2245: * class for a full list of flags. A mask is created by
2246: * combining multiple flags using the binary '|' OR operator.
2247: */
2248: public static void addWrapperEventListener(
2249: WrapperEventListener listener, long mask) {
2250: SecurityManager sm = System.getSecurityManager();
2251: if (sm != null) {
2252: StringBuffer sb = new StringBuffer();
2253: boolean first = true;
2254: if ((mask & WrapperEventListener.EVENT_FLAG_SERVICE) != 0) {
2255: first = false;
2256: sb.append(WrapperEventPermission.EVENT_TYPE_SERVICE);
2257: }
2258: if ((mask & WrapperEventListener.EVENT_FLAG_CONTROL) != 0) {
2259: if (first) {
2260: first = false;
2261: } else {
2262: sb.append(",");
2263: }
2264: sb.append(WrapperEventPermission.EVENT_TYPE_CONTROL);
2265: }
2266: if ((mask & WrapperEventListener.EVENT_FLAG_CORE) != 0) {
2267: if (first) {
2268: first = false;
2269: } else {
2270: sb.append(",");
2271: }
2272: sb.append(WrapperEventPermission.EVENT_TYPE_CORE);
2273: }
2274: sm
2275: .checkPermission(new WrapperEventPermission(sb
2276: .toString()));
2277: }
2278:
2279: synchronized (WrapperManager.class) {
2280: WrapperEventListenerMask listenerMask = new WrapperEventListenerMask();
2281: listenerMask.m_listener = listener;
2282: listenerMask.m_mask = mask;
2283:
2284: m_wrapperEventListenerMaskList.add(listenerMask);
2285: m_wrapperEventListenerMasks = null;
2286: }
2287:
2288: updateWrapperEventListenerFlags();
2289: }
2290:
2291: /**
2292: * Removes a WrapperEventListener so it will not longer receive WrapperEvents.
2293: *
2294: * @param listener WrapperEventListener to be stop receiving events.
2295: */
2296: public static void removeWrapperEventListener(
2297: WrapperEventListener listener) {
2298: SecurityManager sm = System.getSecurityManager();
2299: if (sm != null) {
2300: sm.checkPermission(new WrapperPermission(
2301: "removeWrapperEventListener"));
2302: }
2303:
2304: synchronized (WrapperManager.class) {
2305: // Look for the first instance of a given listener in the list.
2306: for (Iterator iter = m_wrapperEventListenerMaskList
2307: .iterator(); iter.hasNext();) {
2308: WrapperEventListenerMask listenerMask = (WrapperEventListenerMask) iter
2309: .next();
2310: if (listenerMask.m_listener == listener) {
2311: iter.remove();
2312: m_wrapperEventListenerMasks = null;
2313: break;
2314: }
2315: }
2316: }
2317:
2318: updateWrapperEventListenerFlags();
2319: }
2320:
2321: /*---------------------------------------------------------------
2322: * Constructors
2323: *-------------------------------------------------------------*/
2324: /**
2325: * This class can not be instantiated.
2326: */
2327: private WrapperManager() {
2328: }
2329:
2330: /*---------------------------------------------------------------
2331: * Private methods
2332: *-------------------------------------------------------------*/
2333: /**
2334: * Checks for the existence of a SecurityManager and then makes sure that
2335: * the Wrapper jar has been granted AllPermissions. If not then a warning
2336: * will be displayed as this will most likely result in the Wrapper
2337: * failing to function correctly.
2338: *
2339: * This method is called at various points in the startup as it is possible
2340: * and in fact likely that any SecurityManager will be set by user code
2341: * during or shortly after initialization. Once a SecurityManager has
2342: * been located and tested then this method will become a noop.
2343: */
2344: private static void checkSecurityManager() {
2345: if (m_securityManagerChecked) {
2346: return;
2347: }
2348:
2349: SecurityManager securityManager = System.getSecurityManager();
2350: if (securityManager != null) {
2351: if (m_debug) {
2352: m_out.println("Detected a SecurityManager: "
2353: + securityManager.getClass().getName());
2354: }
2355:
2356: try {
2357: securityManager
2358: .checkPermission(new java.security.AllPermission());
2359: } catch (SecurityException e) {
2360: m_out.println();
2361: m_out
2362: .println("WARNING - Detected that a SecurityManager has been installed but the ");
2363: m_out
2364: .println(" wrapper.jar has not been granted the java.security.AllPermission");
2365: m_out
2366: .println(" permission. This will most likely result in SecurityExceptions");
2367: m_out.println(" being thrown by the Wrapper.");
2368: m_out.println();
2369: }
2370:
2371: // Always set the flag.
2372: m_securityManagerChecked = true;
2373: }
2374: }
2375:
2376: /**
2377: * Returns an array of WrapperEventListenerMask instances which can
2378: * be safely used outside of synchronization.
2379: *
2380: * @return An array of WrapperEventListenerMask instances.
2381: */
2382: private static WrapperEventListenerMask[] getWrapperEventListenerMasks() {
2383: WrapperEventListenerMask[] listenerMasks = m_wrapperEventListenerMasks;
2384: if (listenerMasks == null) {
2385: synchronized (WrapperManager.class) {
2386: if (listenerMasks == null) {
2387: listenerMasks = new WrapperEventListenerMask[m_wrapperEventListenerMaskList
2388: .size()];
2389: m_wrapperEventListenerMaskList
2390: .toArray(listenerMasks);
2391: m_wrapperEventListenerMasks = listenerMasks;
2392: }
2393: }
2394: }
2395:
2396: return listenerMasks;
2397: }
2398:
2399: /**
2400: * Updates the internal flags based on the WrapperEventListeners currently
2401: * registered.
2402: */
2403: private static void updateWrapperEventListenerFlags() {
2404: boolean core = false;
2405:
2406: WrapperEventListenerMask[] listenerMasks = getWrapperEventListenerMasks();
2407: for (int i = 0; i < listenerMasks.length; i++) {
2408: long mask = listenerMasks[i].m_mask;
2409:
2410: // See whether particular event types are required.
2411: core = core
2412: | ((mask & WrapperEventListener.EVENT_FLAG_CORE) != 0);
2413: }
2414:
2415: m_produceCoreEvents = core;
2416: }
2417:
2418: /**
2419: * Notifies registered listeners that an event has been fired.
2420: *
2421: * @param event Event to notify the listeners of.
2422: */
2423: private static void fireWrapperEvent(WrapperEvent event) {
2424: long eventMask = event.getFlags();
2425:
2426: WrapperEventListenerMask[] listenerMasks = getWrapperEventListenerMasks();
2427: for (int i = 0; i < listenerMasks.length; i++) {
2428: long listenerMask = listenerMasks[i].m_mask;
2429:
2430: // See if the event should be passed to this listner.
2431: if ((listenerMask & eventMask) != 0) {
2432: // The listener wants the event.
2433: WrapperEventListener listener = listenerMasks[i].m_listener;
2434: try {
2435: listener.fired(event);
2436: } catch (Throwable t) {
2437: m_out
2438: .println("Encountered an uncaught exception while notifying "
2439: + "WrapperEventListener of an event:");
2440: t.printStackTrace(m_out);
2441: }
2442: }
2443: }
2444: }
2445:
2446: /**
2447: * Executed code common to the stop and stopImmediate methods.
2448: */
2449: private static void stopCommon(int exitCode, int delay) {
2450: boolean stopping;
2451: synchronized (WrapperManager.class) {
2452: stopping = m_stopping;
2453: if (!stopping) {
2454: m_stopping = true;
2455: }
2456: }
2457:
2458: if (!stopping) {
2459: if (!m_commRunnerStarted) {
2460: startRunner();
2461: }
2462:
2463: // Always send the stop command
2464: sendCommand(WRAPPER_MSG_STOP, Integer.toString(exitCode));
2465:
2466: // Give the Wrapper a chance to register the stop command before stopping.
2467: // This avoids any errors thrown by the Wrapper because the JVM died before
2468: // it was expected to.
2469: try {
2470: Thread.sleep(delay);
2471: } catch (InterruptedException e) {
2472: }
2473: }
2474: }
2475:
2476: /**
2477: * Dispose of all resources used by the WrapperManager. Closes the server
2478: * socket which is used to listen for events from the
2479: */
2480: private static void dispose() {
2481: synchronized (WrapperManager.class) {
2482: m_disposed = true;
2483:
2484: // Close the open socket if it exists.
2485: closeSocket();
2486:
2487: // Give the Connection Thread a chance to stop itself.
2488: try {
2489: Thread.sleep(500);
2490: } catch (InterruptedException e) {
2491: }
2492: }
2493: }
2494:
2495: /**
2496: * Informs the listener that it should start.
2497: */
2498: private static void startInner() {
2499: // Set the thread priority back to normal so that any spawned threads
2500: // will use the normal priority
2501: int oldPriority = Thread.currentThread().getPriority();
2502: Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
2503:
2504: // This method can be called from the connection thread which must be a
2505: // daemon thread by design. We need to call the WrapperListener.start method
2506: // from a non-daemon thread. This means that if the current thread is a
2507: // daemon we need to launch a new thread while we wait for the start method
2508: // to return.
2509: if (m_listener == null) {
2510: if (m_debug) {
2511: m_out
2512: .println("No WrapperListener has been set. Nothing to start.");
2513: }
2514: } else {
2515: if (m_debug) {
2516: m_out.println("calling WrapperListener.start()");
2517: }
2518:
2519: // These arrays aren't pretty, but we need final variables for the inline
2520: // class and this makes it possible to get the values back.
2521: final Integer[] resultF = new Integer[1];
2522: final Throwable[] tF = new Throwable[1];
2523:
2524: if (Thread.currentThread().isDaemon()) {
2525: // Start in a dedicated thread.
2526: Thread startRunner = new Thread(
2527: "WrapperListener_start_runner") {
2528: public void run() {
2529: if (m_debug) {
2530: m_out
2531: .println("WrapperListener.start runner thread started.");
2532: }
2533:
2534: try {
2535: // This is user code, so don't trust it.
2536: try {
2537: resultF[0] = m_listener.start(m_args);
2538: } catch (Throwable t) {
2539: tF[0] = t;
2540: }
2541: } finally {
2542: if (m_debug) {
2543: m_out
2544: .println("WrapperListener.start runner thread stopped.");
2545: }
2546: }
2547: }
2548: };
2549: startRunner.setDaemon(false);
2550: startRunner.start();
2551:
2552: // Wait for the start runner to complete.
2553: if (m_debug) {
2554: m_out
2555: .println("Waiting for WrapperListener.start runner thread to complete.");
2556: }
2557: while ((startRunner != null) && (startRunner.isAlive())) {
2558: try {
2559: startRunner.join();
2560: startRunner = null;
2561: } catch (InterruptedException e) {
2562: // Ignore and keep waiting.
2563: }
2564: }
2565: } else {
2566: // Start in line.
2567: // This is user code, so don't trust it.
2568: try {
2569: resultF[0] = m_listener.start(m_args);
2570: } catch (Throwable t) {
2571: tF[0] = t;
2572: }
2573: }
2574:
2575: // Now that we are back in the main thread, handle the results.
2576: if (tF[0] != null) {
2577: m_out
2578: .println("Error in WrapperListener.start callback. "
2579: + tF[0]);
2580: tF[0].printStackTrace();
2581: // Kill the JVM, but don't tell the wrapper that we want to stop.
2582: // This may be a problem with this instantiation only.
2583: privilegedStopInner(1);
2584: // Won't make it here.
2585: return;
2586: }
2587:
2588: if (m_debug) {
2589: m_out.println("returned from WrapperListener.start()");
2590: }
2591: if (resultF[0] != null) {
2592: int exitCode = resultF[0].intValue();
2593: if (m_debug) {
2594: m_out
2595: .println("WrapperListener.start() returned an exit code of "
2596: + exitCode + ".");
2597: }
2598:
2599: // Signal the native code.
2600: stop(exitCode);
2601: // Won't make it here.
2602: return;
2603: }
2604: }
2605: m_startedTicks = getTicks();
2606:
2607: // Let the startup thread die since the application has been started.
2608: m_startupRunner = null;
2609:
2610: // Check the SecurityManager here as it is possible that it was set in the
2611: // listener's start method.
2612: checkSecurityManager();
2613:
2614: // Crank the priority back up.
2615: Thread.currentThread().setPriority(oldPriority);
2616:
2617: // Signal that the application has started.
2618: signalStarted();
2619: }
2620:
2621: private static void shutdownJVM(int exitCode) {
2622: if (m_debug) {
2623: m_out.println("shutdownJVM(" + exitCode + ") Thread:"
2624: + Thread.currentThread().getName());
2625: }
2626:
2627: // Do not call System.exit if this is the ShutdownHook
2628: if (Thread.currentThread() == m_hook) {
2629: // Signal that the application has stopped and the JVM is about to shutdown.
2630: signalStopped(exitCode);
2631:
2632: // Dispose the wrapper. (If the hook runs, it will do this.)
2633: dispose();
2634:
2635: // This is the shutdown hook, so fall through because things are
2636: // already shutting down.
2637:
2638: m_shutdownJVMComplete = true;
2639: } else {
2640: // We do not want the ShutdownHook to execute, so unregister it before calling exit.
2641: // It can't be unregistered if it has already fired however. The only way that this
2642: // could happen is if user code calls System.exit from within the listener stop
2643: // method.
2644: if ((!m_hookTriggered) && (m_hook != null)) {
2645: // Remove the shutdown hook using reflection.
2646: try {
2647: m_removeShutdownHookMethod.invoke(Runtime
2648: .getRuntime(), new Object[] { m_hook });
2649: } catch (IllegalAccessException e) {
2650: m_out
2651: .println("Wrapper Manager: Unable to unregister shutdown hook: "
2652: + e);
2653: } catch (InvocationTargetException e) {
2654: Throwable t = e.getTargetException();
2655: if (t == null) {
2656: t = e;
2657: }
2658:
2659: m_out
2660: .println("Wrapper Manager: Unable to unregister shutdown hook: "
2661: + t);
2662: }
2663: }
2664: // Signal that the application has stopped and the JVM is about to shutdown.
2665: signalStopped(exitCode);
2666:
2667: // Dispose the wrapper. (If the hook runs, it will do this.)
2668: dispose();
2669:
2670: if (m_debug) {
2671: m_out.println("calling System.exit(" + exitCode + ")");
2672: }
2673: m_shutdownJVMComplete = true;
2674: safeSystemExit(exitCode);
2675: }
2676: }
2677:
2678: /**
2679: * A user ran into a JVM bug where a call to System exit was causing an
2680: * IllegalThreadStateException to be thrown. Not sure how widespread
2681: * this problem is. But it is easy to avoid it causing serious problems
2682: * for the wrapper.
2683: */
2684: private static void safeSystemExit(int exitCode) {
2685: try {
2686: System.exit(exitCode);
2687: } catch (IllegalThreadStateException e) {
2688: m_out.println("Wrapper Manager: Attempted System.exit("
2689: + exitCode + ") call failed: " + e.toString());
2690: m_out.println(" Trying Runtime.halt("
2691: + exitCode + ")");
2692: Runtime.getRuntime().halt(exitCode);
2693: }
2694: }
2695:
2696: /**
2697: * Informs the listener that the JVM will be shut down.
2698: *
2699: * This should only be called from within a PrivilegedAction or in a
2700: * context that came from a PrivilegedAction.
2701: */
2702: private static void privilegedStopInner(int exitCode) {
2703: boolean block;
2704: synchronized (WrapperManager.class) {
2705: // Always set the stopping flag.
2706: m_stopping = true;
2707:
2708: // Only one thread can be allowed to continue.
2709: if (m_stoppingThread == null) {
2710: m_stoppingThread = Thread.currentThread();
2711: block = false;
2712: } else {
2713: if (Thread.currentThread() == m_stoppingThread) {
2714: throw new IllegalStateException(
2715: "WrapperManager.stop() can not be called recursively.");
2716: }
2717:
2718: block = true;
2719: }
2720: }
2721:
2722: if (block) {
2723: if (m_debug) {
2724: m_out.println("Thread, "
2725: + Thread.currentThread().getName()
2726: + ", waiting for the JVM to exit.");
2727:
2728: if (Thread.currentThread() == m_hook) {
2729: m_out
2730: .println("System.exit appears to have been called from within the");
2731: m_out
2732: .println(" WrapperListener.stop() method. If possible the application");
2733: m_out
2734: .println(" should be modified to avoid this behavior.");
2735: m_out
2736: .println(" To avoid a deadlock, this thread will only wait 5 seconds");
2737: m_out
2738: .println(" for the application to shutdown. This may result in the");
2739: m_out
2740: .println(" application failing to shutdown completely before the JVM");
2741: m_out
2742: .println(" exists. Removing the offending System.exit call will");
2743: m_out.println(" resolve this.");
2744: }
2745: }
2746:
2747: // This thread needs to be put into an infinite loop until the JVM exits.
2748: // This thread can not be allowed to return to the caller, but another
2749: // thread is already responsible for shutting down the JVM, so this
2750: // one can do nothing but wait.
2751: int loops = 0;
2752: int wait = 50;
2753: while (true) {
2754: try {
2755: Thread.sleep(wait);
2756: } catch (InterruptedException e) {
2757: }
2758:
2759: // If this is the wrapper's shutdown hook then we only want to loop until
2760: // the shutdownJVM method has completed. We will only get into this state
2761: // if user code calls System.exit from within the WrapperListener.stop
2762: // method. Failing to return here will cause the shutdown process to hang.
2763: // If the user code calls System.exit directly in the stop method then the
2764: // m_shutdownJVMComplete flag will never be set. Always time out after
2765: // 5 seconds so the JVM will not hang in such cases.
2766: if (Thread.currentThread() == m_hook) {
2767: if (m_shutdownJVMComplete || (loops > 5000 / wait)) {
2768: if (!m_shutdownJVMComplete) {
2769: if (m_debug) {
2770: m_out
2771: .println("Thread, "
2772: + Thread
2773: .currentThread()
2774: .getName()
2775: + ", continuing after 5 seconds.");
2776: }
2777: }
2778:
2779: // To keep the wrapper from showing a JVM exited unexpectedly message
2780: // on shutdown, tell the wrapper that we are ready to stop.
2781: // If the WrapperListener.stop method is taking a long time, we will
2782: // also get here. In that case, the Wrapper will still wait for
2783: // the configured exit timeout before killing the JVM process.
2784: // In theory, the shutdown process of an application will only call
2785: // System.exit after the shutdown is complete so this should be Ok.
2786: // Use the exit code from the thread which initiated the call rather
2787: // than this call as that one is the one we really want.
2788: signalStopped(m_exitCode);
2789:
2790: return;
2791: }
2792: }
2793:
2794: loops++;
2795: }
2796: }
2797:
2798: if (m_debug) {
2799: m_out.println("Thread, " + Thread.currentThread().getName()
2800: + ", handling the shutdown process.");
2801: }
2802: m_exitCode = exitCode;
2803:
2804: // Only stop the listener if the app has been started.
2805: int code = exitCode;
2806: if (m_started) {
2807: // Set the thread priority back to normal so that any spawned threads
2808: // will use the normal priority
2809: int oldPriority = Thread.currentThread().getPriority();
2810: Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
2811:
2812: // This method can be called from the connection thread which must be a
2813: // daemon thread by design. We need to call the WrapperListener.stop method
2814: // from a non-daemon thread. This means that if the current thread is a
2815: // daemon we need to launch a new thread while we wait for the stop method
2816: // to return.
2817: if (m_listener == null) {
2818: if (m_debug) {
2819: m_out
2820: .println("No WrapperListener has been set. Nothing to stop.");
2821: }
2822: } else {
2823: if (m_debug) {
2824: m_out.println("calling listener.stop()");
2825: }
2826:
2827: if (Thread.currentThread().isDaemon()) {
2828: // This array isn't pretty, but we need final variables for the inline
2829: // class and this makes it possible to get the values back.
2830: final Integer[] codeF = new Integer[] { new Integer(
2831: code) };
2832:
2833: // Start in a dedicated thread.
2834: Thread stopRunner = new Thread(
2835: "WrapperListener_stop_runner") {
2836: public void run() {
2837: if (m_debug) {
2838: m_out
2839: .println("WrapperListener.stop runner thread started.");
2840: }
2841:
2842: try {
2843: // This is user code, so don't trust it.
2844: try {
2845: codeF[0] = new Integer(m_listener
2846: .stop(codeF[0].intValue()));
2847: } catch (Throwable t) {
2848: m_out
2849: .println("Error in WrapperListener.stop callback. "
2850: + t);
2851: t.printStackTrace();
2852: }
2853: } finally {
2854: if (m_debug) {
2855: m_out
2856: .println("WrapperListener.stop runner thread stopped.");
2857: }
2858: }
2859: }
2860: };
2861: stopRunner.setDaemon(false);
2862: stopRunner.start();
2863:
2864: // Wait for the start runner to complete.
2865: if (m_debug) {
2866: m_out
2867: .println("Waiting for WrapperListener.stop runner thread to complete.");
2868: }
2869: while ((stopRunner != null)
2870: && (stopRunner.isAlive())) {
2871: try {
2872: stopRunner.join();
2873: stopRunner = null;
2874: } catch (InterruptedException e) {
2875: // Ignore and keep waiting.
2876: }
2877: }
2878:
2879: // Get the exit code back from the array.
2880: code = codeF[0].intValue();
2881: } else {
2882: // This is user code, so don't trust it.
2883: try {
2884: code = m_listener.stop(code);
2885: } catch (Throwable t) {
2886: m_out
2887: .println("Error in WrapperListener.stop callback. "
2888: + t);
2889: t.printStackTrace();
2890: }
2891: }
2892: if (m_debug) {
2893: m_out.println("returned from listener.stop() -> "
2894: + code);
2895: }
2896: }
2897:
2898: // Crank the priority back up.
2899: Thread.currentThread().setPriority(oldPriority);
2900: }
2901:
2902: shutdownJVM(code);
2903: }
2904:
2905: private static void signalStarted() {
2906: sendCommand(WRAPPER_MSG_STARTED, "");
2907: m_started = true;
2908: }
2909:
2910: /**
2911: * Called by the native code when a control event is trapped by native code.
2912: * Can have the values: WRAPPER_CTRL_C_EVENT, WRAPPER_CTRL_CLOSE_EVENT,
2913: * WRAPPER_CTRL_LOGOFF_EVENT, WRAPPER_CTRL_SHUTDOWN_EVENT,
2914: * WRAPPER_CTRL_TERM_EVENT, or WRAPPER_CTRL_HUP_EVENT.
2915: */
2916: private static void controlEvent(int event) {
2917: String eventName;
2918: boolean ignore;
2919: switch (event) {
2920: case WRAPPER_CTRL_C_EVENT:
2921: eventName = "WRAPPER_CTRL_C_EVENT";
2922: ignore = m_ignoreSignals;
2923: break;
2924: case WRAPPER_CTRL_CLOSE_EVENT:
2925: eventName = "WRAPPER_CTRL_CLOSE_EVENT";
2926: ignore = m_ignoreSignals;
2927: break;
2928: case WRAPPER_CTRL_LOGOFF_EVENT:
2929: eventName = "WRAPPER_CTRL_LOGOFF_EVENT";
2930: ignore = false;
2931: break;
2932: case WRAPPER_CTRL_SHUTDOWN_EVENT:
2933: eventName = "WRAPPER_CTRL_SHUTDOWN_EVENT";
2934: ignore = false;
2935: break;
2936: case WRAPPER_CTRL_TERM_EVENT:
2937: eventName = "WRAPPER_CTRL_TERM_EVENT";
2938: ignore = m_ignoreSignals;
2939: break;
2940: case WRAPPER_CTRL_HUP_EVENT:
2941: eventName = "WRAPPER_CTRL_HUP_EVENT";
2942: ignore = m_ignoreSignals;
2943: break;
2944: default:
2945: eventName = "Unexpected event: " + event;
2946: ignore = false;
2947: break;
2948: }
2949:
2950: WrapperControlEvent controlEvent = new WrapperControlEvent(
2951: event, eventName);
2952: if (ignore) {
2953: // Preconsume the event if it is set to be ignored, but go ahead and fire it so
2954: // user can can still have the oportunity to recognize it.
2955: controlEvent.consume();
2956: }
2957: fireWrapperEvent(controlEvent);
2958:
2959: if (!controlEvent.isConsumed()) {
2960: if (ignore) {
2961: if (m_debug) {
2962: m_out.println("Ignoring control event(" + eventName
2963: + ")");
2964: }
2965: } else {
2966: if (m_debug) {
2967: m_out.println("Processing control event("
2968: + eventName + ")");
2969: }
2970:
2971: // This is user code, so don't trust it.
2972: if (m_listener != null) {
2973: try {
2974: m_listener.controlEvent(event);
2975: } catch (Throwable t) {
2976: m_out
2977: .println("Error in WrapperListener.controlEvent callback. "
2978: + t);
2979: t.printStackTrace();
2980: }
2981: } else {
2982: // A listener was never registered. Always respond by exiting.
2983: // This can happen if the user does not initialize things correctly.
2984: stop(0);
2985: }
2986: }
2987: }
2988: }
2989:
2990: /**
2991: * Parses a long tab separated string of properties into an internal
2992: * properties object. Actual tabs are escaped by real tabs.
2993: */
2994: private static char PROPERTY_SEPARATOR = '\t';
2995:
2996: private static void readProperties(String rawProps) {
2997: WrapperProperties properties = new WrapperProperties();
2998:
2999: int len = rawProps.length();
3000: int first = 0;
3001: while (first < len) {
3002: StringBuffer sb = new StringBuffer();
3003: boolean foundEnd = false;
3004: do {
3005: int pos = rawProps.indexOf(PROPERTY_SEPARATOR, first);
3006: if (pos >= 0) {
3007: if (pos > 0) {
3008: sb.append(rawProps.substring(first, pos));
3009: }
3010: if (pos < len - 1) {
3011: if (rawProps.charAt(pos + 1) == PROPERTY_SEPARATOR) {
3012: // Two separators in a row, it was escaped.
3013: sb.append(PROPERTY_SEPARATOR);
3014: first = pos + 2;
3015: } else {
3016: foundEnd = true;
3017: first = pos + 1;
3018: }
3019: } else {
3020: foundEnd = true;
3021: first = pos + 1;
3022: }
3023: } else {
3024: // No more separators. The rest is the last property.
3025: sb.append(rawProps.substring(first));
3026: foundEnd = true;
3027: first = len;
3028: }
3029: } while (!foundEnd);
3030:
3031: String property = sb.toString();
3032:
3033: // Parse the property.
3034: int pos = property.indexOf('=');
3035: if (pos > 0) {
3036: String key = property.substring(0, pos);
3037: String value;
3038: if (pos < property.length() - 1) {
3039: value = property.substring(pos + 1);
3040: } else {
3041: value = "";
3042: }
3043:
3044: properties.setProperty(key, value);
3045: }
3046: }
3047:
3048: // Lock the properties object and store it.
3049: properties.lock();
3050:
3051: m_properties = properties;
3052: }
3053:
3054: private static synchronized Socket openSocket() {
3055: if (m_debug) {
3056: m_out.println("Open socket to wrapper..."
3057: + Thread.currentThread().getName());
3058: }
3059:
3060: InetAddress iNetAddress;
3061: try {
3062: iNetAddress = InetAddress.getByName("127.0.0.1");
3063: } catch (UnknownHostException e) {
3064: // This is pretty fatal.
3065: m_out.println(e);
3066: stop(1);
3067: return null; //please the compiler
3068: }
3069:
3070: // If the user has specified a specific port to use then we want to try that first.
3071: boolean connected = false;
3072: int tryPort;
3073: boolean fixedPort;
3074: if (m_jvmPort > 0) {
3075: tryPort = m_jvmPort;
3076: fixedPort = true;
3077: } else {
3078: tryPort = m_jvmPortMin;
3079: fixedPort = false;
3080: }
3081:
3082: // Loop until we find a port we can connect using.
3083: do {
3084: try {
3085: m_socket = new Socket(iNetAddress, m_port, iNetAddress,
3086: tryPort);
3087: if (m_debug) {
3088: m_out.println("Opened Socket from " + tryPort
3089: + " to " + m_port);
3090: }
3091: connected = true;
3092: break;
3093: } catch (SocketException e) {
3094: String eMessage = e.getMessage();
3095:
3096: if (e instanceof ConnectException) {
3097: m_out
3098: .println("Failed to connect to the Wrapper at port "
3099: + m_port + ".");
3100: m_out.println(e);
3101: // This is fatal because there is nobody listening.
3102: m_out.println("Exiting JVM...");
3103: stopImmediate(1);
3104: } else if ((e instanceof BindException)
3105: || ((eMessage != null) && (eMessage
3106: .indexOf("errno: 48") >= 0))) {
3107: // Most Java implementations throw a BindException when the port is in use,
3108: // but FreeBSD throws a SocketException with a specific message.
3109:
3110: // This happens if the local port is already in use. In this case, we want
3111: // to loop and try again.
3112: if (m_debug) {
3113: m_out
3114: .println("Failed attempt to bind using local port "
3115: + tryPort);
3116: }
3117:
3118: if (fixedPort) {
3119: // The last port checked was the fixed port, switch to the dynamic range.
3120: tryPort = m_jvmPortMin;
3121: fixedPort = false;
3122: } else {
3123: tryPort++;
3124: }
3125: } else {
3126: // Unexpected exception.
3127: m_out.println(e);
3128: m_socket = null;
3129: return null;
3130: }
3131: } catch (IOException e) {
3132: m_out.println(e);
3133: m_socket = null;
3134: return null;
3135: }
3136: } while (tryPort <= m_jvmPortMax);
3137:
3138: if (connected) {
3139: if ((m_jvmPort > 0) && (m_jvmPort != tryPort)) {
3140: m_out.println("Port " + m_jvmPort
3141: + " already in use, using port " + tryPort
3142: + " instead.");
3143: }
3144: } else {
3145: if (m_jvmPortMax > m_jvmPortMin) {
3146: m_out
3147: .println("Failed to connect to the Wrapper at port "
3148: + m_port
3149: + " by binding to any "
3150: + "ports in the range "
3151: + m_jvmPortMin
3152: + " to " + m_jvmPortMax + ".");
3153: } else {
3154: m_out
3155: .println("Failed to connect to the Wrapper at port "
3156: + m_port
3157: + " by binding to port "
3158: + m_jvmPortMin + ".");
3159: }
3160: // This is fatal because there is nobody listening.
3161: m_out.println("Exiting JVM...");
3162: stopImmediate(1);
3163: }
3164:
3165: // Now that we have a connected socket, continue on to configure it.
3166: try {
3167: // Turn on the TCP_NODELAY flag. This is very important for speed!!
3168: m_socket.setTcpNoDelay(true);
3169:
3170: // Set the SO_TIMEOUT for the socket (max block time)
3171: if (m_soTimeout > 0) {
3172: m_socket.setSoTimeout(m_soTimeout);
3173: }
3174: } catch (IOException e) {
3175: m_out.println(e);
3176: }
3177:
3178: // Send the key back to the wrapper so that the wrapper can feel safe
3179: // that it is talking to the correct JVM
3180: sendCommand(WRAPPER_MSG_KEY, m_key);
3181:
3182: return m_socket;
3183: }
3184:
3185: private static synchronized void closeSocket() {
3186: if (m_socket != null) {
3187: if (m_debug) {
3188: m_out.println("Closing socket.");
3189: }
3190:
3191: try {
3192: m_socket.close();
3193: } catch (IOException e) {
3194: } finally {
3195: m_socket = null;
3196: }
3197: }
3198: }
3199:
3200: private static String getPacketCodeName(byte code) {
3201: String name;
3202:
3203: switch (code) {
3204: case WRAPPER_MSG_START:
3205: name = "START";
3206: break;
3207:
3208: case WRAPPER_MSG_STOP:
3209: name = "STOP";
3210: break;
3211:
3212: case WRAPPER_MSG_RESTART:
3213: name = "RESTART";
3214: break;
3215:
3216: case WRAPPER_MSG_PING:
3217: name = "PING";
3218: break;
3219:
3220: case WRAPPER_MSG_STOP_PENDING:
3221: name = "STOP_PENDING";
3222: break;
3223:
3224: case WRAPPER_MSG_START_PENDING:
3225: name = "START_PENDING";
3226: break;
3227:
3228: case WRAPPER_MSG_STARTED:
3229: name = "STARTED";
3230: break;
3231:
3232: case WRAPPER_MSG_STOPPED:
3233: name = "STOPPED";
3234: break;
3235:
3236: case WRAPPER_MSG_KEY:
3237: name = "KEY";
3238: break;
3239:
3240: case WRAPPER_MSG_BADKEY:
3241: name = "BADKEY";
3242: break;
3243:
3244: case WRAPPER_MSG_LOW_LOG_LEVEL:
3245: name = "LOW_LOG_LEVEL";
3246: break;
3247:
3248: case WRAPPER_MSG_PING_TIMEOUT:
3249: name = "PING_TIMEOUT";
3250: break;
3251:
3252: case WRAPPER_MSG_SERVICE_CONTROL_CODE:
3253: name = "SERVICE_CONTROL_CODE";
3254: break;
3255:
3256: case WRAPPER_MSG_PROPERTIES:
3257: name = "PROPERTIES";
3258: break;
3259:
3260: case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_DEBUG:
3261: name = "LOG(DEBUG)";
3262: break;
3263:
3264: case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_INFO:
3265: name = "LOG(INFO)";
3266: break;
3267:
3268: case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_STATUS:
3269: name = "LOG(STATUS)";
3270: break;
3271:
3272: case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_WARN:
3273: name = "LOG(WARN)";
3274: break;
3275:
3276: case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_ERROR:
3277: name = "LOG(ERROR)";
3278: break;
3279:
3280: case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_FATAL:
3281: name = "LOG(FATAL)";
3282: break;
3283:
3284: case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_ADVICE:
3285: name = "LOG(ADVICE)";
3286: break;
3287:
3288: default:
3289: name = "UNKNOWN(" + code + ")";
3290: break;
3291: }
3292: return name;
3293: }
3294:
3295: private static synchronized void sendCommand(byte code,
3296: String message) {
3297: if (m_debug) {
3298: m_out.println("Send a packet " + getPacketCodeName(code)
3299: + " : " + message);
3300: }
3301: if (m_appearHung) {
3302: // The WrapperManager is attempting to make the JVM appear hung, so do nothing
3303: } else {
3304: // Make a copy of the reference to make this more thread safe.
3305: Socket socket = m_socket;
3306: if (socket == null && isControlledByNativeWrapper()
3307: && (!m_stopping)) {
3308: // The socket is not currently open, try opening it.
3309: socket = openSocket();
3310: }
3311:
3312: if ((code == WRAPPER_MSG_START_PENDING)
3313: || (code == WRAPPER_MSG_STARTED)) {
3314: // Set the last ping time so that the startup process does not time out
3315: // thinking that the JVM has not received a Ping for too long.
3316: m_lastPingTicks = getTicks();
3317: }
3318:
3319: // If the socket is open, then send the command, otherwise just throw it away.
3320: if (socket != null) {
3321: try {
3322: // It is possible that a logged message is quite large. Expand the size
3323: // of the command buffer if necessary so that it can be included. This
3324: // means that the command buffer will be the size of the largest message.
3325: byte[] messageBytes = message.getBytes();
3326: if (m_commandBuffer.length < messageBytes.length + 2) {
3327: m_commandBuffer = new byte[messageBytes.length + 2];
3328: }
3329:
3330: // Writing the bytes one by one was sometimes causing the first byte to be lost.
3331: // Try to work around this problem by creating a buffer and sending the whole lot
3332: // at once.
3333: m_commandBuffer[0] = code;
3334: System.arraycopy(messageBytes, 0, m_commandBuffer,
3335: 1, messageBytes.length);
3336: int len = messageBytes.length + 2;
3337: m_commandBuffer[len - 1] = 0;
3338:
3339: OutputStream os = socket.getOutputStream();
3340: os.write(m_commandBuffer, 0, len);
3341: os.flush();
3342: } catch (IOException e) {
3343: m_out.println(e);
3344: e.printStackTrace();
3345: closeSocket();
3346: }
3347: }
3348: }
3349: }
3350:
3351: /**
3352: * Loop reading packets from the native side of the Wrapper until the
3353: * connection is closed or the WrapperManager class is disposed.
3354: * Each packet consists of a packet code followed by a null terminated
3355: * string up to 256 characters in length. If the entire packet has not
3356: * yet been received, then it must not be read until the complete packet
3357: * has arived.
3358: */
3359: private static byte[] m_socketReadBuffer = new byte[256];
3360:
3361: private static void handleSocket() {
3362: WrapperPingEvent pingEvent = new WrapperPingEvent();
3363: try {
3364: if (m_debug) {
3365: m_out.println("handleSocket(" + m_socket + ")");
3366: }
3367: DataInputStream is = new DataInputStream(m_socket
3368: .getInputStream());
3369: while (!m_disposed) {
3370: try {
3371: // A Packet code must exist.
3372: byte code = is.readByte();
3373:
3374: // Always read from the buffer until a null '\0' is encountered.
3375: byte b;
3376: int i = 0;
3377: do {
3378: b = is.readByte();
3379: if (b != 0) {
3380: if (i >= m_socketReadBuffer.length) {
3381: byte[] tmp = m_socketReadBuffer;
3382: m_socketReadBuffer = new byte[tmp.length + 256];
3383: System.arraycopy(tmp, 0,
3384: m_socketReadBuffer, 0,
3385: tmp.length);
3386: }
3387: m_socketReadBuffer[i] = b;
3388: i++;
3389: }
3390: } while (b != 0);
3391:
3392: String msg = new String(m_socketReadBuffer, 0, i);
3393:
3394: if (m_appearHung) {
3395: // The WrapperManager is attempting to make the JVM appear hung,
3396: // so ignore all incoming requests
3397: } else {
3398: if (m_debug) {
3399: String logMsg;
3400: if (code == WRAPPER_MSG_PROPERTIES) {
3401: // The property values are very large and distracting in the log.
3402: // Plus if any triggers are defined, then logging them will fire
3403: // the trigger.
3404: logMsg = "(Property Values)";
3405: } else {
3406: logMsg = msg;
3407: }
3408: m_out.println("Received a packet "
3409: + getPacketCodeName(code) + " : "
3410: + logMsg);
3411: }
3412:
3413: // Ok, we got a packet. Do something with it.
3414: switch (code) {
3415: case WRAPPER_MSG_START:
3416: startInner();
3417: break;
3418:
3419: case WRAPPER_MSG_STOP:
3420: // Don't do anything if we are already stopping
3421: if (!m_stopping) {
3422: privilegedStopInner(0);
3423: // Should never get back here.
3424: }
3425: break;
3426:
3427: case WRAPPER_MSG_PING:
3428: m_lastPingTicks = getTicks();
3429: sendCommand(WRAPPER_MSG_PING, "ok");
3430:
3431: if (m_produceCoreEvents) {
3432: fireWrapperEvent(pingEvent);
3433: }
3434:
3435: break;
3436:
3437: case WRAPPER_MSG_BADKEY:
3438: // The key sent to the wrapper was incorrect. We need to shutdown.
3439: m_out
3440: .println("Authorization key rejected by Wrapper. Exiting JVM.");
3441: closeSocket();
3442: privilegedStopInner(1);
3443: break;
3444:
3445: case WRAPPER_MSG_LOW_LOG_LEVEL:
3446: try {
3447: m_lowLogLevel = Integer.parseInt(msg);
3448: m_debug = (m_lowLogLevel <= WRAPPER_LOG_LEVEL_DEBUG);
3449: if (m_debug) {
3450: m_out
3451: .println("Wrapper Manager: LowLogLevel from Wrapper "
3452: + "is "
3453: + m_lowLogLevel);
3454: }
3455: } catch (NumberFormatException e) {
3456: m_out
3457: .println("Encountered an Illegal LowLogLevel from the "
3458: + "Wrapper: " + msg);
3459: }
3460: break;
3461:
3462: case WRAPPER_MSG_PING_TIMEOUT:
3463: try {
3464: m_pingTimeout = Integer.parseInt(msg) * 1000;
3465: if (m_debug) {
3466: m_out
3467: .println("PingTimeout from Wrapper is "
3468: + m_pingTimeout);
3469: }
3470: } catch (NumberFormatException e) {
3471: m_out
3472: .println("Encountered an Illegal PingTimeout from the "
3473: + "Wrapper: " + msg);
3474: }
3475:
3476: // Make sure that the so timeout is longer than the ping timeout
3477: if (m_pingTimeout <= 0) {
3478: m_socket.setSoTimeout(0);
3479: } else if (m_soTimeout < m_pingTimeout) {
3480: m_socket.setSoTimeout(m_pingTimeout);
3481: }
3482:
3483: break;
3484:
3485: case WRAPPER_MSG_SERVICE_CONTROL_CODE:
3486: try {
3487: int serviceControlCode = Integer
3488: .parseInt(msg);
3489: if (m_debug) {
3490: m_out
3491: .println("ServiceControlCode from Wrapper with code "
3492: + serviceControlCode);
3493: }
3494: WrapperServiceControlEvent event = new WrapperServiceControlEvent(
3495: serviceControlCode);
3496: fireWrapperEvent(event);
3497: } catch (NumberFormatException e) {
3498: m_out
3499: .println("Encountered an Illegal ServiceControlCode from "
3500: + "the Wrapper: " + msg);
3501: }
3502: break;
3503:
3504: case WRAPPER_MSG_PROPERTIES:
3505: readProperties(msg);
3506: break;
3507:
3508: default:
3509: // Ignore unknown messages
3510: m_out
3511: .println("Wrapper code received an unknown packet type: "
3512: + code);
3513: break;
3514: }
3515: }
3516: } catch (InterruptedIOException e) {
3517: int nowTicks = getTicks();
3518:
3519: // Unless the JVM is shutting dowm we want to show warning messages and maybe exit.
3520: if ((m_started) && (!m_stopping)) {
3521: if (m_debug) {
3522: m_out
3523: .println("Read Timed out. (Last Ping was "
3524: + getTickAge(
3525: m_lastPingTicks,
3526: nowTicks)
3527: + " milliseconds ago)");
3528: }
3529:
3530: if (!m_appearHung) {
3531: long lastPingAge = getTickAge(
3532: m_lastPingTicks, nowTicks);
3533: long eventRunnerAge = getTickAge(
3534: m_eventRunnerTicks, nowTicks);
3535:
3536: // We may have timed out because the system was extremely busy or
3537: // suspended. Only restart due to a lack of ping events if the
3538: // event thread has been running.
3539: if (eventRunnerAge < 10000) {
3540: // Only perform ping timeout checks if ping timeouts are enabled.
3541: if (m_pingTimeout > 0) {
3542: // How long has it been since we received the last ping
3543: // from the Wrapper?
3544: if (lastPingAge > m_pingTimeout + 90000) {
3545: // It has been more than the ping timeout + 90 seconds,
3546: // so just give up and kill the JVM
3547: m_out
3548: .println("Wrapper Manager: JVM did not exit. Give up.");
3549: safeSystemExit(1);
3550: } else if (lastPingAge > m_pingTimeout) {
3551: // It has been more than the ping timeout since the
3552: // JVM was last pinged. Ask to be stopped (and restarted).
3553: m_out
3554: .println("Wrapper Manager: The Wrapper code did not ping the "
3555: + "JVM for "
3556: + (lastPingAge / 1000)
3557: + " seconds. "
3558: + "Quit and let the Wrapper resynch.");
3559:
3560: // Don't do anything if we are already stopping
3561: if (!m_stopping) {
3562: // Always send the stop command
3563: sendCommand(
3564: WRAPPER_MSG_RESTART,
3565: "restart");
3566:
3567: // Give the Wrapper a chance to register the stop
3568: // command before stopping.
3569: // This avoids any errors thrown by the Wrapper because
3570: // the JVM died before it was expected to.
3571: try {
3572: Thread.sleep(1000);
3573: } catch (InterruptedException e2) {
3574: }
3575:
3576: privilegedStopInner(1);
3577: }
3578: }
3579: }
3580: }
3581: }
3582: }
3583: }
3584: }
3585: return;
3586:
3587: } catch (SocketException e) {
3588: if (m_debug) {
3589: if (m_socket == null) {
3590: // This error happens if the socket is closed while reading:
3591: // java.net.SocketException: Descriptor not a socket: JVM_recv in socket
3592: // input stream read
3593: } else {
3594: m_out.println("Closed socket: " + e);
3595: }
3596: }
3597: return;
3598: } catch (IOException e) {
3599: // This means that the connection was closed. Allow this to return.
3600: //m_out.println( e );
3601: //e.printStackTrace();
3602: return;
3603: }
3604: }
3605:
3606: private static void startRunner() {
3607: if (isControlledByNativeWrapper()) {
3608: if (m_commRunner == null) {
3609: // Create and launch a new thread to manage this connection
3610: m_commRunner = new Thread(m_instance,
3611: WRAPPER_CONNECTION_THREAD_NAME);
3612: m_commRunner.setDaemon(true);
3613: m_commRunner.start();
3614: }
3615:
3616: // Wait to give the runner a chance to connect.
3617: synchronized (WrapperManager.class) {
3618: while (!m_commRunnerStarted) {
3619: try {
3620: WrapperManager.class.wait(100);
3621: } catch (InterruptedException e) {
3622: }
3623: }
3624: }
3625: } else {
3626: // Immediately mark the runner as started as it will never be used.
3627: synchronized (WrapperManager.class) {
3628: m_commRunnerStarted = true;
3629: WrapperManager.class.notifyAll();
3630: }
3631: }
3632: }
3633:
3634: /*---------------------------------------------------------------
3635: * Runnable Methods
3636: *-------------------------------------------------------------*/
3637: public void run() {
3638: // Make sure that no other threads call this method.
3639: if (Thread.currentThread() != m_commRunner) {
3640: throw new IllegalStateException(
3641: "Only the comm runner thread is allowed to call this method.");
3642: }
3643:
3644: if (m_debug) {
3645: m_out.println("Communications runner thread started.");
3646: }
3647:
3648: // This thread needs to have a very high priority so that it never
3649: // gets put behind other threads.
3650: Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
3651:
3652: // Initialize the last ping tick count.
3653: m_lastPingTicks = getTicks();
3654:
3655: boolean gotPortOnce = false;
3656: while (!m_disposed) {
3657: try {
3658: try {
3659: openSocket();
3660:
3661: // After the socket has been opened the first time, mark the thread as
3662: // started. This must be done here to make sure that exits work correctly
3663: // when called on startup.
3664: if (!m_commRunnerStarted) {
3665: synchronized (WrapperManager.class) {
3666: m_commRunnerStarted = true;
3667: WrapperManager.class.notifyAll();
3668: }
3669: }
3670:
3671: if (m_socket != null) {
3672: handleSocket();
3673: } else {
3674: // Failed, so wait for just a moment
3675: try {
3676: Thread.sleep(100);
3677: } catch (InterruptedException e) {
3678: }
3679: }
3680: } finally {
3681: // Always close the socket here.
3682: closeSocket();
3683: }
3684: } catch (ThreadDeath td) {
3685: m_out.println(m_warning.format("SERVER_DAEMON_KILLED"));
3686: } catch (Throwable t) {
3687: if (!m_shuttingDown) {
3688: // Show a stack trace here because this is fairly critical
3689: m_out.println(m_error.format("SERVER_DAEMON_DIED"));
3690: t.printStackTrace();
3691: }
3692: }
3693: }
3694:
3695: // Make sure that noone is ever left waiting for this thread to start.
3696: synchronized (WrapperManager.class) {
3697: if (!m_commRunnerStarted) {
3698: m_commRunnerStarted = true;
3699: WrapperManager.class.notifyAll();
3700: }
3701: }
3702:
3703: if (m_debug) {
3704: m_out.println(m_info.format("SERVER_DAEMON_SHUT_DOWN"));
3705: }
3706: }
3707:
3708: /*---------------------------------------------------------------
3709: * Inner Classes
3710: *-------------------------------------------------------------*/
3711: /**
3712: * Mapping between WrapperEventListeners and their registered masks.
3713: * This is necessary to support the case where the same listener is
3714: * registered more than once. It also makes it possible to reference
3715: * an array of these mappings without synchronization.
3716: */
3717: private static class WrapperEventListenerMask {
3718: private WrapperEventListener m_listener;
3719: private long m_mask;
3720: }
3721:
3722: private static class WrapperTickEventImpl extends WrapperTickEvent {
3723: private int m_ticks;
3724: private int m_tickOffset;
3725:
3726: /**
3727: * Returns the tick count at the point the event is fired.
3728: *
3729: * @return The tick count at the point the event is fired.
3730: */
3731: public int getTicks() {
3732: return m_ticks;
3733: }
3734:
3735: /**
3736: * Returns the offset between the tick count used by the Wrapper for time
3737: * keeping and the tick count generated directly from the system time.
3738: *
3739: * This will be 0 in most cases. But will be a positive value if the
3740: * system time is ever set back for any reason. It will be a negative
3741: * value if the system time is set forward or if the system is under
3742: * heavy load. If the wrapper.use_system_time property is set to TRUE
3743: * then the Wrapper will be using the system tick count for internal
3744: * timing and this value will always be 0.
3745: *
3746: * @return The tick count offset.
3747: */
3748: public int getTickOffset() {
3749: return m_tickOffset;
3750: }
3751: }
3752: }
|