1 package expectj;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import org.apache.commons.logging.Log;
7 import org.apache.commons.logging.LogFactory;
8
9 /***
10 * This class spawns a process that ExpectJ can control.
11 *
12 * @author Sachin Shekar Shetty
13 * @author Johan Walles
14 */
15 public class ProcessSpawn extends AbstractSpawnable implements Spawnable {
16 /***
17 * Log messages go here.
18 */
19 private final static Log LOG = LogFactory.getLog(ProcessSpawn.class);
20
21 /***
22 * The spawned process.
23 */
24 private ProcessThread processThread = null;
25
26 /***
27 * This constructor allows to run a process with indefinite time-out
28 * @param executor Will be called upon to create the new process
29 */
30 ProcessSpawn (Executor executor) {
31 if (executor == null) {
32 throw new NullPointerException("Executor is null, must get something to run");
33 }
34
35
36 processThread = new ProcessThread(executor);
37 }
38
39 /***
40 * This method stops the spawned process.
41 */
42 public void stop() {
43 processThread.stop();
44 }
45
46 /***
47 * This method executes the given command within the specified time
48 * limit. It starts the process thread and also the timer when
49 * enabled. It starts the piped streams to enable copying of process
50 * stream contents to standard streams.
51 * @throws IOException on trouble launching the process
52 */
53 public void start() throws IOException {
54
55 processThread.start();
56 }
57
58 /***
59 * @return the input stream of the process.
60 */
61 public InputStream getStdout() {
62 return processThread.process.getInputStream();
63 }
64
65 /***
66 * @return the output stream of the process.
67 */
68 public OutputStream getStdin() {
69 return processThread.process.getOutputStream();
70 }
71
72 /***
73 * @return the error stream of the process.
74 */
75 public InputStream getStderr() {
76 return processThread.process.getErrorStream();
77 }
78
79 /***
80 * @return true if the process has exited.
81 */
82 public boolean isClosed() {
83 return processThread.isClosed;
84 }
85
86 /***
87 * If the process representes by this object has already exited, it
88 * returns the exit code. isClosed() should be used in conjunction
89 * with this method.
90 * @return The exit code of the finished process.
91 * @throws ExpectJException if the process is still running.
92 */
93 public int getExitValue()
94 throws ExpectJException
95 {
96 if (!isClosed()) {
97 throw new ExpectJException("Process is still running");
98 }
99 return processThread.exitValue;
100 }
101
102 /***
103 * This class is responsible for executing the process in a separate
104 * thread.
105 */
106 class ProcessThread implements Runnable {
107 /***
108 * Process object for execution of the commandLine
109 */
110 private Process process = null;
111
112 /***
113 * Thread object to run this file
114 */
115 private Thread thread = null;
116
117 /***
118 * true if the process is done executing
119 */
120 private volatile boolean isClosed = false;
121
122 /***
123 * The exit value of the process if it is done executing
124 */
125 private int exitValue;
126
127 /***
128 * This is what we use to create our process.
129 */
130 private Executor executor;
131
132 /***
133 * Prepare for starting a process through the given executor.
134 * <p>
135 * Call {@link #start()} to actually start running the process.
136 *
137 * @param executor Will be called upon to start the new process.
138 */
139 public ProcessThread(Executor executor) {
140 this.executor = executor;
141 }
142
143 /***
144 * This method spawns the thread and runs the process within the
145 * thread
146 * @throws IOException if process spawning fails
147 */
148 public void start() throws IOException {
149 LOG.debug("Starting process '" + executor + "'");
150 thread = new Thread(this, "ExpectJ: " + executor);
151 process = executor.execute();
152 thread.start();
153 }
154
155 /***
156 * Wait for the process to finish
157 */
158 public void run() {
159 try {
160 process.waitFor();
161 exitValue = process.exitValue();
162 isClosed = true;
163 onClose();
164 } catch (Exception e) {
165 LOG.error("Failed waiting for process termination", e);
166 }
167 }
168
169 /***
170 * This method interrupts and stops the thread.
171 */
172 public void stop() {
173 LOG.debug("Process '" + executor + "' killed");
174 process.destroy();
175 try {
176 thread.join();
177 } catch (InterruptedException e) {
178
179
180
181 LOG.error("Interrupted waiting for process supervisor thread to finish", e);
182 }
183 }
184 }
185 }