/** * Copyright (c) 2002-2011 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.kernel.ha; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; import java.nio.charset.Charset; import java.util.concurrent.TimeUnit; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBufferFactory; import org.jboss.netty.buffer.ChannelBufferIndexFinder; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.handler.queue.BlockingReadHandler; public class DechunkingChannelBuffer implements ChannelBuffer { private final BlockingReadHandler<ChannelBuffer> reader; private ChannelBuffer buffer; private boolean more; private boolean hasMarkedReaderIndex; DechunkingChannelBuffer( BlockingReadHandler<ChannelBuffer> reader ) { this.reader = reader; readNextChunk(); } protected ChannelBuffer readNext() { try { return reader.read( MasterClient.READ_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS ); } catch ( IOException e ) { throw new HaCommunicationException( e ); } catch ( InterruptedException e ) { throw new HaCommunicationException( e ); } } private void readNextChunkIfNeeded( int bytesPlus ) { if ( buffer.readableBytes() < bytesPlus && more ) { readNextChunk(); } } private void readNextChunk() { ChannelBuffer readBuffer = readNext(); more = readBuffer.readByte() == ChunkingChannelBuffer.CONTINUATION_MORE; if ( !more && buffer == null ) { // Optimization: this is the first chunk and it'll be the only chunk // in this message. buffer = readBuffer; } else { buffer = buffer == null ? ChannelBuffers.dynamicBuffer() : buffer; discardReadBytes(); buffer.writeBytes( readBuffer ); } } public ChannelBufferFactory factory() { return buffer.factory(); } /** * Will return the capacity of the current chunk only */ public int capacity() { return buffer.capacity(); } public ByteOrder order() { return buffer.order(); } public boolean isDirect() { return buffer.isDirect(); } public int readerIndex() { return buffer.readerIndex(); } public void readerIndex( int readerIndex ) { buffer.readerIndex( readerIndex ); } public int writerIndex() { return buffer.writerIndex(); } public void writerIndex( int writerIndex ) { buffer.writerIndex( writerIndex ); } public void setIndex( int readerIndex, int writerIndex ) { buffer.setIndex( readerIndex, writerIndex ); } /** * Will return amount of readable bytes in this chunk only */ public int readableBytes() { return buffer.readableBytes(); } public int writableBytes() { return 0; } /** * Can fetch the next chunk if needed */ public boolean readable() { readNextChunkIfNeeded( 1 ); return buffer.readable(); } public boolean writable() { return buffer.writable(); } public void clear() { buffer.clear(); } public void markReaderIndex() { buffer.markReaderIndex(); hasMarkedReaderIndex = true; } public void resetReaderIndex() { buffer.resetReaderIndex(); hasMarkedReaderIndex = false; } public void markWriterIndex() { buffer.markWriterIndex(); } public void resetWriterIndex() { buffer.resetWriterIndex(); } public void discardReadBytes() { int oldReaderIndex = buffer.readerIndex(); if ( hasMarkedReaderIndex ) { buffer.resetReaderIndex(); } int bytesToDiscard = buffer.readerIndex(); buffer.discardReadBytes(); if ( hasMarkedReaderIndex ) { buffer.readerIndex( oldReaderIndex-bytesToDiscard ); } } public void ensureWritableBytes( int writableBytes ) { buffer.ensureWritableBytes( writableBytes ); } public byte getByte( int index ) { readNextChunkIfNeeded( 1 ); return buffer.getByte( index ); } public short getUnsignedByte( int index ) { readNextChunkIfNeeded( 1 ); return buffer.getUnsignedByte( index ); } public short getShort( int index ) { readNextChunkIfNeeded( 2 ); return buffer.getShort( index ); } public int getUnsignedShort( int index ) { readNextChunkIfNeeded( 2 ); return buffer.getUnsignedShort( index ); } public int getMedium( int index ) { readNextChunkIfNeeded( 4 ); return buffer.getMedium( index ); } public int getUnsignedMedium( int index ) { readNextChunkIfNeeded( 4 ); return buffer.getUnsignedMedium( index ); } public int getInt( int index ) { readNextChunkIfNeeded( 4 ); return buffer.getInt( index ); } public long getUnsignedInt( int index ) { readNextChunkIfNeeded( 4 ); return buffer.getUnsignedInt( index ); } public long getLong( int index ) { readNextChunkIfNeeded( 8 ); return buffer.getLong( index ); } public char getChar( int index ) { readNextChunkIfNeeded( 2 ); return buffer.getChar( index ); } public float getFloat( int index ) { readNextChunkIfNeeded( 8 ); return buffer.getFloat( index ); } public double getDouble( int index ) { readNextChunkIfNeeded( 8 ); return buffer.getDouble( index ); } public void getBytes( int index, ChannelBuffer dst ) { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( dst.writableBytes() ); buffer.getBytes( index, dst ); } public void getBytes( int index, ChannelBuffer dst, int length ) { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( length ); buffer.getBytes( index, dst, length ); } public void getBytes( int index, ChannelBuffer dst, int dstIndex, int length ) { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( length ); buffer.getBytes( index, dst, dstIndex, length ); } public void getBytes( int index, byte[] dst ) { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( dst.length ); buffer.getBytes( index, dst ); } public void getBytes( int index, byte[] dst, int dstIndex, int length ) { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( length ); buffer.getBytes( index, dst, dstIndex, length ); } public void getBytes( int index, ByteBuffer dst ) { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( dst.limit() ); buffer.getBytes( index, dst ); } public void getBytes( int index, OutputStream out, int length ) throws IOException { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( length ); buffer.getBytes( index, out, length ); } public int getBytes( int index, GatheringByteChannel out, int length ) throws IOException { // TODO We need a loop for this (if dst is bigger than chunk size) readNextChunkIfNeeded( length ); return buffer.getBytes( index, out, length ); } private UnsupportedOperationException unsupportedOperation() { return new UnsupportedOperationException( "Not supported in a DechunkingChannelBuffer, it's used merely for reading" ); } public void setByte( int index, int value ) { throw unsupportedOperation(); } public void setShort( int index, int value ) { throw unsupportedOperation(); } public void setMedium( int index, int value ) { throw unsupportedOperation(); } public void setInt( int index, int value ) { throw unsupportedOperation(); } public void setLong( int index, long value ) { throw unsupportedOperation(); } public void setChar( int index, int value ) { throw unsupportedOperation(); } public void setFloat( int index, float value ) { throw unsupportedOperation(); } public void setDouble( int index, double value ) { throw unsupportedOperation(); } public void setBytes( int index, ChannelBuffer src ) { throw unsupportedOperation(); } public void setBytes( int index, ChannelBuffer src, int length ) { throw unsupportedOperation(); } public void setBytes( int index, ChannelBuffer src, int srcIndex, int length ) { throw unsupportedOperation(); } public void setBytes( int index, byte[] src ) { throw unsupportedOperation(); } public void setBytes( int index, byte[] src, int srcIndex, int length ) { throw unsupportedOperation(); } public void setBytes( int index, ByteBuffer src ) { throw unsupportedOperation(); } public int setBytes( int index, InputStream in, int length ) throws IOException { throw unsupportedOperation(); } public int setBytes( int index, ScatteringByteChannel in, int length ) throws IOException { throw unsupportedOperation(); } public void setZero( int index, int length ) { throw unsupportedOperation(); } public byte readByte() { readNextChunkIfNeeded( 1 ); return buffer.readByte(); } public short readUnsignedByte() { readNextChunkIfNeeded( 1 ); return buffer.readUnsignedByte(); } public short readShort() { readNextChunkIfNeeded( 2 ); return buffer.readShort(); } public int readUnsignedShort() { readNextChunkIfNeeded( 2 ); return buffer.readUnsignedShort(); } public int readMedium() { readNextChunkIfNeeded( 4 ); return buffer.readMedium(); } public int readUnsignedMedium() { readNextChunkIfNeeded( 4 ); return buffer.readUnsignedMedium(); } public int readInt() { readNextChunkIfNeeded( 4 ); return buffer.readInt(); } public long readUnsignedInt() { readNextChunkIfNeeded( 4 ); return buffer.readUnsignedInt(); } public long readLong() { readNextChunkIfNeeded( 8 ); return buffer.readLong(); } public char readChar() { readNextChunkIfNeeded( 2 ); return buffer.readChar(); } public float readFloat() { readNextChunkIfNeeded( 8 ); return buffer.readFloat(); } public double readDouble() { readNextChunkIfNeeded( 8 ); return buffer.readDouble(); } public ChannelBuffer readBytes( int length ) { readNextChunkIfNeeded( length ); return buffer.readBytes( length ); } public ChannelBuffer readBytes( ChannelBufferIndexFinder indexFinder ) { throw unsupportedOperation(); } public ChannelBuffer readSlice( int length ) { readNextChunkIfNeeded( length ); return buffer.readSlice( length ); } public ChannelBuffer readSlice( ChannelBufferIndexFinder indexFinder ) { throw unsupportedOperation(); } public void readBytes( ChannelBuffer dst ) { readNextChunkIfNeeded( dst.writableBytes() ); buffer.readBytes( dst ); } public void readBytes( ChannelBuffer dst, int length ) { readNextChunkIfNeeded( length ); buffer.readBytes( dst, length ); } public void readBytes( ChannelBuffer dst, int dstIndex, int length ) { readNextChunkIfNeeded( length ); buffer.readBytes( dst, dstIndex, length ); } public void readBytes( byte[] dst ) { readNextChunkIfNeeded( dst.length ); buffer.readBytes( dst ); } public void readBytes( byte[] dst, int dstIndex, int length ) { readNextChunkIfNeeded( length ); buffer.readBytes( dst, dstIndex, length ); } public void readBytes( ByteBuffer dst ) { readNextChunkIfNeeded( dst.limit() ); buffer.readBytes( dst ); } public void readBytes( OutputStream out, int length ) throws IOException { readNextChunkIfNeeded( length ); buffer.readBytes( out, length ); } public int readBytes( GatheringByteChannel out, int length ) throws IOException { readNextChunkIfNeeded( length ); return buffer.readBytes( out, length ); } public void skipBytes( int length ) { readNextChunkIfNeeded( length ); buffer.skipBytes( length ); } public int skipBytes( ChannelBufferIndexFinder indexFinder ) { throw unsupportedOperation(); } public void writeByte( int value ) { throw unsupportedOperation(); } public void writeShort( int value ) { throw unsupportedOperation(); } public void writeMedium( int value ) { throw unsupportedOperation(); } public void writeInt( int value ) { throw unsupportedOperation(); } public void writeLong( long value ) { throw unsupportedOperation(); } public void writeChar( int value ) { throw unsupportedOperation(); } public void writeFloat( float value ) { throw unsupportedOperation(); } public void writeDouble( double value ) { throw unsupportedOperation(); } public void writeBytes( ChannelBuffer src ) { throw unsupportedOperation(); } public void writeBytes( ChannelBuffer src, int length ) { throw unsupportedOperation(); } public void writeBytes( ChannelBuffer src, int srcIndex, int length ) { throw unsupportedOperation(); } public void writeBytes( byte[] src ) { throw unsupportedOperation(); } public void writeBytes( byte[] src, int srcIndex, int length ) { throw unsupportedOperation(); } public void writeBytes( ByteBuffer src ) { throw unsupportedOperation(); } public int writeBytes( InputStream in, int length ) throws IOException { throw unsupportedOperation(); } public int writeBytes( ScatteringByteChannel in, int length ) throws IOException { throw unsupportedOperation(); } public void writeZero( int length ) { throw unsupportedOperation(); } public int indexOf( int fromIndex, int toIndex, byte value ) { throw unsupportedOperation(); } public int indexOf( int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder ) { throw unsupportedOperation(); } public int bytesBefore( byte value ) { throw unsupportedOperation(); } public int bytesBefore( ChannelBufferIndexFinder indexFinder ) { throw unsupportedOperation(); } public int bytesBefore( int length, byte value ) { throw unsupportedOperation(); } public int bytesBefore( int length, ChannelBufferIndexFinder indexFinder ) { throw unsupportedOperation(); } public int bytesBefore( int index, int length, byte value ) { throw unsupportedOperation(); } public int bytesBefore( int index, int length, ChannelBufferIndexFinder indexFinder ) { throw unsupportedOperation(); } public ChannelBuffer copy() { throw unsupportedOperation(); } public ChannelBuffer copy( int index, int length ) { throw unsupportedOperation(); } public ChannelBuffer slice() { throw unsupportedOperation(); } public ChannelBuffer slice( int index, int length ) { throw unsupportedOperation(); } public ChannelBuffer duplicate() { throw unsupportedOperation(); } public ByteBuffer toByteBuffer() { throw unsupportedOperation(); } public ByteBuffer toByteBuffer( int index, int length ) { throw unsupportedOperation(); } public ByteBuffer[] toByteBuffers() { throw unsupportedOperation(); } public ByteBuffer[] toByteBuffers( int index, int length ) { throw unsupportedOperation(); } public boolean hasArray() { throw unsupportedOperation(); } public byte[] array() { throw unsupportedOperation(); } public int arrayOffset() { throw unsupportedOperation(); } public String toString( Charset charset ) { return buffer.toString( charset ); } public String toString( int index, int length, Charset charset ) { return buffer.toString( index, length, charset ); } public String toString( String charsetName ) { return buffer.toString( charsetName ); } public String toString( String charsetName, ChannelBufferIndexFinder terminatorFinder ) { return buffer.toString( charsetName, terminatorFinder ); } public String toString( int index, int length, String charsetName ) { return buffer.toString( index, length, charsetName ); } public String toString( int index, int length, String charsetName, ChannelBufferIndexFinder terminatorFinder ) { return buffer.toString( index, length, charsetName, terminatorFinder ); } public int hashCode() { return buffer.hashCode(); } public boolean equals( Object obj ) { return buffer.equals( obj ); } public int compareTo( ChannelBuffer buffer ) { return buffer.compareTo( buffer ); } public String toString() { return buffer.toString(); } }