org.ow2.easywsdl.wsdl.util.InputStreamForker Maven / Gradle / Ivy
/**
* PETALS - PETALS Services Platform.
* Copyright (c) 2008 OW2 Consortium, http://www.ow2.org/
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* -------------------------------------------------------------------------
* $Id$
* -------------------------------------------------------------------------
*/
package org.ow2.easywsdl.wsdl.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Initial developer(s): Christophe DENEUX
*
* @author cdeneux - Capgemini Sud
*
*/
public class InputStreamForker {
private InputStream originalInputStream;
private final ForkedInputStream is1;
private final ForkedInputStream is2;
/**
* true if the close() method has been called on two forked input
* streams
*/
protected boolean originalInputStreamClosed = false;
public InputStreamForker(final InputStream originalInputStream) {
this.originalInputStream = originalInputStream;
this.is1 = new ForkedInputStream();
this.is2 = new ForkedInputStream();
this.is1.setOtherForkedInputStream(this.is2);
this.is2.setOtherForkedInputStream(this.is1);
}
public InputStream getInputStreamOne() {
return this.is1;
}
public InputStream getInputStreamTwo() {
return this.is2;
}
protected class ForkedInputStream extends InputStream {
final private LinkedList bufferList;
private boolean isClosed = false;
/**
* The other forked input stream.
*/
private ForkedInputStream otherForkedInputStream;
/**
* A reference on the other branch of inputstream to improve
* performances (avoid to call {@link ForkedInputStream#getBufferList()}
* on {@link #otherForkedInputStream}).
*/
private LinkedList otherBufferList;
public ForkedInputStream() {
this.bufferList = new LinkedList();;
}
public void setOtherForkedInputStream(final ForkedInputStream otherForkedInputStream) {
this.otherForkedInputStream = otherForkedInputStream;
this.otherBufferList = this.otherForkedInputStream.getBufferList();
}
public LinkedList getBufferList() {
return this.bufferList;
}
public boolean isClosed() {
return this.isClosed;
}
/**
* {@inheritDoc}
*/
@Override
public int available() throws IOException {
synchronized (InputStreamForker.this) {
int available = 0;
final Iterator iterator = this.bufferList.iterator();
while (iterator.hasNext()) {
available += iterator.next().length;
}
if (!InputStreamForker.this.originalInputStreamClosed && !this.isClosed) {
available += InputStreamForker.this.originalInputStream
.available();
}
return available;
}
}
/**
* {@inheritDoc}
* If both forked input streams are closed, the original input stream is closed.
* If the forked stream has already been closed, this method has no effect.
*/
@Override
public void close() throws IOException {
if (!this.isClosed) {
synchronized (InputStreamForker.this) {
this.isClosed = true;
// Note: It's not needed to check if the original input
// stream is closed because it is closed when closing one of
// two forked input streams. So, as the current forked input
// stream is not closed, the original input stream is not
// closed.
if (this.otherForkedInputStream.isClosed) {
// The other forked input stream is closed, we close the
// original input stream
InputStreamForker.this.originalInputStream.close();
InputStreamForker.this.originalInputStreamClosed = true;
}
}
}
}
@Override
public boolean markSupported() {
return false;
}
/**
*
* Reads the next byte of data from the input stream. The value byte is
* returned as an int
in the range 0
to
* 255
. If no byte is available because the end of the
* stream has been reached, the value -1
is returned.
* This method blocks until input data is available, the end of the
* stream is detected, or an exception is thrown.
*
*
* The byte is read in the internal buffer. If no data is available in
* the internal buffer, the byte is read from the original input stream.
*
*
* @return the next byte of data, or -1
if the end of the
* stream is reached.
* @exception IOException
* if an I/O error occurs.
*/
@Override
public int read() throws IOException {
byte[] buffer = new byte[1];
int nbByteRead = this.read(buffer);
if (nbByteRead != 1) {
throw new IOException("No byte read.");
}
return buffer[0];
}
@Override
public int read(final byte[] cbuf) throws IOException {
return this.read(cbuf, 0, cbuf.length);
}
@Override
public int read(final byte[] cbuf, final int off, final int len)
throws IOException {
return this.localRead(cbuf, off, len, false);
}
@Override
public long skip(long n) throws IOException, IllegalArgumentException {
if (n > Long.MAX_VALUE) {
throw new IllegalArgumentException("Only value lesser "
+ Integer.MAX_VALUE + "are accepted.");
}
return this.localRead(null, 0, (int) n, true);
}
private int localRead(final byte[] cbuf, final int off, final int len,
final boolean skip) throws IOException {
int remainingLen = len;
int offset = off;
int byteRead;
// Original input stream reading and buffer list management must
// be synchronized. Otherwise, inversions can occur.
synchronized (InputStreamForker.this) {
// First we read from the internal buffers
int nbBuffers = this.bufferList.size();
while (nbBuffers > 0 && remainingLen != 0) {
final byte[] buffer = this.bufferList.getFirst();
if (remainingLen >= buffer.length) {
// Full copy needed
if (!skip) {
System.arraycopy(buffer, 0, cbuf, offset,
buffer.length);
}
this.bufferList.removeFirst();
remainingLen -= buffer.length;
if (remainingLen == 0) {
// No more bytes must be read
return len;
}
offset += buffer.length;
nbBuffers--;
} else {
// Partial copy needed because the buffer len to read is
// reached
if (!skip) {
System.arraycopy(buffer, 0, cbuf, offset,
remainingLen);
}
// Replace byte buffer by a buffer containing remaining
// bytes
final byte[] newBuffer = new byte[buffer.length
- remainingLen];
System.arraycopy(buffer, remainingLen, newBuffer, 0,
buffer.length - remainingLen);
this.bufferList.removeFirst();
this.bufferList.addFirst(newBuffer);
return len;
}
}
// Next, we read missing bytes from the original input stream
if (InputStreamForker.this.originalInputStreamClosed) {
throw new IOException(
"The original InputStream has been closed; cannot read from a closed InputStream.");
}
byte[] buffer = new byte[remainingLen];
byteRead = InputStreamForker.this.originalInputStream.read(
buffer, 0, remainingLen);
if (byteRead != -1) {
// We add the read bytes into the other branch, only if it is not closed.
if (!this.otherForkedInputStream.isClosed()) {
if (byteRead == remainingLen) {
// We can add the buffer as is because of its length is
// right.
this.otherBufferList.addLast(buffer);
} else {
final byte[] newBuffer = new byte[byteRead];
System.arraycopy(buffer, 0, newBuffer, 0, byteRead);
this.otherBufferList.addLast(newBuffer);
}
}
if (!skip) {
System.arraycopy(buffer, 0, cbuf, offset, remainingLen);
}
return len - remainingLen + byteRead;
} else {
// EOF of the originial input stream is reached, we must
// return -1 (EOF) if no bytes have been read from the
// buffer list.
if (len == remainingLen) {
// no bytes have been read from the buffer list.
return -1;
}
else {
// bytes have been read from the buffer list.
return len - remainingLen;
}
}
}
}
}
}