/*
* 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.
*
* Note: 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.
*
* Note: 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);
} // (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.
*
* Note: 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".
*
* Note: For this wrapped output stream to be used,
* the application must call close()
* 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.
*
* Note: 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);
} // (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);
} // (OutputStream)
//
// OutputStream methods
//
/**
* Writes a single byte to the output.
*
* Note: 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 must be
* called when done writing all data to the output stream.
*
* Note: This method does not 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