/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.util.exec; import java.io.OutputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.rhq.core.util.UtilI18NResourceKeys; /** * Data describing a process to start. * * @author John Mazzitelli */ public class ProcessToStart { private static final String PARAM_PROGRAM_TITLE = "programTitle"; private static final String PARAM_PROGRAM_EXE = "programExecutable"; private static final String PARAM_PROGRAM_DIR = "programDirectory"; private static final String PARAM_ARGS = "arguments"; private static final String PARAM_ENV = "environmentVariables"; private static final String PARAM_WORKING_DIR = "workingDirectory"; private static final String PARAM_OUTPUT_DIR = "outputDirectory"; private static final String PARAM_OUTPUT_FILE = "outputFile"; private static final String PARAM_OUTPUT_STREAM = "outputStream"; private static final String PARAM_INPUT_DIR = "inputDirectory"; private static final String PARAM_INPUT_FILE = "inputFile"; private static final String PARAM_CAPTURE_OUTPUT = "captureOutput"; private static final String PARAM_BACKUP_OUTPUT_FILE = "backupOutputFile"; private static final String PARAM_WAIT_FOR_EXIT = "waitForExit"; private static final String PARAM_KILL_ON_TIMEOUT = "killOnTimeout"; private static final String PARAM_CHECK_EXECUTABLE_EXISTS = "checkExecutableExists"; private Map<String, Object> map = new HashMap<String, Object>(); /** * the UID to identify the serializable version of this class */ private static final long serialVersionUID = 1L; /** * Returns the title for this program. This is a simple name to help identify this start command configuration or to * just help identify the program being launched. * * <p>This is useful when, for example, a Java JVM needs to be started and you want to set the title to describe the * main class being launched (since all Java executables that are launched typically have the same executable name, * that of "java.exe").</p> * * <p>If the title was never {@link #setProgramTitle(String) set}, this will return {@link #getProgramExecutable()} * .</p> * * @return the title of the program */ public String getProgramTitle() { String title = (String) map.get(PARAM_PROGRAM_TITLE); if (title == null) { title = getProgramExecutable(); } return title; } /** * Sets the title for this program. * * @param value the title of the program (may be <code>null</code>) * * @see #getProgramTitle() */ public void setProgramTitle(String value) { // the title is optional but non-nullable; remove the param value if null if (value != null) { map.put(PARAM_PROGRAM_TITLE, value); } else { map.remove(PARAM_PROGRAM_TITLE); } } /** * Returns the name of the program to execute. This is usually just the executable file name without any path * information - for that information, see {@link #getProgramDirectory()}. If, however, * {@link #getProgramDirectory()} returns <code>null</code>, the full path to the executable may be returned by this * method. * * @return program executable name */ public String getProgramExecutable() { return (String) map.get(PARAM_PROGRAM_EXE); } /** * Sets the name of the program to execute. * * @param value program executable name * * @see #getProgramExecutable() */ public void setProgramExecutable(String value) { map.put(PARAM_PROGRAM_EXE, value); } /** * Returns the full path to the {@link #getProgramExecutable() program executable}. This is just the directory where * the executable file is located without the name of the executable itself - for that information, see * {@link #getProgramExecutable()}. If this returns <code>null</code>, then the full path may be included in the * {@link #getProgramExecutable() executable name itself}. * * @return program executable directory location */ public String getProgramDirectory() { return (String) map.get(PARAM_PROGRAM_DIR); } /** * Sets the full path to the program executable. * * @param value program executable directory location * * @see #getProgramExecutable() */ public void setProgramDirectory(String value) { map.put(PARAM_PROGRAM_DIR, value); } /** * Returns an array of strings that are the argument values passed on the command line to the program executable. If * <code>null</code> or empty, no arguments will be passed to the program. * * @return array of program arguments */ public String[] getArguments() { return (String[]) map.get(PARAM_ARGS); } /** * Sets the argument values that are to be passed on the command line to the program executable. If <code> * null</code> or empty, no arguments will be passed to the program. * * @param value array of program arguments (may be <code>null</code> or empty) */ public void setArguments(String[] value) { // args are optional but non-nullable; remove the param value if null if (value != null) { map.put(PARAM_ARGS, value); } else { map.remove(PARAM_ARGS); } } /** * Returns environment variable name/value pairs that define the environment to be passed to the started process. A * <code>null</code> will allow the subprocess to inherit the parent process environment. Each string must be in the * format: <code>name=value</code>. * * @return environment variables (may be <code>null</code> or empty) */ public String[] getEnvironment() { return (String[]) map.get(PARAM_ENV); } /** * Convenience method that not only returns the environment variables as a String array, but the environment * variables are also populated in the given <code>Properties</code> map so the caller can more easily look up * environment variables by name. Note that any properties in the <code>properties</code> object will be cleared out * - only the environment variables in this object will populate the <code>properties</code> object once this method * returns. * * <p>Note that the returned array should still be examined - at the least to see if it is <code>null</code> - even * if the caller only wants to use the <code>Properties</code> object for retrieval of the environment variables. * This is because a <code>null</code> return value has special semantics, as opposed to a non- <code>null</code> * but empty array. See {@link #getEnvironment()} for more.</p> * * @param properties a Properties object where the environment variables can be stored (must not be <code> * null</code>) * * @return environment variables (may be <code>null</code> or empty) * * @throws IllegalArgumentException if an environment variable string doesn't conform to the format: <code> * name=value</code> or <code>properties</code> is <code>null</code> */ public String[] getEnvironment(Properties properties) throws IllegalArgumentException { if (properties != null) { properties.clear(); } else { throw new IllegalArgumentException("properties=null"); } String[] envVarArray = getEnvironment(); if (envVarArray != null) { for (int i = 0; i < envVarArray.length; i++) { String envVarString = envVarArray[i]; int equals = envVarString.indexOf('='); if (equals >= 0) { properties.setProperty(envVarString.substring(0, equals), envVarString.substring(equals + 1)); } else { throw new IllegalArgumentException(UtilI18NResourceKeys.MSG.getMsg( UtilI18NResourceKeys.START_COMMAND_ENV_VAR_BAD_FORMAT, envVarString)); } } } return envVarArray; } /** * Sets the environment variable name/value pairs that define the environment to be passed to the started process. A * <code>null</code> will allow the subprocess to inherit the parent process environment. Each string must be in the * format: <code>name=value</code>. * * @param value environment variables (may be <code>null</code> or empty) */ public void setEnvironment(String[] value) { map.put(PARAM_ENV, value); } /** * Convienence method that takes a <code>Properties</code> object containing the environment variables, as opposed * to an array of strings (see {@link #setEnvironment(String[])}. Sets the environment variable name/value pairs * that define the environment to be passed to the started process. A <code>null</code> will allow the subprocess to * inherit the parent process environment. * * @param value property name/values stored in a <i>Properties</i> object * * @see #setEnvironment(String[]) */ public void setEnvironment(Properties value) { String[] env = null; if (value != null) { env = new String[value.size()]; Enumeration propNames = value.propertyNames(); for (int i = 0; propNames.hasMoreElements(); i++) { String propName = (String) propNames.nextElement(); String propValue = value.getProperty(propName); env[i] = propName + "=" + propValue; } } setEnvironment(env); return; } /** * Returns the working directory of the new process (known also as the current directory or the startup directory). * A <code>null</code> allows the subprocess to inherit the current working directory of the parent process. * * @return the working directory path (may be <code>null</code>) */ public String getWorkingDirectory() { return (String) map.get(PARAM_WORKING_DIR); } /** * Sets the working directory of the new process * * @param value the working directory path (may be <code>null</code>) * * @see #getWorkingDirectory() */ public void setWorkingDirectory(String value) { map.put(PARAM_WORKING_DIR, value); } /** * Returns the directory where the program's output log file will be written. If <code>null</code>, a directory will * be assigned (typically the <code>java.io.tmpdir</code> directory). * * <p>If {@link #getOutputStream()} is not <code>null</code>, it overrides this (that is, the stream will get the * output, not the output file).</p> * * <p>Regardless of this return value, output will not be captured unless * {@link #setCaptureOutput(Boolean) explicitly told to do so}.</p> * * @return the directory where the output log will be written to (may be <code>null</code>) */ public String getOutputDirectory() { return (String) map.get(PARAM_OUTPUT_DIR); } /** * Sets the directory where the program's output log file will be written. * * @param value the directory where the output log will be written to (may be <code>null</code>) * * @see #getOutputDirectory() */ public void setOutputDirectory(String value) { // output dir is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_OUTPUT_DIR, value); } else { map.remove(PARAM_OUTPUT_DIR); } } /** * The file (to be placed in the {@link #getOutputDirectory() output directory}) where the program's output will be * written. This should just be the filename; its full path will be specified by the * {@link #getOutputDirectory() output directory}. * * <p>It is in this file where you can view the program's stdout/stderr output stream data.</p> * * <p>If <code>null</code>, an auto-generated filename will be used.</p> * * <p>If {@link #getOutputStream()} is not <code>null</code>, it overrides this (that is, the stream will get the * output, not the output file).</p> * * <p>Regardless of this return value, output will not be captured unless * {@link #setCaptureOutput(Boolean) explicitly told to do so}.</p> * * @return the program's output log file (may be <code>null</code>) */ public String getOutputFile() { return (String) map.get(PARAM_OUTPUT_FILE); } /** * Sets the file (to be placed in the {@link #getOutputDirectory() output directory}) where the program's output * will be written. * * @param value the program's output log file (may be <code>null</code>) * * @see #getOutputFile() */ public void setOutputFile(String value) { // output file is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_OUTPUT_FILE, value); } else { map.remove(PARAM_OUTPUT_FILE); } } /** * The output stream where the program's output will be written. If this is <code>null</code>, then * {@link #getOutputDirectory()}/{@link #getOutputFile()} will be examined if the output should be written to a * file. * * <p>Regardless of this return value, output will not be captured unless * {@link #setCaptureOutput(Boolean) explicitly told to do so}.</p> * * @return the program's output stream (may be <code>null</code>) */ public OutputStream getOutputStream() { return (OutputStream) map.get(PARAM_OUTPUT_STREAM); } /** * The output stream where the program's output will be written. * * @param value the program's output stream (may be <code>null</code>) * * @see #getOutputStream() */ public void setOutputStream(OutputStream value) { // output stream is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_OUTPUT_STREAM, value); } else { map.remove(PARAM_OUTPUT_STREAM); } } /** * Returns the directory where the {@link #getInputFile() input file} is located. * * <p>If this is specified, the {@link #setInputFile(String) input file} must also be specified.</p> * * @return directory where the input file is located (may be <code>null</code>) */ public String getInputDirectory() { return (String) map.get(PARAM_INPUT_DIR); } /** * Sets the directory where the {@link #getInputFile() input file} is located. * * @param value directory where the input file is located (may be <code>null</code>) * * @see #getInputDirectory() */ public void setInputDirectory(String value) { // input directory is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_INPUT_DIR, value); } else { map.remove(PARAM_INPUT_DIR); } } /** * Returns the name of the file that contains data to be input to the program. The data found in this file will be * passed into the started program via the stdin input stream. * * <p>If this is specified, the {@link #setInputDirectory(String) input directory} must also be specified.</p> * * @return name of the input file (may be <code>null</code>) */ public String getInputFile() { return (String) map.get(PARAM_INPUT_FILE); } /** * Sets the name of the file that contains data to be input to the program. * * @param value name of the input file (may be <code>null</code>) * * @see #getInputFile() */ public void setInputFile(String value) { // input file is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_INPUT_FILE, value); } else { map.remove(PARAM_INPUT_FILE); } } /** * If 0 or less, the process executor will not wait for the process to exit before returning control. Otherwise, * this is the number of milliseconds the process executor will wait for the process to exit. If the time expires, * control will return but the process will continue to run (an attempt to kill the process will not be made). * * @return wait time (will never be <code>null</code>) */ public Long getWaitForExit() { Long waitTimeout = (Long) map.get(PARAM_WAIT_FOR_EXIT); if (waitTimeout == null) { waitTimeout = Long.valueOf(0L); } return waitTimeout; } /** * If <code>null</code> or is 0 or less, the process executor will not wait for the process to exit before returning * control. Otherwise, this is the number of milliseconds the process executor will wait for the process to exit. If * the time expires, control will return but the process will continue to run (an attempt to kill the process will * not be made). * * @param value time */ public void setWaitForExit(Long value) { // flag is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_WAIT_FOR_EXIT, value); } else { map.remove(PARAM_WAIT_FOR_EXIT); } } /** * If <code>true</code>, the started process' output will be captured and written to the output * {@link #getOutputFile() file} or {@link #getOutputStream() stream}. If <code>false</code>, no output file is * created and the process' output is simply consumed and ignored even if a non-<code>null</code> output file or * stream has been set. * * @return capture output flag (default is <code>false</code>, this will never be <code>null</code>) */ public Boolean isCaptureOutput() { Boolean flag = (Boolean) map.get(PARAM_CAPTURE_OUTPUT); if (flag == null) { flag = Boolean.FALSE; } return flag; } /** * Sets the flag to indicate if the process' output should be captured in the output file. * * @param value capture output flag * * @see #isCaptureOutput() */ public void setCaptureOutput(Boolean value) { // flag is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_CAPTURE_OUTPUT, value); } else { map.remove(PARAM_CAPTURE_OUTPUT); } } /** * If <code>true</code>, any previously existing output file will be backed up by renaming it with a date/timestamp. * If <code>false</code>, any previously existing output file will be overwritten. * * @return backup output file flag */ public Boolean isBackupOutputFile() { return (Boolean) map.get(PARAM_BACKUP_OUTPUT_FILE); } /** * Sets the flag to indicate if any previously existing output file should be backed up. * * @param value the backup flag * * @see #isBackupOutputFile() */ public void setBackupOutputFile(Boolean value) { // backup flag is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_BACKUP_OUTPUT_FILE, value); } else { map.remove(PARAM_BACKUP_OUTPUT_FILE); } } /** * If <code>true</code>, then the process will be forcibly killed if it doesn't exit within the * {@link #getWaitForExit() wait time}. If <code>false</code>, the process will be allowed to continue to run for as * long as it needs - {@link #getWaitForExit()} will only force the caller to "wake up" and not block waiting for * the process to finish. * * @return kill flag (default is <code>false</code>, will never be <code>null</code>) */ public Boolean isKillOnTimeout() { Boolean flag = (Boolean) map.get(PARAM_KILL_ON_TIMEOUT); if (flag == null) { flag = Boolean.FALSE; } return flag; } /** * Sets the flag to indicate if the process should be killed after the wait timeout expires. * * @param value the kill flag * * @see #isKillOnTimeout() */ public void setKillOnTimeout(Boolean value) { // flag is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_KILL_ON_TIMEOUT, value); } else { map.remove(PARAM_KILL_ON_TIMEOUT); } } /** * If <code>true</code>, then the executable will first be checked for its existance. * If the executable does not exist, the execution should fail-fast. If <code>false</code>, * the process will attempt to be executed no matter what. This will allow the operating * system to check its executable PATH to find the executable if required. * * @return check flag (default is <code>true</code>, will never be <code>null</code>) */ public Boolean isCheckExecutableExists() { Boolean flag = (Boolean) map.get(PARAM_CHECK_EXECUTABLE_EXISTS); if (flag == null) { flag = Boolean.TRUE; } return flag; } /** * Sets the flag to indicate if the executable should be checked for existence first. * * @param value the check flag * * @see #isCheckExecutableExists() */ public void setCheckExecutableExists(Boolean value) { // flag is optional, but non-nullable; remove it if null is passed in if (value != null) { map.put(PARAM_CHECK_EXECUTABLE_EXISTS, value); } else { map.remove(PARAM_CHECK_EXECUTABLE_EXISTS); } } }