Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / qadevOOo / runner / helper / ProcessHandler.java
blobc30a501e191c604c5a5eb98b479f2eb1ef4a6eb6
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
27 package helper;
29 import java.io.BufferedReader;
30 import java.io.InputStream;
31 import java.io.File;
32 import java.io.PrintWriter;
33 import java.io.PrintStream;
34 import java.io.LineNumberReader;
35 import java.io.InputStreamReader;
36 import java.io.OutputStreamWriter;
37 import java.io.Writer;
38 import java.util.Calendar;
39 import java.util.Date;
40 import java.util.GregorianCalendar;
41 import lib.TestParameters;
42 import share.LogWriter;
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;
64 private boolean bOutput;
66 /**
67 * Creates Pump for specified <code>InputStream</code>.
68 * This Pump also synchronously output text read to
69 * log by prefixed lines. Constructor immediately
70 * starts reading in a separate thread.
72 * @param is Stream which requires permanent reading.
73 * @param log Writer where prefixed text lines to be output
74 * @param outPrefix A prefix which is printed at the
75 * beginning of each output line.
77 public Pump(InputStream is, PrintWriter log, String outPrefix, boolean _bOutput)
79 this.pref = (outPrefix == null) ? "" : outPrefix;
80 reader = new LineNumberReader(new InputStreamReader(is));
81 this.log = log;
82 this.bOutput = _bOutput;
83 start();
86 public void run()
88 try
90 String line = reader.readLine();
91 while (line != null)
93 if (bOutput)
95 log.println(pref + line);
96 log.flush();
98 buf.append(line).append('\n');
99 line = reader.readLine();
102 catch (java.io.IOException e)
104 log.println(pref + "Exception occurred: " + e);
109 * Returns the text collected from input stream.
111 public String getStringBuffer()
113 return buf.toString();
118 * Class provides convenient way for running external program
119 * handle its standard streams, control execution and check results.
120 * Instance of this class must be created only for a single
121 * execution. If you need to execute the same command again you
122 * should create a new instance for this.
124 public class ProcessHandler
127 private String cmdLine;
128 private String[] cmdLineArray;
129 private String[] envVars = null;
130 private File workDir = null;
131 private PrintWriter log;
132 private int exitValue = -1;
133 private boolean isFinished = false;
134 private boolean isStarted = false;
135 private boolean mbTimedOut = false;
136 private long mTimeOut = 0;
137 private String stdInBuff = "";
138 private Pump stdout = null;
139 private Pump stderr = null;
140 private PrintStream stdIn = null;
141 private Process m_aProcess = null;
142 private TestParameters param = null;
143 private boolean debug = false;
144 private boolean bUseOutput = true;
146 private int m_nProcessTimeout = 0;
147 private String m_sProcessKiller;
148 private ProcessWatcher m_aWatcher;
151 * Creates instance with specified external command.
152 * Debug info and output
153 * of external command is printed to stdout.
154 * @param cmdLine
156 public ProcessHandler(String cmdLine)
158 this(cmdLine, null, null, null, 0);
162 * Creates instance with specified external command
163 * including parameters as an array.
164 * Debug info and output
165 * of external command is printed to stdout.
166 * @param cmdLines
168 public ProcessHandler(String[] cmdLines)
170 this(null, null, null, null, 0);
171 cmdLineArray = cmdLines;
175 * Creates instance with specified external command
176 * including parameters as an array, with environment
177 * variables.
178 * Debug info and output
179 * of external command is printed to stdout.
180 * @param cmdLines
181 * @param envVars
182 * @see java.lang.Runtime exec(String[], String[])
184 public ProcessHandler(String[] cmdLines, String[] envVars)
186 this(null, null, null, envVars, 0);
187 cmdLineArray = cmdLines;
191 * Creates instance with specified external command
192 * including parameters as an array, with environment
193 * variables. The command will be started in workDir.
194 * Debug info and output
195 * of external command is printed to stdout.
196 * @param cmdLines
197 * @param workDir
199 public ProcessHandler(String[] cmdLines, File workDir)
201 this(null, null, workDir, null, 0);
202 cmdLineArray = cmdLines;
207 * Creates instance with specified external command and
208 * log stream where debug info and output
209 * of external command is printed out. The command will be started in workDir.
210 * @param cmdLines
211 * @param log
212 * @param workDir
214 public ProcessHandler(String[] cmdLines, PrintWriter log, File workDir)
216 this(null, log, workDir, null, 0);
217 cmdLineArray = cmdLines;
221 * Creates instance with specified external command and
222 * log stream where debug info and output
223 * of external command is printed out.
224 * @param cmdLine
225 * @param log
227 public ProcessHandler(String cmdLine, PrintWriter log)
229 this(cmdLine, log, null, null, 0);
233 * Creates instance with specified external command and set the time out for the command.
234 * @param cmdLine
235 * @param timeOut
237 public ProcessHandler(String cmdLine, int timeOut)
239 this(cmdLine, null, null, null, timeOut);
243 * Creates instance with specified external command which
244 * will be executed in the some work directory.
245 * Debug info and output
246 * of external commandis printed to stdout.
247 * @param cmdLine
248 * @param workDir
250 public ProcessHandler(String cmdLine, File workDir)
252 this(cmdLine, null, workDir, null, 0);
256 * Creates instance with specified external command which
257 * will be executed in the some work directory.
258 * Debug info and output printed in log stream.
259 * @param cmdLine
260 * @param log
261 * @param workDir
263 public ProcessHandler(String cmdLine, PrintWriter log, File workDir)
265 this(cmdLine, log, workDir, null, 0);
269 * Creates instance with specified external command which
270 * will be executed in the some work directory and
271 * log stream where debug info and output
272 * of external command is printed .
273 * The specified environment variables are set for the new process.
274 * If log stream is null, logging is printed to stdout.
275 * @param cmdLine
276 * @param log
277 * @param workDir
278 * @param envVars
280 public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars)
282 this(cmdLine, log, workDir, envVars, 0);
286 * Creates instance with specified external command which
287 * will be executed in the some work directory and
289 * @param cmdLine the command to be executed
290 * @param log log stream where debug info and output
291 * of external command is printed .
292 * @param workDir The working directory of the new process
293 * @param envVars The specified environment variables are
294 * set for the new process.
295 * If log stream is null, logging is printed to stdout.
296 * @param timeOut When started sychronisly, the maximum time the
297 * process will live. When the process being destroyed
298 * a log will be written out. It can be asked on
299 * <code>isTimedOut()</code> if it has been terminated.
301 * timeOut > 0
302 * Waits specified time in miliSeconds for
303 * process to exit and return its status.
305 * timeOut = 0
306 * Waits for the process to end regulary
308 * timeOut < 0
309 * Kills the process immediately
313 public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars, long timeOut)
315 this.cmdLine = cmdLine;
316 this.workDir = workDir;
317 this.log = log;
318 this.cmdLine = cmdLine;
319 this.envVars = envVars;
320 if (log == null)
322 this.log = new PrintWriter(new OutputStreamWriter(System.out));
324 else
326 this.log = log;
328 this.mTimeOut = timeOut;
332 * Creates instance with specified external command which
333 * will be executed in the some work directory and
334 * log stream where debug info and output of external command is printed.
335 * If log stream is null, logging is printed to stdout.
336 * From the <CODE>TestParameters</CODE> the <CODE>OfficeWachter</CODE> get a ping.
337 * @param commands
338 * @param log
339 * @param workDir
340 * @param shortWait If this parameter is ture the <CODE>mTimeOut</CODE> is set to 5000 ms, else it is set to
341 * half of time out from parameter timeout.
342 * @param param the TestParameters
343 * @see lib.TestParameters
344 * @see helper.OfficeWatcher
346 public ProcessHandler(String[] commands, PrintWriter log, File workDir, int shortWait, TestParameters param)
348 this(null, log, workDir, null, 0);
349 this.cmdLineArray = commands;
350 this.param = param;
351 if (shortWait != 0)
353 this.mTimeOut = shortWait;
355 else
357 this.mTimeOut = (long) (param.getInt(PropertyName.TIME_OUT) / 1.3);
359 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE);
364 * If not equal 0, the time to maximal wait.
365 * @param _n
367 public void setProcessTimeout(int _n)
369 m_nProcessTimeout = _n;
373 * This command will call after ProcessTimeout is arrived.
374 * @param _s
376 public void setProcessKiller(String _s)
378 m_sProcessKiller = _s;
382 * This method do an asynchronous execution of the commands. To avoid a interruption on long running processes
383 * caused by <CODE>OfficeWatcher</CODE>, the OfficeWatcher get frequently a ping.
384 * @see helper.OfficeWatcher
386 public void runCommand()
389 boolean changedText = true;
390 int count = 0;
391 String memText = "";
393 this.executeAsynchronously();
395 OfficeWatcher ow = null;
396 if (param != null)
398 ow = (OfficeWatcher) param.get(PropertyName.OFFICE_WATCHER);
400 if (ow != null)
402 ow.ping();
405 int hangcheck = 10;
406 while (!this.isFinished() && changedText)
408 count++;
409 // dbg("runCommand: waiting " + mTimeOut / 1000 + " seconds while command execution is ongoing... " + count);
410 // shortWait(mTimeOut);
411 // shortWait(2000); // wait 2 seconds.
412 //waitFor(mTimeOut);
413 waitFor(2000, false); // wait but don't kill
415 if (ow != null)
417 ow.ping();
419 // check for changes in the output stream. If there are no changes, the process maybe hangs
420 if (!this.isFinished())
422 hangcheck--;
423 if (hangcheck < 0)
425 String sOutputText = getOutputText();
426 if (sOutputText.length() == memText.length())
428 changedText = false;
429 // dbg("runCommand Could not detect changes in output stream!!!");
431 hangcheck = 10;
432 memText = this.getOutputText();
437 if (!this.isFinished())
439 dbg("runCommand Process ist not finished but there are no changes in output stream.");
440 this.kill();
444 public boolean isTimedOut()
446 return mbTimedOut;
449 private void setTimedOut(boolean bTimedOut)
451 mbTimedOut = bTimedOut;
455 * Executes the command and returns only when the process
456 * exits.
458 * @return <code>true</code> if process was successfully
459 * started and correcly exits (exit code doesn't affect
460 * to this result).
462 public boolean executeSynchronously()
464 execute();
465 return waitFor(mTimeOut);
469 * Executes the command immediately returns. The process
470 * remains in running state. Control of its state should
471 * be made by <code>waitFor</code> methods.
473 * @return <code>true</code> if process was successfully
474 * started.
476 public boolean executeAsynchronously()
478 execute();
479 return isStarted();
482 public synchronized void kill()
484 if (!isStarted())
486 return;
488 boolean exit = false;
489 int counter = 1;
490 while (counter < 3 && !exit)
492 m_aProcess.destroy();
496 Thread.sleep(1000 * counter); // 5000
498 catch (java.lang.InterruptedException e)
503 final int exit_Value = m_aProcess.exitValue();
504 if (exit_Value < 1)
506 exit = true;
508 else
510 counter++;
512 dbg("kill: process closed with exit code " + exit_Value);
514 catch (java.lang.IllegalThreadStateException e)
516 if (counter < 3)
518 dbg("kill: Couldn't close process after " + counter + " attempts, trying again");
520 counter++;
523 isStarted = false;
527 * Returns the time in seconds since 1st January 1970
528 * @return
530 public static long getSystemTime()
532 // Calendar cal = new GregorianCalendar();
533 // final long nTime = cal.getTimeInMillis();
534 final long nTime = System.currentTimeMillis();
535 return nTime;
537 private long m_nExactStartTimeInMillisec;
539 private void initialExactStartTime()
541 m_nExactStartTimeInMillisec = getSystemTime();
544 public long getProcessStartTime()
546 return m_nExactStartTimeInMillisec;
549 private void showEnvVars()
551 if (envVars != null)
553 for (int i = 0; i < envVars.length; i++)
555 log.println("env: " + envVars[i]);
558 else
560 log.println("env: null");
564 protected void execute()
566 if (isStarted())
568 throw new RuntimeException(
569 "The process handler has already been executed.");
571 final Runtime runtime = Runtime.getRuntime();
574 if (cmdLine == null)
576 log.println(utils.getDateTime() + "execute: Starting command from array: ");
577 for (int i = 0; i < cmdLineArray.length; i++)
579 log.println(cmdLineArray[i]);
580 // log.print(" ");
582 showEnvVars();
583 log.println("");
584 initialExactStartTime();
585 initializeProcessKiller();
586 m_aProcess = runtime.exec(cmdLineArray, envVars);
588 else
590 if (workDir != null)
592 log.println(utils.getDateTime() + "execute: Starting command: ");
593 log.println(cmdLine + " path=" + workDir.getAbsolutePath());
594 showEnvVars();
595 m_aProcess = runtime.exec(cmdLine, envVars, workDir);
597 else
599 log.println(utils.getDateTime() + "execute: Starting command: ");
600 log.println(cmdLine);
601 showEnvVars();
602 m_aProcess = runtime.exec(cmdLine, envVars);
605 isStarted = true;
607 catch (java.io.IOException e)
609 if (cmdLine == null)
611 log.println(utils.getDateTime() + "execute: The command array can't be started: " + e);
613 else
615 log.println(utils.getDateTime() + "execute: The command " + cmdLine + " can't be started: " + e);
617 return;
619 dbg("execute: pump io-streams");
620 stdout = new Pump(m_aProcess.getInputStream(), log, "out > ", bUseOutput);
621 stderr = new Pump(m_aProcess.getErrorStream(), log, "err > ", bUseOutput);
622 stdIn = new PrintStream(m_aProcess.getOutputStream());
624 // int nExitValue = m_aProcess.exitValue();
625 // int dummy = 0;
627 dbg("execute: flush io-streams");
629 flushInput();
633 * This method is useful when the process was executed
634 * asynchronously. Waits for process to exit and return
635 * its result.
637 * @return <code>true</code> if process correctly exited
638 * (exit code doesn't affect to this result).
640 public boolean waitFor()
642 return waitFor(0);
646 * This method is useful when the process was executed
647 * asynchronously. Waits during specified time for process
648 * to exit and return its status.
650 * @param timeout > 0
651 * Waits specified time in miliSeconds for
652 * process to exit and return its status.
654 * = 0
655 * Waits for the process to end regulary
657 * < 0
658 * Kills the process immediately
660 * @return <code>true</code> if process correctly exited
661 * (exit code doesn't affect to this result).
663 public boolean waitFor(long timeout)
665 return waitFor(timeout, true);
668 private boolean waitFor(long timeout, boolean bKillProcessAfterTimeout)
670 if (isFinished())
672 return true;
674 if (!isStarted())
676 return false;
679 if (timeout == 0)
683 m_aProcess.waitFor();
685 catch (InterruptedException e)
687 log.println("The process was interrupted: " + e);
689 isFinished = true;
692 exitValue = m_aProcess.exitValue();
694 catch (IllegalThreadStateException e)
698 else
702 while (!isFinished && timeout > 0)
704 isFinished = true;
705 Thread.sleep(1000);
706 timeout -= 1000;
709 exitValue = m_aProcess.exitValue(); // throws exception if not finished
711 catch (IllegalThreadStateException e)
713 isFinished = false;
716 if (timeout < 0)
718 setTimedOut(true);
719 log.println("The process has timed out!");
722 catch (InterruptedException ex)
724 log.println("The process was interrupted: " + ex);
728 if (bKillProcessAfterTimeout == true)
730 if (!isFinished)
732 log.println("Going to destroy the process!!");
733 m_aProcess.destroy();
734 log.println("Process has been destroyed!");
737 // Removed as hung up in SDK test 'PathSettings'
738 // try {
739 // stdout.join();
740 // stderr.join();
741 // } catch (InterruptedException e) {}
743 return isFinished();
746 protected void flushInput()
748 if (stdIn == null)
750 return;
753 synchronized(stdInBuff)
755 stdIn.print(stdInBuff);
756 stdIn.flush();
757 stdInBuff = "";
762 * Returns the text output by external command to stdout.
763 * @return the text output by external command to stdout
765 public String getOutputText()
767 if (stdout == null)
769 return "";
771 else
773 return stdout.getStringBuffer();
778 * Returns the text output by external command to stderr.
779 * @return the text output by external command to stderr
781 public String getErrorText()
783 if (stderr == null)
785 return "";
787 else
789 return stderr.getStringBuffer();
794 * Prints the string specified to sdtin of external
795 * command. '\n' is not added so if you need you
796 * should terminate the string with '\n'. <p>
798 * The method can also be called before the command
799 * starts its execution. Then the text is buffered
800 * and transfered to command when it will be started.
801 * @param str
803 public void printInputText(String str)
805 stdInBuff += str;
806 flushInput();
810 * Returns information about was the command started or
811 * not.
813 * @return <code>true</code> if the external command was
814 * found and successfully started.
816 public boolean isStarted()
818 return isStarted;
822 * Returns the information about the final state of command
823 * execution.
825 * @return <code>true</code> if the command correctly starts,
826 * exits and was not interrupted due to timeout.
828 public boolean isFinished()
830 return isFinished;
834 * Returns exit code of the external command.
836 * @return exit code of command if it was finished,
837 * -1 if not.
839 public int getExitCode()
843 exitValue = m_aProcess.exitValue();
845 catch (Exception e)
847 //System.out.println("No ExitValue available");
850 return exitValue;
853 /** Causes the thread to sleep some time.
854 * @param milliseconds
856 public static void shortWait(long milliseconds)
860 Thread.sleep(milliseconds);
862 catch (InterruptedException e)
864 System.out.println("While waiting :" + e);
868 private void dbg(String message)
870 if (debug)
872 log.println(utils.getDateTime() + "PH." + message);
876 public void noOutput()
878 bUseOutput = false;
880 // -------------------------------------------------------------------------
881 class ProcessWatcher extends Thread
884 private int m_nTimeoutInSec;
885 private String m_sProcessToStart;
886 private boolean m_bInterrupt;
888 public ProcessWatcher(int _nTimeOut, String _sProcess)
890 m_nTimeoutInSec = _nTimeOut;
891 m_sProcessToStart = _sProcess;
892 m_bInterrupt = false;
896 * returns true, if the thread should hold on
897 * @return
899 public synchronized boolean isInHoldOn()
901 return m_bInterrupt;
904 * Marks the thread to hold on, next time
905 * STUPID: The thread must poll this flag itself.
907 * Reason: interrupt() seems not to work as expected.
909 public synchronized void holdOn()
911 m_bInterrupt = true;
912 interrupt();
915 public void run()
917 while (m_nTimeoutInSec > 0)
919 m_nTimeoutInSec--;
922 sleep(1000);
924 catch(java.lang.InterruptedException e)
926 // interrupt flag is set back to 'not interrupted' :-(
928 if (isInHoldOn())
930 break;
933 if (m_nTimeoutInSec <= 0 && !isInHoldOn()) // not zero, so we are interrupted.
935 system(m_sProcessToStart);
940 * Start an external Process
941 * @param _sProcess
943 private void system(String _sProcess)
945 if (_sProcess == null)
947 return;
953 // run a _sProcess command
954 // using the Runtime exec method:
955 Process p = Runtime.getRuntime().exec(_sProcess);
957 BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
959 BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
961 // read the output from the command
962 String s;
963 while ((s = stdInput.readLine()) != null)
965 System.out.println("out:" + s);
968 // read any errors from the attempted command
969 while ((s = stdError.readLine()) != null)
971 System.out.println("err:" + s);
975 catch (java.io.IOException e)
977 System.out.println("exception caught: ");
978 e.printStackTrace();
985 * If the timeout only given by setProcessTimeout(int seconds) function is != 0,
986 * a extra thread is created and after time has run out, the ProcessKiller string
987 * given by function setProcessKiller(string) will execute.
988 * So it is possible to kill a running office after a given time of seconds.
990 private void initializeProcessKiller()
992 if (m_nProcessTimeout != 0)
994 m_aWatcher = new ProcessWatcher(m_nProcessTimeout, m_sProcessKiller);
995 m_aWatcher.start();
1000 * to stop the extra thread, before he will kill a running office. This will stop the thread.
1002 public void stopWatcher()
1004 if (m_aWatcher != null)
1006 m_aWatcher.holdOn();
1007 shortWait(5000);