update dev300-m58
[ooovba.git] / qadevOOo / runner / helper / ProcessHandler.java
blob5aff4644d35825a10f1ea3babc63e6caa23efd2a
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: ProcessHandler.java,v $
10 * $Revision: 1.14.2.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
30 package helper;
32 import java.io.InputStream;
33 import java.io.File;
34 import java.io.PrintWriter;
35 import java.io.PrintStream;
36 import java.io.LineNumberReader;
37 import java.io.InputStreamReader;
38 import java.io.OutputStreamWriter;
39 import java.util.Calendar;
40 import java.util.Date;
41 import java.util.GregorianCalendar;
42 import lib.TestParameters;
43 import util.PropertyName;
44 import util.utils;
46 /**
47 * Class collect information from input stream in
48 * background (sparate thread) and outputs it to
49 * some log stream. I helps to avoid buffer overflow
50 * when output stream has small buffer size (e.g.
51 * in case when handling stdout from external
52 * <code>Process</code>)
54 * This class is currently used by ProcesHandler
55 * internally only.
57 class Pump extends Thread
60 private LineNumberReader reader;
61 private String pref;
62 private StringBuffer buf = new StringBuffer(256);
63 private PrintWriter log;
65 /**
66 * Creates Pump for specified <code>InputStream</code>.
67 * This Pump also synchronously output text read to
68 * log by prefixed lines. Constructor immediately
69 * starts reading in a separate thread.
71 * @param is Stream which requires permanent reading.
72 * @param log Writer where prefixed text lines to be output
73 * @param outPrefix A prefix which is printed at the
74 * beginning of each output line.
76 public Pump(InputStream is, PrintWriter log, String outPrefix)
78 this.pref = (outPrefix == null) ? "" : outPrefix;
79 reader = new LineNumberReader(new InputStreamReader(is));
80 this.log = log;
81 start();
84 public void run()
86 try
88 String line = reader.readLine();
89 while (line != null)
91 log.println(pref + line);
92 log.flush();
93 buf.append(line).append('\n');
94 line = reader.readLine();
97 catch (java.io.IOException e)
99 log.println(pref + "Exception occured: " + e);
104 * Returns the text collected from input stream.
106 public String getStringBuffer()
108 return buf.toString();
113 * Class provides convenient way for running external program
114 * handle its standard streams, control execution and check results.
115 * Instance of this class must be created only for a single
116 * execution. If you need to execute the same command again you
117 * should create a new instance for this.
119 public class ProcessHandler
122 private String cmdLine;
123 private String[] cmdLineArray;
124 private String[] envVars = null;
125 private File workDir = null;
126 private PrintWriter log;
127 private int exitValue = -1;
128 private boolean isFinished = false;
129 private boolean isStarted = false;
130 private boolean mbTimedOut = false;
131 private long mTimeOut = 0;
132 private String stdInBuff = "";
133 private Pump stdout = null;
134 private Pump stderr = null;
135 private PrintStream stdIn = null;
136 private Process m_aProcess = null;
137 private TestParameters param = null;
138 private boolean debug = false;
141 * Creates instance with specified external command.
142 * Debug info and output
143 * of external command is printed to stdout.
144 * @param cmdLine
146 public ProcessHandler(String cmdLine)
148 this(cmdLine, null, null, null, 0);
152 * Creates instance with specified external command
153 * including parameters as an array.
154 * Debug info and output
155 * of external command is printed to stdout.
156 * @param cmdLines
158 public ProcessHandler(String[] cmdLines)
160 this(null, null, null, null, 0);
161 cmdLineArray = cmdLines;
165 * Creates instance with specified external command
166 * including parameters as an array, with environment
167 * variables.
168 * Debug info and output
169 * of external command is printed to stdout.
170 * @param cmdLines
171 * @param envVars
172 * @see java.lang.Runtime exec(String[], String[])
174 public ProcessHandler(String[] cmdLines, String[] envVars)
176 this(null, null, null, envVars, 0);
177 cmdLineArray = cmdLines;
181 * Creates instance with specified external command
182 * including parameters as an array, with environment
183 * variables. The command will be started in workDir.
184 * Debug info and output
185 * of external command is printed to stdout.
186 * @param cmdLines
187 * @param workDir
189 public ProcessHandler(String[] cmdLines, File workDir)
191 this(null, null, workDir, null, 0);
192 cmdLineArray = cmdLines;
197 * Creates instance with specified external command and
198 * log stream where debug info and output
199 * of external command is printed out. The command will be started in workDir.
200 * @param cmdLines
201 * @param log
202 * @param workDir
204 public ProcessHandler(String[] cmdLines, PrintWriter log, File workDir)
206 this(null, log, workDir, null, 0);
207 cmdLineArray = cmdLines;
211 * Creates instance with specified external command and
212 * log stream where debug info and output
213 * of external command is printed out.
214 * @param cmdLine
215 * @param log
217 public ProcessHandler(String cmdLine, PrintWriter log)
219 this(cmdLine, log, null, null, 0);
223 * Creates instance with specified external command and set the time out for the command.
224 * @param cmdLine
225 * @param timeOut
227 public ProcessHandler(String cmdLine, int timeOut)
229 this(cmdLine, null, null, null, timeOut);
233 * Creates instance with specified external command which
234 * will be executed in the some work directory.
235 * Debug info and output
236 * of external commandis printed to stdout.
237 * @param cmdLine
238 * @param workDir
240 public ProcessHandler(String cmdLine, File workDir)
242 this(cmdLine, null, workDir, null, 0);
246 * Creates instance with specified external command which
247 * will be executed in the some work directory.
248 * Debug info and output printed in log stream.
249 * @param cmdLine
250 * @param log
251 * @param workDir
253 public ProcessHandler(String cmdLine, PrintWriter log, File workDir)
255 this(cmdLine, log, workDir, null, 0);
259 * Creates instance with specified external command which
260 * will be executed in the some work directory and
261 * log stream where debug info and output
262 * of external command is printed .
263 * The specified environment variables are set for the new process.
264 * If log stream is null, logging is printed to stdout.
265 * @param cmdLine
266 * @param log
267 * @param workDir
268 * @param envVars
270 public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars)
272 this(cmdLine, log, workDir, envVars, 0);
276 * Creates instance with specified external command which
277 * will be executed in the some work directory and
279 * @param cmdLine the command to be executed
280 * @param log log stream where debug info and output
281 * of external command is printed .
282 * @param workDir The working directory of the new process
283 * @param envVars The specified environment variables are
284 * set for the new process.
285 * If log stream is null, logging is printed to stdout.
286 * @param timeOut When started sychronisly, the maximum time the
287 * process will live. When the process being destroyed
288 * a log will be written out. It can be asked on
289 * <code>isTimedOut()</code> if it has been terminated.
291 * timeOut > 0
292 * Waits specified time in miliSeconds for
293 * process to exit and return its status.
295 * timeOut = 0
296 * Waits for the process to end regulary
298 * timeOut < 0
299 * Kills the process immediately
303 public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars, long timeOut)
305 this.cmdLine = cmdLine;
306 this.workDir = workDir;
307 this.log = log;
308 this.cmdLine = cmdLine;
309 this.envVars = envVars;
310 if (log == null)
312 this.log = new PrintWriter(new OutputStreamWriter(System.out));
314 else
316 this.log = log;
318 this.mTimeOut = timeOut;
322 * Creates instance with specified external command which
323 * will be executed in the some work directory and
324 * log stream where debug info and output of external command is printed.
325 * If log stream is null, logging is printed to stdout.
326 * From the <CODE>TestParameters</CODE> the <CODE>OfficeWachter</CODE> get a ping.
327 * @param commands
328 * @param log
329 * @param workDir
330 * @param shortWait If this parameter is ture the <CODE>mTimeOut</CODE> is set to 5000 ms, else it is set to
331 * half of time out from parameter timeout.
332 * @param param the TestParameters
333 * @see lib.TestParameters
334 * @see helper.OfficeWatcher
336 public ProcessHandler(String[] commands, PrintWriter log, File workDir, int shortWait, TestParameters param)
338 this(null, log, workDir, null, 0);
339 this.cmdLineArray = commands;
340 this.param = param;
341 if (shortWait != 0)
343 this.mTimeOut = shortWait;
345 else
347 this.mTimeOut = (long) (param.getInt(PropertyName.TIME_OUT) / 1.3);
349 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE);
354 * This method do an asynchronous execution of the commands. To avoid a interruption on long running processes
355 * caused by <CODE>OfficeWatcher</CODE>, the OfficeWatcher get frequently a ping.
356 * @see helper.OfficeWatcher
358 public void runCommand()
361 boolean changedText = true;
362 int count = 0;
363 String memText = "";
365 this.executeAsynchronously();
367 OfficeWatcher ow = null;
368 if (param != null)
370 ow = (OfficeWatcher) param.get(PropertyName.OFFICE_WATCHER);
372 if (ow != null)
374 ow.ping();
377 int hangcheck = 10;
378 while (!this.isFinished() && changedText)
380 count++;
381 // dbg("runCommand: waiting " + mTimeOut / 1000 + " seconds while command execution is ongoing... " + count);
382 // shortWait(mTimeOut);
383 // shortWait(2000); // wait 2 seconds.
384 //waitFor(mTimeOut);
385 waitFor(2000, false); // wait but don't kill
387 if (ow != null)
389 ow.ping();
391 // check for changes in the output stream. If there are no changes, the process maybe hangs
392 if (!this.isFinished())
394 hangcheck--;
395 if (hangcheck < 0)
397 String sOutputText = getOutputText();
398 if (sOutputText.length() == memText.length())
400 changedText = false;
401 // dbg("runCommand Could not detect changes in output stream!!!");
403 hangcheck = 10;
404 memText = this.getOutputText();
409 if (!this.isFinished())
411 dbg("runCommand Process ist not finished but there are no changes in output stream.");
412 this.kill();
416 public boolean isTimedOut()
418 return mbTimedOut;
421 private void setTimedOut(boolean bTimedOut)
423 mbTimedOut = bTimedOut;
427 * Executes the command and returns only when the process
428 * exits.
430 * @return <code>true</code> if process was successfully
431 * started and correcly exits (exit code doesn't affect
432 * to this result).
434 public boolean executeSynchronously()
436 execute();
437 return waitFor(mTimeOut);
441 * Executes the command immediately returns. The process
442 * remains in running state. Control of its state should
443 * be made by <code>waitFor</code> methods.
445 * @return <code>true</code> if process was successfully
446 * started.
448 public boolean executeAsynchronously()
450 execute();
451 return isStarted();
454 public synchronized void kill()
456 if (!isStarted())
458 return;
460 boolean exit = false;
461 int counter = 1;
462 while (counter < 3 && !exit)
464 m_aProcess.destroy();
468 Thread.sleep(1000 * counter); // 5000
470 catch (java.lang.InterruptedException e)
475 final int exit_Value = m_aProcess.exitValue();
476 if (exit_Value < 1)
478 exit = true;
480 else
482 counter++;
484 dbg("kill: process closed with exit code " + exit_Value);
486 catch (java.lang.IllegalThreadStateException e)
488 if (counter < 3)
490 dbg("kill: Couldn't close process after " + counter + " attempts, trying again");
492 counter++;
495 isStarted = false;
499 * Returns the time in seconds since 1st January 1970
500 * @return
502 public static long getSystemTime()
504 // Calendar cal = new GregorianCalendar();
505 // final long nTime = cal.getTimeInMillis();
506 final long nTime = System.currentTimeMillis();
507 return nTime;
509 private long m_nExactStartTimeInMillisec;
511 private void initialExactStartTime()
513 m_nExactStartTimeInMillisec = getSystemTime();
516 public long getProcessStartTime()
518 return m_nExactStartTimeInMillisec;
521 protected void execute()
523 if (isStarted())
525 throw new RuntimeException(
526 "The process handler has already been executed.");
528 final Runtime runtime = Runtime.getRuntime();
531 if (cmdLine == null)
533 log.print(utils.getDateTime() + "execute: Starting command from array: ");
534 for (int i = 0; i < cmdLineArray.length; i++)
536 log.print(cmdLineArray[i]);
537 log.print(" ");
539 log.println("");
540 initialExactStartTime();
541 m_aProcess = runtime.exec(cmdLineArray, envVars);
543 else
545 if (workDir != null)
547 log.println(utils.getDateTime() + "execute: Starting command: " + cmdLine + " " +
548 workDir.getAbsolutePath());
549 m_aProcess = runtime.exec(cmdLine, envVars, workDir);
551 else
553 log.println(utils.getDateTime() + "execute: Starting command: " + cmdLine);
554 m_aProcess = runtime.exec(cmdLine, envVars);
557 isStarted = true;
559 catch (java.io.IOException e)
561 if (cmdLine == null)
563 log.println(utils.getDateTime() + "execute: The command array can't be started: " + e);
565 else
567 log.println(utils.getDateTime() + "execute: The command " + cmdLine + " can't be started: " + e);
569 return;
571 dbg("execute: pump io-streams");
572 stdout = new Pump(m_aProcess.getInputStream(), log, "out > ");
573 stderr = new Pump(m_aProcess.getErrorStream(), log, "err > ");
574 stdIn = new PrintStream(m_aProcess.getOutputStream());
576 // int nExitValue = m_aProcess.exitValue();
577 // int dummy = 0;
579 dbg("execute: flush io-streams");
581 flushInput();
585 * This method is useful when the process was executed
586 * asynchronously. Waits for process to exit and return
587 * its result.
589 * @return <code>true</code> if process correctly exited
590 * (exit code doesn't affect to this result).
592 public boolean waitFor()
594 return waitFor(0);
598 * This method is useful when the process was executed
599 * asynchronously. Waits during specified time for process
600 * to exit and return its status.
602 * @param timeout > 0
603 * Waits specified time in miliSeconds for
604 * process to exit and return its status.
606 * = 0
607 * Waits for the process to end regulary
609 * < 0
610 * Kills the process immediately
612 * @return <code>true</code> if process correctly exited
613 * (exit code doesn't affect to this result).
615 public boolean waitFor(long timeout)
617 return waitFor(timeout, true);
620 private boolean waitFor(long timeout, boolean bKillProcessAfterTimeout)
622 if (isFinished())
624 return true;
626 if (!isStarted())
628 return false;
631 if (timeout == 0)
635 m_aProcess.waitFor();
637 catch (InterruptedException e)
639 log.println("The process was interrupted: " + e);
641 isFinished = true;
644 exitValue = m_aProcess.exitValue();
646 catch (IllegalThreadStateException e)
650 else
654 while (!isFinished && timeout > 0)
656 isFinished = true;
657 Thread.sleep(1000);
658 timeout -= 1000;
661 exitValue = m_aProcess.exitValue(); // throws exception if not finished
663 catch (IllegalThreadStateException e)
665 isFinished = false;
668 if (timeout < 0)
670 setTimedOut(true);
671 log.println("The process has timed out!");
674 catch (InterruptedException ex)
676 log.println("The process was interrupted: " + ex);
680 if (bKillProcessAfterTimeout == true)
682 if (!isFinished)
684 log.println("Going to destroy the process!!");
685 m_aProcess.destroy();
686 log.println("Process has been destroyed!");
689 // Removed as hung up in SDK test 'PathSettings'
690 // try {
691 // stdout.join();
692 // stderr.join();
693 // } catch (InterruptedException e) {}
695 return isFinished();
698 protected void flushInput()
700 if (stdIn == null)
702 return;
705 synchronized(stdInBuff)
707 stdIn.print(stdInBuff);
708 stdIn.flush();
709 stdInBuff = "";
714 * Returns the text output by external command to stdout.
715 * @return the text output by external command to stdout
717 public String getOutputText()
719 if (stdout == null)
721 return "";
723 else
725 return stdout.getStringBuffer();
730 * Returns the text output by external command to stderr.
731 * @return the text output by external command to stderr
733 public String getErrorText()
735 if (stderr == null)
737 return "";
739 else
741 return stderr.getStringBuffer();
746 * Prints the string specified to sdtin of external
747 * command. '\n' is not added so if you need you
748 * should terminate the string with '\n'. <p>
750 * The method can also be called before the command
751 * starts its execution. Then the text is buffered
752 * and transfered to command when it will be started.
753 * @param str
755 public void printInputText(String str)
757 stdInBuff += str;
758 flushInput();
762 * Returns information about was the command started or
763 * not.
765 * @return <code>true</code> if the external command was
766 * found and successfully started.
768 public boolean isStarted()
770 return isStarted;
774 * Returns the information about the final state of command
775 * execution.
777 * @return <code>true</code> if the command correctly starts,
778 * exits and was not interrupted due to timeout.
780 public boolean isFinished()
782 return isFinished;
786 * Returns exit code of the external command.
788 * @return exit code of command if it was finished,
789 * -1 if not.
791 public int getExitCode()
795 exitValue = m_aProcess.exitValue();
797 catch (Exception e)
799 //System.out.println("No ExitValue available");
802 return exitValue;
805 /** Causes the thread to sleep some time.
806 * @param milliseconds
808 public static void shortWait(long milliseconds)
812 Thread.sleep(milliseconds);
814 catch (InterruptedException e)
816 System.out.println("While waiting :" + e);
820 private void dbg(String message)
822 if (debug)
824 log.println(utils.getDateTime() + "PH." + message);