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
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 }