office-gobmx/qadevOOo/runner/helper/ProcessHandler.java

669 lines
21 KiB
Java
Raw Normal View History

2003-01-27 09:27:53 -06:00
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2003-01-27 09:27:53 -06:00
*
* Copyright 2008 by Sun Microsystems, Inc.
2003-01-27 09:27:53 -06:00
*
* OpenOffice.org - a multi-platform office productivity suite
2003-01-27 09:27:53 -06:00
*
* $RCSfile: ProcessHandler.java,v $
2008-09-30 02:44:27 -05:00
* $Revision: 1.14.2.2 $
2003-01-27 09:27:53 -06:00
*
* This file is part of OpenOffice.org.
2003-01-27 09:27:53 -06:00
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
2003-01-27 09:27:53 -06:00
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
2003-01-27 09:27:53 -06:00
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
2003-01-27 09:27:53 -06:00
*
************************************************************************/
package helper;
import java.io.InputStream;
import java.io.File;
import java.io.PrintWriter;
import java.io.PrintStream;
import java.io.LineNumberReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import lib.TestParameters;
import util.PropertyName;
import util.utils;
2003-01-27 09:27:53 -06:00
/**
* Class collect information from input stream in
* background (sparate thread) and outputs it to
* some log stream. I helps to avoid buffer overflow
* when output stream has small buffer size (e.g.
* in case when handling stdout from external
* <code>Process</code>)
*
* This class is currently used by ProcesHandler
* internally only.
*/
class Pump extends Thread {
2003-01-27 09:27:53 -06:00
private LineNumberReader reader;
private String pref;
2003-01-27 09:27:53 -06:00
private StringBuffer buf = new StringBuffer(256);
private PrintWriter log;
2003-01-27 09:27:53 -06:00
/**
* Creates Pump for specified <code>InputStream</code>.
* This Pump also synchronously output text read to
* log by prefixed lines. Constructor immediately
* starts reading in a separate thread.
*
* @param is Stream which requires permanent reading.
* @param log Writer where prefixed text lines to be output
* @param outPrefix A prefix which is printed at the
* beginning of each output line.
*/
public Pump(InputStream is, PrintWriter log, String outPrefix) {
this.pref = outPrefix == null ? "" : outPrefix;
2003-01-27 09:27:53 -06:00
reader = new LineNumberReader(new InputStreamReader(is));
this.log = log;
start();
2003-01-27 09:27:53 -06:00
}
public void run() {
try {
String line = reader.readLine();
2003-01-27 09:27:53 -06:00
while (line != null) {
log.println(pref + line);
log.flush();
buf.append(line).append('\n');
line = reader.readLine();
2003-01-27 09:27:53 -06:00
}
} catch (java.io.IOException e) {
log.println(pref + "Exception occured: " + e);
2003-01-27 09:27:53 -06:00
}
}
/**
* Returns the text collected from input stream.
*/
public String getStringBuffer() {
return buf.toString();
}
}
/**
* Class provides convenient way for running external program
* handle its standard streams, control execution and check results.
* Instance of this class must be created only for a single
* execution. If you need to execute the same command again you
* should create a new instance for this.
*/
public class ProcessHandler {
2003-01-27 09:27:53 -06:00
private String cmdLine;
private String[] cmdLineArray;
2003-01-27 09:27:53 -06:00
private String[] envVars = null;
private File workDir = null;
private PrintWriter log;
private int exitValue = -1;
private boolean isFinished = false;
private boolean isStarted = false;
private boolean mbTimedOut = false;
private long mTimeOut = 0;
2003-01-27 09:27:53 -06:00
private String stdInBuff = "";
private Pump stdout = null;
private Pump stderr = null;
private PrintStream stdIn = null;
private Process proc = null;
private TestParameters param = null;
private boolean debug = false;
2003-01-27 09:27:53 -06:00
/**
* Creates instance with specified external command.
* Debug info and output
* of external command is printed to stdout.
* @param cmdLine
*/
public ProcessHandler(String cmdLine) {
this(cmdLine, null, null, null, 0);
}
/**
* Creates instance with specified external command
* including parameters as an array.
* Debug info and output
* of external command is printed to stdout.
* @param cmdLines
*/
public ProcessHandler(String[] cmdLines) {
this(null, null, null, null, 0);
cmdLineArray = cmdLines;
}
/**
* Creates instance with specified external command
* including parameters as an array, with environment
* variables.
* Debug info and output
* of external command is printed to stdout.
* @param cmdLines
* @param envVars
* @see java.lang.Runtime exec(String[], String[])
*/
public ProcessHandler(String[] cmdLines, String[] envVars) {
this(null, null, null, envVars, 0);
cmdLineArray = cmdLines;
}
/**
* Creates instance with specified external command
* including parameters as an array, with environment
* variables. The command will be started in workDir.
* Debug info and output
* of external command is printed to stdout.
* @param cmdLines
* @param workDir
*/
public ProcessHandler(String[] cmdLines, File workDir) {
this(null, null, workDir, null, 0);
cmdLineArray = cmdLines;
}
2003-01-27 09:27:53 -06:00
/**
* Creates instance with specified external command and
* log stream where debug info and output
* of external command is printed out. The command will be started in workDir.
* @param cmdLines
* @param log
* @param workDir
*/
public ProcessHandler(String[] cmdLines, PrintWriter log, File workDir) {
this(null, log, workDir, null, 0);
cmdLineArray = cmdLines;
}
/**
* Creates instance with specified external command and
* log stream where debug info and output
* of external command is printed out.
* @param cmdLine
* @param log
2003-01-27 09:27:53 -06:00
*/
public ProcessHandler(String cmdLine, PrintWriter log) {
this(cmdLine, log, null, null, 0);
2003-01-27 09:27:53 -06:00
}
/**
* Creates instance with specified external command and set the time out for the command.
* @param cmdLine
* @param timeOut
*/
public ProcessHandler(String cmdLine, int timeOut) {
this(cmdLine, null, null, null, timeOut);
}
2003-01-27 09:27:53 -06:00
/**
* Creates instance with specified external command which
* will be executed in the some work directory.
2003-01-27 09:27:53 -06:00
* Debug info and output
* of external commandis printed to stdout.
* @param cmdLine
* @param workDir
2003-01-27 09:27:53 -06:00
*/
public ProcessHandler(String cmdLine, File workDir) {
this(cmdLine, null, workDir, null, 0);
2003-01-27 09:27:53 -06:00
}
/**
* Creates instance with specified external command which
* will be executed in the some work directory.
* Debug info and output printed in log stream.
* @param cmdLine
* @param log
* @param workDir
*/
public ProcessHandler(String cmdLine, PrintWriter log, File workDir) {
this(cmdLine, log, workDir, null, 0);
}
2003-01-27 09:27:53 -06:00
/**
* Creates instance with specified external command which
* will be executed in the some work directory and
* log stream where debug info and output
* of external command is printed .
* The specified environment variables are set for the new process.
* If log stream is null, logging is printed to stdout.
* @param cmdLine
* @param log
* @param workDir
* @param envVars
2003-01-27 09:27:53 -06:00
*/
public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars) {
this(cmdLine, log, workDir, envVars, 0);
}
/**
* Creates instance with specified external command which
* will be executed in the some work directory and
*
* @param cmdLine the command to be executed
* @param log log stream where debug info and output
* of external command is printed .
* @param workDir The working directory of the new process
* @param envVars The specified environment variables are
* set for the new process.
* If log stream is null, logging is printed to stdout.
* @param timeOut When started sychronisly, the maximum time the
* process will live. When the process being destroyed
* a log will be written out. It can be asked on
* <code>isTimedOut()</code> if it has been terminated.
*
* timeOut > 0
* Waits specified time in miliSeconds for
* process to exit and return its status.
*
* timeOut = 0
* Waits for the process to end regulary
*
* timeOut < 0
* Kills the process immediately
*
*
*/
public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars, long timeOut) {
this.cmdLine = cmdLine;
2003-01-27 09:27:53 -06:00
this.workDir = workDir;
this.log = log;
this.cmdLine = cmdLine;
2003-01-27 09:27:53 -06:00
this.envVars = envVars;
if (log == null) {
this.log = new PrintWriter(new OutputStreamWriter(System.out));
} else {
2003-01-27 09:27:53 -06:00
this.log = log;
}
this.mTimeOut = timeOut;
2003-01-27 09:27:53 -06:00
}
/**
* Creates instance with specified external command which
* will be executed in the some work directory and
* log stream where debug info and output of external command is printed.
* If log stream is null, logging is printed to stdout.
* From the <CODE>TestParameters</CODE> the <CODE>OfficeWachter</CODE> get a ping.
* @param commands
* @param log
* @param workDir
* @param shortWait If this parameter is ture the <CODE>mTimeOut</CODE> is set to 3000 ms, else it is set to
* half of time out from parameter timeout.
* @param param the TestParameters
* @see lib.TestParameters
* @see helper.OfficeWatcher
*/
public ProcessHandler(String[] commands, PrintWriter log, File workDir, boolean shortWait, TestParameters param) {
this(null, log, workDir, null, 0);
this.cmdLineArray = commands;
this.param = param;
if (shortWait) {
this.mTimeOut = 5000;
} else {
this.mTimeOut = (long) (param.getInt(PropertyName.TIME_OUT) / 1.3);
}
debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE);
}
/**
* This method do an asynchronous execution of the commands. To avoid a interruption on long running processes
* caused by <CODE>OfficeWatcher</CODE>, the OfficeWatcher get frequently a ping.
* @see helper.OfficeWatcher
*/
public void runCommand() {
boolean changedText = true;
int count = 0;
String memText = "";
this.executeAsynchronously();
OfficeWatcher ow = null;
if (param != null) {
ow = (OfficeWatcher) param.get(PropertyName.OFFICE_WATCHER);
}
while (changedText && !this.isFinished()) {
count++;
if (ow != null) {
ow.ping();
}
dbg("runCommand: waiting " + mTimeOut / 1000 + " seconds while command execution is ongoing... " + count);
shortWait(mTimeOut);
if (ow != null) {
ow.ping();
}
// check for changes in the output stream. If there are no changes, the process maybe hangs
if (!this.isFinished()) {
if (this.getOutputText().equals(memText)) {
changedText = false;
dbg("runCommand Could not detect changes in output stream!!!");
}
memText = this.getOutputText();
}
}
if (!this.isFinished()) {
dbg("runCommand Process ist not finished but there are no changes in output stream.");
this.kill();
}
}
public boolean isTimedOut() {
return mbTimedOut;
}
private void setTimedOut(boolean bTimedOut) {
mbTimedOut = bTimedOut;
2003-01-27 09:27:53 -06:00
}
/**
* Executes the command and returns only when the process
* exits.
*
* @return <code>true</code> if process was successfully
* started and correcly exits (exit code doesn't affect
* to this result).
*/
public boolean executeSynchronously() {
execute();
return waitFor(mTimeOut);
2003-01-27 09:27:53 -06:00
}
/**
* Executes the command immediately returns. The process
* remains in running state. Control of its state should
* be made by <code>waitFor</code> methods.
*
* @return <code>true</code> if process was successfully
* started.
*/
public boolean executeAsynchronously() {
execute();
return isStarted();
2003-01-27 09:27:53 -06:00
}
public synchronized void kill() {
if (!isStarted()) {
return;
}
boolean exit = false;
int counter = 1;
while (counter < 3 && !exit) {
proc.destroy();
try {
2008-09-30 02:44:27 -05:00
Thread.sleep(1000 * counter); // 5000
} catch (java.lang.InterruptedException e) {
}
try {
final int exit_Value = proc.exitValue();
if (exit_Value < 1) {
exit = true;
} else {
counter++;
}
dbg("kill: process closed with exit code " + exit_Value);
} catch (java.lang.IllegalThreadStateException e) {
if (counter < 3) {
dbg("kill: Couldn't close process after " + counter + " attempts, trying again");
}
counter++;
}
}
2003-01-27 09:27:53 -06:00
isStarted = false;
}
protected void execute() {
if (isStarted()) {
throw new RuntimeException(
"The process handler has already been executed.");
2003-01-27 09:27:53 -06:00
}
final Runtime runtime = Runtime.getRuntime();
2003-01-27 09:27:53 -06:00
try {
if (cmdLine == null) {
log.print(utils.getDateTime() + "execute: Starting command from array: ");
for (int i = 0; i < cmdLineArray.length; i++) {
log.print(cmdLineArray[i]);
log.print(" ");
}
2008-09-30 02:44:27 -05:00
log.println("");
proc = runtime.exec(cmdLineArray, envVars);
} else {
if (workDir != null) {
log.println(utils.getDateTime() + "execute: Starting command: " + cmdLine + " " +
workDir.getAbsolutePath());
proc = runtime.exec(cmdLine, envVars, workDir);
} else {
log.println(utils.getDateTime() + "execute: Starting command: " + cmdLine);
proc = runtime.exec(cmdLine, envVars);
}
2003-01-27 09:27:53 -06:00
}
isStarted = true;
} catch (java.io.IOException e) {
if (cmdLine == null) {
log.println(utils.getDateTime() + "execute: The command array can't be started: " + e);
} else {
log.println(utils.getDateTime() + "execute: The command " + cmdLine + " can't be started: " + e);
}
2003-01-27 09:27:53 -06:00
return;
}
dbg("execute: pump io-streams");
2003-01-27 09:27:53 -06:00
stdout = new Pump(proc.getInputStream(), log, "out > ");
stderr = new Pump(proc.getErrorStream(), log, "err > ");
stdIn = new PrintStream(proc.getOutputStream());
dbg("execute: flush io-streams");
2003-01-27 09:27:53 -06:00
flushInput();
2003-01-27 09:27:53 -06:00
}
/**
* This method is useful when the process was executed
* asynchronously. Waits for process to exit and return
* its result.
*
* @return <code>true</code> if process correctly exited
* (exit code doesn't affect to this result).
*/
public boolean waitFor() {
return waitFor(0);
2003-01-27 09:27:53 -06:00
}
/**
* This method is useful when the process was executed
* asynchronously. Waits during specified time for process
* to exit and return its status.
*
* @param timeout > 0
* Waits specified time in miliSeconds for
* process to exit and return its status.
*
* = 0
* Waits for the process to end regulary
*
* < 0
* Kills the process immediately
*
2003-01-27 09:27:53 -06:00
* @return <code>true</code> if process correctly exited
* (exit code doesn't affect to this result).
*/
public boolean waitFor(long timeout) {
if (isFinished()) {
return true;
}
if (!isStarted()) {
return false;
}
2003-01-27 09:27:53 -06:00
if (timeout == 0) {
try {
proc.waitFor();
2003-01-27 09:27:53 -06:00
} catch (InterruptedException e) {
log.println("The process was interrupted: " + e);
}
isFinished = true;
2003-01-27 09:27:53 -06:00
try {
exitValue = proc.exitValue();
} catch (IllegalThreadStateException e) {
}
2003-01-27 09:27:53 -06:00
} else {
try {
while (!isFinished && timeout > 0) {
isFinished = true;
2003-01-27 09:27:53 -06:00
Thread.sleep(1000);
timeout -= 1000;
2003-01-27 09:27:53 -06:00
try {
exitValue = proc.exitValue(); // throws exception if not finished
2003-01-27 09:27:53 -06:00
} catch (IllegalThreadStateException e) {
isFinished = false;
2003-01-27 09:27:53 -06:00
}
}
if (timeout < 0) {
setTimedOut(true);
log.println("The process has timed out!");
}
2003-01-27 09:27:53 -06:00
} catch (InterruptedException ex) {
log.println("The process was interrupted: " + ex);
}
}
if (!isFinished) {
log.println("Going to destroy the process!!");
2003-01-27 09:27:53 -06:00
proc.destroy();
log.println("Process has been destroyed!");
2003-01-27 09:27:53 -06:00
}
// Removed as hung up in SDK test 'PathSettings'
// try {
// stdout.join();
// stderr.join();
// } catch (InterruptedException e) {}
2003-01-27 09:27:53 -06:00
return isFinished();
2003-01-27 09:27:53 -06:00
}
protected void flushInput() {
if (stdIn == null) {
return;
}
2003-01-27 09:27:53 -06:00
synchronized (stdInBuff) {
2003-01-27 09:27:53 -06:00
stdIn.print(stdInBuff);
stdIn.flush();
stdInBuff = "";
2003-01-27 09:27:53 -06:00
}
}
/**
* Returns the text output by external command to stdout.
* @return the text output by external command to stdout
2003-01-27 09:27:53 -06:00
*/
public String getOutputText() {
if (stdout == null) {
return "";
} else {
return stdout.getStringBuffer();
}
2003-01-27 09:27:53 -06:00
}
2003-01-27 09:27:53 -06:00
/**
* Returns the text output by external command to stderr.
* @return the text output by external command to stderr
2003-01-27 09:27:53 -06:00
*/
public String getErrorText() {
if (stderr == null) {
return "";
} else {
return stderr.getStringBuffer();
}
2003-01-27 09:27:53 -06:00
}
/**
* Prints the string specified to sdtin of external
* command. '\n' is not added so if you need you
* should terminate the string with '\n'. <p>
*
* The method can also be called before the command
* starts its execution. Then the text is buffered
* and transfered to command when it will be started.
* @param str
2003-01-27 09:27:53 -06:00
*/
public void printInputText(String str) {
stdInBuff += str;
2003-01-27 09:27:53 -06:00
flushInput();
}
/**
* Returns information about was the command started or
* not.
*
* @return <code>true</code> if the external command was
* found and successfully started.
*/
public boolean isStarted() {
return isStarted;
2003-01-27 09:27:53 -06:00
}
/**
* Returns the information about the final state of command
* execution.
*
* @return <code>true</code> if the command correctly starts,
* exits and was not interrupted due to timeout.
*/
public boolean isFinished() {
return isFinished;
2003-01-27 09:27:53 -06:00
}
/**
* Returns exit code of the external command.
*
* @return exit code of command if it was finished,
* -1 if not.
*/
public int getExitCode() {
try {
exitValue = proc.exitValue();
} catch (Exception e) {
//System.out.println("No ExitValue available");
}
return exitValue;
}
/** Causes the thread to sleep some time.
* @param milliseconds
*/
public static void shortWait(long milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
System.out.println("While waiting :" + e);
}
}
private void dbg(String message) {
if (debug) {
log.println(utils.getDateTime() + "PH." + message);
}
2003-01-27 09:27:53 -06:00
}
}