bump product version to 5.0.4.1
[LibreOffice.git] / qadevOOo / runner / helper / ProcessHandler.java
blob313a160554e096ff660e82c47f61970efa2794bd
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.InputStream;
21 import java.io.File;
22 import java.io.PrintWriter;
23 import java.io.PrintStream;
24 import java.io.LineNumberReader;
25 import java.io.InputStreamReader;
26 import java.io.OutputStreamWriter;
27 import lib.TestParameters;
28 import util.PropertyName;
29 import util.utils;
31 /**
32 * Class collect information from input stream in
33 * background (sparate thread) and outputs it to
34 * some log stream. I helps to avoid buffer overflow
35 * when output stream has small buffer size (e.g.
36 * in case when handling stdout from external
37 * <code>Process</code>)
39 * This class is currently used by ProcesHandler
40 * internally only.
42 class Pump extends Thread
45 private final LineNumberReader reader;
46 private final String pref;
47 private final StringBuffer buf = new StringBuffer(256);
48 private final PrintWriter log;
49 private final boolean bOutput;
51 /**
52 * Creates Pump for specified <code>InputStream</code>.
53 * This Pump also synchronously output text read to
54 * log by prefixed lines. Constructor immediately
55 * starts reading in a separate thread.
57 * @param is Stream which requires permanent reading.
58 * @param log Writer where prefixed text lines to be output
59 * @param outPrefix A prefix which is printed at the
60 * beginning of each output line.
62 public Pump(InputStream is, PrintWriter log, String outPrefix, boolean _bOutput)
64 this.pref = (outPrefix == null) ? "" : outPrefix;
65 reader = new LineNumberReader(new InputStreamReader(is));
66 this.log = log;
67 this.bOutput = _bOutput;
68 start();
71 @Override
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 long mTimeOut = 0;
122 private Pump stdout = null;
123 private Pump stderr = null;
124 private PrintStream stdIn = null;
125 private Process m_aProcess = null;
126 private TestParameters param = null;
127 private boolean debug = false;
128 private boolean bUseOutput = true;
130 private int m_nProcessTimeout = 0;
131 private ProcessWatcher m_aWatcher;
134 * Creates instance with specified external command.
135 * Debug info and output
136 * of external command is printed to stdout.
138 public ProcessHandler(String cmdLine)
140 this(cmdLine, null, null, null, 0);
144 * Creates instance with specified external command and
145 * log stream where debug info and output
146 * of external command is printed out.
148 public ProcessHandler(String cmdLine, PrintWriter log)
150 this(cmdLine, log, null, null, 0);
154 * Creates instance with specified external command which
155 * will be executed in the some work directory and
157 * @param cmdLine the command to be executed
158 * @param log log stream where debug info and output
159 * of external command is printed .
160 * @param workDir The working directory of the new process
161 * @param envVars The specified environment variables are
162 * set for the new process.
163 * If log stream is null, logging is printed to stdout.
164 * @param timeOut When started sychronisly, the maximum time the
165 * process will live. When the process being destroyed
166 * a log will be written out. It can be asked on
167 * <code>isTimedOut()</code> if it has been terminated.
169 * timeOut > 0
170 * Waits specified time in miliSeconds for
171 * process to exit and return its status.
173 * timeOut = 0
174 * Waits for the process to end regulary
176 * timeOut < 0
177 * Kills the process immediately
181 private ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars, long timeOut)
183 this.cmdLine = cmdLine;
184 this.workDir = workDir;
185 this.log = log;
186 this.cmdLine = cmdLine;
187 this.envVars = envVars;
188 if (log == null)
190 this.log = new PrintWriter(new OutputStreamWriter(System.out));
192 else
194 this.log = log;
196 this.mTimeOut = timeOut;
200 * Creates instance with specified external command which
201 * will be executed in the some work directory and
202 * log stream where debug info and output of external command is printed.
203 * If log stream is null, logging is printed to stdout.
204 * From the <CODE>TestParameters</CODE> the <CODE>OfficeWachter</CODE> get a ping.
205 * @param shortWait If this parameter is true the <CODE>mTimeOut</CODE> is set to 5000 ms, else it is set to
206 * half of time out from parameter timeout.
207 * @param param the TestParameters
208 * @see lib.TestParameters
209 * @see helper.OfficeWatcher
211 public ProcessHandler(String[] commands, PrintWriter log, File workDir, int shortWait, TestParameters param)
213 this(null, log, workDir, null, 0);
214 this.cmdLineArray = commands;
215 this.param = param;
216 if (shortWait != 0)
218 this.mTimeOut = shortWait;
220 else
222 this.mTimeOut = (long) (param.getInt(PropertyName.TIME_OUT) / 1.3);
224 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE);
229 * This method do an asynchronous execution of the commands. To avoid a interruption on long running processes
230 * caused by <CODE>OfficeWatcher</CODE>, the OfficeWatcher get frequently a ping.
231 * @see helper.OfficeWatcher
233 public void runCommand()
236 boolean changedText = true;
237 String memText = "";
239 this.executeAsynchronously();
241 OfficeWatcher ow = null;
242 if (param != null)
244 ow = (OfficeWatcher) param.get(PropertyName.OFFICE_WATCHER);
246 if (ow != null)
248 ow.ping();
251 int hangcheck = 10;
252 while (!this.isFinished() && changedText)
254 waitFor(2000, false); // wait but don't kill
256 if (ow != null)
258 ow.ping();
260 // check for changes in the output stream. If there are no changes, the process maybe hangs
261 if (!this.isFinished())
263 hangcheck--;
264 if (hangcheck < 0)
266 String sOutputText = getOutputText();
267 if (sOutputText.length() == memText.length())
269 changedText = false;
271 hangcheck = 10;
272 memText = this.getOutputText();
277 if (!this.isFinished())
279 dbg("runCommand Process is not finished but there are no changes in output stream.");
280 this.kill();
285 * Executes the command and returns only when the process
286 * exits.
288 * @return <code>true</code> if process was successfully
289 * started and correcly exits (exit code doesn't affect
290 * to this result).
292 public boolean executeSynchronously()
294 execute();
295 return waitFor(mTimeOut);
299 * Executes the command immediately returns. The process
300 * remains in running state. Control of its state should
301 * be made by <code>waitFor</code> methods.
303 * @return <code>true</code> if process was successfully
304 * started.
306 public boolean executeAsynchronously()
308 execute();
309 return isStarted();
312 public synchronized void kill()
314 if (!isStarted())
316 return;
318 boolean exit = false;
319 int counter = 1;
320 while (counter < 3 && !exit)
322 m_aProcess.destroy();
324 util.utils.pause(1000 * counter);
327 final int exit_Value = m_aProcess.exitValue();
328 if (exit_Value < 1)
330 exit = true;
332 else
334 counter++;
336 dbg("kill: process closed with exit code " + exit_Value);
338 catch (java.lang.IllegalThreadStateException e)
340 if (counter < 3)
342 dbg("kill: Couldn't close process after " + counter + " attempts, trying again");
344 counter++;
347 isStarted = false;
350 private void showEnvVars()
352 if (envVars != null)
354 for (int i = 0; i < envVars.length; i++)
356 log.println("env: " + envVars[i]);
359 else
361 log.println("env: null");
365 private void execute()
367 if (isStarted())
369 throw new RuntimeException(
370 "The process handler has already been executed.");
372 final Runtime runtime = Runtime.getRuntime();
375 if (cmdLine == null)
377 log.println(utils.getDateTime() + "execute: Starting command from array: ");
378 for (int i = 0; i < cmdLineArray.length; i++)
380 log.println(cmdLineArray[i]);
382 showEnvVars();
383 log.println("");
384 initializeProcessKiller();
385 m_aProcess = runtime.exec(cmdLineArray, envVars);
387 else
389 if (workDir != null)
391 log.println(utils.getDateTime() + "execute: Starting command: ");
392 log.println(cmdLine + " path=" + workDir.getAbsolutePath());
393 showEnvVars();
394 m_aProcess = runtime.exec(cmdLine, envVars, workDir);
396 else
398 log.println(utils.getDateTime() + "execute: Starting command: ");
399 log.println(cmdLine);
400 showEnvVars();
401 m_aProcess = runtime.exec(cmdLine, envVars);
404 isStarted = true;
406 catch (java.io.IOException e)
408 if (cmdLine == null)
410 log.println(utils.getDateTime() + "execute: The command array can't be started: " + e);
412 else
414 log.println(utils.getDateTime() + "execute: The command " + cmdLine + " can't be started: " + e);
416 return;
418 dbg("execute: pump io-streams");
419 stdout = new Pump(m_aProcess.getInputStream(), log, "out > ", bUseOutput);
420 stderr = new Pump(m_aProcess.getErrorStream(), log, "err > ", bUseOutput);
421 stdIn = new PrintStream(m_aProcess.getOutputStream());
423 dbg("execute: flush io-streams");
425 flushInput();
431 * This method is useful when the process was executed
432 * asynchronously. Waits during specified time for process
433 * to exit and return its status.
435 * @param timeout > 0
436 * Waits specified time in miliSeconds for
437 * process to exit and return its status.
439 * = 0
440 * Waits for the process to end regulary
442 * < 0
443 * Kills the process immediately
445 * @return <code>true</code> if process correctly exited
446 * (exit code doesn't affect to this result).
448 private boolean waitFor(long timeout)
450 return waitFor(timeout, true);
453 private boolean waitFor(long timeout, boolean bKillProcessAfterTimeout)
455 if (isFinished())
457 return true;
459 if (!isStarted())
461 return false;
464 if (timeout == 0)
468 m_aProcess.waitFor();
470 catch (InterruptedException e)
472 log.println("The process was interrupted: " + e);
474 isFinished = true;
477 exitValue = m_aProcess.exitValue();
479 catch (IllegalThreadStateException e)
483 else
487 while (!isFinished && timeout > 0)
489 isFinished = true;
490 Thread.sleep(1000);
491 timeout -= 1000;
494 exitValue = m_aProcess.exitValue(); // throws exception if not finished
496 catch (IllegalThreadStateException e)
498 isFinished = false;
501 if (timeout < 0)
503 log.println("The process has timed out!");
506 catch (InterruptedException ex)
508 log.println("The process was interrupted: " + ex);
512 if (bKillProcessAfterTimeout)
514 if (!isFinished)
516 log.println("Going to destroy the process!!");
517 m_aProcess.destroy();
518 log.println("Process has been destroyed!");
522 return isFinished();
525 private void flushInput()
527 if (stdIn == null)
529 return;
532 synchronized(this)
534 stdIn.flush();
539 * Returns the text output by external command to stdout.
540 * @return the text output by external command to stdout
542 public String getOutputText()
544 if (stdout == null)
546 return "";
548 else
550 return stdout.getStringBuffer();
555 * Returns the text output by external command to stderr.
556 * @return the text output by external command to stderr
558 public String getErrorText()
560 if (stderr == null)
562 return "";
564 else
566 return stderr.getStringBuffer();
573 * Returns information about was the command started or
574 * not.
576 * @return <code>true</code> if the external command was
577 * found and successfully started.
579 private boolean isStarted()
581 return isStarted;
585 * Returns the information about the final state of command
586 * execution.
588 * @return <code>true</code> if the command correctly starts,
589 * exits and was not interrupted due to timeout.
591 private boolean isFinished()
593 return isFinished;
597 * Returns exit code of the external command.
599 * @return exit code of command if it was finished,
600 * -1 if not.
602 public int getExitCode()
606 exitValue = m_aProcess.exitValue();
608 catch (Exception e)
612 return exitValue;
615 private void dbg(String message)
617 if (debug)
619 log.println(utils.getDateTime() + "PH." + message);
623 public void noOutput()
625 bUseOutput = false;
628 private class ProcessWatcher extends Thread
631 private int m_nTimeoutInSec;
632 private final boolean m_bInterrupt;
634 private ProcessWatcher(int _nTimeOut)
636 m_nTimeoutInSec = _nTimeOut;
637 m_bInterrupt = false;
641 * returns true, if the thread should hold on
643 public synchronized boolean isInHoldOn()
645 return m_bInterrupt;
647 @Override
648 public void run()
650 while (m_nTimeoutInSec > 0)
652 m_nTimeoutInSec--;
653 util.utils.pause(1000);
654 if (isInHoldOn())
656 break;
664 * If the timeout only given by setProcessTimeout(int seconds) function is != 0,
665 * a extra thread is created and after time has run out, the ProcessKiller string
666 * given by function setProcessKiller(string) will execute.
667 * So it is possible to kill a running office after a given time of seconds.
669 private void initializeProcessKiller()
671 if (m_nProcessTimeout != 0)
673 m_aWatcher = new ProcessWatcher(m_nProcessTimeout);
674 m_aWatcher.start();