/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.tools.internal.utils;
import java.io.File;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.constants.TimingConstantsServer;
/**
* Utility class for executing external commands.
*
* @author BREDEX GmbH
* @created Sep 11, 2007
*/
@Deprecated
public class ExternalCommandExecutor {
/** Constant for command for executing commands under DOS */
private static final String [] DOS_CMD = {"command.com", "/c"}; //$NON-NLS-1$ //$NON-NLS-2$
/** Constant for command for executing commands under Windows NT */
private static final String [] WIN_NT_CMD = {"cmd.exe", "/c"}; //$NON-NLS-1$ //$NON-NLS-2$
/** Constant for command for executing commands under Windows 9x */
private static final String [] WIN_9X_CMD = {"command.com", "/c"}; //$NON-NLS-1$ //$NON-NLS-2$
/**
* This class is a timer, which monitors the inner class ExecuteTask. After
* the given time, it asks the user, if the executed task should stop. This
* class is for JUnit tests public.
*/
public class MonitorTask extends TimerTask {
/** New thread to execute the given file. */
private ExecuteTask m_task = null;
/** Process state. True if inner process is finished. */
private boolean m_finished = false;
/** Inner process, started by the Runtime.exec() in the ExecuteTask. */
private Process m_process = null;
/** whether a timeout has occurred */
private boolean m_isTimeout;
/** whether the command is valid */
private boolean m_isCmdValid;
/** redirect object */
private SysoRedirect m_redirect;
/**
* The inner thread starts a new process to execute an command. It will
* be instantiate by the MonitorTask, which can also stop this thread.
*/
class ExecuteTask extends IsAliveThread {
/** The command to start by the Runtime. */
private String m_cmd;
/** the parameters for the command */
private String[] m_cmdParams;
/** The OS command needed in order to start the given command. */
private String [] m_prepend;
/** The folder, in whose the command will be started. */
private File m_dir = null;
/**
* Instantiate a new ExecuteTask and assigns the parameters to its
* own.
*
* @param path
* Path in whose the command should start.
* @param prepend
* The OS command required to execute the given command.
* @param cmdLine
* Command to start script-file plus the parameters.
*/
public ExecuteTask(File path, String [] prepend, String cmdLine) {
splitCmdLine(cmdLine); // sets m_cmd and m_cmdParams
m_prepend = prepend != null ? prepend : new String[0];
m_dir = path;
m_isTimeout = false;
m_isCmdValid = true;
}
/**
* Separates the command and the parameters from a command line
* @param cmdLine The command line and its parameters
*/
private void splitCmdLine(String cmdLine) {
try {
String cmdLinePar =
cmdLine.replaceAll("[\\\\]", "\\\\\\\\"); //$NON-NLS-1$ //$NON-NLS-2$
StreamTokenizer tk =
new StreamTokenizer(new StringReader(cmdLinePar));
tk.slashSlashComments(false);
tk.slashStarComments(false);
tk.lowerCaseMode(false);
tk.resetSyntax();
tk.wordChars(Character.MIN_VALUE, Character.MAX_VALUE);
tk.whitespaceChars(' ', ' ');
tk.whitespaceChars('\t', '\t');
tk.quoteChar('"');
tk.quoteChar('\'');
if (tk.nextToken() != StreamTokenizer.TT_EOF) {
m_cmd = tk.sval;
} else {
m_cmd = StringConstants.EMPTY;
}
List<String> params = new ArrayList<String>();
while (tk.nextToken() != StreamTokenizer.TT_EOF) {
params.add(tk.sval);
}
m_cmdParams = params.toArray(new String[params
.size()]);
} catch (IOException e) {
// error handling below
}
if (m_cmd == null) {
m_cmd = StringConstants.EMPTY;
}
if (m_cmdParams == null) {
m_cmdParams = new String[0];
}
}
/**
* Executes the command in the directory, which were set by the constructor. The process will
* be start by the Runtime.exec method and runs till it will be
* terminated. A Terminate can be caused by the user or the process,
* if it ends proper.
*/
public void run() {
Runtime runtime = Runtime.getRuntime();
try {
File cmdFile = new File(m_cmd);
if (!cmdFile.isAbsolute()) {
File absCmdFile = new File(m_dir, m_cmd);
if (absCmdFile.exists()) {
// Use the script in the working directory, if it exists
m_cmd = absCmdFile.getCanonicalPath();
}
}
List<String> commandTokens = new ArrayList<String>();
for (int i = 0; i < m_prepend.length; ++i) {
commandTokens.add(m_prepend[i]);
}
if (EnvironmentUtils.isWindowsOS()) {
commandTokens.add(StringConstants.QUOTE
+ m_cmd + StringConstants.QUOTE);
} else {
commandTokens.add(m_cmd);
}
for (int i = 0; i < m_cmdParams.length; ++i) {
commandTokens.add(m_cmdParams[i]);
}
ProcessBuilder pb =
new ProcessBuilder(commandTokens);
pb.directory(m_dir);
pb.redirectErrorStream(true);
m_process = pb.start();
if (m_process != null) {
m_redirect = new SysoRedirect(
m_process.getInputStream(),
"Command sysout: "); //$NON-NLS-1$
m_redirect.start();
m_process.waitFor();
} else {
m_isCmdValid = false;
}
} catch (IOException e) {
/* file or folder for command not found. */
m_isCmdValid = false;
} catch (InterruptedException e) {
/* waitFor is interrupted. */
}
// switch to signalize, that process is finished
m_finished = true;
}
}
/**
* Instantiates and starts the ExecuteTask.
*
* @param path
* Path in which the command will be started.
* @param prepend
* The OS command required to execute the given command.
* Can be <code>null</code>.
* @param cmd
* Command to start script-file
*/
public MonitorTask(File path, String [] prepend, String cmd) {
// instantiate inner thread
m_task = new ExecuteTask(path, prepend, cmd);
m_task.start(); // starts inner thread
}
/**
* Terminates the inner process and ends the corresponding task.
*/
public void run() {
if (finishTask()) {
m_isTimeout = true;
}
}
/**
* Terminates the inner thread.
*
* @return <code>true</code> if task was running when this method was
* called. Otherwise, <code>false</code>.
*/
public boolean finishTask() {
if (!isFinished()) {
if (m_process != null) {
m_process.destroy();
}
m_finished = true;
return true;
}
return false;
}
/**
* @return <code>true</code> if the task is finished. Otherwise,
* <code>false</code>.
*/
public boolean isFinished() {
return m_finished;
}
/**
* @return the exit code for the task.
* @throws IllegalThreadStateException if the task has not finished.
*/
public int getExitCode() throws IllegalThreadStateException {
return m_process.exitValue();
}
/**
* @return the standard output and error
* @throws IllegalThreadStateException if the task has not finished.
*/
public String getOutput() {
if (m_redirect != null) {
return m_redirect.getTruncatedLog();
}
return StringConstants.EMPTY;
}
/**
* @return <code>true</code> if a timeout occurred. Otherwise,
* <code>false</code>. Note that this method will always return
* <code>false</code> until a timeout actually occurs.
*/
public boolean hasTimeoutOccurred() {
return m_isTimeout;
}
/**
* @return <code>true</code> if the command was started successfully.
* Otherwise, <code>false</code>. Note that a return value of
* <code>true</code> does not guarantee that the command
* finished execution successfully.
*/
public boolean wasCmdValid() {
return m_isCmdValid;
}
}
/**
* Executes the given command in the given path and waits the given amount
* of time for the execution to finish. If the command finishes in good
* time, this method returns immediately after the script finishes.
* Otherwise, it returns after the given amount of time.
*
* @param path
* Path in which the command will be executed
* @param cmd
* Command to execute
* @param timeout Time (in milliseconds) to wait for the execution to finish.
* @return the monitor for the task.
*/
public MonitorTask executeCommand(File path, String cmd, int timeout) {
String [] prepend = new String[0];
if (EnvironmentUtils.isWin9xOS()) {
prepend = WIN_9X_CMD;
} else if (EnvironmentUtils.isDosOS()) {
prepend = DOS_CMD;
} else if (EnvironmentUtils.isWindowsOS()) {
// Windows NT; 2000; 2003; XP; ...
prepend = WIN_NT_CMD;
}
// For all other OSes, we don't bother with an interpreter
MonitorTask mt = new MonitorTask(path, prepend, cmd);
Timer timer = new Timer();
timer.schedule(mt, timeout);
while (!mt.isFinished()) {
TimeUtil.delay(TimingConstantsServer
.POLLING_DELAY_EXECUTE_EXTERNAL_COMMAND);
}
timer.cancel();
return mt;
}
}