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 .
20 import java
.io
.InputStream
;
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
;
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
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
;
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
));
68 this.bOutput
= _bOutput
;
77 String line
= reader
.readLine();
82 log
.println(pref
+ line
);
85 buf
.append(line
).append('\n');
86 line
= reader
.readLine();
89 catch (java
.io
.IOException e
)
91 log
.println(pref
+ "Exception occurred: " + e
);
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.
155 * Waits specified time in milliSeconds for
156 * process to exit and return its status.
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
;
167 this.envVars
= envVars
;
170 this.log
= new PrintWriter(new OutputStreamWriter(System
.out
, "UTF-8"));
179 * Executes the command immediately returns. The process
180 * remains in running state.
182 * @return <code>true</code> if process was successfully
185 public boolean executeAsynchronously()
191 public synchronized void kill()
197 boolean exit
= false;
199 while (counter
< 3 && !exit
)
201 m_aProcess
.destroy();
203 util
.utils
.pause(1000 * counter
);
206 final int exit_Value
= m_aProcess
.exitValue();
215 dbg("kill: process closed with exit code " + exit_Value
);
217 catch (java
.lang
.IllegalThreadStateException e
)
221 dbg("kill: Couldn't close process after " + counter
+ " attempts, trying again");
229 private void showEnvVars()
233 for (int i
= 0; i
< envVars
.length
; i
++)
235 log
.println("env: " + envVars
[i
]);
240 log
.println("env: null");
244 private void execute()
248 throw new RuntimeException(
249 "The process handler has already been executed.");
251 final Runtime runtime
= Runtime
.getRuntime();
256 log
.println(utils
.getDateTime() + "execute: Starting command: ");
257 log
.println(cmdLine
+ " path=" + workDir
.getAbsolutePath());
259 m_aProcess
= runtime
.exec(cmdLine
, envVars
, workDir
);
263 log
.println(utils
.getDateTime() + "execute: Starting command: ");
264 log
.println(cmdLine
);
266 m_aProcess
= runtime
.exec(cmdLine
, envVars
);
270 catch (java
.io
.IOException e
)
272 log
.println(utils
.getDateTime() + "execute: The command " + cmdLine
+ " can't be started: " + e
);
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");
285 private void flushInput()
299 * Returns information about was the command started or
302 * @return <code>true</code> if the external command was
303 * found and successfully started.
305 private boolean isStarted()
311 * Returns the information about the final state of command
314 * @return <code>true</code> if the command correctly starts,
315 * exits and was not interrupted due to timeout.
317 private boolean isFinished()
323 * Returns exit code of the external command.
325 * @return exit code of command if it was finished,
328 public int getExitCode()
332 exitValue
= m_aProcess
.exitValue();
341 private void dbg(String message
)
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()
371 while (m_nTimeoutInSec
> 0)
374 util
.utils
.pause(1000);
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
);