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