WritableByteChannel.write may consume only part of a ByteBuffer, so a complete write requires checking remaining bytes.
Java WritableByteChannel short writes: drain the ByteBuffer
Loop on the buffer state
The buffer position advances by the count actually written. Continue until hasRemaining is false. The fixture uses a channel that accepts at most two bytes per call, making the short-write path deterministic instead of relying on a particular filesystem or socket.
FileChannel writing follows the same position rule. For a nonblocking channel, write can return zero because it is not ready; register for writability or apply a deadline instead of spinning forever.
Define completion before publication
Only publish or acknowledge a record once its entire buffer has been accepted by the channel. Acceptance by a channel is not always durable storage: a file may also require a force policy, while a socket needs an application-level acknowledgment if remote processing matters. Atomic file publication handles a separate visibility boundary.
Working program
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
public class ManifestChannelWriter {
static class TwoByteChannel implements WritableByteChannel {
private final ByteArrayOutputStream sink = new ByteArrayOutputStream();
private boolean open = true;
public int write(ByteBuffer source) {
int count = Math.min(2, source.remaining());
for (int index = 0; index < count; index++) sink.write(source.get());
return count;
}
public boolean isOpen() { return open; }
public void close() { open = false; }
String contents() { return new String(sink.toByteArray(), StandardCharsets.US_ASCII); }
}
public static void main(String[] args) throws IOException {
TwoByteChannel channel = new TwoByteChannel();
ByteBuffer record = ByteBuffer.wrap("LOT-47".getBytes(StandardCharsets.US_ASCII));
int calls = 0;
while (record.hasRemaining()) { channel.write(record); calls++; }
System.out.println(channel.contents());
System.out.println("calls=" + calls);
channel.close();
}
}Output
LOT-47
calls=3Cost and ownership
The loop processes n bytes in O(n) total transfer work and keeps only the input buffer plus the sink. A production nonblocking channel must wait for readiness on zero-byte writes; this blocking-style demonstration always advances and therefore cannot spin.
Common Mistakes
- Do not assume one write drains the buffer.
- Do not reset position between partial writes.
- Do not busy-loop on a nonblocking channel that returns zero.
Read next
Java file channels: buffer positions and partial transfers, Java ByteBuffer flip and compact: preserve an incomplete frame, Java Files.move: atomic publication is a filesystem contract, Java byte streams: partial reads and bounded copying.
