Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 7
example.flip();

Figure 5. The flipped ByteBuffer
After the flip, the buffer can be read. In this example, get() returns four bytes before it throws a BufferUnderflowException.
Any data type larger than a byte must be stored in multiple bytes. A short (16 bits) requires two bytes, while an int (32 bits) requires four bytes. For a variety of historical reasons, different CPU architectures pack these bytes differently. On big-endian architectures, the most significant byte goes in the lowest address, as shown in Figure 6. Big-endian order is often referred to as network order.

Figure 6. Big-endian byte ordering

Figure 7. Little-endian byte ordering
Anyone who programs networks in C or C++ can rant at length about byte-ordering problems. Host byte order, network byte order, big endian, little endian ... they're a pain. If you put a short into a byte array in big-endian ordering and remove it in little-endian ordering, you receive a different number than you put in! (See Figure 8.)

Figure 8. The result of mismatched byte ordering
You might have noticed that the call to example.putShort() illustrated in Figure 4 resulted in 0xFE at Position 1 and 0xBA at Position 2. In other words, the most significant byte went into the lowest numbered slot. Therefore, Figure 4 offers an
example of big-endian byte ordering. java.nio.ByteBuffer defaults to big-endian byte ordering on all machines, no matter what the underlying CPU might use. (In fact, Intel microprocessors
are little endian.) ByteBuffer uses instances of java.nio.ByteOrder to determine its byte ordering. The static constants ByteOrder.BIG_ENDIAN and ByteOrder.LITTLE_ENDIAN do exactly what you would expect.
Essentially, if you talk to another Java program, leave the byte ordering alone and it will work. If you talk to a well-behaved socket application in any language, you should also leave the byte ordering alone. You fiddle with byte ordering in only two instances: when you talk to a poorly-behaved network application that does not respect network byte ordering, or when you deal with binary data files created on a little-endian machine.
So how can buffers improve performance and cut down on garbage? You could create a pool of direct Buffers to avoid allocations during request processing. Or you could create Buffers for common situations and keep them around. The following fragment from our sample HTTP server illustrates the latter approach:
class ReadWriteThread extends Thread {
...
private WeakHashMap fileCache = new WeakHashMap();
private ByteBuffer[] responseBuffers = new ByteBuffer[2];
...
public ReadWriteThread(Selector readSelector,
ConnectionList acceptedConnections,
File dir)
throws Exception
{
super("Reader-Writer");
...
responseBuffers[0] = initializeResponseHeader();
...
}
...
protected ByteBuffer initializeResponseHeader() throws Exception {
// Pre-load a "good" HTTP response as characters.
CharBuffer chars = CharBuffer.allocate(88);
chars.put("HTTP/1.1 200 OK\n");
chars.put("Connection: close\n");
chars.put("Server: Java New I/O Example\n");
chars.put("Content-Type: text/html\n");
chars.put("\n");
chars.flip();
// Translate the Unicode characters into ASCII bytes.
ByteBuffer buffer = ascii.newEncoder().encode(chars);
ByteBuffer directBuffer = ByteBuffer.allocateDirect(buffer.limit());
directBuffer.put(buffer);
return directBuffer;
}
...
}
ByteBuffers for the responses. The first buffer always contains the HTTP response header. This particular server always sends the same
headers and the same response code. To send error responses, the method sendError() (not shown above) creates a similar buffer with an HTTP error response for a particular status code. It saves the error response
headers in a WeakHashMap, keyed by the HTTP status code. The initializeResponseHeader() method actually uses three buffers. It fills a CharBuffer with Strings. The character set encoder turns the Unicode strings into bytes. I will cover character conversion later. Since this header
is sent at the beginning of every response from the server, it saves time to create the response once, save it in a buffer,
and just send the buffer every time. Notice the call to flip the CharBuffer after we put our data into it. The third buffer used in initializeResponseHeader() seems a bit odd. Why convert the characters into a ByteBuffer just to then copy them into another ByteBuffer? The answer: because CharsetEncoder creates a nondirect ByteBuffer. When you write a direct buffer to a channel, it immediately passes to native calls. However, when you pass a nondirect buffer to a channel, the channel provider creates a new, direct buffer and copies the nondirect buffer's contents. That means
extra garbage and a data copy. It worsens when the buffer with the response header is sent in every HTTP response. Why let
the channel provider create a direct buffer on every request if we can do it once and get it over with?