Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / qadevOOo / runner / helper / ProcessHandler.java
blob49aba431a50ec5391b359af668ac02275ce04a85
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.OutputStreamWriter;
23 import java.io.UnsupportedEncodingException;
24 import java.io.PrintWriter;
25 import java.io.PrintStream;
26 import java.io.LineNumberReader;
27 import java.io.InputStreamReader;
28 import lib.TestParameters;
29 import util.PropertyName;
30 import util.utils;
33 * Class collect information from input stream in
34 * background (separate 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 ProcessHandler
41 * internally only.
43 class Pump extends Thread
46 private final LineNumberReader reader;
47 private final String pref;
48 private final StringBuffer buf = new StringBuffer(256);
49 private final PrintWriter log;
50 private final 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 @Override
73 public void run()
75 try
77 String line = reader.readLine();
78 while (line != null)
80 if (bOutput)
82 log.println(pref + line);
83 log.flush();
85 buf.append(line).append('\n');
86 line = reader.readLine();
89 catch (java.io.IOException e)
91 log.println(pref + "Exception occurred: " + e);
95 /**
96 * Returns the text collected from input stream.
98 public String getStringBuffer()
100 return buf.toString();
105 * Class provides convenient way for running external program
106 * handle its standard streams, control execution and check results.
107 * Instance of this class must be created only for a single
108 * execution. If you need to execute the same command again you
109 * should create a new instance for this.
111 public class ProcessHandler
113 private String cmdLine;
114 private String[] envVars = null;
115 private File workDir = null;
116 private PrintWriter log;
117 private int exitValue = -1;
118 private boolean isFinished = false;
119 private boolean isStarted = false;
120 private PrintStream stdIn = null;
121 private Process m_aProcess = null;
122 private boolean debug = false;
123 private boolean bUseOutput = true;
125 private int m_nProcessTimeout = 0;
126 private ProcessWatcher m_aWatcher;
129 * Creates instance with specified external command and
130 * log stream where debug info and output
131 * of external command is printed out.
133 public ProcessHandler(String cmdLine, PrintWriter log) throws UnsupportedEncodingException
135 this(cmdLine, log, null, null);
139 * Creates instance with specified external command which
140 * will be executed in the same work directory and
142 * @param cmdLine the command to be executed
143 * @param log log stream where debug info and output
144 * of external command is printed .
145 * @param workDir The working directory of the new process
146 * @param envVars The specified environment variables are
147 * set for the new process.
148 * If log stream is null, logging is printed to stdout.
149 * @param timeOut When started synchronously, the maximum time the
150 * process will live. When the process being destroyed
151 * a log will be written out. It can be asked on
152 * <code>isTimedOut()</code> if it has been terminated.
154 * timeOut > 0
155 * Waits specified time in milliSeconds for
156 * process to exit and return its status.
158 * timeOut = 0
159 * Waits for the process to end regularly
162 private ProcessHandler(String cmdLine, PrintWriter log, File workDir, String[] envVars) throws UnsupportedEncodingException
164 this.cmdLine = cmdLine;
165 this.workDir = workDir;
166 this.log = log;
167 this.envVars = envVars;
168 if (log == null)
170 this.log = new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"));
172 else
174 this.log = log;
179 * Executes the command immediately returns. The process
180 * remains in running state.
182 * @return <code>true</code> if process was successfully
183 * started.
185 public boolean executeAsynchronously()
187 execute();
188 return isStarted();
191 public synchronized void kill()
193 if (!isStarted())
195 return;
197 boolean exit = false;
198 int counter = 1;
199 while (counter < 3 && !exit)
201 m_aProcess.destroy();
203 util.utils.pause(1000 * counter);
206 final int exit_Value = m_aProcess.exitValue();
207 if (exit_Value < 1)
209 exit = true;
211 else
213 counter++;
215 dbg("kill: process closed with exit code " + exit_Value);
217 catch (java.lang.IllegalThreadStateException e)
219 if (counter < 3)
221 dbg("kill: Couldn't close process after " + counter + " attempts, trying again");
223 counter++;
226 isStarted = false;
229 private void showEnvVars()
231 if (envVars != null)
233 for (int i = 0; i < envVars.length; i++)
235 log.println("env: " + envVars[i]);
238 else
240 log.println("env: null");
244 private void execute()
246 if (isStarted())
248 throw new RuntimeException(
249 "The process handler has already been executed.");
251 final Runtime runtime = Runtime.getRuntime();
254 if (workDir != null)
256 log.println(utils.getDateTime() + "execute: Starting command: ");
257 log.println(cmdLine + " path=" + workDir.getAbsolutePath());
258 showEnvVars();
259 m_aProcess = runtime.exec(cmdLine, envVars, workDir);
261 else
263 log.println(utils.getDateTime() + "execute: Starting command: ");
264 log.println(cmdLine);
265 showEnvVars();
266 m_aProcess = runtime.exec(cmdLine, envVars);
268 isStarted = true;
270 catch (java.io.IOException e)
272 log.println(utils.getDateTime() + "execute: The command " + cmdLine + " can't be started: " + e);
273 return;
275 dbg("execute: pump io-streams");
276 new Pump(m_aProcess.getInputStream(), log, "out > ", bUseOutput);
277 new Pump(m_aProcess.getErrorStream(), log, "err > ", bUseOutput);
278 stdIn = new PrintStream(m_aProcess.getOutputStream());
280 dbg("execute: flush io-streams");
282 flushInput();
285 private void flushInput()
287 if (stdIn == null)
289 return;
292 synchronized(this)
294 stdIn.flush();
299 * Returns information about was the command started or
300 * not.
302 * @return <code>true</code> if the external command was
303 * found and successfully started.
305 private boolean isStarted()
307 return isStarted;
311 * Returns the information about the final state of command
312 * execution.
314 * @return <code>true</code> if the command correctly starts,
315 * exits and was not interrupted due to timeout.
317 private boolean isFinished()
319 return isFinished;
323 * Returns exit code of the external command.
325 * @return exit code of command if it was finished,
326 * -1 if not.
328 public int getExitCode()
332 exitValue = m_aProcess.exitValue();
334 catch (Exception e)
338 return exitValue;
341 private void dbg(String message)
343 if (debug)
345 log.println(utils.getDateTime() + "PH." + message);
349 private static class ProcessWatcher extends Thread
352 private int m_nTimeoutInSec;
353 private final boolean m_bInterrupt;
355 private ProcessWatcher(int _nTimeOut)
357 m_nTimeoutInSec = _nTimeOut;
358 m_bInterrupt = false;
362 * returns true, if the thread should hold on
364 public synchronized boolean isInHoldOn()
366 return m_bInterrupt;
368 @Override
369 public void run()
371 while (m_nTimeoutInSec > 0)
373 m_nTimeoutInSec--;
374 util.utils.pause(1000);
375 if (isInHoldOn())
377 break;
385 * If the timeout only given by setProcessTimeout(int seconds) function is != 0,
386 * an extra thread is created and after time has run out, the ProcessKiller string
387 * given by function setProcessKiller(string) will execute.
388 * So it is possible to kill a running office after a given time of seconds.
390 private void initializeProcessKiller()
392 if (m_nProcessTimeout != 0)
394 m_aWatcher = new ProcessWatcher(m_nProcessTimeout);
395 m_aWatcher.start();