1. /*
  2. * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/AutoCloseInputStream.java,v 1.9 2004/04/18 23:51:34 jsdever Exp $
  3. * $Revision: 1.9 $
  4. * $Date: 2004/04/18 23:51:34 $
  5. *
  6. * ====================================================================
  7. *
  8. * Copyright 2002-2004 The Apache Software Foundation
  9. *
  10. * Licensed under the Apache License, Version 2.0 (the "License");
  11. * you may not use this file except in compliance with the License.
  12. * You may obtain a copy of the License at
  13. *
  14. * http://www.apache.org/licenses/LICENSE-2.0
  15. *
  16. * Unless required by applicable law or agreed to in writing, software
  17. * distributed under the License is distributed on an "AS IS" BASIS,
  18. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. * See the License for the specific language governing permissions and
  20. * limitations under the License.
  21. * ====================================================================
  22. *
  23. * This software consists of voluntary contributions made by many
  24. * individuals on behalf of the Apache Software Foundation. For more
  25. * information on the Apache Software Foundation, please see
  26. * <http://www.apache.org/>.
  27. *
  28. */
  29. package org.apache.commons.httpclient;
  30. import java.io.FilterInputStream;
  31. import java.io.IOException;
  32. import java.io.InputStream;
  33. /**
  34. * Closes an underlying stream as soon as the end of the stream is reached, and
  35. * notifies a client when it has done so.
  36. *
  37. * @author Ortwin Glück
  38. * @author Eric Johnson
  39. * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  40. *
  41. * @since 2.0
  42. */
  43. class AutoCloseInputStream extends FilterInputStream {
  44. /**
  45. * True if this stream is open. Assume that the underlying stream
  46. * is open until we get an EOF indication.
  47. */
  48. private boolean streamOpen = true;
  49. /** True if the stream closed itself. */
  50. private boolean selfClosed = false;
  51. /**
  52. * The watcher is notified when the contents of the stream have
  53. * been exhausted
  54. */
  55. private ResponseConsumedWatcher watcher = null;
  56. /**
  57. * Create a new auto closing stream for the provided connection
  58. *
  59. * @param in the input stream to read from
  60. * @param watcher To be notified when the contents of the stream have been
  61. * consumed.
  62. */
  63. public AutoCloseInputStream(
  64. final InputStream in, final ResponseConsumedWatcher watcher) {
  65. super(in);
  66. this.watcher = watcher;
  67. }
  68. /**
  69. * Reads the next byte of data from the input stream.
  70. *
  71. * @throws IOException when there is an error reading
  72. * @return the character read, or -1 for EOF
  73. */
  74. public int read() throws IOException {
  75. int l = -1;
  76. if (isReadAllowed()) {
  77. // underlying stream not closed, go ahead and read.
  78. l = super.read();
  79. checkClose(l);
  80. }
  81. return l;
  82. }
  83. /**
  84. * Reads up to <code>len</code> bytes of data from the stream.
  85. *
  86. * @param b a <code>byte</code> array to read data into
  87. * @param off an offset within the array to store data
  88. * @param len the maximum number of bytes to read
  89. * @return the number of bytes read or -1 for EOF
  90. * @throws IOException if there are errors reading
  91. */
  92. public int read(byte[] b, int off, int len) throws IOException {
  93. int l = -1;
  94. if (isReadAllowed()) {
  95. l = super.read(b, off, len);
  96. checkClose(l);
  97. }
  98. return l;
  99. }
  100. /**
  101. * Reads some number of bytes from the input stream and stores them into the
  102. * buffer array b.
  103. *
  104. * @param b a <code>byte</code> array to read data into
  105. * @return the number of bytes read or -1 for EOF
  106. * @throws IOException if there are errors reading
  107. */
  108. public int read(byte[] b) throws IOException {
  109. int l = -1;
  110. if (isReadAllowed()) {
  111. l = super.read(b);
  112. checkClose(l);
  113. }
  114. return l;
  115. }
  116. /**
  117. * Close the stream, and also close the underlying stream if it is not
  118. * already closed.
  119. * @throws IOException If an IO problem occurs.
  120. */
  121. public void close() throws IOException {
  122. if (!selfClosed) {
  123. selfClosed = true;
  124. notifyWatcher();
  125. }
  126. }
  127. /**
  128. * Close the underlying stream should the end of the stream arrive.
  129. *
  130. * @param readResult The result of the read operation to check.
  131. * @throws IOException If an IO problem occurs.
  132. */
  133. private void checkClose(int readResult) throws IOException {
  134. if (readResult == -1) {
  135. notifyWatcher();
  136. }
  137. }
  138. /**
  139. * See whether a read of the underlying stream should be allowed, and if
  140. * not, check to see whether our stream has already been closed!
  141. *
  142. * @return <code>true</code> if it is still OK to read from the stream.
  143. * @throws IOException If an IO problem occurs.
  144. */
  145. private boolean isReadAllowed() throws IOException {
  146. if (!streamOpen && selfClosed) {
  147. throw new IOException("Attempted read on closed stream.");
  148. }
  149. return streamOpen;
  150. }
  151. /**
  152. * Notify the watcher that the contents have been consumed.
  153. * @throws IOException If an IO problem occurs.
  154. */
  155. private void notifyWatcher() throws IOException {
  156. if (streamOpen) {
  157. super.close();
  158. streamOpen = false;
  159. if (watcher != null) {
  160. watcher.responseConsumed();
  161. }
  162. }
  163. }
  164. }