WrappedInputStream.java Source code

Java tutorial

Introduction

Here is the source code for WrappedInputStream.java

Source

    /*
     * 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.
     */

    import java.io.DataInputStream;
    import java.io.FilterInputStream;
    import java.io.InputStream;
    import java.io.IOException;

/**
 * This input stream works in conjunction with the WrappedOutputStream
 * to introduce a protocol for reading arbitrary length data in a
 * uniform way.
 * <p>
 * <strong>Note:</strong> See the javadoc for WrappedOutputStream for
 * more information.
 *
 * @see WrappedOutputStream
 *
 * @author Andy Clark, IBM
 *
 * @version $Id: WrappedInputStream.java 447688 2006-09-19 02:39:49Z mrglavas $
 */
public class WrappedInputStream
    extends FilterInputStream {

    //
    // Data
    //

    /** Bytes left on input stream for current packet. */
    protected int fPacketCount;

    /** 
     * Data input stream. This stream is used to input the block sizes
     * from the data stream that are written by the WrappedOutputStream.
     * <p>
     * <strong>Note:</strong> The data input stream is only used for
     * reading the byte count for performance reasons. We avoid the
     * method indirection for reading the byte data.
     */
    protected DataInputStream fDataInputStream;

    /** To mark that the stream is "closed". */
    protected boolean fClosed;

    //
    // Constructors
    //

    /** Constructs a wrapper for the given an input stream. */
    public WrappedInputStream(InputStream stream) {
        super(stream);
        fDataInputStream = new DataInputStream(stream);
    } // <init>(InputStream)

    //
    // InputStream methods
    //

    /** Reads a single byte. */
    public int read() throws IOException {

        // ignore, if already closed
        if (fClosed) {
            return -1;
        }

        // read packet header
        if (fPacketCount == 0) {
            fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
            if (fPacketCount == 0) {
                fClosed = true;
                return -1;
            }
        }

        // read a byte from the packet
        fPacketCount--;
        return super.in.read();

    } // read():int

    /** 
     * Reads a block of bytes and returns the total number of bytes read. 
     */
    public int read(byte[] b, int offset, int length) throws IOException {

        // ignore, if already closed
        if (fClosed) {
            return -1;
        }

        // read packet header
        if (fPacketCount == 0) {
            fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
            if (fPacketCount == 0) {
                fClosed = true;
                return -1;
            }
        }

        // read bytes from packet
        if (length > fPacketCount) {
            length = fPacketCount;
        }
        int count = super.in.read(b, offset, length);
        if (count == -1) {
            // NOTE: This condition should not happen. The end of 
            //       the stream should always be designated by a 
            //       byte count header of 0. -Ac
            fClosed = true;
            return -1;
        }
        fPacketCount -= count;

        // return total bytes read
        return count;

    } // read(byte[],int,int):int

    /** Skips the specified number of bytes from the input stream. */
    public long skip(long n) throws IOException {
        if (!fClosed) {
            // NOTE: This should be rewritten to be more efficient. -Ac
            for (long i = 0; i < n; i++) {
                int b = read();
                if (b == -1) {
                    return i + 1;
                }
            }
            return n;
        }
        return 0;
    } // skip(long):long

    /** 
     * Closes the input stream. This method will search for the end of
     * the wrapped input, positioning the stream at after the end packet.
     * <p>
     * <strong>Note:</strong> This method does not close the underlying
     * input stream.
     */
    public void close() throws IOException {
        if (!fClosed) {
            fClosed = true;
            do {
                super.in.skip(fPacketCount);
                fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
            } while (fPacketCount > 0);
        }
    } // close()

} // class WrappedInputStream
    /*
     * 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.
     */

    import java.io.DataOutputStream;
    import java.io.FilterOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;

    /**
     * This output stream works in conjunction with the WrappedInputStream
     * to introduce a protocol for sending arbitrary length data in a
     * uniform way. This output stream allows variable length data to be
     * inserted into an existing output stream so that it can be read by
     * an input stream without reading too many bytes (in case of buffering
     * by the input stream).
     * <p>
     * This output stream is used like any normal output stream. The protocol
     * is introduced by the WrappedOutputStream and does not need to be known
     * by the user of this class. However, for those that are interested, the
     * method is described below.
     * <p>
     * The output stream writes the requested bytes as packets of binary
     * information. The packet consists of a header and payload. The header
     * is two bytes of a single unsigned short (written in network order) 
     * that specifies the length of bytes in the payload. A header value of
     * 0 indicates that the stream is "closed".
     * <p>
     * <strong>Note:</strong> For this wrapped output stream to be used,
     * the application <strong>must</strong> call <code>close()</code>
     * to end the output.
     *
     * @see WrappedInputStream
     *
     * @author Andy Clark, IBM
     *
     * @version $Id: WrappedOutputStream.java 447688 2006-09-19 02:39:49Z mrglavas $
     */
    public class WrappedOutputStream extends FilterOutputStream {

        //
        // Constants
        //

        /** Default buffer size (1024). */
        public static final int DEFAULT_BUFFER_SIZE = 1024;

        //
        // Data
        //

        /** Buffer. */
        protected byte[] fBuffer;

        /** Buffer position. */
        protected int fPosition;

        /** 
         * Data output stream. This stream is used to output the block sizes
         * into the data stream that are read by the WrappedInputStream.
         * <p>
         * <strong>Note:</strong> The data output stream is only used for
         * writing the byte count for performance reasons. We avoid the
         * method indirection for writing the byte data.
         */
        protected DataOutputStream fDataOutputStream;

        //
        // Constructors
        //

        /** Constructs a wrapper for the given output stream. */
        public WrappedOutputStream(OutputStream stream) {
            this(stream, DEFAULT_BUFFER_SIZE);
        } // <init>(OutputStream)

        /** 
         * Constructs a wrapper for the given output stream with the
         * given buffer size.
         */
        public WrappedOutputStream(OutputStream stream, int bufferSize) {
            super(stream);
            fBuffer = new byte[bufferSize];
            fDataOutputStream = new DataOutputStream(stream);
        } // <init>(OutputStream)

        //
        // OutputStream methods
        //

        /** 
         * Writes a single byte to the output. 
         * <p>
         * <strong>Note:</strong> Single bytes written to the output stream
         * will be buffered
         */
        public void write(int b) throws IOException {
            fBuffer[fPosition++] = (byte) b;
            if (fPosition == fBuffer.length) {
                fPosition = 0;
                fDataOutputStream.writeInt(fBuffer.length);
                super.out.write(fBuffer, 0, fBuffer.length);
            }
        } // write(int)

        /** Writes an array of bytes to the output. */
        public void write(byte[] b, int offset, int length) throws IOException {

            // flush existing buffer
            if (fPosition > 0) {
                flush0();
            }

            // write header followed by actual bytes
            fDataOutputStream.writeInt(length);
            super.out.write(b, offset, length);

        } // write(byte[])

        /** 
         * Flushes the output buffer, writing all bytes currently in
         * the buffer to the output.
         */
        public void flush() throws IOException {
            flush0();
            super.out.flush();
        } // flush()

        /** 
         * Closes the output stream. This method <strong>must</strong> be
         * called when done writing all data to the output stream.
         * <p>
         * <strong>Note:</strong> This method does <em>not</em> close the
         * actual output stream, only makes the input stream see the stream
         * closed. Do not write bytes after closing the output stream.
         */
        public void close() throws IOException {
            flush0();
            fDataOutputStream.writeInt(0);
            super.out.flush();
        } // close()

        //
        // Protected methods
        //

        /** 
         * Flushes the output buffer, writing all bytes currently in
         * the buffer to the output. This method does not call the
         * flush() method of the output stream; it merely writes the
         * remaining bytes in the buffer.
         */
        public void flush0() throws IOException {
            int length = fPosition;
            fPosition = 0;
            if (length > 0) {
                fDataOutputStream.writeInt(length);
                super.out.write(fBuffer, 0, length);
            }
        } // flush0()

    } // class WrappedOutputStream