Java tutorial
/* * 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. * * <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. * * <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. * * <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). * * 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. * * 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". * * <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. * * <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. * * <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. * * <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