View Javadoc

1   package expectj;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   import java.io.OutputStream;
6   import java.io.PrintStream;
7   
8   import org.apache.commons.logging.Log;
9   import org.apache.commons.logging.LogFactory;
10  
11  /***
12   * This class is responsible for piping the output of one stream to the
13   * other. Optionally it also copies the content to standard out or
14   * standard err.
15   *
16   * @author	Sachin Shekar Shetty
17   */
18  
19  class StreamPiper extends Thread implements Runnable {
20      /***
21       * Log messages go here.
22       */
23      private final static Log LOG = LogFactory.getLog(StreamPiper.class);
24  
25      /***
26       * Read data from here.
27       */
28      private InputStream inputStream = null;
29  
30      /***
31       * Write data to here.
32       */
33      private OutputStream outputStream = null;
34  
35      /***
36       * Optionally send a copy of all piped data to here.
37       */
38      private PrintStream copyStream = null;
39  
40      /***
41       * When true we drop data from {@link #inputStream} rather than passing it
42       * to {@link #outputStream}.
43       */
44      boolean pipingPaused = false;
45  
46      /***
47       * When this turns false, we shut down.  All accesses to this variable should be
48       * synchronized.
49       */
50      private boolean continueProcessing = true;
51  
52      /***
53       * String Buffer to hold the contents of output and err.
54       */
55      private volatile StringBuffer sCurrentOut = new StringBuffer();
56  
57      /***
58       * When data piping is paused, we just drop data from the input stream
59       * rather than copying it to the output stream.
60       *
61       * @return True if piping is paused.  False otherwise.
62       */
63      private synchronized boolean getPipingPaused() {
64          return pipingPaused;
65      }
66  
67      /***
68       * @param copyStream Stream to copy the contents to before piping
69       * the data to another stream. When this parameter is null, it does
70       * not copy the contents
71       * @param pi Input stream to read the data
72       * @param po Output stream to write the data
73       */
74      StreamPiper(PrintStream copyStream, InputStream pi, OutputStream po) {
75          if (pi == null) {
76              throw new NullPointerException("Input stream must not be null");
77          }
78          this.inputStream = pi;
79          this.outputStream = po;
80          this.copyStream = copyStream;
81          // So that JVM does not wait for these threads
82          this.setDaemon(true);
83          this.setName("ExpectJ Stream Piper");
84      }
85  
86      /***
87       * This method is used to stop copying on to Standard out and err.
88       * This is used after interact.
89       */
90      public synchronized void stopPipingToStandardOut() {
91          pipingPaused = true;
92      }
93  
94      /***
95       * This method is used to start copying on to Standard out and err.
96       * This is used after interact.
97       */
98      public synchronized void startPipingToStandardOut() {
99          pipingPaused = false;
100     }
101 
102     /***
103      * This is used to stop the thread, after the process is killed
104      */
105     public synchronized void stopProcessing() {
106         continueProcessing = false;
107     }
108 
109     /***
110      * Should we keep doing our thing?
111      *
112      * @return True if we should keep piping data.  False if we should shut down.
113      */
114     private synchronized boolean getContinueProcessing() {
115         return continueProcessing;
116     }
117 
118     /***
119      * @return the entire available contents read from the stream
120      */
121     synchronized String getCurrentContents() {
122         return sCurrentOut.toString();
123     }
124 
125     /***
126      * Thread method that reads from the stream and writes to the other.
127      */
128     public void run() {
129         byte[] buffer = new byte[512];
130         int bytes_read;
131 
132         try {
133             while(getContinueProcessing()) {
134                 bytes_read = inputStream.read(buffer);
135                 if (bytes_read == -1) {
136                     LOG.debug("Stream ended, closing");
137                     inputStream.close();
138                     outputStream.close();
139                     return;
140                 }
141                 outputStream.write(buffer, 0, bytes_read);
142                 sCurrentOut.append(new String(buffer, 0, bytes_read));
143                 if (copyStream != null && !getPipingPaused()) {
144                     copyStream.write(buffer, 0, bytes_read);
145                     copyStream.flush();
146                 }
147                 outputStream.flush();
148             }
149         } catch (IOException e) {
150             if (getContinueProcessing()) {
151                 LOG.error("Trouble while pushing data between streams", e);
152             }
153         }
154     }
155 
156 }