Source:
<code><START> protected abstract void write(ByteBuffer header, ByteBuffer data) throws IOException; <END> @Nonnull public final synchronized void receive(@Nonnull ByteBuffer data) throws IOException, InterruptedException { while (receiver != null && readCommandIndex > 0) { processCommand(); } while (data.hasRemaining() || readState == READ_STATE_COMMAND_READY) { switch (readState) { case READ_STATE_NEED_HEADER: if (data.remaining() >= 2) { readFrameHeader = ChunkHeader.read(data); readFrameRemaining = ChunkHeader.length(readFrameHeader); readState = READ_STATE_FRAME_BODY; } else { readFrameHeader = data.get(); readState = READ_STATE_MORE_HEADER; } break; case READ_STATE_MORE_HEADER: readFrameHeader = ChunkHeader.parse(readFrameHeader, data.get()); readFrameRemaining = ChunkHeader.length(readFrameHeader); readState = READ_STATE_FRAME_BODY; break; case READ_STATE_FRAME_BODY: if (data.remaining() < readFrameRemaining) { readCommandSizes[readCommandIndex] += data.remaining(); readFrameRemaining -= data.remaining(); receiveQueue.put(data); } else { readCommandSizes[readCommandIndex] += readFrameRemaining; int oldLimit = data.limit(); data.limit(data.position() + readFrameRemaining); receiveQueue.put(data); data.limit(oldLimit); readFrameRemaining = 0; if (ChunkHeader.isLast(readFrameHeader)) { readState = READ_STATE_COMMAND_READY; } else { readState = READ_STATE_NEED_HEADER; } } break; case READ_STATE_COMMAND_READY: readCommandIndex++; if (readCommandIndex > readCommandSizes.length) { readCommandSizes = Arrays.copyOf(readCommandSizes, readCommandSizes.length * 2); } readCommandSizes[readCommandIndex] = 0; readState = READ_STATE_NEED_HEADER; if (receiver != null) { processCommand(); } break; } } }</code><technical_language>InterruptedException is option here? API is symmetric then, this command transport good for sync protocols</technical_language>

Target:
protected abstract void write(ByteBuffer header, ByteBuffer data) throws IOException; public final void receive(@Nonnull ByteBuffer data) throws IOException, InterruptedException { while (receiver != null && readCommandIndex > 0) { processCommand(); } while (data.hasRemaining() || readState == READ_STATE_COMMAND_READY) { switch (readState) { case READ_STATE_NEED_HEADER: if (data.remaining() >= 2) { readFrameHeader = ChunkHeader.read(data); readFrameRemaining = ChunkHeader.length(readFrameHeader); readState = READ_STATE_FRAME_BODY; } else { readFrameHeader = data.get(); readState = READ_STATE_MORE_HEADER; } break; case READ_STATE_MORE_HEADER: readFrameHeader = ChunkHeader.parse(readFrameHeader, data.get()); readFrameRemaining = ChunkHeader.length(readFrameHeader); readState = READ_STATE_FRAME_BODY; break; case READ_STATE_FRAME_BODY: if (data.remaining() < readFrameRemaining) { readCommandSizes[readCommandIndex] += data.remaining(); readFrameRemaining -= data.remaining(); receiveQueue.put(data); } else { readCommandSizes[readCommandIndex] += readFrameRemaining; int oldLimit = data.limit(); data.limit(data.position() + readFrameRemaining); receiveQueue.put(data); data.limit(oldLimit); readFrameRemaining = 0; if (ChunkHeader.isLast(readFrameHeader)) { readState = READ_STATE_COMMAND_READY; } else { readState = READ_STATE_NEED_HEADER; } } break; case READ_STATE_COMMAND_READY: readCommandIndex++; if (readCommandIndex > readCommandSizes.length) { readCommandSizes = Arrays.copyOf(readCommandSizes, readCommandSizes.length * 2); } readCommandSizes[readCommandIndex] = 0; readState = READ_STATE_NEED_HEADER; if (receiver != null) { processCommand(); } break; default: throw new IllegalStateException("Unknown readState = " + readState); } } }