Echo server with CompletableFuture
As far as I see supplyAsync and thenAcceptAsync uses ForkJoinPool.commonPool() which does not use more than three threads on my machine (and it depends on the number of available processors). It could be a bottleneck if you have more than three clients at the same time. Both methods can have an Executor argument which would be used instead of commonPool().
The code calls completeExceptionally in the catch block here:


readerPromise.thenAcceptAsync((buffer) -&gt; {
    if (buffer != null) {
        try {
            connection.write(buffer).get();
            connection.close();
        } catch (InterruptedException | ExecutionException | IOException e {
            readerPromise.completeExceptionally(e);
        } 
    }
});



It seems to me that it does not have any effect. Javadoc of completeExceptionally says the following:


  If not already completed, causes invocations of get() and related methods to throw the given exception.


The lambda passed to thenAcceptAsync is called when the CompleteFuture (readerPromise in this case) is already completed so it doesn't change anything. Consider the following PoC:

@Test
public void test3() throws Exception {
    final CompletableFuture&lt;String&gt; reader = CompletableFuture.supplyAsync(() -&gt; {
        return "data";
    });
    reader.thenAcceptAsync(x -&gt; {
        System.out.println("reader.thenAcceptAsync: " + x);
        boolean transitionToCompleted = reader.completeExceptionally(new RuntimeException(
                "overridden Future result"));
        System.out.println(transitionToCompleted); // prints "false"
    });

    Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
    System.out.println("reader result: " + reader.get()); // prints "data"
}


(Note that there is an obtrudeException method but I guess you don't need that either.)
The same is true for connectionPromise.completeExceptionally(e), furthermore, connectionPromise seems completely unused in the code above. I'd remove it.
thenAcceptAsync will run only when the the previous stage completes normally. The following also could be useful:


  In all other cases, if a stage's computation terminates abruptly with an (unchecked) exception or error, then all dependent stages requiring its completion complete exceptionally as well, with a CompletionException holding the exception as its cause.


Source: CompletionStage javadoc

According to that you might be able to change the reader lambda to the following:

final CompletableFuture&lt;ByteBuffer&gt; readerPromise = CompletableFuture.supplyAsync(() -&gt; {
    try {
        final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
        connection.read(buffer).get();
        buffer.flip();
        return buffer;
    } catch (final Exception e) {
        throw new RuntimeException(e);
    }
});


It never returns null, so you can remove the null check from writer lambda:

final CompletableFuture&lt;Void&gt; writerPromise = readerPromise.thenAcceptAsync((buffer) -&gt; {
    try {
        connection.write(buffer).get();
        connection.close();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
});


Finally, if there's an exception, you could log that with the following:

writerPromise.exceptionally(x -&gt; {
    x.printStackTrace(); // TODO: use logging instead
    return null;
});


(I'm not familiar with these APIs but this looks right for me.)
Note that I've removed the ugly casting with changing


return (ByteBuffer) buffer.flip();



to

buffer.flip();
return buffer;

Calling Future.get() in the lambdas does not seem to asynchronous for me too (althought they're run by another threads) but I've not idea how should it be handled. The two APIs does not seem compatible. (I guess you could get better answers on Stack Overflow with a more specific question.)