View Javadoc

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          // Initialise the process thread.
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          // Start the process
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                 // Process should have died when calling process.destroy().
179                 // After that, process.waitFor() should return, causing the
180                 // run() method above to terminate.
181                 LOG.error("Interrupted waiting for process supervisor thread to finish", e);
182             }
183         }
184     }
185 }