1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
29 import java
.io
.BufferedReader
;
30 import java
.io
.InputStream
;
32 import java
.io
.PrintWriter
;
33 import java
.io
.PrintStream
;
34 import java
.io
.LineNumberReader
;
35 import java
.io
.InputStreamReader
;
36 import java
.io
.OutputStreamWriter
;
37 import java
.io
.Writer
;
38 import java
.util
.Calendar
;
39 import java
.util
.Date
;
40 import java
.util
.GregorianCalendar
;
41 import lib
.TestParameters
;
42 import share
.LogWriter
;
43 import util
.PropertyName
;
47 * Class collect information from input stream in
48 * background (sparate thread) and outputs it to
49 * some log stream. I helps to avoid buffer overflow
50 * when output stream has small buffer size (e.g.
51 * in case when handling stdout from external
52 * <code>Process</code>)
54 * This class is currently used by ProcesHandler
57 class Pump
extends Thread
60 private LineNumberReader reader
;
62 private StringBuffer buf
= new StringBuffer(256);
63 private PrintWriter log
;
64 private boolean bOutput
;
67 * Creates Pump for specified <code>InputStream</code>.
68 * This Pump also synchronously output text read to
69 * log by prefixed lines. Constructor immediately
70 * starts reading in a separate thread.
72 * @param is Stream which requires permanent reading.
73 * @param log Writer where prefixed text lines to be output
74 * @param outPrefix A prefix which is printed at the
75 * beginning of each output line.
77 public Pump(InputStream is
, PrintWriter log
, String outPrefix
, boolean _bOutput
)
79 this.pref
= (outPrefix
== null) ?
"" : outPrefix
;
80 reader
= new LineNumberReader(new InputStreamReader(is
));
82 this.bOutput
= _bOutput
;
90 String line
= reader
.readLine();
95 log
.println(pref
+ line
);
98 buf
.append(line
).append('\n');
99 line
= reader
.readLine();
102 catch (java
.io
.IOException e
)
104 log
.println(pref
+ "Exception occurred: " + e
);
109 * Returns the text collected from input stream.
111 public String
getStringBuffer()
113 return buf
.toString();
118 * Class provides convenient way for running external program
119 * handle its standard streams, control execution and check results.
120 * Instance of this class must be created only for a single
121 * execution. If you need to execute the same command again you
122 * should create a new instance for this.
124 public class ProcessHandler
127 private String cmdLine
;
128 private String
[] cmdLineArray
;
129 private String
[] envVars
= null;
130 private File workDir
= null;
131 private PrintWriter log
;
132 private int exitValue
= -1;
133 private boolean isFinished
= false;
134 private boolean isStarted
= false;
135 private boolean mbTimedOut
= false;
136 private long mTimeOut
= 0;
137 private String stdInBuff
= "";
138 private Pump stdout
= null;
139 private Pump stderr
= null;
140 private PrintStream stdIn
= null;
141 private Process m_aProcess
= null;
142 private TestParameters param
= null;
143 private boolean debug
= false;
144 private boolean bUseOutput
= true;
146 private int m_nProcessTimeout
= 0;
147 private String m_sProcessKiller
;
148 private ProcessWatcher m_aWatcher
;
151 * Creates instance with specified external command.
152 * Debug info and output
153 * of external command is printed to stdout.
156 public ProcessHandler(String cmdLine
)
158 this(cmdLine
, null, null, null, 0);
162 * Creates instance with specified external command
163 * including parameters as an array.
164 * Debug info and output
165 * of external command is printed to stdout.
168 public ProcessHandler(String
[] cmdLines
)
170 this(null, null, null, null, 0);
171 cmdLineArray
= cmdLines
;
175 * Creates instance with specified external command
176 * including parameters as an array, with environment
178 * Debug info and output
179 * of external command is printed to stdout.
182 * @see java.lang.Runtime exec(String[], String[])
184 public ProcessHandler(String
[] cmdLines
, String
[] envVars
)
186 this(null, null, null, envVars
, 0);
187 cmdLineArray
= cmdLines
;
191 * Creates instance with specified external command
192 * including parameters as an array, with environment
193 * variables. The command will be started in workDir.
194 * Debug info and output
195 * of external command is printed to stdout.
199 public ProcessHandler(String
[] cmdLines
, File workDir
)
201 this(null, null, workDir
, null, 0);
202 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. The command will be started in workDir.
214 public ProcessHandler(String
[] cmdLines
, PrintWriter log
, File workDir
)
216 this(null, log
, workDir
, null, 0);
217 cmdLineArray
= cmdLines
;
221 * Creates instance with specified external command and
222 * log stream where debug info and output
223 * of external command is printed out.
227 public ProcessHandler(String cmdLine
, PrintWriter log
)
229 this(cmdLine
, log
, null, null, 0);
233 * Creates instance with specified external command and set the time out for the command.
237 public ProcessHandler(String cmdLine
, int timeOut
)
239 this(cmdLine
, null, null, null, timeOut
);
243 * Creates instance with specified external command which
244 * will be executed in the some work directory.
245 * Debug info and output
246 * of external commandis printed to stdout.
250 public ProcessHandler(String cmdLine
, File workDir
)
252 this(cmdLine
, null, workDir
, null, 0);
256 * Creates instance with specified external command which
257 * will be executed in the some work directory.
258 * Debug info and output printed in log stream.
263 public ProcessHandler(String cmdLine
, PrintWriter log
, File workDir
)
265 this(cmdLine
, log
, workDir
, null, 0);
269 * Creates instance with specified external command which
270 * will be executed in the some work directory and
271 * log stream where debug info and output
272 * of external command is printed .
273 * The specified environment variables are set for the new process.
274 * If log stream is null, logging is printed to stdout.
280 public ProcessHandler(String cmdLine
, PrintWriter log
, File workDir
, String
[] envVars
)
282 this(cmdLine
, log
, workDir
, envVars
, 0);
286 * Creates instance with specified external command which
287 * will be executed in the some work directory and
289 * @param cmdLine the command to be executed
290 * @param log log stream where debug info and output
291 * of external command is printed .
292 * @param workDir The working directory of the new process
293 * @param envVars The specified environment variables are
294 * set for the new process.
295 * If log stream is null, logging is printed to stdout.
296 * @param timeOut When started sychronisly, the maximum time the
297 * process will live. When the process being destroyed
298 * a log will be written out. It can be asked on
299 * <code>isTimedOut()</code> if it has been terminated.
302 * Waits specified time in miliSeconds for
303 * process to exit and return its status.
306 * Waits for the process to end regulary
309 * Kills the process immediately
313 public ProcessHandler(String cmdLine
, PrintWriter log
, File workDir
, String
[] envVars
, long timeOut
)
315 this.cmdLine
= cmdLine
;
316 this.workDir
= workDir
;
318 this.cmdLine
= cmdLine
;
319 this.envVars
= envVars
;
322 this.log
= new PrintWriter(new OutputStreamWriter(System
.out
));
328 this.mTimeOut
= timeOut
;
332 * Creates instance with specified external command which
333 * will be executed in the some work directory and
334 * log stream where debug info and output of external command is printed.
335 * If log stream is null, logging is printed to stdout.
336 * From the <CODE>TestParameters</CODE> the <CODE>OfficeWachter</CODE> get a ping.
340 * @param shortWait If this parameter is ture the <CODE>mTimeOut</CODE> is set to 5000 ms, else it is set to
341 * half of time out from parameter timeout.
342 * @param param the TestParameters
343 * @see lib.TestParameters
344 * @see helper.OfficeWatcher
346 public ProcessHandler(String
[] commands
, PrintWriter log
, File workDir
, int shortWait
, TestParameters param
)
348 this(null, log
, workDir
, null, 0);
349 this.cmdLineArray
= commands
;
353 this.mTimeOut
= shortWait
;
357 this.mTimeOut
= (long) (param
.getInt(PropertyName
.TIME_OUT
) / 1.3);
359 debug
= param
.getBool(PropertyName
.DEBUG_IS_ACTIVE
);
364 * If not equal 0, the time to maximal wait.
367 public void setProcessTimeout(int _n
)
369 m_nProcessTimeout
= _n
;
373 * This command will call after ProcessTimeout is arrived.
376 public void setProcessKiller(String _s
)
378 m_sProcessKiller
= _s
;
382 * This method do an asynchronous execution of the commands. To avoid a interruption on long running processes
383 * caused by <CODE>OfficeWatcher</CODE>, the OfficeWatcher get frequently a ping.
384 * @see helper.OfficeWatcher
386 public void runCommand()
389 boolean changedText
= true;
393 this.executeAsynchronously();
395 OfficeWatcher ow
= null;
398 ow
= (OfficeWatcher
) param
.get(PropertyName
.OFFICE_WATCHER
);
406 while (!this.isFinished() && changedText
)
409 // dbg("runCommand: waiting " + mTimeOut / 1000 + " seconds while command execution is ongoing... " + count);
410 // shortWait(mTimeOut);
411 // shortWait(2000); // wait 2 seconds.
413 waitFor(2000, false); // wait but don't kill
419 // check for changes in the output stream. If there are no changes, the process maybe hangs
420 if (!this.isFinished())
425 String sOutputText
= getOutputText();
426 if (sOutputText
.length() == memText
.length())
429 // dbg("runCommand Could not detect changes in output stream!!!");
432 memText
= this.getOutputText();
437 if (!this.isFinished())
439 dbg("runCommand Process ist not finished but there are no changes in output stream.");
444 public boolean isTimedOut()
449 private void setTimedOut(boolean bTimedOut
)
451 mbTimedOut
= bTimedOut
;
455 * Executes the command and returns only when the process
458 * @return <code>true</code> if process was successfully
459 * started and correcly exits (exit code doesn't affect
462 public boolean executeSynchronously()
465 return waitFor(mTimeOut
);
469 * Executes the command immediately returns. The process
470 * remains in running state. Control of its state should
471 * be made by <code>waitFor</code> methods.
473 * @return <code>true</code> if process was successfully
476 public boolean executeAsynchronously()
482 public synchronized void kill()
488 boolean exit
= false;
490 while (counter
< 3 && !exit
)
492 m_aProcess
.destroy();
496 Thread
.sleep(1000 * counter
); // 5000
498 catch (java
.lang
.InterruptedException e
)
503 final int exit_Value
= m_aProcess
.exitValue();
512 dbg("kill: process closed with exit code " + exit_Value
);
514 catch (java
.lang
.IllegalThreadStateException e
)
518 dbg("kill: Couldn't close process after " + counter
+ " attempts, trying again");
527 * Returns the time in seconds since 1st January 1970
530 public static long getSystemTime()
532 // Calendar cal = new GregorianCalendar();
533 // final long nTime = cal.getTimeInMillis();
534 final long nTime
= System
.currentTimeMillis();
537 private long m_nExactStartTimeInMillisec
;
539 private void initialExactStartTime()
541 m_nExactStartTimeInMillisec
= getSystemTime();
544 public long getProcessStartTime()
546 return m_nExactStartTimeInMillisec
;
549 private void showEnvVars()
553 for (int i
= 0; i
< envVars
.length
; i
++)
555 log
.println("env: " + envVars
[i
]);
560 log
.println("env: null");
564 protected void execute()
568 throw new RuntimeException(
569 "The process handler has already been executed.");
571 final Runtime runtime
= Runtime
.getRuntime();
576 log
.println(utils
.getDateTime() + "execute: Starting command from array: ");
577 for (int i
= 0; i
< cmdLineArray
.length
; i
++)
579 log
.println(cmdLineArray
[i
]);
584 initialExactStartTime();
585 initializeProcessKiller();
586 m_aProcess
= runtime
.exec(cmdLineArray
, envVars
);
592 log
.println(utils
.getDateTime() + "execute: Starting command: ");
593 log
.println(cmdLine
+ " path=" + workDir
.getAbsolutePath());
595 m_aProcess
= runtime
.exec(cmdLine
, envVars
, workDir
);
599 log
.println(utils
.getDateTime() + "execute: Starting command: ");
600 log
.println(cmdLine
);
602 m_aProcess
= runtime
.exec(cmdLine
, envVars
);
607 catch (java
.io
.IOException e
)
611 log
.println(utils
.getDateTime() + "execute: The command array can't be started: " + e
);
615 log
.println(utils
.getDateTime() + "execute: The command " + cmdLine
+ " can't be started: " + e
);
619 dbg("execute: pump io-streams");
620 stdout
= new Pump(m_aProcess
.getInputStream(), log
, "out > ", bUseOutput
);
621 stderr
= new Pump(m_aProcess
.getErrorStream(), log
, "err > ", bUseOutput
);
622 stdIn
= new PrintStream(m_aProcess
.getOutputStream());
624 // int nExitValue = m_aProcess.exitValue();
627 dbg("execute: flush io-streams");
633 * This method is useful when the process was executed
634 * asynchronously. Waits for process to exit and return
637 * @return <code>true</code> if process correctly exited
638 * (exit code doesn't affect to this result).
640 public boolean waitFor()
646 * This method is useful when the process was executed
647 * asynchronously. Waits during specified time for process
648 * to exit and return its status.
651 * Waits specified time in miliSeconds for
652 * process to exit and return its status.
655 * Waits for the process to end regulary
658 * Kills the process immediately
660 * @return <code>true</code> if process correctly exited
661 * (exit code doesn't affect to this result).
663 public boolean waitFor(long timeout
)
665 return waitFor(timeout
, true);
668 private boolean waitFor(long timeout
, boolean bKillProcessAfterTimeout
)
683 m_aProcess
.waitFor();
685 catch (InterruptedException e
)
687 log
.println("The process was interrupted: " + e
);
692 exitValue
= m_aProcess
.exitValue();
694 catch (IllegalThreadStateException e
)
702 while (!isFinished
&& timeout
> 0)
709 exitValue
= m_aProcess
.exitValue(); // throws exception if not finished
711 catch (IllegalThreadStateException e
)
719 log
.println("The process has timed out!");
722 catch (InterruptedException ex
)
724 log
.println("The process was interrupted: " + ex
);
728 if (bKillProcessAfterTimeout
== true)
732 log
.println("Going to destroy the process!!");
733 m_aProcess
.destroy();
734 log
.println("Process has been destroyed!");
737 // Removed as hung up in SDK test 'PathSettings'
741 // } catch (InterruptedException e) {}
746 protected void flushInput()
753 synchronized(stdInBuff
)
755 stdIn
.print(stdInBuff
);
762 * Returns the text output by external command to stdout.
763 * @return the text output by external command to stdout
765 public String
getOutputText()
773 return stdout
.getStringBuffer();
778 * Returns the text output by external command to stderr.
779 * @return the text output by external command to stderr
781 public String
getErrorText()
789 return stderr
.getStringBuffer();
794 * Prints the string specified to sdtin of external
795 * command. '\n' is not added so if you need you
796 * should terminate the string with '\n'. <p>
798 * The method can also be called before the command
799 * starts its execution. Then the text is buffered
800 * and transfered to command when it will be started.
803 public void printInputText(String str
)
810 * Returns information about was the command started or
813 * @return <code>true</code> if the external command was
814 * found and successfully started.
816 public boolean isStarted()
822 * Returns the information about the final state of command
825 * @return <code>true</code> if the command correctly starts,
826 * exits and was not interrupted due to timeout.
828 public boolean isFinished()
834 * Returns exit code of the external command.
836 * @return exit code of command if it was finished,
839 public int getExitCode()
843 exitValue
= m_aProcess
.exitValue();
847 //System.out.println("No ExitValue available");
853 /** Causes the thread to sleep some time.
854 * @param milliseconds
856 public static void shortWait(long milliseconds
)
860 Thread
.sleep(milliseconds
);
862 catch (InterruptedException e
)
864 System
.out
.println("While waiting :" + e
);
868 private void dbg(String message
)
872 log
.println(utils
.getDateTime() + "PH." + message
);
876 public void noOutput()
880 // -------------------------------------------------------------------------
881 class ProcessWatcher
extends Thread
884 private int m_nTimeoutInSec
;
885 private String m_sProcessToStart
;
886 private boolean m_bInterrupt
;
888 public ProcessWatcher(int _nTimeOut
, String _sProcess
)
890 m_nTimeoutInSec
= _nTimeOut
;
891 m_sProcessToStart
= _sProcess
;
892 m_bInterrupt
= false;
896 * returns true, if the thread should hold on
899 public synchronized boolean isInHoldOn()
904 * Marks the thread to hold on, next time
905 * STUPID: The thread must poll this flag itself.
907 * Reason: interrupt() seems not to work as expected.
909 public synchronized void holdOn()
917 while (m_nTimeoutInSec
> 0)
924 catch(java
.lang
.InterruptedException e
)
926 // interrupt flag is set back to 'not interrupted' :-(
933 if (m_nTimeoutInSec
<= 0 && !isInHoldOn()) // not zero, so we are interrupted.
935 system(m_sProcessToStart
);
940 * Start an external Process
943 private void system(String _sProcess
)
945 if (_sProcess
== null)
953 // run a _sProcess command
954 // using the Runtime exec method:
955 Process p
= Runtime
.getRuntime().exec(_sProcess
);
957 BufferedReader stdInput
= new BufferedReader(new InputStreamReader(p
.getInputStream()));
959 BufferedReader stdError
= new BufferedReader(new InputStreamReader(p
.getErrorStream()));
961 // read the output from the command
963 while ((s
= stdInput
.readLine()) != null)
965 System
.out
.println("out:" + s
);
968 // read any errors from the attempted command
969 while ((s
= stdError
.readLine()) != null)
971 System
.out
.println("err:" + s
);
975 catch (java
.io
.IOException e
)
977 System
.out
.println("exception caught: ");
985 * If the timeout only given by setProcessTimeout(int seconds) function is != 0,
986 * a extra thread is created and after time has run out, the ProcessKiller string
987 * given by function setProcessKiller(string) will execute.
988 * So it is possible to kill a running office after a given time of seconds.
990 private void initializeProcessKiller()
992 if (m_nProcessTimeout
!= 0)
994 m_aWatcher
= new ProcessWatcher(m_nProcessTimeout
, m_sProcessKiller
);
1000 * to stop the extra thread, before he will kill a running office. This will stop the thread.
1002 public void stopWatcher()
1004 if (m_aWatcher
!= null)
1006 m_aWatcher
.holdOn();