001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.util;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021:
022: /**
023: * The class PostInputStream is a wrapper for InputStream associated with POST message.
024: * It allows to control read operation, restricting the number of bytes read to the value returned by getContentLen() method.
025: *
026: * @author <a href="mailto:Kinga_Dziembowski@hp.com">Kinga Dziembowski</a>
027: * @version CVS $Id: PostInputStream.java 433543 2006-08-22 06:22:54Z crossley $
028: */
029:
030: public class PostInputStream extends InputStream {
031:
032: /**
033: * Class name
034: */
035: public static final String CLASS = PostInputStream.class.getName();
036:
037: /** The real InputStream object */
038: private InputStream m_inputStream = null;
039:
040: /** The length of InputStream */
041: private int m_contentLen = 0;
042:
043: /** The number of bytes read */
044: protected int m_bytesRead = 0;
045:
046: /**
047: * Creates a PostInputStream
048: */
049: public PostInputStream() {
050: super ();
051: }
052:
053: /**
054: * Creates a <code>PostInputStream</code> based on a real InputStream object with the specified
055: * post message body length. Saves its argument, the input stream
056: * <code>m_inputStream</code>, for later use.
057: *
058: * @param input the underlying input stream.
059: * @param len the post message body length.
060: * @exception IllegalArgumentException len <= 0.
061: */
062:
063: public PostInputStream(final InputStream input, final int len)
064: throws IllegalArgumentException {
065: super ();
066: init(input, len);
067: }
068:
069: /**
070: * Sets the underlying input stream and contentLen value .
071: *
072: * @param input the input stream; can not be null.
073: * @param len the post message body length.
074: *
075: * @throws IllegalArgumentException
076: */
077: protected void init(final InputStream input, final int len)
078: throws IllegalArgumentException {
079: if (len <= 0) {
080: throw new IllegalArgumentException("contentLen <= 0 ");
081: }
082: this .m_inputStream = input;
083: this .m_contentLen = len;
084: }
085:
086: /**
087: * Sets the underlying input stream and contentLen value .
088: *
089: * @param input the input stream; can not be null.
090: * @param len the post message body length.
091: *
092: * @throws IOException
093: */
094: public synchronized void setInputStream(final InputStream input,
095: final int len) throws IOException {
096: if (m_inputStream != null) {
097: close();
098: }
099: init(input, len);
100: }
101:
102: /**
103: * Returns the underlying input stream.
104: *
105: * @return inputStream the underlying InputStream.
106: */
107: public InputStream getInputStream() {
108: return m_inputStream;
109: }
110:
111: /**
112: * Returns the post message body length.
113: *
114: * @return m_contentLen;
115: */
116:
117: public int getContentLen() {
118: return (m_contentLen);
119: }
120:
121: /**
122: * Reads the next byte from the input stream. If the end of the stream has been reached, this method returns -1.
123: *
124: * @return the next byte or -1 if at the end of the stream.
125: *
126: * @throws IOException
127: */
128: public synchronized int read() throws IOException {
129:
130: checkOpen();
131: if (m_bytesRead == m_contentLen) {
132: return -1;
133: }
134: int byt = m_inputStream.read();
135: if (byt != -1) {
136: m_bytesRead++;
137: }
138: return byt;
139: }
140:
141: /**
142: * Reads bytes from this byte-input stream into the specified byte array,
143: * starting at the given offset.
144: *
145: * <p> This method implements the general contract of the corresponding
146: * <code>{@link InputStream#read(byte[], int, int) read}</code> method of
147: * the <code>{@link InputStream}</code> class.
148: * This method delegetes the read operation to the underlying InputStream implementation class but it
149: * controlls the number of bytes read from the stream. In the remote situation the underlying InputStream has no knowledge of
150: * the length of the stream and the notion of the "end" is undefined. This wrapper class has a knowledge of the
151: * length of data send by the requestor by the means of contentLength. This method returns the number of bytes read and
152: * accumulates the total number of bytes read in m_bytesRead. When the m_bytesRead is equal to the specified contentLength
153: * value the method returns returns -1 to signal the end of data.
154: *
155: * @param buffer the byte array to read into; can not be null.
156: * @param offset the starting offset in the byte array.
157: * @param len the maximum number of bytes to read.
158: *
159: * @return the number of bytes read, or <code>-1</code> if the end of
160: * the stream has been reached.
161: * @exception IOException if an I/O error occurs.
162: */
163:
164: public synchronized int read(byte[] buffer, int offset, int len)
165: throws IOException {
166: checkOpen();
167: if (m_bytesRead == m_contentLen) {
168: return -1;
169: }
170: int available = Math.min(available(), len);
171: int num = m_inputStream.read(buffer, offset, available);
172: if (num > 0) {
173: m_bytesRead += num;
174: }
175: return num;
176: }
177:
178: public synchronized int read(byte[] buffer) throws IOException {
179:
180: return read(buffer, 0, buffer.length);
181: }
182:
183: /**
184: * Checks to see if this stream is closed; if it is, an IOException is thrown.
185: *
186: * @throws IOException
187: */
188: protected void checkOpen() throws IOException {
189: if (m_inputStream == null) {
190: throw new IOException("InputStream closed");
191: }
192: }
193:
194: /**
195: * See the general contract of the <code>skip</code>
196: * method of <code>InputStream</code>.
197: * Delegates execution to the underlying InputStream implementation class.
198: * Checks to see if this stream is closed; if it is, an IOException is thrown.
199: * @param n the number of bytes to be skipped.
200: * @return the actual number of bytes skipped.
201: * @exception IOException if an I/O error occurs.
202: */
203: public synchronized long skip(long n) throws IOException {
204: checkOpen();
205: if (m_bytesRead == m_contentLen) {
206: return (0);
207: } else {
208: return (m_inputStream.skip(n));
209: }
210:
211: }
212:
213: /**
214: * Returns the number of bytes available from this input stream that can be read without the stream blocking.
215: * Delegates execution to the underlying InputStream implementation class.
216: * @return available the number of available bytes.
217: *
218: * @throws IOException
219: */
220: public synchronized int available() throws IOException {
221: checkOpen();
222: int avail = m_inputStream.available();
223: return (avail == 0 ? (m_contentLen - m_bytesRead) : avail);
224: }
225:
226: /**
227: * Tests if this input stream supports the <code>mark</code>
228: * and <code>reset</code> methods. The <code>markSupported</code>
229: * method of <code>BufferedInputStream</code> returns
230: * <code>false</code>.
231: *
232: * @return a <code>boolean</code> indicating if this stream type supports
233: * the <code>mark</code> and <code>reset</code> methods.
234: * @see java.io.InputStream#mark(int)
235: * @see java.io.InputStream#reset()
236: */
237: public boolean markSupported() {
238: return false;
239: }
240:
241: /**
242: * Closes this input stream by closing the underlying stream and marking this one as closed.
243: *
244: * @throws IOException
245: */
246: public synchronized void close() throws IOException {
247: if (m_inputStream == null) {
248: return;
249: }
250: m_inputStream.close();
251: m_inputStream = null;
252: m_contentLen = 0;
253: m_bytesRead = 0;
254: }
255:
256: /**
257: * Returns a String representation of this.
258: *
259: * @return string the String representation of this.
260: */
261: public String toString() {
262: return new StringBuffer(getClass().getName()).append(
263: "[inputStream=").append(m_inputStream).append(
264: ", contentLen=").append(m_contentLen).append(
265: "bytesRead=").append(m_bytesRead).append("]")
266: .toString();
267: }
268: }
|