Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / qadevOOo / runner / helper / ProcessHandler.java
blob14409d9b62a5c68c04ea2eaaeeb1e28849b3f358
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;
32 * Class collect information from input stream in
33 * background (separate 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 ProcessHandler
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
112 private String cmdLine;
113 private String[] envVars = null;
114 private File workDir = null;
115 private PrintWriter log;
116 private int exitValue = -1;
117 private boolean isFinished = false;
118 private boolean isStarted = false;
119 private PrintStream stdIn = null;
120 private Process m_aProcess = null;
121 private boolean debug = false;
122 private boolean bUseOutput = true;
124 private int m_nProcessTimeout = 0;
125 private ProcessWatcher m_aWatcher;
128 * Creates instance with specified external command and
129 * log stream where debug info and output
130 * of external command is printed out.
132 public ProcessHandler(String cmdLine, PrintWriter log)
134 this(cmdLine, log, null, null);
138 * Creates instance with specified external command which
139 * will be executed in the some work directory and
141 * @param cmdLine the command to be executed
142 * @param log log stream where debug info and output
143 * of external command is printed .
144 * @param workDir The working directory of the new process
145 * @param envVars The specified environment variables are
146 * set for the new process.
147 * If log stream is null, logging is printed to stdout.
148 * @param timeOut When started synchronously, the maximum time the
149 * process will live. When the process being destroyed
150 * a log will be written out. It can be asked on
151 * <code>isTimedOut()</code> if it has been terminated.
153 * timeOut > 0
154 * Waits specified time in miliSeconds for
155 * process to exit and return its status.
157 * timeOut = 0
158 * Waits for the process to end regulary
161 private ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars)
163 this.cmdLine = cmdLine;
164 this.workDir = workDir;
165 this.log = log;
166 this.envVars = envVars;
167 if (log == null)
169 this.log = new PrintWriter(new OutputStreamWriter(System.out));
171 else
173 this.log = log;
178 * Executes the command immediately returns. The process
179 * remains in running state.
181 * @return <code>true</code> if process was successfully
182 * started.
184 public boolean executeAsynchronously()
186 execute();
187 return isStarted();
190 public synchronized void kill()
192 if (!isStarted())
194 return;
196 boolean exit = false;
197 int counter = 1;
198 while (counter < 3 && !exit)
200 m_aProcess.destroy();
202 util.utils.pause(1000 * counter);
205 final int exit_Value = m_aProcess.exitValue();
206 if (exit_Value < 1)
208 exit = true;
210 else
212 counter++;
214 dbg("kill: process closed with exit code " + exit_Value);
216 catch (java.lang.IllegalThreadStateException e)
218 if (counter < 3)
220 dbg("kill: Couldn't close process after " + counter + " attempts, trying again");
222 counter++;
225 isStarted = false;
228 private void showEnvVars()
230 if (envVars != null)
232 for (int i = 0; i < envVars.length; i++)
234 log.println("env: " + envVars[i]);
237 else
239 log.println("env: null");
243 private void execute()
245 if (isStarted())
247 throw new RuntimeException(
248 "The process handler has already been executed.");
250 final Runtime runtime = Runtime.getRuntime();
253 if (workDir != null)
255 log.println(utils.getDateTime() + "execute: Starting command: ");
256 log.println(cmdLine + " path=" + workDir.getAbsolutePath());
257 showEnvVars();
258 m_aProcess = runtime.exec(cmdLine, envVars, workDir);
260 else
262 log.println(utils.getDateTime() + "execute: Starting command: ");
263 log.println(cmdLine);
264 showEnvVars();
265 m_aProcess = runtime.exec(cmdLine, envVars);
267 isStarted = true;
269 catch (java.io.IOException e)
271 log.println(utils.getDateTime() + "execute: The command " + cmdLine + " can't be started: " + e);
272 return;
274 dbg("execute: pump io-streams");
275 new Pump(m_aProcess.getInputStream(), log, "out > ", bUseOutput);
276 new Pump(m_aProcess.getErrorStream(), log, "err > ", bUseOutput);
277 stdIn = new PrintStream(m_aProcess.getOutputStream());
279 dbg("execute: flush io-streams");
281 flushInput();
284 private void flushInput()
286 if (stdIn == null)
288 return;
291 synchronized(this)
293 stdIn.flush();
298 * Returns information about was the command started or
299 * not.
301 * @return <code>true</code> if the external command was
302 * found and successfully started.
304 private boolean isStarted()
306 return isStarted;
310 * Returns the information about the final state of command
311 * execution.
313 * @return <code>true</code> if the command correctly starts,
314 * exits and was not interrupted due to timeout.
316 private boolean isFinished()
318 return isFinished;
322 * Returns exit code of the external command.
324 * @return exit code of command if it was finished,
325 * -1 if not.
327 public int getExitCode()
331 exitValue = m_aProcess.exitValue();
333 catch (Exception e)
337 return exitValue;
340 private void dbg(String message)
342 if (debug)
344 log.println(utils.getDateTime() + "PH." + message);
348 private static class ProcessWatcher extends Thread
351 private int m_nTimeoutInSec;
352 private final boolean m_bInterrupt;
354 private ProcessWatcher(int _nTimeOut)
356 m_nTimeoutInSec = _nTimeOut;
357 m_bInterrupt = false;
361 * returns true, if the thread should hold on
363 public synchronized boolean isInHoldOn()
365 return m_bInterrupt;
367 @Override
368 public void run()
370 while (m_nTimeoutInSec > 0)
372 m_nTimeoutInSec--;
373 util.utils.pause(1000);
374 if (isInHoldOn())
376 break;
384 * If the timeout only given by setProcessTimeout(int seconds) function is != 0,
385 * a extra thread is created and after time has run out, the ProcessKiller string
386 * given by function setProcessKiller(string) will execute.
387 * So it is possible to kill a running office after a given time of seconds.
389 private void initializeProcessKiller()
391 if (m_nProcessTimeout != 0)
393 m_aWatcher = new ProcessWatcher(m_nProcessTimeout);
394 m_aWatcher.start();