bump product version to 4.2.0.1
[LibreOffice.git] / qadevOOo / runner / helper / ProcessHandler.java
blobc70dd1d3be19630438511e2a25d4f60b3da579b1
1 /*
2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 package helper;
20 import java.io.BufferedReader;
21 import java.io.InputStream;
22 import java.io.File;
23 import java.io.PrintWriter;
24 import java.io.PrintStream;
25 import java.io.LineNumberReader;
26 import java.io.InputStreamReader;
27 import java.io.OutputStreamWriter;
28 import lib.TestParameters;
29 import util.PropertyName;
30 import util.utils;
32 /**
33 * Class collect information from input stream in
34 * background (sparate thread) and outputs it to
35 * some log stream. I helps to avoid buffer overflow
36 * when output stream has small buffer size (e.g.
37 * in case when handling stdout from external
38 * <code>Process</code>)
40 * This class is currently used by ProcesHandler
41 * internally only.
43 class Pump extends Thread
46 private LineNumberReader reader;
47 private String pref;
48 private StringBuffer buf = new StringBuffer(256);
49 private PrintWriter log;
50 private boolean bOutput;
52 /**
53 * Creates Pump for specified <code>InputStream</code>.
54 * This Pump also synchronously output text read to
55 * log by prefixed lines. Constructor immediately
56 * starts reading in a separate thread.
58 * @param is Stream which requires permanent reading.
59 * @param log Writer where prefixed text lines to be output
60 * @param outPrefix A prefix which is printed at the
61 * beginning of each output line.
63 public Pump(InputStream is, PrintWriter log, String outPrefix, boolean _bOutput)
65 this.pref = (outPrefix == null) ? "" : outPrefix;
66 reader = new LineNumberReader(new InputStreamReader(is));
67 this.log = log;
68 this.bOutput = _bOutput;
69 start();
72 public void run()
74 try
76 String line = reader.readLine();
77 while (line != null)
79 if (bOutput)
81 log.println(pref + line);
82 log.flush();
84 buf.append(line).append('\n');
85 line = reader.readLine();
88 catch (java.io.IOException e)
90 log.println(pref + "Exception occurred: " + e);
94 /**
95 * Returns the text collected from input stream.
97 public String getStringBuffer()
99 return buf.toString();
104 * Class provides convenient way for running external program
105 * handle its standard streams, control execution and check results.
106 * Instance of this class must be created only for a single
107 * execution. If you need to execute the same command again you
108 * should create a new instance for this.
110 public class ProcessHandler
113 private String cmdLine;
114 private String[] cmdLineArray;
115 private String[] envVars = null;
116 private File workDir = null;
117 private PrintWriter log;
118 private int exitValue = -1;
119 private boolean isFinished = false;
120 private boolean isStarted = false;
121 private boolean mbTimedOut = false;
122 private long mTimeOut = 0;
123 private String stdInBuff = "";
124 private Pump stdout = null;
125 private Pump stderr = null;
126 private PrintStream stdIn = null;
127 private Process m_aProcess = null;
128 private TestParameters param = null;
129 private boolean debug = false;
130 private boolean bUseOutput = true;
132 private int m_nProcessTimeout = 0;
133 private String m_sProcessKiller;
134 private ProcessWatcher m_aWatcher;
137 * Creates instance with specified external command.
138 * Debug info and output
139 * of external command is printed to stdout.
140 * @param cmdLine
142 public ProcessHandler(String cmdLine)
144 this(cmdLine, null, null, null, 0);
148 * Creates instance with specified external command
149 * including parameters as an array.
150 * Debug info and output
151 * of external command is printed to stdout.
152 * @param cmdLines
154 public ProcessHandler(String[] cmdLines)
156 this(null, null, null, null, 0);
157 cmdLineArray = cmdLines;
161 * Creates instance with specified external command
162 * including parameters as an array, with environment
163 * variables.
164 * Debug info and output
165 * of external command is printed to stdout.
166 * @param cmdLines
167 * @param envVars
168 * @see java.lang.Runtime exec(String[], String[])
170 public ProcessHandler(String[] cmdLines, String[] envVars)
172 this(null, null, null, envVars, 0);
173 cmdLineArray = cmdLines;
177 * Creates instance with specified external command
178 * including parameters as an array, with environment
179 * variables. The command will be started in workDir.
180 * Debug info and output
181 * of external command is printed to stdout.
182 * @param cmdLines
183 * @param workDir
185 public ProcessHandler(String[] cmdLines, File workDir)
187 this(null, null, workDir, null, 0);
188 cmdLineArray = cmdLines;
193 * Creates instance with specified external command and
194 * log stream where debug info and output
195 * of external command is printed out. The command will be started in workDir.
196 * @param cmdLines
197 * @param log
198 * @param workDir
200 public ProcessHandler(String[] cmdLines, PrintWriter log, File workDir)
202 this(null, log, workDir, null, 0);
203 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.
210 * @param cmdLine
211 * @param log
213 public ProcessHandler(String cmdLine, PrintWriter log)
215 this(cmdLine, log, null, null, 0);
219 * Creates instance with specified external command and set the time out for the command.
220 * @param cmdLine
221 * @param timeOut
223 public ProcessHandler(String cmdLine, int timeOut)
225 this(cmdLine, null, null, null, timeOut);
229 * Creates instance with specified external command which
230 * will be executed in the some work directory.
231 * Debug info and output
232 * of external commandis printed to stdout.
233 * @param cmdLine
234 * @param workDir
236 public ProcessHandler(String cmdLine, File workDir)
238 this(cmdLine, null, workDir, null, 0);
242 * Creates instance with specified external command which
243 * will be executed in the some work directory.
244 * Debug info and output printed in log stream.
245 * @param cmdLine
246 * @param log
247 * @param workDir
249 public ProcessHandler(String cmdLine, PrintWriter log, File workDir)
251 this(cmdLine, log, workDir, null, 0);
255 * Creates instance with specified external command which
256 * will be executed in the some work directory and
257 * log stream where debug info and output
258 * of external command is printed .
259 * The specified environment variables are set for the new process.
260 * If log stream is null, logging is printed to stdout.
261 * @param cmdLine
262 * @param log
263 * @param workDir
264 * @param envVars
266 public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars)
268 this(cmdLine, log, workDir, envVars, 0);
272 * Creates instance with specified external command which
273 * will be executed in the some work directory and
275 * @param cmdLine the command to be executed
276 * @param log log stream where debug info and output
277 * of external command is printed .
278 * @param workDir The working directory of the new process
279 * @param envVars The specified environment variables are
280 * set for the new process.
281 * If log stream is null, logging is printed to stdout.
282 * @param timeOut When started sychronisly, the maximum time the
283 * process will live. When the process being destroyed
284 * a log will be written out. It can be asked on
285 * <code>isTimedOut()</code> if it has been terminated.
287 * timeOut > 0
288 * Waits specified time in miliSeconds for
289 * process to exit and return its status.
291 * timeOut = 0
292 * Waits for the process to end regulary
294 * timeOut < 0
295 * Kills the process immediately
299 public ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars, long timeOut)
301 this.cmdLine = cmdLine;
302 this.workDir = workDir;
303 this.log = log;
304 this.cmdLine = cmdLine;
305 this.envVars = envVars;
306 if (log == null)
308 this.log = new PrintWriter(new OutputStreamWriter(System.out));
310 else
312 this.log = log;
314 this.mTimeOut = timeOut;
318 * Creates instance with specified external command which
319 * will be executed in the some work directory and
320 * log stream where debug info and output of external command is printed.
321 * If log stream is null, logging is printed to stdout.
322 * From the <CODE>TestParameters</CODE> the <CODE>OfficeWachter</CODE> get a ping.
323 * @param commands
324 * @param log
325 * @param workDir
326 * @param shortWait If this parameter is ture the <CODE>mTimeOut</CODE> is set to 5000 ms, else it is set to
327 * half of time out from parameter timeout.
328 * @param param the TestParameters
329 * @see lib.TestParameters
330 * @see helper.OfficeWatcher
332 public ProcessHandler(String[] commands, PrintWriter log, File workDir, int shortWait, TestParameters param)
334 this(null, log, workDir, null, 0);
335 this.cmdLineArray = commands;
336 this.param = param;
337 if (shortWait != 0)
339 this.mTimeOut = shortWait;
341 else
343 this.mTimeOut = (long) (param.getInt(PropertyName.TIME_OUT) / 1.3);
345 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE);
350 * If not equal 0, the time to maximal wait.
351 * @param _n
353 public void setProcessTimeout(int _n)
355 m_nProcessTimeout = _n;
359 * This command will call after ProcessTimeout is arrived.
360 * @param _s
362 public void setProcessKiller(String _s)
364 m_sProcessKiller = _s;
368 * This method do an asynchronous execution of the commands. To avoid a interruption on long running processes
369 * caused by <CODE>OfficeWatcher</CODE>, the OfficeWatcher get frequently a ping.
370 * @see helper.OfficeWatcher
372 public void runCommand()
375 boolean changedText = true;
376 String memText = "";
378 this.executeAsynchronously();
380 OfficeWatcher ow = null;
381 if (param != null)
383 ow = (OfficeWatcher) param.get(PropertyName.OFFICE_WATCHER);
385 if (ow != null)
387 ow.ping();
390 int hangcheck = 10;
391 while (!this.isFinished() && changedText)
393 // dbg("runCommand: waiting " + mTimeOut / 1000 + " seconds while command execution is ongoing... " + count);
394 // shortWait(mTimeOut);
395 // shortWait(2000); // wait 2 seconds.
396 //waitFor(mTimeOut);
397 waitFor(2000, false); // wait but don't kill
399 if (ow != null)
401 ow.ping();
403 // check for changes in the output stream. If there are no changes, the process maybe hangs
404 if (!this.isFinished())
406 hangcheck--;
407 if (hangcheck < 0)
409 String sOutputText = getOutputText();
410 if (sOutputText.length() == memText.length())
412 changedText = false;
413 // dbg("runCommand Could not detect changes in output stream!!!");
415 hangcheck = 10;
416 memText = this.getOutputText();
421 if (!this.isFinished())
423 dbg("runCommand Process is not finished but there are no changes in output stream.");
424 this.kill();
428 public boolean isTimedOut()
430 return mbTimedOut;
433 private void setTimedOut(boolean bTimedOut)
435 mbTimedOut = bTimedOut;
439 * Executes the command and returns only when the process
440 * exits.
442 * @return <code>true</code> if process was successfully
443 * started and correcly exits (exit code doesn't affect
444 * to this result).
446 public boolean executeSynchronously()
448 execute();
449 return waitFor(mTimeOut);
453 * Executes the command immediately returns. The process
454 * remains in running state. Control of its state should
455 * be made by <code>waitFor</code> methods.
457 * @return <code>true</code> if process was successfully
458 * started.
460 public boolean executeAsynchronously()
462 execute();
463 return isStarted();
466 public synchronized void kill()
468 if (!isStarted())
470 return;
472 boolean exit = false;
473 int counter = 1;
474 while (counter < 3 && !exit)
476 m_aProcess.destroy();
480 Thread.sleep(1000 * counter); // 5000
482 catch (java.lang.InterruptedException e)
487 final int exit_Value = m_aProcess.exitValue();
488 if (exit_Value < 1)
490 exit = true;
492 else
494 counter++;
496 dbg("kill: process closed with exit code " + exit_Value);
498 catch (java.lang.IllegalThreadStateException e)
500 if (counter < 3)
502 dbg("kill: Couldn't close process after " + counter + " attempts, trying again");
504 counter++;
507 isStarted = false;
511 * Returns the time in seconds since 1st January 1970
512 * @return
514 public static long getSystemTime()
516 // Calendar cal = new GregorianCalendar();
517 // final long nTime = cal.getTimeInMillis();
518 final long nTime = System.currentTimeMillis();
519 return nTime;
521 private long m_nExactStartTimeInMillisec;
523 private void initialExactStartTime()
525 m_nExactStartTimeInMillisec = getSystemTime();
528 public long getProcessStartTime()
530 return m_nExactStartTimeInMillisec;
533 private void showEnvVars()
535 if (envVars != null)
537 for (int i = 0; i < envVars.length; i++)
539 log.println("env: " + envVars[i]);
542 else
544 log.println("env: null");
548 protected void execute()
550 if (isStarted())
552 throw new RuntimeException(
553 "The process handler has already been executed.");
555 final Runtime runtime = Runtime.getRuntime();
558 if (cmdLine == null)
560 log.println(utils.getDateTime() + "execute: Starting command from array: ");
561 for (int i = 0; i < cmdLineArray.length; i++)
563 log.println(cmdLineArray[i]);
564 // log.print(" ");
566 showEnvVars();
567 log.println("");
568 initialExactStartTime();
569 initializeProcessKiller();
570 m_aProcess = runtime.exec(cmdLineArray, envVars);
572 else
574 if (workDir != null)
576 log.println(utils.getDateTime() + "execute: Starting command: ");
577 log.println(cmdLine + " path=" + workDir.getAbsolutePath());
578 showEnvVars();
579 m_aProcess = runtime.exec(cmdLine, envVars, workDir);
581 else
583 log.println(utils.getDateTime() + "execute: Starting command: ");
584 log.println(cmdLine);
585 showEnvVars();
586 m_aProcess = runtime.exec(cmdLine, envVars);
589 isStarted = true;
591 catch (java.io.IOException e)
593 if (cmdLine == null)
595 log.println(utils.getDateTime() + "execute: The command array can't be started: " + e);
597 else
599 log.println(utils.getDateTime() + "execute: The command " + cmdLine + " can't be started: " + e);
601 return;
603 dbg("execute: pump io-streams");
604 stdout = new Pump(m_aProcess.getInputStream(), log, "out > ", bUseOutput);
605 stderr = new Pump(m_aProcess.getErrorStream(), log, "err > ", bUseOutput);
606 stdIn = new PrintStream(m_aProcess.getOutputStream());
608 // int nExitValue = m_aProcess.exitValue();
609 // int dummy = 0;
611 dbg("execute: flush io-streams");
613 flushInput();
617 * This method is useful when the process was executed
618 * asynchronously. Waits for process to exit and return
619 * its result.
621 * @return <code>true</code> if process correctly exited
622 * (exit code doesn't affect to this result).
624 public boolean waitFor()
626 return waitFor(0);
630 * This method is useful when the process was executed
631 * asynchronously. Waits during specified time for process
632 * to exit and return its status.
634 * @param timeout > 0
635 * Waits specified time in miliSeconds for
636 * process to exit and return its status.
638 * = 0
639 * Waits for the process to end regulary
641 * < 0
642 * Kills the process immediately
644 * @return <code>true</code> if process correctly exited
645 * (exit code doesn't affect to this result).
647 public boolean waitFor(long timeout)
649 return waitFor(timeout, true);
652 private boolean waitFor(long timeout, boolean bKillProcessAfterTimeout)
654 if (isFinished())
656 return true;
658 if (!isStarted())
660 return false;
663 if (timeout == 0)
667 m_aProcess.waitFor();
669 catch (InterruptedException e)
671 log.println("The process was interrupted: " + e);
673 isFinished = true;
676 exitValue = m_aProcess.exitValue();
678 catch (IllegalThreadStateException e)
682 else
686 while (!isFinished && timeout > 0)
688 isFinished = true;
689 Thread.sleep(1000);
690 timeout -= 1000;
693 exitValue = m_aProcess.exitValue(); // throws exception if not finished
695 catch (IllegalThreadStateException e)
697 isFinished = false;
700 if (timeout < 0)
702 setTimedOut(true);
703 log.println("The process has timed out!");
706 catch (InterruptedException ex)
708 log.println("The process was interrupted: " + ex);
712 if (bKillProcessAfterTimeout == true)
714 if (!isFinished)
716 log.println("Going to destroy the process!!");
717 m_aProcess.destroy();
718 log.println("Process has been destroyed!");
721 // Removed as hung up in SDK test 'PathSettings'
722 // try {
723 // stdout.join();
724 // stderr.join();
725 // } catch (InterruptedException e) {}
727 return isFinished();
730 protected void flushInput()
732 if (stdIn == null)
734 return;
737 synchronized(stdInBuff)
739 stdIn.print(stdInBuff);
740 stdIn.flush();
741 stdInBuff = "";
746 * Returns the text output by external command to stdout.
747 * @return the text output by external command to stdout
749 public String getOutputText()
751 if (stdout == null)
753 return "";
755 else
757 return stdout.getStringBuffer();
762 * Returns the text output by external command to stderr.
763 * @return the text output by external command to stderr
765 public String getErrorText()
767 if (stderr == null)
769 return "";
771 else
773 return stderr.getStringBuffer();
778 * Prints the string specified to sdtin of external
779 * command. '\n' is not added so if you need you
780 * should terminate the string with '\n'. <p>
782 * The method can also be called before the command
783 * starts its execution. Then the text is buffered
784 * and transfered to command when it will be started.
785 * @param str
787 public void printInputText(String str)
789 stdInBuff += str;
790 flushInput();
794 * Returns information about was the command started or
795 * not.
797 * @return <code>true</code> if the external command was
798 * found and successfully started.
800 public boolean isStarted()
802 return isStarted;
806 * Returns the information about the final state of command
807 * execution.
809 * @return <code>true</code> if the command correctly starts,
810 * exits and was not interrupted due to timeout.
812 public boolean isFinished()
814 return isFinished;
818 * Returns exit code of the external command.
820 * @return exit code of command if it was finished,
821 * -1 if not.
823 public int getExitCode()
827 exitValue = m_aProcess.exitValue();
829 catch (Exception e)
831 //System.out.println("No ExitValue available");
834 return exitValue;
837 /** Causes the thread to sleep some time.
838 * @param milliseconds
840 public static void shortWait(long milliseconds)
844 Thread.sleep(milliseconds);
846 catch (InterruptedException e)
848 System.out.println("While waiting :" + e);
852 private void dbg(String message)
854 if (debug)
856 log.println(utils.getDateTime() + "PH." + message);
860 public void noOutput()
862 bUseOutput = false;
864 // -------------------------------------------------------------------------
865 class ProcessWatcher extends Thread
868 private int m_nTimeoutInSec;
869 private String m_sProcessToStart;
870 private boolean m_bInterrupt;
872 public ProcessWatcher(int _nTimeOut, String _sProcess)
874 m_nTimeoutInSec = _nTimeOut;
875 m_sProcessToStart = _sProcess;
876 m_bInterrupt = false;
880 * returns true, if the thread should hold on
881 * @return
883 public synchronized boolean isInHoldOn()
885 return m_bInterrupt;
888 * Marks the thread to hold on, next time
889 * STUPID: The thread must poll this flag itself.
891 * Reason: interrupt() seems not to work as expected.
893 public synchronized void holdOn()
895 m_bInterrupt = true;
896 interrupt();
899 public void run()
901 while (m_nTimeoutInSec > 0)
903 m_nTimeoutInSec--;
906 sleep(1000);
908 catch(java.lang.InterruptedException e)
910 // interrupt flag is set back to 'not interrupted' :-(
912 if (isInHoldOn())
914 break;
917 if (m_nTimeoutInSec <= 0 && !isInHoldOn()) // not zero, so we are interrupted.
919 system(m_sProcessToStart);
924 * Start an external Process
925 * @param _sProcess
927 private void system(String _sProcess)
929 if (_sProcess == null)
931 return;
937 // run a _sProcess command
938 // using the Runtime exec method:
939 Process p = Runtime.getRuntime().exec(_sProcess);
941 BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
943 BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
945 // read the output from the command
946 String s;
947 while ((s = stdInput.readLine()) != null)
949 System.out.println("out:" + s);
952 // read any errors from the attempted command
953 while ((s = stdError.readLine()) != null)
955 System.out.println("err:" + s);
959 catch (java.io.IOException e)
961 System.out.println("exception caught: ");
962 e.printStackTrace();
969 * If the timeout only given by setProcessTimeout(int seconds) function is != 0,
970 * a extra thread is created and after time has run out, the ProcessKiller string
971 * given by function setProcessKiller(string) will execute.
972 * So it is possible to kill a running office after a given time of seconds.
974 private void initializeProcessKiller()
976 if (m_nProcessTimeout != 0)
978 m_aWatcher = new ProcessWatcher(m_nProcessTimeout, m_sProcessKiller);
979 m_aWatcher.start();
984 * to stop the extra thread, before he will kill a running office. This will stop the thread.
986 public void stopWatcher()
988 if (m_aWatcher != null)
990 m_aWatcher.holdOn();
991 shortWait(5000);