NIO2 and ByteBuffer use for reading variable length messages I am reading variable length messages where each message is an int + msg bytes. Messages could be any size.

Right now my strategy is to have a direct buffer that reads from the socket, which then transfers the bytes read into an array-backed nondirect buffer. However my solution has a lot of copying, some loops and just feels a bit "icky".

Is there a better solution?

Here is the read completion handler. I'm hoping it should be pretty obvious what is going on:

class DelimitedReadCompletionHandler implements CompletionHandler&lt;Integer, Object&gt; {

    private final AsynchronousByteChannel channel;
    private final Consumer&lt;byte[]&gt; onMessageCallback;
    private final ByteBuffer channelReadBuffer; // this is a direct buffer.

    private volatile ByteBuffer currentMessageBuffer;

    public DelimitedReadCompletionHandler(AsynchronousByteChannel channel,
                                          Consumer&lt;byte[]&gt; onMessageCallback,
                                          ByteBuffer channelReadBuffer) {

        this.channel = channel;
        this.onMessageCallback = onMessageCallback;
        this.channelReadBuffer = channelReadBuffer;
    }

    @Override
    public void completed(Integer result,
                          Object attachment) {

        channelReadBuffer.flip();

        do {
            if (currentMessageBuffer == null) {
                if (channelReadBuffer.remaining() &lt; Integer.BYTES) {
                    channelReadBuffer.compact();
                    break;
                }

                currentMessageBuffer = ByteBuffer.allocate(channelReadBuffer.getInt());
            }

            int copyLength = Integer.min(currentMessageBuffer.remaining(), channelReadBuffer.remaining());

            for (int i = 0; i &lt; copyLength; i++) {
                currentMessageBuffer.put(channelReadBuffer.get());
            }

            if (!currentMessageBuffer.hasRemaining()) {
                byte[] msg = currentMessageBuffer.array();
                ForkJoinPool.commonPool().execute(() -&gt; onMessageCallback.accept(msg));

                currentMessageBuffer = null;
            }

            if (channelReadBuffer.hasRemaining()) {
                continue;
            }

            channelReadBuffer.clear();
            break;
        } while (true);

        channel.read(channelReadBuffer, null, this);
    }

    @Override
    public void failed(Throwable exc,
                       Object attachment) {

        exc.printStackTrace();
    }
}
Write some JavaDoc!

As stated above, I was very confused by your code. However, if your methods included JavaDoc, then I wouldn't be so confused by the names.

Here is an example of JavaDoc on a simple method:

/**
    Returns the sum of both of the parameters.
*/
public int add(int a, int b) {
    return a + b;
}


Note the /**? This is required for it to be valid JavaDoc.

In more complicated functions where the parameters aren't clear or the return value isn't clear, you can include something like this in your JavaDoc:

@param nameOfParameter -- description/purpose of parameter
@return -- description of return
        -- description of return
        -- description of return


You would have multiple -- description of returns if there were multiple potential return values.



There is no point in having a do/while loop where the condition being checked is true; if it were just a normal while loop, it would run atleast once anyway.

I recommend that you change your do/while to just a while as while loops are much more common.



I can not go into the way you tried to solve the problem because I am having trouble understanding your code. I believe this is just on me, but I think that having more documentation would help anyway.