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
.BufferedReader
;
21 import java
.io
.InputStream
;
23 import java
.io
.PrintWriter
;
24 import java
.io
.PrintStream
;
25 import java
.io
.LineNumberReader
;
26 import java
.io
.InputStreamReader
;
27 import java
.io
.OutputStreamWriter
;
28 import lib
.TestParameters
;
29 import util
.PropertyName
;
33 * Class collect information from input stream in
34 * background (sparate 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 ProcesHandler
43 class Pump
extends Thread
46 private LineNumberReader reader
;
48 private StringBuffer buf
= new StringBuffer(256);
49 private PrintWriter log
;
50 private 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
;
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
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 boolean mbTimedOut
= false;
122 private long mTimeOut
= 0;
123 private String stdInBuff
= "";
124 private Pump stdout
= null;
125 private Pump stderr
= null;
126 private PrintStream stdIn
= null;
127 private Process m_aProcess
= null;
128 private TestParameters param
= null;
129 private boolean debug
= false;
130 private boolean bUseOutput
= true;
132 private int m_nProcessTimeout
= 0;
133 private String m_sProcessKiller
;
134 private ProcessWatcher m_aWatcher
;
137 * Creates instance with specified external command.
138 * Debug info and output
139 * of external command is printed to stdout.
142 public ProcessHandler(String cmdLine
)
144 this(cmdLine
, null, null, null, 0);
148 * Creates instance with specified external command
149 * including parameters as an array.
150 * Debug info and output
151 * of external command is printed to stdout.
154 public ProcessHandler(String
[] cmdLines
)
156 this(null, null, null, null, 0);
157 cmdLineArray
= cmdLines
;
161 * Creates instance with specified external command
162 * including parameters as an array, with environment
164 * Debug info and output
165 * of external command is printed to stdout.
168 * @see java.lang.Runtime exec(String[], String[])
170 public ProcessHandler(String
[] cmdLines
, String
[] envVars
)
172 this(null, null, null, envVars
, 0);
173 cmdLineArray
= cmdLines
;
177 * Creates instance with specified external command
178 * including parameters as an array, with environment
179 * variables. The command will be started in workDir.
180 * Debug info and output
181 * of external command is printed to stdout.
185 public ProcessHandler(String
[] cmdLines
, File workDir
)
187 this(null, null, workDir
, null, 0);
188 cmdLineArray
= cmdLines
;
193 * Creates instance with specified external command and
194 * log stream where debug info and output
195 * of external command is printed out. The command will be started in workDir.
200 public ProcessHandler(String
[] cmdLines
, PrintWriter log
, File workDir
)
202 this(null, log
, workDir
, null, 0);
203 cmdLineArray
= cmdLines
;
207 * Creates instance with specified external command and
208 * log stream where debug info and output
209 * of external command is printed out.
213 public ProcessHandler(String cmdLine
, PrintWriter log
)
215 this(cmdLine
, log
, null, null, 0);
219 * Creates instance with specified external command and set the time out for the command.
223 public ProcessHandler(String cmdLine
, int timeOut
)
225 this(cmdLine
, null, null, null, timeOut
);
229 * Creates instance with specified external command which
230 * will be executed in the some work directory.
231 * Debug info and output
232 * of external commandis printed to stdout.
236 public ProcessHandler(String cmdLine
, File workDir
)
238 this(cmdLine
, null, workDir
, null, 0);
242 * Creates instance with specified external command which
243 * will be executed in the some work directory.
244 * Debug info and output printed in log stream.
249 public ProcessHandler(String cmdLine
, PrintWriter log
, File workDir
)
251 this(cmdLine
, log
, workDir
, null, 0);
255 * Creates instance with specified external command which
256 * will be executed in the some work directory and
257 * log stream where debug info and output
258 * of external command is printed .
259 * The specified environment variables are set for the new process.
260 * If log stream is null, logging is printed to stdout.
266 public ProcessHandler(String cmdLine
, PrintWriter log
, File workDir
, String
[] envVars
)
268 this(cmdLine
, log
, workDir
, envVars
, 0);
272 * Creates instance with specified external command which
273 * will be executed in the some work directory and
275 * @param cmdLine the command to be executed
276 * @param log log stream where debug info and output
277 * of external command is printed .
278 * @param workDir The working directory of the new process
279 * @param envVars The specified environment variables are
280 * set for the new process.
281 * If log stream is null, logging is printed to stdout.
282 * @param timeOut When started sychronisly, the maximum time the
283 * process will live. When the process being destroyed
284 * a log will be written out. It can be asked on
285 * <code>isTimedOut()</code> if it has been terminated.
288 * Waits specified time in miliSeconds for
289 * process to exit and return its status.
292 * Waits for the process to end regulary
295 * Kills the process immediately
299 public ProcessHandler(String cmdLine
, PrintWriter log
, File workDir
, String
[] envVars
, long timeOut
)
301 this.cmdLine
= cmdLine
;
302 this.workDir
= workDir
;
304 this.cmdLine
= cmdLine
;
305 this.envVars
= envVars
;
308 this.log
= new PrintWriter(new OutputStreamWriter(System
.out
));
314 this.mTimeOut
= timeOut
;
318 * Creates instance with specified external command which
319 * will be executed in the some work directory and
320 * log stream where debug info and output of external command is printed.
321 * If log stream is null, logging is printed to stdout.
322 * From the <CODE>TestParameters</CODE> the <CODE>OfficeWachter</CODE> get a ping.
326 * @param shortWait If this parameter is ture the <CODE>mTimeOut</CODE> is set to 5000 ms, else it is set to
327 * half of time out from parameter timeout.
328 * @param param the TestParameters
329 * @see lib.TestParameters
330 * @see helper.OfficeWatcher
332 public ProcessHandler(String
[] commands
, PrintWriter log
, File workDir
, int shortWait
, TestParameters param
)
334 this(null, log
, workDir
, null, 0);
335 this.cmdLineArray
= commands
;
339 this.mTimeOut
= shortWait
;
343 this.mTimeOut
= (long) (param
.getInt(PropertyName
.TIME_OUT
) / 1.3);
345 debug
= param
.getBool(PropertyName
.DEBUG_IS_ACTIVE
);
350 * If not equal 0, the time to maximal wait.
353 public void setProcessTimeout(int _n
)
355 m_nProcessTimeout
= _n
;
359 * This command will call after ProcessTimeout is arrived.
362 public void setProcessKiller(String _s
)
364 m_sProcessKiller
= _s
;
368 * This method do an asynchronous execution of the commands. To avoid a interruption on long running processes
369 * caused by <CODE>OfficeWatcher</CODE>, the OfficeWatcher get frequently a ping.
370 * @see helper.OfficeWatcher
372 public void runCommand()
375 boolean changedText
= true;
378 this.executeAsynchronously();
380 OfficeWatcher ow
= null;
383 ow
= (OfficeWatcher
) param
.get(PropertyName
.OFFICE_WATCHER
);
391 while (!this.isFinished() && changedText
)
393 // dbg("runCommand: waiting " + mTimeOut / 1000 + " seconds while command execution is ongoing... " + count);
394 // shortWait(mTimeOut);
395 // shortWait(2000); // wait 2 seconds.
397 waitFor(2000, false); // wait but don't kill
403 // check for changes in the output stream. If there are no changes, the process maybe hangs
404 if (!this.isFinished())
409 String sOutputText
= getOutputText();
410 if (sOutputText
.length() == memText
.length())
413 // dbg("runCommand Could not detect changes in output stream!!!");
416 memText
= this.getOutputText();
421 if (!this.isFinished())
423 dbg("runCommand Process is not finished but there are no changes in output stream.");
428 public boolean isTimedOut()
433 private void setTimedOut(boolean bTimedOut
)
435 mbTimedOut
= bTimedOut
;
439 * Executes the command and returns only when the process
442 * @return <code>true</code> if process was successfully
443 * started and correcly exits (exit code doesn't affect
446 public boolean executeSynchronously()
449 return waitFor(mTimeOut
);
453 * Executes the command immediately returns. The process
454 * remains in running state. Control of its state should
455 * be made by <code>waitFor</code> methods.
457 * @return <code>true</code> if process was successfully
460 public boolean executeAsynchronously()
466 public synchronized void kill()
472 boolean exit
= false;
474 while (counter
< 3 && !exit
)
476 m_aProcess
.destroy();
480 Thread
.sleep(1000 * counter
); // 5000
482 catch (java
.lang
.InterruptedException e
)
487 final int exit_Value
= m_aProcess
.exitValue();
496 dbg("kill: process closed with exit code " + exit_Value
);
498 catch (java
.lang
.IllegalThreadStateException e
)
502 dbg("kill: Couldn't close process after " + counter
+ " attempts, trying again");
511 * Returns the time in seconds since 1st January 1970
514 public static long getSystemTime()
516 // Calendar cal = new GregorianCalendar();
517 // final long nTime = cal.getTimeInMillis();
518 final long nTime
= System
.currentTimeMillis();
521 private long m_nExactStartTimeInMillisec
;
523 private void initialExactStartTime()
525 m_nExactStartTimeInMillisec
= getSystemTime();
528 public long getProcessStartTime()
530 return m_nExactStartTimeInMillisec
;
533 private void showEnvVars()
537 for (int i
= 0; i
< envVars
.length
; i
++)
539 log
.println("env: " + envVars
[i
]);
544 log
.println("env: null");
548 protected void execute()
552 throw new RuntimeException(
553 "The process handler has already been executed.");
555 final Runtime runtime
= Runtime
.getRuntime();
560 log
.println(utils
.getDateTime() + "execute: Starting command from array: ");
561 for (int i
= 0; i
< cmdLineArray
.length
; i
++)
563 log
.println(cmdLineArray
[i
]);
568 initialExactStartTime();
569 initializeProcessKiller();
570 m_aProcess
= runtime
.exec(cmdLineArray
, envVars
);
576 log
.println(utils
.getDateTime() + "execute: Starting command: ");
577 log
.println(cmdLine
+ " path=" + workDir
.getAbsolutePath());
579 m_aProcess
= runtime
.exec(cmdLine
, envVars
, workDir
);
583 log
.println(utils
.getDateTime() + "execute: Starting command: ");
584 log
.println(cmdLine
);
586 m_aProcess
= runtime
.exec(cmdLine
, envVars
);
591 catch (java
.io
.IOException e
)
595 log
.println(utils
.getDateTime() + "execute: The command array can't be started: " + e
);
599 log
.println(utils
.getDateTime() + "execute: The command " + cmdLine
+ " can't be started: " + e
);
603 dbg("execute: pump io-streams");
604 stdout
= new Pump(m_aProcess
.getInputStream(), log
, "out > ", bUseOutput
);
605 stderr
= new Pump(m_aProcess
.getErrorStream(), log
, "err > ", bUseOutput
);
606 stdIn
= new PrintStream(m_aProcess
.getOutputStream());
608 // int nExitValue = m_aProcess.exitValue();
611 dbg("execute: flush io-streams");
617 * This method is useful when the process was executed
618 * asynchronously. Waits for process to exit and return
621 * @return <code>true</code> if process correctly exited
622 * (exit code doesn't affect to this result).
624 public boolean waitFor()
630 * This method is useful when the process was executed
631 * asynchronously. Waits during specified time for process
632 * to exit and return its status.
635 * Waits specified time in miliSeconds for
636 * process to exit and return its status.
639 * Waits for the process to end regulary
642 * Kills the process immediately
644 * @return <code>true</code> if process correctly exited
645 * (exit code doesn't affect to this result).
647 public boolean waitFor(long timeout
)
649 return waitFor(timeout
, true);
652 private boolean waitFor(long timeout
, boolean bKillProcessAfterTimeout
)
667 m_aProcess
.waitFor();
669 catch (InterruptedException e
)
671 log
.println("The process was interrupted: " + e
);
676 exitValue
= m_aProcess
.exitValue();
678 catch (IllegalThreadStateException e
)
686 while (!isFinished
&& timeout
> 0)
693 exitValue
= m_aProcess
.exitValue(); // throws exception if not finished
695 catch (IllegalThreadStateException e
)
703 log
.println("The process has timed out!");
706 catch (InterruptedException ex
)
708 log
.println("The process was interrupted: " + ex
);
712 if (bKillProcessAfterTimeout
== true)
716 log
.println("Going to destroy the process!!");
717 m_aProcess
.destroy();
718 log
.println("Process has been destroyed!");
721 // Removed as hung up in SDK test 'PathSettings'
725 // } catch (InterruptedException e) {}
730 protected void flushInput()
737 synchronized(stdInBuff
)
739 stdIn
.print(stdInBuff
);
746 * Returns the text output by external command to stdout.
747 * @return the text output by external command to stdout
749 public String
getOutputText()
757 return stdout
.getStringBuffer();
762 * Returns the text output by external command to stderr.
763 * @return the text output by external command to stderr
765 public String
getErrorText()
773 return stderr
.getStringBuffer();
778 * Prints the string specified to sdtin of external
779 * command. '\n' is not added so if you need you
780 * should terminate the string with '\n'. <p>
782 * The method can also be called before the command
783 * starts its execution. Then the text is buffered
784 * and transfered to command when it will be started.
787 public void printInputText(String str
)
794 * Returns information about was the command started or
797 * @return <code>true</code> if the external command was
798 * found and successfully started.
800 public boolean isStarted()
806 * Returns the information about the final state of command
809 * @return <code>true</code> if the command correctly starts,
810 * exits and was not interrupted due to timeout.
812 public boolean isFinished()
818 * Returns exit code of the external command.
820 * @return exit code of command if it was finished,
823 public int getExitCode()
827 exitValue
= m_aProcess
.exitValue();
831 //System.out.println("No ExitValue available");
837 /** Causes the thread to sleep some time.
838 * @param milliseconds
840 public static void shortWait(long milliseconds
)
844 Thread
.sleep(milliseconds
);
846 catch (InterruptedException e
)
848 System
.out
.println("While waiting :" + e
);
852 private void dbg(String message
)
856 log
.println(utils
.getDateTime() + "PH." + message
);
860 public void noOutput()
864 // -------------------------------------------------------------------------
865 class ProcessWatcher
extends Thread
868 private int m_nTimeoutInSec
;
869 private String m_sProcessToStart
;
870 private boolean m_bInterrupt
;
872 public ProcessWatcher(int _nTimeOut
, String _sProcess
)
874 m_nTimeoutInSec
= _nTimeOut
;
875 m_sProcessToStart
= _sProcess
;
876 m_bInterrupt
= false;
880 * returns true, if the thread should hold on
883 public synchronized boolean isInHoldOn()
888 * Marks the thread to hold on, next time
889 * STUPID: The thread must poll this flag itself.
891 * Reason: interrupt() seems not to work as expected.
893 public synchronized void holdOn()
901 while (m_nTimeoutInSec
> 0)
908 catch(java
.lang
.InterruptedException e
)
910 // interrupt flag is set back to 'not interrupted' :-(
917 if (m_nTimeoutInSec
<= 0 && !isInHoldOn()) // not zero, so we are interrupted.
919 system(m_sProcessToStart
);
924 * Start an external Process
927 private void system(String _sProcess
)
929 if (_sProcess
== null)
937 // run a _sProcess command
938 // using the Runtime exec method:
939 Process p
= Runtime
.getRuntime().exec(_sProcess
);
941 BufferedReader stdInput
= new BufferedReader(new InputStreamReader(p
.getInputStream()));
943 BufferedReader stdError
= new BufferedReader(new InputStreamReader(p
.getErrorStream()));
945 // read the output from the command
947 while ((s
= stdInput
.readLine()) != null)
949 System
.out
.println("out:" + s
);
952 // read any errors from the attempted command
953 while ((s
= stdError
.readLine()) != null)
955 System
.out
.println("err:" + s
);
959 catch (java
.io
.IOException e
)
961 System
.out
.println("exception caught: ");
969 * If the timeout only given by setProcessTimeout(int seconds) function is != 0,
970 * a extra thread is created and after time has run out, the ProcessKiller string
971 * given by function setProcessKiller(string) will execute.
972 * So it is possible to kill a running office after a given time of seconds.
974 private void initializeProcessKiller()
976 if (m_nProcessTimeout
!= 0)
978 m_aWatcher
= new ProcessWatcher(m_nProcessTimeout
, m_sProcessKiller
);
984 * to stop the extra thread, before he will kill a running office. This will stop the thread.
986 public void stopWatcher()
988 if (m_aWatcher
!= null)