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
.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
;
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
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
;
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
));
67 this.bOutput
= _bOutput
;
76 String line
= reader
.readLine();
81 log
.println(pref
+ line
);
84 buf
.append(line
).append('\n');
85 line
= reader
.readLine();
88 catch (java
.io
.IOException e
)
90 log
.println(pref
+ "Exception occurred: " + e
);
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.
154 * Waits specified time in miliSeconds for
155 * process to exit and return its status.
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
;
166 this.envVars
= envVars
;
169 this.log
= new PrintWriter(new OutputStreamWriter(System
.out
));
178 * Executes the command immediately returns. The process
179 * remains in running state.
181 * @return <code>true</code> if process was successfully
184 public boolean executeAsynchronously()
190 public synchronized void kill()
196 boolean exit
= false;
198 while (counter
< 3 && !exit
)
200 m_aProcess
.destroy();
202 util
.utils
.pause(1000 * counter
);
205 final int exit_Value
= m_aProcess
.exitValue();
214 dbg("kill: process closed with exit code " + exit_Value
);
216 catch (java
.lang
.IllegalThreadStateException e
)
220 dbg("kill: Couldn't close process after " + counter
+ " attempts, trying again");
228 private void showEnvVars()
232 for (int i
= 0; i
< envVars
.length
; i
++)
234 log
.println("env: " + envVars
[i
]);
239 log
.println("env: null");
243 private void execute()
247 throw new RuntimeException(
248 "The process handler has already been executed.");
250 final Runtime runtime
= Runtime
.getRuntime();
255 log
.println(utils
.getDateTime() + "execute: Starting command: ");
256 log
.println(cmdLine
+ " path=" + workDir
.getAbsolutePath());
258 m_aProcess
= runtime
.exec(cmdLine
, envVars
, workDir
);
262 log
.println(utils
.getDateTime() + "execute: Starting command: ");
263 log
.println(cmdLine
);
265 m_aProcess
= runtime
.exec(cmdLine
, envVars
);
269 catch (java
.io
.IOException e
)
271 log
.println(utils
.getDateTime() + "execute: The command " + cmdLine
+ " can't be started: " + e
);
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");
284 private void flushInput()
298 * Returns information about was the command started or
301 * @return <code>true</code> if the external command was
302 * found and successfully started.
304 private boolean isStarted()
310 * Returns the information about the final state of command
313 * @return <code>true</code> if the command correctly starts,
314 * exits and was not interrupted due to timeout.
316 private boolean isFinished()
322 * Returns exit code of the external command.
324 * @return exit code of command if it was finished,
327 public int getExitCode()
331 exitValue
= m_aProcess
.exitValue();
340 private void dbg(String message
)
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()
370 while (m_nTimeoutInSec
> 0)
373 util
.utils
.pause(1000);
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
);