/*
* RHQ Management Platform
* Copyright (C) 2005-2012 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.pluginapi.util;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.List;
import org.rhq.core.system.ProcessExecution;
/**
* A set of utility methods for creating {@link ProcessExecution}s.
*
* @author Ian Springer
*/
public class ProcessExecutionUtility {
private static final boolean OS_IS_WINDOWS = (File.separatorChar == '\\');
private ProcessExecutionUtility() {
}
/**
* Creates a ProcessExecution for the specified file for the current platform. If the current platform is Windows
* and the file name ends with ".bat" or ".cmd", the file will be assumed to be a Windows batch file, and the
* process execution will be initialized accordingly. Note, if the file is a UNIX script, its first line must be a
* valid #! reference to a script interpreter (e.g. #!/bin/sh), otherwise it will fail to execute. The returned
* ProcessExecution will have a non-null arguments list, an environment map that is a copy of the current process's
* environment, and a working directory set to its executable's parent directory.
*
* @param file an executable or a batch file
*
* @return a process execution
*/
public static ProcessExecution createProcessExecution(File file) {
return createProcessExecution((String) null, file);
}
/**
* Creates a ProcessExecution for the specified file for the current platform. If the current platform is Windows
* and the file name ends with ".bat" or ".cmd", the file will be assumed to be a Windows batch file, and the
* process execution will be initialized accordingly. Note, if the file is a UNIX script, its first line must be a
* valid #! reference to a script interpreter (e.g. #!/bin/sh), otherwise it will fail to execute. The returned
* ProcessExecution will have a non-null arguments list, an environment map that is a copy of the current process's
* environment, and a working directory set to its executable's parent directory.
*
* @param prefix a prefix command line that should be prepended to the executable's command line
* (e.g. "/usr/bin/nohup /usr/bin/sudo -u jboss -g jboss"). any files on the
* command line should be absolute paths. if null, no prefix command line will be
* prepended
* @param file an executable or a batch file
*
* @return a process execution
*/
public static ProcessExecution createProcessExecution(String prefix, File file) {
ProcessExecution processExecution;
List<String> prefixArgs;
if (prefix != null) {
// TODO (ips, 04/27/10): Ideally, the prefix should be a String[], not a String.
prefixArgs = Arrays.asList(prefix.split("[ \t]+"));
} else {
prefixArgs = Collections.emptyList();
}
String executable;
List<String> args = new ArrayList<String>();
if (OS_IS_WINDOWS && isBatchFile(file)) {
// Windows batch files cannot be executed directly - they must be passed as arguments to cmd.exe, e.g.
// "C:\Windows\System32\cmd.exe /c C:\opt\jboss-as\bin\run.bat".
executable = getCmdExeFile().getPath();
args.add("/c");
args.addAll(prefixArgs);
args.add(file.getPath());
} else {
// UNIX
if (prefixArgs.isEmpty()) {
executable = file.getPath();
} else {
executable = prefixArgs.get(0);
if (prefixArgs.size() > 1) {
args.addAll(prefixArgs.subList(1, prefixArgs.size()));
}
args.add(file.getPath());
}
}
processExecution = new ProcessExecution(executable);
processExecution.setArguments(args);
// Start out with a copy of our own environment, since Windows needs
// certain system environment variables to find DLLs, etc., and even
// on UNIX, many scripts will require certain environment variables
// (PATH, JAVA_HOME, etc.).
// TODO (ips, 04/27/12): We probably should not just do this by default.
Map<String, String> envVars = new LinkedHashMap<String, String>(System.getenv());
processExecution.setEnvironmentVariables(envVars);
// Many scripts (e.g. JBossAS scripts) assume their working directory is the directory containing the script.
processExecution.setWorkingDirectory(file.getParent());
return processExecution;
}
private static boolean isBatchFile(File file) {
return file.getName().matches(".*\\.((bat)|(cmd))$(?i)");
}
private static File getCmdExeFile() {
String cmdExe = System.getenv("COMSPEC");
if (cmdExe == null) {
throw new RuntimeException("COMSPEC environment variable is not defined.");
// TODO: Try to find cmd.exe by checking the various usual locations.
}
File cmdExeFile = new File(cmdExe);
if (!cmdExeFile.exists()) {
throw new RuntimeException("COMSPEC environment variable specifies a non-existent path: " + cmdExe);
}
return cmdExeFile;
}
}