/** * Abstracts a server within the watchdog that manages a Process. This class handles creation of the * server process, reading its stdout/stderr (if necessary), waiting for the process to die, * restarting it, etc. Concrete classes are derived from this and must implement a heartbeat method * to verify that the server is functioning correctly. * * @see ProcessMonitor */ public abstract class ProcessExecutor extends Server { /** * This is the ProcessWaitForThread who's sole job is to notice when the server process dies and * communicate that death back to the state machine. */ protected ProcessWaitForThread mProcessWaitForThread = null; /** The thread that gathers the output from stdout and transfers it to the log */ protected ServerOutputThread mStdout; /** The thread that gathers the output from stderr and transfers it to the log */ protected ServerOutputThread mStderr; /** * If true, this class has the responsibility of taking the stdout/stderr output and moving to the * log. Otherwise the server process itself can manage it logging (e.g using the logging API) */ protected boolean mNativeLoggingUsed; /** The arguments array to exec the process */ protected String[] mExecArgs = null; /** This is used in logging */ protected String mPrintableCommand = ""; /** Is this a Java based server */ protected boolean mIsJavaServer = false; /** ServerCommand */ private ServerCommand mServerCmd; /** * This is the Java debug port. If this is a Java server. Set the first time the server is * debugged. */ protected String mDebugPort = null; /** * This is the OptimizeIt port. If this is a Java server. Set the first time the server is * profiled. */ protected String mOptitAuditPort = null; /** Handle to the server process */ protected Process mProcess = null; /** @param name This server's name. */ public ProcessExecutor(String name) { super(name); mServerStatus.setMonitor(false); mProcessWaitForThread = new ProcessWaitForThread(name); mProcessWaitForThread.start(); } /** * Returns true if the Watchdog is responsible for moving the stdout/stderr output to a log file. */ protected boolean isNativeLoggingUsed() { return mNativeLoggingUsed; } /** * If param b is set to true, starts the necessary threads to monitor the process' stdout/stderr * and transfer the contents to a log file. */ protected void setNativeLoggingUsed(boolean b) { if (b) { mServerStatus.setLogNative(shouldIndicateNativeLogging()); FileHandler fileHandler = null; try { String logLocation = LogUtil.getLogLocation(); int logFileSize = -1; try { logFileSize = DCPLib.getIntProperty("Logging.Servers." + mName + ".logFileSize"); } catch (NoSuchProperty nsp) { logFileSize = DCPLib.getIntProperty( "Logging.Defaults.logFileSize", CiscoLogger.DEFAULT_LOG_FILE_SIZE); } int logFileNumber = -1; try { logFileNumber = DCPLib.getIntProperty("Logging.Servers." + mName + ".logFileNumber"); } catch (NoSuchProperty nsp) { logFileNumber = DCPLib.getIntProperty( "Logging.Defaults.logFileNumber", CiscoLogger.DEFAULT_LOG_FILE_NUMBER); } if (LogUtil.getAppType().equals("unknown")) { fileHandler = new FileHandler( logLocation + File.separator + getProcessNameForLog(), logFileSize, logFileNumber); } else { fileHandler = new FileHandler( logLocation + File.separator + LogUtil.getAppType() + "." + LogUtil.getAppInst() + "." + getProcessNameForLog(), logFileSize, logFileNumber); } fileHandler.setFormatter( new java.util.logging.SimpleFormatter() { public synchronized String format(LogRecord record) { return formatMessage(record) + "\n"; } }); } catch (Exception ex) { mLogger.severe("Could not set up native handler for external process", ex); } mStdout = new ServerOutputThread(mName + ".stdout", mLogger, fileHandler); mStderr = new ServerOutputThread(mName + ".stderr", mLogger, fileHandler); // Get them running mStdout.start(); mStderr.start(); } mNativeLoggingUsed = b; } /** * If the server is being logged natively, this method can be used to find out if the ServerStatus * should have nativeLogging flag set to true or not. Normally This method doesn't do anything. * But if the server generates a normal CiscoLog (along with the native log) this method can be * overridden to indicate that there is a CiscoLog. This has been introduced to workaround the * problem of httpd processes which generate both native logs and Cisco logs. * * <p>This has to work in conjunction with a overriden getProcessNameForLog * * @see getProcessNameForLog */ protected boolean shouldIndicateNativeLogging() { return true; } /** * Returns the name of the process. Used for log file name generation. * * @return the name of the process. Used for log file name generation. */ protected String getProcessNameForLog() { return mName; } /** * In this method the required configuration is read to start the server process and the command * to execute it is built. */ protected void _prepare() throws Exception { // long l1 = System.currentTimeMillis(); prepare(); // System.out.println(mName + " : " +(System.currentTimeMillis() - l1)); } protected void prepare() throws Exception { try { List execCommand = buildExecCommand(); if (execCommand == null) { mLogger.severe("No command property was specified; disabling server"); synchronized (this) { changeState(STATE_DISABLED); } return; } else { mExecArgs = (String[]) execCommand.toArray(new String[0]); StringBuffer printableCmd = new StringBuffer(4096); int index = 0; for (; index < mExecArgs.length - 1; index++) { printableCmd.append(mExecArgs[index]).append(" "); } printableCmd.append(mExecArgs[index]); mPrintableCommand = printableCmd.toString(); if (useOptimizeIt()) { System.err.println(mPrintableCommand); } } } catch (Exception ex) { mLogger.severe("Failed In preparing execargs"); throw ex; } } /** * Hook method called by the super-class if the server enters unexpected state. This method * destroys the server process and changes state to disabled. */ protected void handleUnexpectedState(Boolean stateChanged) { if (mProcess != null) { try { mProcess.exitValue(); // the process has already exitted, try to restart it changeState(STATE_START_DEPENDENCIES); } catch (IllegalThreadStateException itse) { // the process is still running, so kill it mProcess.destroy(); } } else { setDisabled(true); changeState(STATE_DISABLED); } } /** * Retrieves the command from the configuration, that indicates how to start the server process. */ protected String getCommand() { return getProperty(mPropertyPrefix + ".cmd"); } /** * Looks for properties "watchdog.server.<SERVER_NAME>.java.home" followed by * "watchdog.java.home" in the configuration and returns the first non-null value. If both are not * available, returns the "java.home" system property. */ protected String getJavaHome() { List queue = new LinkedList(); queue.add(mPropertyPrefix + ".java.home"); queue.add(WDConstants.WD_PREFIX + ".java.home"); String javaHome = getProperty(queue); if (javaHome == null) javaHome = System.getProperty("java.home"); return javaHome; } /** * Looks for properties "watchdog.server.<SERVER_NAME>.java.class.path" followed by * "watchdog.java.class.path" in the configuration and returns the first non-null value. If both * are not available, returns the "java.class.path" system property. */ protected String getJavaClasspath() { List queue = new LinkedList(); queue.add(mPropertyPrefix + ".java.class.path"); queue.add(WDConstants.WD_PREFIX + ".java.class.path"); String cpath = getProperty(queue); if (cpath == null) { cpath = System.getProperty("java.class.path"); } return cpath; } /** * Looks for properties "watchdog.server.<SERVER_NAME>.java.cp" followed by * "watchdog.java.cp" in the configuration and returns the first non-null value. If both are null, * returns null. */ protected String getJavaAdditionalClasspath() { List queue = new LinkedList(); queue.add(mPropertyPrefix + ".java.cp"); queue.add(WDConstants.WD_PREFIX + ".java.cp"); return getProperty(queue); } /** * Looks for properties "watchdog.server.<SERVER_NAME>.java.jvm" followed by * "watchdog.java.jvm" in the configuration and returns the first non-null value. If both are not * available, returns the /bin/java in the directory returned by getJavaHome * * @see ProcessExecutor#getJavaHome */ protected String getJavaJVM() { List queue = new LinkedList(); queue.add(mPropertyPrefix + ".java.jvm"); queue.add(WDConstants.WD_PREFIX + ".java.jvm"); String javaVM = getProperty(queue); if (javaVM == null) { String fileSeparator = System.getProperty("file.separator"); String javaHome = getJavaHome(); javaVM = javaHome + fileSeparator + "bin" + fileSeparator + "java"; } return javaVM; } /** * Looks for properties "watchdog.server.<SERVER_NAME>.java.flags" followed by * "watchdog.java.flags" in the configuration and returns the first non-null value. If both are * not available, returns an empty list. */ protected List getJVMFlags() { String servJVMFlags = getProperty(mPropertyPrefix + ".java.flags"); String wdJVMFlags = getProperty(WDConstants.WD_PREFIX + ".java.flags"); StringBuffer sb = new StringBuffer(); if (wdJVMFlags != null) sb.append(wdJVMFlags); if (servJVMFlags != null) sb.append(servJVMFlags); String javaFlags = sb.toString(); String debugFlags = getJVMDebugFlags(); if (debugFlags == null) debugFlags = ""; javaFlags = debugFlags + " " + javaFlags; javaFlags = javaFlags.trim(); if (javaFlags.equals("")) { return new LinkedList(); } return tokenizeCommand(javaFlags); } /** * Return the java debugger flags. If useJDB is true for this server, then this method updates the * debug port in watchdog.java.debug.flags and returns the resultant flags. */ protected String getJVMDebugFlags() { if (useJDB()) { String port = getDebugPort(); System.out.println(mName + " Java debug port is : " + port); mLogger.config("Java debug port is : " + port); String dflags = getProperty(WDConstants.WD_PREFIX + ".java.debug.flags"); if (dflags == null) return ""; dflags = WDUtil.sreplace(dflags, "PORT", port); return dflags; } return ""; } /** * If true, run this java server under debugger. Checks * watchdog.server.<SERVER_NAME>.debugOrProfile to see if it is set to JAVA_DEBUG. */ protected boolean useJDB() { String debOrProfile = getProperty(mPropertyPrefix + ".debugOrProfile"); if ("JAVA_DEBUG".equals(debOrProfile)) return true; return false; } /** * The default number from which the allocation of optimizeit prot number for the server processes * should start. */ protected static int msJDBPort = DCPLib.getIntProperty("watchdog.java.debug.startPort", 14000); /** * This method finds out the next port on which Java debuggere can send its debug output. * * <p>If "watchdog.server.<SERVER_NAME>.profilers.OptimizeIt.port is specified then, the * property value is returned as an int. * * <p>If not the "watchdog.java.profilers.OptimizeIt.startPort" property is looked up to find out * where the port numbers start for all the servers (and incremented by one for each server) * * <p>If this property is unspecified too, then the default is OPTIMIZEIT_START_PORT. */ private static synchronized String getJDBPort(String serverPropertyPrefix) { String rc = DCPLib.getProperty(serverPropertyPrefix + ".java.debug.port", null); if (rc != null) return rc; return Integer.toString(msJDBPort++); } protected String getDebugPort() { if (mDebugPort == null) { mDebugPort = getJDBPort(mPropertyPrefix); } return mDebugPort; } /** * Looks for properties "watchdog.server.<SERVER_NAME>.java.vmtype" followed by * "watchdog.java.vmtype" in the configuration and returns the first non-null value. If both are * not available, returns null. */ protected String getJVMType() { List queue = new LinkedList(); queue.add(mPropertyPrefix + ".java.vmtype"); queue.add(WDConstants.WD_PREFIX + ".java.vmtype"); String vmType = getProperty(queue); return vmType; } /** * Looks for property "watchdog.server.<SERVER_NAME>.app_args" and if it is non-null, * tokenizes the args and returns the list. If it is null, returns an empty list. */ protected List getAppArgs() { String appArgs = getProperty(mPropertyPrefix + ".app_args"); if (appArgs == null) { return new LinkedList(); } return tokenizeCommand(appArgs); } /** * Looks for properties "watchdog.server.<SERVER_NAME>.useOptimizeIt" followed by * "watchdog.java.useOptimizeIt" in the configuration and returns true if one of them is true. If * not, returns false. */ protected boolean useOptimizeIt() { String optimizeIt = getProperty(mPropertyPrefix + ".debugOrProfile"); boolean useOptimizeIt = (optimizeIt != null) && (optimizeIt.equals("OPTIMIZEIT")); mLogger.config(mPropertyPrefix + " using optit " + useOptimizeIt); return useOptimizeIt; } private static int msOptimizeItStartPort = -1; /** * The default number from which the allocation of optimizeit prot number for the server processes * should start. */ public static final int OPTIMIZEIT_START_PORT = 60000; /** * This method finds out the next port on which OptimizeIt can send its profiler output. * * <p>If "watchdog.server.<SERVER_NAME>.profilers.OptimizeIt.port is specified then, the * property value is returned as an int. * * <p>If not the "watchdog.profilers.OptimizeIt.startPort" property is looked up to find out where * the port numbers start for all the servers (and incremented by one for each server) * * <p>If this property is unspecified too, then the default is OPTIMIZEIT_START_PORT. */ private static synchronized String getOptimizeItAuditPort(String serverPropertyPrefix) { String rc = DCPLib.getProperty(serverPropertyPrefix + ".profilers.OptimizeIt.port", null); if (rc != null) return rc; if (msOptimizeItStartPort < 0) { rc = DCPLib.getProperty("watchdog.java.profilers.OptimizeIt.startPort", null); if (rc == null) msOptimizeItStartPort = OPTIMIZEIT_START_PORT; else { try { msOptimizeItStartPort = Integer.parseInt(rc); } catch (NumberFormatException ex) { msOptimizeItStartPort = OPTIMIZEIT_START_PORT; } } } return Integer.toString(msOptimizeItStartPort++); } /** * This method should update the ServerCommand "struct" and setup the necessary values for the * server to be executed under OptimizeIt. */ protected void updateServerCommandForOptimizeIt(ServerCommand serverCmd) { mLogger.config(mPropertyPrefix + " checking for optit"); String optitPrefix = WDConstants.WD_PREFIX + ".java.profilers.OptimizeIt."; String home = DCPLib.getProperty(optitPrefix + "home", null); if (home == null) { mLogger.warning("OptimizeItHome is not specified. Cannot start OptimizeIt"); return; } if (!(new java.io.File(home)).exists()) { mLogger.warning("OptimizeItHome : " + home + " does not exist." + " Cannot start OptimizeIt"); return; } String classPath = DCPLib.getProperty(optitPrefix + "addonClassPath", ""); classPath = WDUtil.sreplace(classPath, "OPTIMIZEIT_HOME", home); serverCmd.setClasspath(serverCmd.getClasspath() + File.pathSeparator + classPath); String libPath = DCPLib.getProperty(optitPrefix + "addonLibPath", ""); libPath = WDUtil.sreplace(libPath, "OPTIMIZEIT_HOME", home); String sysLibPath = serverCmd.getLibPath(); if (sysLibPath == null) sysLibPath = System.getProperty("java.library.path", ""); serverCmd.setLibPath(libPath + File.pathSeparator + sysLibPath); String javaArgs = DCPLib.getProperty(optitPrefix + "javaArgs", ""); javaArgs = WDUtil.sreplace(javaArgs, "OPTIMIZEIT_HOME", home); List jvmFlags = serverCmd.getJVMFlags(); jvmFlags.add(0, javaArgs); String profilerClass = DCPLib.getProperty(optitPrefix + "class", "intuitive.audit.Audit"); if (mOptitAuditPort == null) { mOptitAuditPort = getOptimizeItAuditPort(mPropertyPrefix); } System.out.println("OptimizeIt for " + mName + " is running at " + mOptitAuditPort); mLogger.config("OptimizeIt running at " + mOptitAuditPort); String optitArgs = DCPLib.getProperty(optitPrefix + "args", ""); optitArgs = WDUtil.sreplace(optitArgs, "OPTIMIZEIT_HOME", home); optitArgs = WDUtil.sreplace(optitArgs, "AUDIT_PORT", mOptitAuditPort); String mainClass = serverCmd.getMainClassName(); List appArgs = serverCmd.getAppArgs(); serverCmd.setMainClassName(profilerClass); appArgs.add(0, mainClass); appArgs.add(0, optitArgs); } /* ****************************************************************** ****************************************************************** ****************************************************************** ****************************************************************** protected boolean useJProbe() { List queue = new LinkedList(); queue.add(mPropertyPrefix + ".JProbe"); queue.add(WDConstants.WD_PREFIX + ".JProbe"); String jProbe = getProperty(queue); boolean useJProbe = jProbe != null && Boolean.valueOf(jProbe).booleanValue(); return useJProbe; } protected void updateServerCommandForJProbe(ServerCommand serverCmd){ // now build up the command line List newExecTokens = new LinkedList(); // Identify any extra arguments to be used for JProbe List queue = new LinkedList(); queue.add(mPropertyPrefix + ".JProbe.args"); queue.add(WDConstants.WD_PREFIX + ".JProbe.args"); String jProbeArgs = getProperty(queue); // Locate JProbe's installation directory queue.clear(); queue.add(mPropertyPrefix + ".JProbe.home"); queue.add(WDConstants.WD_PREFIX + ".JProbe.home"); String jProbeHome = getProperty(queue); if(jProbeHome == null) { jProbeHome = "/auto/ntglocalsol/java" + "/JProbe-2.5b2/JProbeProfiler25Beta/profiler"; } serverCmd.setJVM(jProbeHome + File.separator + "jplauncher"); boolean needProfile = true; List jProbeArgList = new LinkedList(); // Additional OptimizeIt arguments boolean addDllPath = true; if(jProbeArgs != null) { List cmds = tokenizeCommand(jProbeArgs); Iterator e = cmds.iterator(); while(e.hasNext()) { String arg = (String) e.next(); if (arg.startsWith("-jp_function=")) { needProfile = false; } jProbeArgList.add(arg); } } // JProbe needs the following additional arguments if (needProfile) { jProbeArgList.add("-jp_function=profile"); jProbeArgList.add("-jp_vm=jdk11"); jProbeArgList.add("-Djava.compiler=NONE"); } serverCmd.setJVMFlags(jProbeArgList); serverCmd.setSpecialHandling(true); } ****************************************************************** ****************************************************************** ****************************************************************** ****************************************************************** *************************************************************************/ protected void updateServerCommandForSpecialHandling(ServerCommand args) { /** * if(useJProbe()) { updateServerCommandForJProbe(args); } else if(useOptimizeIt()) { * updateServerCommandForOptimizeIt(args); } */ if (useOptimizeIt()) { updateServerCommandForOptimizeIt(args); } } /** * This method should setup the flags/switches for the java command which will determine the * actual command to be executed. * * <p>Sets up flags like : * * <ul> * <li>-Dem.home * <li>-Dprocess.name * </ul> * * <br> * add computes the classpath. */ protected void updateFlags(ServerCommand serverCmd) { String bootDir = System.getProperty("em.home"); String appType = System.getProperty("appType"); String appInst = System.getProperty("appInst"); String javaClasspath = serverCmd.getClasspath(); String processName = getProcessNameForLog(); List jvmFlags = serverCmd.getJVMFlags(); int cpToken = -1; for (int i = 0; i < jvmFlags.size(); i++) { String token = (String) jvmFlags.get(i); if (token.startsWith("-classpath")) { cpToken = i; javaClasspath = null; } else if (token.startsWith("-Dem.home")) { bootDir = null; } else if (token.startsWith("-Dprocess.name")) { processName = null; } } List addonJVMFlags = new LinkedList(); if (bootDir != null) { addonJVMFlags.add("-Dem.home=" + bootDir); } if (processName != null) { addonJVMFlags.add("-Dprocess.name=" + processName); } if (!(appType == null || appType.trim().equals(""))) { addonJVMFlags.add("-DappType=" + appType); } if (!(appInst == null || appInst.trim().equals(""))) { addonJVMFlags.add("-DappInst=" + appInst); } if (cpToken != -1) { jvmFlags.remove(cpToken); String str = (String) jvmFlags.remove(cpToken); serverCmd.setClasspath(str); } jvmFlags.addAll(addonJVMFlags); } /** Tokenizes a command string into a list. Returns an empty list if cmd cannot be tokenized. */ public static LinkedList tokenizeCommand(String cmd) { try { LinkedList tmp = _tokenizeCommand(cmd); return tmp; } catch (Exception ex) { return new LinkedList(); } } /** * Tokenizes a command string into a list. * * @throws Exception if the command cannot be tokenized */ protected static LinkedList _tokenizeCommand(String cmd) throws Exception { LinkedList tokens = new LinkedList(); int startIndex = 0; int dQuoteAt = cmd.indexOf('"'); int sQuoteAt = cmd.indexOf('\''); if (dQuoteAt == -1 && sQuoteAt == -1) { StringTokenizer st = new StringTokenizer(cmd.trim()); while (st.hasMoreTokens()) { tokens.add(st.nextToken()); } return tokens; } char[] chArray = cmd.trim().toCharArray(); int endIndex = 0; boolean inQuotes = false; char c = 0; char lastc = 0; char lastqc = 0; StringBuffer sb = new StringBuffer(80); while (endIndex < chArray.length) { c = chArray[endIndex]; if (!Character.isWhitespace(c)) { if (c == '"' || c == '\'') { if (inQuotes && lastc != '\\' && lastqc == c) { tokens.add(sb.toString()); inQuotes = false; sb.setLength(0); } else if (!inQuotes) { inQuotes = true; lastqc = c; } else { sb.append(c); } } else if (c == '\\') { if (lastc == '\\') sb.append(c); } else { sb.append(c); } } else { if (inQuotes) { sb.append(c); } else { if (sb.length() > 0) { tokens.add(sb.toString()); sb.setLength(0); } } } lastc = c; ++endIndex; } if (inQuotes) { throw new Exception( WDExUtil.formatMessage(WDExConstants.UNTERMINATED_STRING, WDUtil.toArray(cmd))); } return tokens; } /** * Look at the various property settings and create the command line for this server. * * @return the command line for this server. */ protected List buildExecCommand() { try { String cmd = getCommand(); if (cmd == null) { return null; } mLogger.finest(LogUtil.splitLine(cmd)); // Get java specific properties LinkedList execTokens = tokenizeCommand(cmd); String firstToken = (String) execTokens.get(0); if (firstToken.equals("java")) { // This is a java command, so rework it if (execTokens.size() < 2) { return null; } mIsJavaServer = true; List props = new LinkedList(); props.add(mPropertyPrefix + ".nativeLogging"); props.add(WDConstants.WD_PREFIX + ".nativeLogging"); String nativeLog = getProperty(props); if (nativeLog == null || nativeLog.equals("false")) { setNativeLoggingUsed(false); } else { setNativeLoggingUsed(true); } String mainClassName = (String) execTokens.getLast(); List cmdLineFlags = null; if (execTokens.size() > 2) { cmdLineFlags = execTokens.subList(1, execTokens.size() - 1); } String javaClasspath = getJavaClasspath(); String addCp = getJavaAdditionalClasspath(); if (addCp != null) { javaClasspath += File.pathSeparator + addCp; } String jvm = getJavaJVM(); String jvmType = getJVMType(); List jvmFlags = getJVMFlags(); List appArgs = getAppArgs(); mServerCmd = new ServerCommand( jvm, jvmType, jvmFlags, javaClasspath, mainClassName, cmdLineFlags, appArgs); updateFlags(mServerCmd); updateServerCommandForSpecialHandling(mServerCmd); execTokens = mServerCmd.getTokens(); mLogger.finest(LogUtil.splitLine(execTokens.toString())); } else { setNativeLoggingUsed(true); } return execTokens; } catch (Exception e) { mLogger.severe("Failed to buildExecCmd", e); return null; } } /** Stop this server hard. Destroy the server process. */ protected void stopServerHard() { if (mProcess != null) { String propertyName = getPropertyPrefix() + ".shutdown.cmd"; String shutdownCmd = DCPLib.getProperty(propertyName, null); if (shutdownCmd != null) { mLogger.finer(propertyName + " " + shutdownCmd); ExecuteShutdownCmdThread script = new ExecuteShutdownCmdThread(shutdownCmd); script.start(); } mLogger.finer("Destroying server process using destory()"); mProcess.destroy(); mLogger.finer("Destroyed server process"); mProcess = null; mPid = 0; } } /** * Exec the server process, notify mProcessWaitForThread (so that it will notice when the process * dies) and enable the heartbeat thread. */ protected void startExecution() { customStartup(); if (mGeneration > 0 && mIsJavaServer) { try { prepare(); } catch (Exception ex) { mLogger.severe("Improper configuration for server", ex); synchronized (this) { mDisabled = true; changeState(STATE_DISABLED); } return; } } try { String runSeparator = "-------------------- Generation " + (mGeneration + 1) + " --------------------"; mLogger.finest(runSeparator); mLogger.finest(LogUtil.splitLine(mPrintableCommand)); mProcess = Runtime.getRuntime().exec(mPrintableCommand); new GetPidThread("server." + getName() + ".getpid").start(); mExecTime = System.currentTimeMillis(); mGeneration++; updateServerStatus(); mLogger.finer("Spawned process"); if (isNativeLoggingUsed()) { mStderr.setStream(mProcess.getErrorStream()); mStdout.setStream(mProcess.getInputStream()); } // Wake up the thread that waits for a process to die synchronized (mProcessWaitForThread) { mProcessWaitForThread.notifyAll(); } } catch (IOException ioe) { mLogger.severe("Failure in starting server; Generation : mGeneration", ioe); synchronized (this) { mDisabled = true; changeState(STATE_DISABLED); } } } /** * This thread is responsible for detecting the death of the external server processes and * communicating that death back to the ProcessExecutor class. */ class ProcessWaitForThread extends Thread { int mNoticedGeneration = 0; /** Create a new ProcessWaitForThread. */ public ProcessWaitForThread(String name) { super(name + "wait"); setDaemon(true); } public void run() { while (true) { synchronized (this) { while (mNoticedGeneration == mGeneration) { try { mLogger.finer("ProcessWaitForThread is waiting"); wait(); } catch (InterruptedException ie) { // ignore } } mNoticedGeneration = mGeneration; } while (true) { try { mLogger.finer("Calling Process.waitFor"); int exitStatus = mProcess.waitFor(); mLogger.severe("Server exited with status " + exitStatus); synchronized (ProcessExecutor.this) { changeState(STATE_STOPPED); } break; } catch (InterruptedException ie) { mLogger.warning("Interrupted while waiting for server to exit"); } } } } } /** * This thread instance is created and run to obtain the process id of the server process started. * Uses native code in UnixStdlib (which navigates and reads files in the /proc filesystem to * obtain the process id) */ class GetPidThread extends Thread { public GetPidThread(String name) { super(name); } public void run() { int i; String cmdToLookFor = getServerProperty("pidHint"); if (cmdToLookFor == null) { StringBuffer buf = new StringBuffer(); for (i = 0; i < mExecArgs.length; i++) { if (i > 0) { buf.append(" "); } buf.append(mExecArgs[i]); } cmdToLookFor = buf.toString(); } mLogger.finer("Looking for pid for command: " + cmdToLookFor); i = 0; do { try { Thread.sleep(1000); } catch (InterruptedException ie) { } mPid = UnixStdlib.getProcessID(cmdToLookFor); } while (mPid == 0 && ++i < 300); // up to 5 minutes if (mPid == 0) { mLogger.warning("Giving up on trying to identify pid"); } else { mLogger.finer("Serverpid server=" + mName + " pid=" + mPid); } } } /** * Class that encapsulates all the information needed to build the command for running a server * program implemented in Java. */ static class ServerCommand { private String mJVM = ""; private String mVmType; private List mJVMFlags; private String mClasspath = ""; private String mLibPath = null; private String mMainClassName = ""; private List mAppArgs; private boolean mSpecialHandling; private List mCmdLineFlags; /** * @param jvm path to java executable * @param vmType -server or -client or null * @param jvmFlags -D settings and other java specfic flags * @param classPath classpath * @param mainClassName name of the class that serves as the entry point for the server * @param appArgs list of arguments to the server program */ ServerCommand( String jvm, String vmType, List jvmFlags, String classpath, String mainClassName, List cmdLineFlags, List appArgs) { mJVM = jvm; mVmType = vmType; mJVMFlags = jvmFlags; mClasspath = classpath; mMainClassName = mainClassName; mCmdLineFlags = cmdLineFlags; mAppArgs = appArgs; } void setJVM(String jvm) { mJVM = jvm; } String getJVM() { return mJVM; } void setVmType(String type) { mVmType = type; } String getVmType() { return mVmType; } void setSpecialHandling(boolean b) { mSpecialHandling = b; } boolean isSpecialHandling() { return mSpecialHandling; } void setMainClassName(String s) { mMainClassName = s; } String getMainClassName() { return mMainClassName; } void setAppArgs(List flags) { mAppArgs = flags; } List getAppArgs() { return mAppArgs; } void setJVMFlags(List flags) { mJVMFlags = flags; } List getJVMFlags() { return mJVMFlags; } String getClasspath() { return mClasspath; } void setClasspath(String str) { mClasspath = str; } String getLibPath() { return mLibPath; } void setLibPath(String str) { mLibPath = str; } /** * Converts the state of this object into a list of tokens, which when combined give the command * to execute for starting the server program. */ LinkedList getTokens() { LinkedList tokens = new LinkedList(); tokens.add(mJVM); if (mCmdLineFlags != null) tokens.addAll(mCmdLineFlags); if (mVmType != null) tokens.add(mVmType); if (mJVMFlags != null) tokens.addAll(mJVMFlags); if (mLibPath != null) { tokens.add("-Djava.library.path=" + mLibPath); } tokens.add("-classpath"); tokens.add(mClasspath); tokens.add(mMainClassName); if (mAppArgs != null) tokens.addAll(mAppArgs); return tokens; } } }
/** * If param b is set to true, starts the necessary threads to monitor the process' stdout/stderr * and transfer the contents to a log file. */ protected void setNativeLoggingUsed(boolean b) { if (b) { mServerStatus.setLogNative(shouldIndicateNativeLogging()); FileHandler fileHandler = null; try { String logLocation = LogUtil.getLogLocation(); int logFileSize = -1; try { logFileSize = DCPLib.getIntProperty("Logging.Servers." + mName + ".logFileSize"); } catch (NoSuchProperty nsp) { logFileSize = DCPLib.getIntProperty( "Logging.Defaults.logFileSize", CiscoLogger.DEFAULT_LOG_FILE_SIZE); } int logFileNumber = -1; try { logFileNumber = DCPLib.getIntProperty("Logging.Servers." + mName + ".logFileNumber"); } catch (NoSuchProperty nsp) { logFileNumber = DCPLib.getIntProperty( "Logging.Defaults.logFileNumber", CiscoLogger.DEFAULT_LOG_FILE_NUMBER); } if (LogUtil.getAppType().equals("unknown")) { fileHandler = new FileHandler( logLocation + File.separator + getProcessNameForLog(), logFileSize, logFileNumber); } else { fileHandler = new FileHandler( logLocation + File.separator + LogUtil.getAppType() + "." + LogUtil.getAppInst() + "." + getProcessNameForLog(), logFileSize, logFileNumber); } fileHandler.setFormatter( new java.util.logging.SimpleFormatter() { public synchronized String format(LogRecord record) { return formatMessage(record) + "\n"; } }); } catch (Exception ex) { mLogger.severe("Could not set up native handler for external process", ex); } mStdout = new ServerOutputThread(mName + ".stdout", mLogger, fileHandler); mStderr = new ServerOutputThread(mName + ".stderr", mLogger, fileHandler); // Get them running mStdout.start(); mStderr.start(); } mNativeLoggingUsed = b; }