package org.rzo.netty.ahessian.io; import java.io.IOException; import java.io.InputStream; import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jboss.netty.buffer.ChannelBuffer; import org.rzo.netty.ahessian.Constants; /** * InputStreamBuffer pipes bytes read from the channel to an input stream */ public class InputStreamBuffer extends InputStream { /** Buffer for storing incoming bytes */ // private ChannelBuffer _buf = dynamicBuffer(); final LinkedList<ChannelBuffer> _bufs = new LinkedList<ChannelBuffer>(); /** Indicates if the stream has been closed */ private volatile boolean _closed = false; final private Lock _lock = new ReentrantLock(); /** Sync condition indicating that buffer is not empty. */ final private Condition _notEmpty = _lock.newCondition(); private volatile int _available = 0; boolean blocking = false; long _readTimeout = 3000; /* * (non-Javadoc) * * @see java.io.InputStream#read() */ @Override public int read() throws IOException { int result = -1; if (_closed) return -1; _lock.lock(); try { while (!_closed && available() == 0) if (blocking) { if (_readTimeout > 0) { if (!_notEmpty.await(_readTimeout, TimeUnit.MILLISECONDS)) throw new IOException("read timeout"); } else _notEmpty.await(); } else { throw new IOException("no data"); } if (!_closed) { result = (int) _bufs.getFirst().readByte() & 0xFF; _available--; checkBufs(); } } catch (Exception ex) { throw new IOException(ex.getMessage()); } finally { _lock.unlock(); } //System.out.println("read "+_available); return result; } private void checkBufs() { if (!_bufs.isEmpty() && _bufs.getFirst().readableBytes() == 0) { _bufs.removeFirst(); } } /* * (non-Javadoc) * * @see java.io.InputStream#close() */ @Override public void close() throws IOException { _lock.lock(); try { _closed = true; _notEmpty.signal(); super.close(); } catch (Exception ex) { Constants.ahessianLogger.warn("error closing input stream", ex); } finally { _lock.unlock(); } } /** * Insert bytes to the input stream * * @param buf * bytes received from previous upstream handler * * @throws IOException * Signals that an I/O exception has occurred. */ public void write(ChannelBuffer buf) throws IOException { if (_closed) throw new IOException("stream closed"); _lock.lock(); // if (_available == 0) // System.out.println("input not empty"); try { if (_bufs.isEmpty() || buf != _bufs.getLast()) { _bufs.addLast(buf); } _available += buf.readableBytes(); _notEmpty.signal(); } catch (Exception ex) { Constants.ahessianLogger.warn("", ex); } finally { _lock.unlock(); } } /* * (non-Javadoc) * * @see java.io.InputStream#available() */ public int available() throws IOException { if (_closed) throw new IOException("stream closed"); return _available; } /* * (non-Javadoc) * * @see java.io.InputStream#read(byte[], int, int) */ @Override public int read(byte[] b, int off, int len) throws IOException { int result = -1; if (_closed) return -1; _lock.lock(); try { while (!_closed && available() == 0) { if (_readTimeout > 0) { if (!_notEmpty.await(_readTimeout, TimeUnit.MILLISECONDS)) throw new IOException("read timeout"); } else _notEmpty.awaitUninterruptibly(); } if (!_closed) { int length = Math.min(len, _bufs.getFirst().readableBytes()); _bufs.getFirst().readBytes(b, off, length); result = length; _available -= length; checkBufs(); // if (_available == 0) // System.out.println("input empty"); } } catch (Exception ex) { throw new IOException(ex.getMessage()); } finally { _lock.unlock(); } return result; } /** * Checks if the stream is closed. * * @return true, if is closed */ public boolean isClosed() { return _closed; } public void setReadTimeout(long timeout) { _readTimeout = timeout; } public boolean isBlocking() { return blocking; } public void setBlocking(boolean blocking) { this.blocking = blocking; } }