Apache MINA Core is a network application framework for building high-performance, scalable network applications with event-driven, asynchronous I/O over various transports including TCP/IP and UDP/IP via Java NIO.
—
MINA Core provides the IoBuffer class as an enhanced replacement for Java NIO's ByteBuffer. IoBuffer offers automatic expansion, convenient data manipulation methods, and optimized memory management for network applications.
public abstract class IoBuffer implements Comparable<IoBuffer> {
// Allocation methods
static IoBuffer allocate(int capacity);
static IoBuffer allocate(int capacity, boolean direct);
static IoBuffer wrap(byte[] array);
static IoBuffer wrap(ByteBuffer nioBuffer);
// Buffer properties
int capacity();
int position();
IoBuffer position(int newPosition);
int limit();
IoBuffer limit(int newLimit);
int remaining();
boolean hasRemaining();
// Auto-expansion
boolean isAutoExpand();
IoBuffer setAutoExpand(boolean autoExpand);
boolean isAutoShrink();
IoBuffer setAutoShrink(boolean autoShrink);
// Buffer manipulation
IoBuffer clear();
IoBuffer flip();
IoBuffer rewind();
IoBuffer mark();
IoBuffer reset();
IoBuffer compact();
// Data access
byte get();
IoBuffer put(byte b);
byte get(int index);
IoBuffer put(int index, byte b);
// Bulk operations
IoBuffer get(byte[] dst);
IoBuffer put(byte[] src);
IoBuffer get(byte[] dst, int offset, int length);
IoBuffer put(byte[] src, int offset, int length);
// String operations
String getString(CharsetDecoder decoder) throws CharacterCodingException;
String getString(int fieldSize, CharsetDecoder decoder) throws CharacterCodingException;
IoBuffer putString(String value, CharsetEncoder encoder) throws CharacterCodingException;
String getHexDump();
String getHexDump(int lengthLimit);
}// Allocate heap buffer
IoBuffer buffer = IoBuffer.allocate(1024);
// Allocate direct buffer (off-heap)
IoBuffer directBuffer = IoBuffer.allocate(1024, true);
// Set default buffer type
IoBuffer.setUseDirectBuffer(true); // Use direct buffers by default
IoBuffer defaultBuffer = IoBuffer.allocate(1024); // Now allocates direct buffer
// Check if buffer is direct
if (buffer.isDirect()) {
System.out.println("Using direct buffer");
} else {
System.out.println("Using heap buffer");
}// Wrap byte array
byte[] data = "Hello, World!".getBytes("UTF-8");
IoBuffer wrapped = IoBuffer.wrap(data);
// Wrap NIO ByteBuffer
ByteBuffer nioBuffer = ByteBuffer.allocate(1024);
IoBuffer fromNio = IoBuffer.wrap(nioBuffer);
// Wrap with automatic expansion
IoBuffer autoExpanding = IoBuffer.wrap(data);
autoExpanding.setAutoExpand(true);
// Create buffer from existing buffer
IoBuffer original = IoBuffer.allocate(100);
IoBuffer duplicate = original.duplicate();
IoBuffer slice = original.slice();
IoBuffer readOnlyView = original.asReadOnlyBuffer();// Enable auto-expansion to handle variable-length data
IoBuffer buffer = IoBuffer.allocate(16);
buffer.setAutoExpand(true);
// Write data without worrying about capacity
String longMessage = "This is a very long message that exceeds the initial buffer capacity";
buffer.putString(longMessage, Charset.forName("UTF-8").newEncoder());
System.out.println("Buffer capacity grew to: " + buffer.capacity());
// Auto-expansion in action
public class AutoExpandExample {
public void writeVariableLengthData(IoSession session, List<String> messages) {
IoBuffer buffer = IoBuffer.allocate(64);
buffer.setAutoExpand(true);
// Write message count
buffer.putInt(messages.size());
// Write each message with length prefix
for (String message : messages) {
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
buffer.putInt(messageBytes.length); // Length prefix
buffer.put(messageBytes); // Message data
}
// Send the automatically sized buffer
buffer.flip();
session.write(buffer);
}
}// Enable auto-shrinking to reclaim unused memory
IoBuffer buffer = IoBuffer.allocate(1024);
buffer.setAutoShrink(true);
// Fill buffer with data
byte[] data = new byte[500];
buffer.put(data);
buffer.flip();
// Process and clear data
processData(buffer);
buffer.clear();
// Buffer may shrink on next allocation if much smaller capacity is needed
// Auto-shrinking helps with memory efficiency in long-running applications// Write primitive types
IoBuffer buffer = IoBuffer.allocate(1024);
// Integers
buffer.putByte((byte) 42);
buffer.putShort((short) 1000);
buffer.putInt(123456);
buffer.putLong(123456789L);
// Floating point
buffer.putFloat(3.14f);
buffer.putDouble(2.71828);
// Character
buffer.putChar('A');
// Boolean (as byte)
buffer.put((byte) (true ? 1 : 0));
// Read primitive types
buffer.flip();
byte byteValue = buffer.get();
short shortValue = buffer.getShort();
int intValue = buffer.getInt();
long longValue = buffer.getLong();
float floatValue = buffer.getFloat();
double doubleValue = buffer.getDouble();
char charValue = buffer.getChar();
boolean boolValue = buffer.get() != 0;public class StringOperations {
public void stringExamples() throws CharacterCodingException {
IoBuffer buffer = IoBuffer.allocate(1024);
buffer.setAutoExpand(true);
// Write strings with different encodings
CharsetEncoder utf8Encoder = StandardCharsets.UTF_8.newEncoder();
CharsetEncoder utf16Encoder = StandardCharsets.UTF_16.newEncoder();
buffer.putString("Hello, World!", utf8Encoder);
buffer.putString("Привет, мир!", utf8Encoder); // Russian text
buffer.putString("こんにちは", utf16Encoder); // Japanese text
// Read strings back
buffer.flip();
CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
CharsetDecoder utf16Decoder = StandardCharsets.UTF_16.newDecoder();
String hello = buffer.getString(utf8Decoder);
String russian = buffer.getString(utf8Decoder);
String japanese = buffer.getString(utf16Decoder);
System.out.println("English: " + hello);
System.out.println("Russian: " + russian);
System.out.println("Japanese: " + japanese);
}
public void fixedLengthStrings() throws CharacterCodingException {
IoBuffer buffer = IoBuffer.allocate(100);
CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
// Write fixed-length string (padded/truncated to exact size)
String name = "John Doe";
buffer.putString(name, 20, encoder); // Always writes exactly 20 bytes
// Read fixed-length string
buffer.flip();
String readName = buffer.getString(20, decoder).trim();
System.out.println("Name: " + readName);
}
public void nullTerminatedStrings() throws CharacterCodingException {
IoBuffer buffer = IoBuffer.allocate(100);
CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
// Write null-terminated string
buffer.putString("Hello", encoder);
buffer.put((byte) 0); // Null terminator
// Read until null terminator
buffer.flip();
StringBuilder sb = new StringBuilder();
while (buffer.hasRemaining()) {
byte b = buffer.get();
if (b == 0) break;
sb.append((char) b);
}
System.out.println("Null-terminated string: " + sb.toString());
}
}public class BinaryOperations {
public void binaryDataExample() {
IoBuffer buffer = IoBuffer.allocate(1024);
// Write binary protocol header
buffer.put((byte) 0x01); // Version
buffer.put((byte) 0x02); // Message type
buffer.putShort((short) 1000); // Message length
buffer.putInt(System.currentTimeMillis()); // Timestamp
// Write payload
byte[] payload = createPayload();
buffer.put(payload);
// Update length field
int currentPos = buffer.position();
int messageLength = currentPos - 4; // Exclude header
buffer.putShort(2, (short) messageLength);
buffer.flip();
processMessage(buffer);
}
public void bitManipulation(IoBuffer buffer) {
// Pack multiple boolean flags into a single byte
boolean flag1 = true;
boolean flag2 = false;
boolean flag3 = true;
boolean flag4 = false;
byte flags = 0;
if (flag1) flags |= 0x01;
if (flag2) flags |= 0x02;
if (flag3) flags |= 0x04;
if (flag4) flags |= 0x08;
buffer.put(flags);
// Unpack flags
buffer.flip();
byte packedFlags = buffer.get();
boolean unpackedFlag1 = (packedFlags & 0x01) != 0;
boolean unpackedFlag2 = (packedFlags & 0x02) != 0;
boolean unpackedFlag3 = (packedFlags & 0x04) != 0;
boolean unpackedFlag4 = (packedFlags & 0x08) != 0;
}
}public class BufferDebugging {
public void debugBuffer(IoBuffer buffer) {
System.out.println("=== Buffer Debug Information ===");
System.out.println("Capacity: " + buffer.capacity());
System.out.println("Position: " + buffer.position());
System.out.println("Limit: " + buffer.limit());
System.out.println("Remaining: " + buffer.remaining());
System.out.println("Auto-expand: " + buffer.isAutoExpand());
System.out.println("Auto-shrink: " + buffer.isAutoShrink());
System.out.println("Direct: " + buffer.isDirect());
System.out.println("Read-only: " + buffer.isReadOnly());
// Print hex dump
System.out.println("Hex dump:");
System.out.println(buffer.getHexDump());
// Print limited hex dump for large buffers
System.out.println("Hex dump (first 256 bytes):");
System.out.println(buffer.getHexDump(256));
}
public void compareBuffers(IoBuffer buffer1, IoBuffer buffer2) {
// Compare buffer contents
int comparison = buffer1.compareTo(buffer2);
if (comparison == 0) {
System.out.println("Buffers are identical");
} else if (comparison < 0) {
System.out.println("Buffer1 is lexicographically less than Buffer2");
} else {
System.out.println("Buffer1 is lexicographically greater than Buffer2");
}
// Check if buffers have same content
if (buffer1.equals(buffer2)) {
System.out.println("Buffers have same content");
}
}
}// Custom buffer allocator for performance optimization
public class CustomBufferAllocator implements IoBufferAllocator {
private final boolean useDirectBuffer;
public CustomBufferAllocator(boolean useDirectBuffer) {
this.useDirectBuffer = useDirectBuffer;
}
@Override
public IoBuffer allocate(int capacity) {
return wrap(ByteBuffer.allocate(capacity));
}
@Override
public IoBuffer allocate(int capacity, boolean direct) {
ByteBuffer nioBuffer;
if (direct || useDirectBuffer) {
nioBuffer = ByteBuffer.allocateDirect(capacity);
} else {
nioBuffer = ByteBuffer.allocate(capacity);
}
return wrap(nioBuffer);
}
@Override
public IoBuffer wrap(ByteBuffer nioBuffer) {
return new CustomIoBuffer(nioBuffer);
}
@Override
public void dispose() {
// Cleanup resources if needed
}
}
// Set custom allocator
IoBuffer.setAllocator(new CustomBufferAllocator(true));
// Cached buffer allocator for better performance
CachedBufferAllocator cachedAllocator = new CachedBufferAllocator();
IoBuffer.setAllocator(cachedAllocator);public class BufferViews {
public void bufferSlicing() {
// Create original buffer with data
IoBuffer original = IoBuffer.allocate(100);
for (int i = 0; i < 50; i++) {
original.putInt(i);
}
// Create slice starting at position 20
original.position(20);
IoBuffer slice = original.slice();
// Slice shares data with original but has independent position/limit
slice.putInt(999); // Modifies original buffer at position 20
// Create duplicate with same content but independent position
original.rewind();
IoBuffer duplicate = original.duplicate();
// Create read-only view
IoBuffer readOnly = original.asReadOnlyBuffer();
// Read-only buffer throws exception on write attempts
try {
readOnly.putInt(123);
} catch (ReadOnlyBufferException e) {
System.out.println("Cannot write to read-only buffer");
}
}
public void bufferTypeViews() {
IoBuffer buffer = IoBuffer.allocate(100);
// Get typed views of buffer content
IntBuffer intView = buffer.asIntBuffer();
ShortBuffer shortView = buffer.asShortBuffer();
LongBuffer longView = buffer.asLongBuffer();
FloatBuffer floatView = buffer.asFloatBuffer();
DoubleBuffer doubleView = buffer.asDoubleBuffer();
CharBuffer charView = buffer.asCharBuffer();
// Use typed view for bulk operations
int[] intArray = {1, 2, 3, 4, 5};
intView.put(intArray);
// Read back using view
intView.rewind();
int[] readBack = new int[5];
intView.get(readBack);
}
}public class StreamIntegration {
public void bufferToStream() throws IOException {
IoBuffer buffer = IoBuffer.allocate(1024);
// Write data to buffer
buffer.putString("Hello, Stream!", StandardCharsets.UTF_8.newEncoder());
buffer.flip();
// Convert buffer to InputStream
InputStream inputStream = buffer.asInputStream();
// Read from stream
byte[] data = new byte[buffer.remaining()];
inputStream.read(data);
String result = new String(data, StandardCharsets.UTF_8);
System.out.println("Read from stream: " + result);
}
public void streamToBuffer() throws IOException {
// Create buffer as OutputStream
IoBuffer buffer = IoBuffer.allocate(1024);
buffer.setAutoExpand(true);
OutputStream outputStream = buffer.asOutputStream();
// Write to buffer via stream
String message = "Hello from OutputStream!";
outputStream.write(message.getBytes(StandardCharsets.UTF_8));
// Prepare buffer for reading
buffer.flip();
// Read the data
byte[] result = new byte[buffer.remaining()];
buffer.get(result);
System.out.println("Written via stream: " + new String(result, StandardCharsets.UTF_8));
}
}public class MemoryMappedBuffers {
public void createMappedBuffer(String filename) throws IOException {
try (RandomAccessFile file = new RandomAccessFile(filename, "rw")) {
FileChannel channel = file.getChannel();
// Create memory-mapped buffer
MappedByteBuffer mappedBuffer = channel.map(
FileChannel.MapMode.READ_WRITE, 0, 1024);
// Wrap in IoBuffer
IoBuffer buffer = IoBuffer.wrap(mappedBuffer);
// Write data directly to file via buffer
buffer.putString("Memory-mapped data", StandardCharsets.UTF_8.newEncoder());
// Force write to disk
if (mappedBuffer instanceof MappedByteBuffer) {
((MappedByteBuffer) buffer.buf()).force();
}
}
}
}public class BufferPerformance {
public void efficientBufferReuse() {
// Reuse buffers to reduce GC pressure
IoBuffer buffer = IoBuffer.allocate(1024);
for (int i = 0; i < 1000; i++) {
buffer.clear(); // Reset for reuse
// Write data
buffer.putInt(i);
buffer.putString("Message " + i, StandardCharsets.UTF_8.newEncoder());
// Process buffer
buffer.flip();
processBuffer(buffer);
// Buffer is reused in next iteration
}
}
public void bulkOperations() {
IoBuffer source = IoBuffer.allocate(10000);
IoBuffer destination = IoBuffer.allocate(10000);
// Fill source with data
byte[] data = new byte[5000];
Arrays.fill(data, (byte) 42);
source.put(data);
// Bulk copy is more efficient than individual byte operations
source.flip();
destination.put(source); // Bulk copy
// Instead of:
// while (source.hasRemaining()) {
// destination.put(source.get()); // Inefficient
// }
}
public void directBufferBenefits() {
// Direct buffers are more efficient for I/O operations
IoBuffer directBuffer = IoBuffer.allocate(1024, true);
IoBuffer heapBuffer = IoBuffer.allocate(1024, false);
// Direct buffer: no copying between Java heap and native memory
// Heap buffer: requires copying for I/O operations
// Use direct buffers for:
// - Large buffers
// - Long-lived buffers
// - High I/O throughput scenarios
// Use heap buffers for:
// - Small, short-lived buffers
// - Frequent allocation/deallocation
// - When avoiding off-heap memory limits
}
}public class BufferSizing {
public IoBuffer createOptimalBuffer(int estimatedSize) {
// Start with estimated size
int initialSize = Math.max(estimatedSize, 64); // Minimum 64 bytes
// Round up to next power of 2 for better memory alignment
int optimalSize = Integer.highestOneBit(initialSize - 1) << 1;
IoBuffer buffer = IoBuffer.allocate(optimalSize);
buffer.setAutoExpand(true); // Allow growth if needed
return buffer;
}
public void adaptiveBufferSizing(IoSession session) {
// Adapt buffer size based on session history
Long avgMessageSize = (Long) session.getAttribute("avgMessageSize");
if (avgMessageSize == null) {
avgMessageSize = 1024L; // Default size
}
// Create buffer with 50% headroom
int bufferSize = (int) (avgMessageSize * 1.5);
IoBuffer buffer = IoBuffer.allocate(bufferSize);
buffer.setAutoExpand(true);
// Update average after processing
updateAverageMessageSize(session, buffer.position());
}
private void updateAverageMessageSize(IoSession session, int currentSize) {
Long avgSize = (Long) session.getAttribute("avgMessageSize");
Long messageCount = (Long) session.getAttribute("messageCount");
if (avgSize == null) {
avgSize = (long) currentSize;
messageCount = 1L;
} else {
messageCount++;
avgSize = ((avgSize * (messageCount - 1)) + currentSize) / messageCount;
}
session.setAttribute("avgMessageSize", avgSize);
session.setAttribute("messageCount", messageCount);
}
}IoBuffer provides a powerful and flexible foundation for handling binary data in MINA applications, with features that significantly simplify network programming compared to raw ByteBuffer usage.
Install with Tessl CLI
npx tessl i tessl/maven-org-apache-mina--mina-core