1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package org.apache.commons.httpclient;
32
33 import java.io.FilterInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36
37 /***
38 * Closes an underlying stream as soon as the end of the stream is reached, and
39 * notifies a client when it has done so.
40 *
41 * @author Ortwin Glueck
42 * @author Eric Johnson
43 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
44 *
45 * @since 2.0
46 */
47 class AutoCloseInputStream extends FilterInputStream {
48
49 /***
50 * True if this stream is open. Assume that the underlying stream
51 * is open until we get an EOF indication.
52 */
53 private boolean streamOpen = true;
54
55 /*** True if the stream closed itself. */
56 private boolean selfClosed = false;
57
58 /***
59 * The watcher is notified when the contents of the stream have
60 * been exhausted
61 */
62 private ResponseConsumedWatcher watcher = null;
63
64 /***
65 * Create a new auto closing stream for the provided connection
66 *
67 * @param in the input stream to read from
68 * @param watcher To be notified when the contents of the stream have been
69 * consumed.
70 */
71 public AutoCloseInputStream(
72 final InputStream in, final ResponseConsumedWatcher watcher) {
73 super(in);
74 this.watcher = watcher;
75 }
76
77 /***
78 * Reads the next byte of data from the input stream.
79 *
80 * @throws IOException when there is an error reading
81 * @return the character read, or -1 for EOF
82 */
83 public int read() throws IOException {
84 int l = -1;
85
86 if (isReadAllowed()) {
87
88 l = super.read();
89 checkClose(l);
90 }
91
92 return l;
93 }
94
95 /***
96 * Reads up to <code>len</code> bytes of data from the stream.
97 *
98 * @param b a <code>byte</code> array to read data into
99 * @param off an offset within the array to store data
100 * @param len the maximum number of bytes to read
101 * @return the number of bytes read or -1 for EOF
102 * @throws IOException if there are errors reading
103 */
104 public int read(byte[] b, int off, int len) throws IOException {
105 int l = -1;
106
107 if (isReadAllowed()) {
108 l = super.read(b, off, len);
109 checkClose(l);
110 }
111
112 return l;
113 }
114
115 /***
116 * Reads some number of bytes from the input stream and stores them into the
117 * buffer array b.
118 *
119 * @param b a <code>byte</code> array to read data into
120 * @return the number of bytes read or -1 for EOF
121 * @throws IOException if there are errors reading
122 */
123 public int read(byte[] b) throws IOException {
124 int l = -1;
125
126 if (isReadAllowed()) {
127 l = super.read(b);
128 checkClose(l);
129 }
130 return l;
131 }
132
133 /***
134 * Obtains the number of bytes that can be read without blocking.
135 *
136 * @return the number of bytes available without blocking
137 * @throws IOException in case of a problem
138 */
139 public int available() throws IOException {
140 int a = 0;
141
142 if (isReadAllowed()) {
143 a = super.available();
144
145 }
146
147 return a;
148 }
149
150 /***
151 * Close the stream, and also close the underlying stream if it is not
152 * already closed.
153 * @throws IOException If an IO problem occurs.
154 */
155 public void close() throws IOException {
156 if (!selfClosed) {
157 selfClosed = true;
158 notifyWatcher();
159 }
160 }
161
162 /***
163 * Close the underlying stream should the end of the stream arrive.
164 *
165 * @param readResult The result of the read operation to check.
166 * @throws IOException If an IO problem occurs.
167 */
168 private void checkClose(int readResult) throws IOException {
169 if (readResult == -1) {
170 notifyWatcher();
171 }
172 }
173
174 /***
175 * See whether a read of the underlying stream should be allowed, and if
176 * not, check to see whether our stream has already been closed!
177 *
178 * @return <code>true</code> if it is still OK to read from the stream.
179 * @throws IOException If an IO problem occurs.
180 */
181 private boolean isReadAllowed() throws IOException {
182 if (!streamOpen && selfClosed) {
183 throw new IOException("Attempted read on closed stream.");
184 }
185 return streamOpen;
186 }
187
188 /***
189 * Notify the watcher that the contents have been consumed.
190 * @throws IOException If an IO problem occurs.
191 */
192 private void notifyWatcher() throws IOException {
193 if (streamOpen) {
194 super.close();
195 streamOpen = false;
196
197 if (watcher != null) {
198 watcher.responseConsumed();
199 }
200 }
201 }
202 }
203