/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.util; import java.io.IOException; import java.io.InputStream; /** * The class PostInputStream is a wrapper for InputStream associated with POST message. * It allows to control read operation, restricting the number of bytes read to the value returned by getContentLen() method. * * @author <a href="mailto:Kinga_Dziembowski@hp.com">Kinga Dziembowski</a> * @version CVS $Id$ */ public class PostInputStream extends InputStream { /** * Class name */ public static final String CLASS = PostInputStream.class.getName(); /** The real InputStream object */ private InputStream m_inputStream = null; /** The length of InputStream */ private int m_contentLen = 0; /** The number of bytes read */ protected int m_bytesRead = 0; /** * Creates a PostInputStream */ public PostInputStream() { super(); } /** * Creates a <code>PostInputStream</code> based on a real InputStream object with the specified * post message body length. Saves its argument, the input stream * <code>m_inputStream</code>, for later use. * * @param input the underlying input stream. * @param len the post message body length. * @exception IllegalArgumentException len <= 0. */ public PostInputStream(final InputStream input, final int len) throws IllegalArgumentException { super(); init(input, len ); } /** * Sets the underlying input stream and contentLen value . * * @param input the input stream; can not be null. * @param len the post message body length. * * @throws IllegalArgumentException */ protected void init(final InputStream input, final int len) throws IllegalArgumentException { if (len <= 0) { throw new IllegalArgumentException("contentLen <= 0 "); } this.m_inputStream = input; this.m_contentLen = len; } /** * Sets the underlying input stream and contentLen value . * * @param input the input stream; can not be null. * @param len the post message body length. * * @throws IOException */ public synchronized void setInputStream(final InputStream input, final int len) throws IOException { if (m_inputStream != null) { close(); } init(input, len); } /** * Returns the underlying input stream. * * @return inputStream the underlying InputStream. */ public InputStream getInputStream() { return m_inputStream; } /** * Returns the post message body length. * * @return m_contentLen; */ public int getContentLen() { return( m_contentLen ); } /** * Reads the next byte from the input stream. If the end of the stream has been reached, this method returns -1. * * @return the next byte or -1 if at the end of the stream. * * @throws IOException */ public synchronized int read() throws IOException { checkOpen(); if (m_bytesRead == m_contentLen) { return -1; } int byt = m_inputStream.read(); if (byt != -1) { m_bytesRead++; } return byt; } /** * Reads bytes from this byte-input stream into the specified byte array, * starting at the given offset. * * <p> This method implements the general contract of the corresponding * <code>{@link InputStream#read(byte[], int, int) read}</code> method of * the <code>{@link InputStream}</code> class. * This method delegetes the read operation to the underlying InputStream implementation class but it * controlls the number of bytes read from the stream. In the remote situation the underlying InputStream has no knowledge of * the length of the stream and the notion of the "end" is undefined. This wrapper class has a knowledge of the * length of data send by the requestor by the means of contentLength. This method returns the number of bytes read and * accumulates the total number of bytes read in m_bytesRead. When the m_bytesRead is equal to the specified contentLength * value the method returns returns -1 to signal the end of data. * * @param buffer the byte array to read into; can not be null. * @param offset the starting offset in the byte array. * @param len the maximum number of bytes to read. * * @return the number of bytes read, or <code>-1</code> if the end of * the stream has been reached. * @exception IOException if an I/O error occurs. */ public synchronized int read(byte[] buffer, int offset, int len) throws IOException { checkOpen(); if (m_bytesRead == m_contentLen) { return -1; } int available = Math.min( available(), len ); int num = m_inputStream.read( buffer, offset, available ); if (num > 0) { m_bytesRead += num; } return num; } public synchronized int read(byte[] buffer) throws IOException { return read( buffer, 0, buffer.length); } /** * Checks to see if this stream is closed; if it is, an IOException is thrown. * * @throws IOException */ protected void checkOpen() throws IOException { if (m_inputStream == null) { throw new IOException("InputStream closed"); } } /** * See the general contract of the <code>skip</code> * method of <code>InputStream</code>. * Delegates execution to the underlying InputStream implementation class. * Checks to see if this stream is closed; if it is, an IOException is thrown. * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. * @exception IOException if an I/O error occurs. */ public synchronized long skip(long n) throws IOException { checkOpen(); if ( m_bytesRead == m_contentLen ) { return ( 0 ); } else { return ( m_inputStream.skip( n ) ); } } /** * Returns the number of bytes available from this input stream that can be read without the stream blocking. * Delegates execution to the underlying InputStream implementation class. * @return available the number of available bytes. * * @throws IOException */ public synchronized int available() throws IOException { checkOpen(); int avail = m_inputStream.available(); return (avail == 0 ? (m_contentLen - m_bytesRead) : avail); } /** * Tests if this input stream supports the <code>mark</code> * and <code>reset</code> methods. The <code>markSupported</code> * method of <code>BufferedInputStream</code> returns * <code>false</code>. * * @return a <code>boolean</code> indicating if this stream type supports * the <code>mark</code> and <code>reset</code> methods. * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ public boolean markSupported() { return false; } /** * Closes this input stream by closing the underlying stream and marking this one as closed. * * @throws IOException */ public synchronized void close() throws IOException { if (m_inputStream == null) { return; } m_inputStream.close(); m_inputStream = null; m_contentLen = 0; m_bytesRead = 0; } /** * Returns a String representation of this. * * @return string the String representation of this. */ public String toString() { return new StringBuffer(getClass().getName()) .append("[inputStream=").append(m_inputStream) .append(", contentLen=").append(m_contentLen) .append("bytesRead=").append(m_bytesRead) .append("]").toString(); } }