Archive for October, 2006

public abstract boolean isBlocking(); public abstract Object blockingLock();

Monday, October 30th, 2006

serverChannel.configureBlocking (false); socket = serverChannel.accept(); serverChannel.configureBlocking (prevState); } // lock is now released, mode is allowed to change if (socket != null) { doSomethingWithTheSocket (socket); } 3.5.2 ServerSocketChannel Let’s begin the discussion of the socket channels classes with the simplest: ServerSocketChannel. This is the complete API of ServerSocketChannel: public abstract class ServerSocketChannel extends AbstractSelectableChannel { public static ServerSocketChannel open() throws IOException public abstract ServerSocket socket(); public abstract ServerSocket accept() throws IOException; public final int validOps() } The ServerSocketChannel class is a channel-based socket listener. It performs the same basic task as the familiar java.net.ServerSocket but adds channel semantics, including the ability to operate in nonblocking mode. Create a new ServerSocketChannel object with the static open() factory method, which returns a channel associated with an unbound java.net.ServerSocket object. This peer ServerSocket can be obtained by invoking the socket() method on the returned ServerSocketChannel object. The ServerSocket objects created as peers of ServerSocketChannels are tied to the channel implementation. They are sockets whose associated SocketImpl knows about channels. Channels cannot be wrapped around arbitrary Socket objects. Because ServerSocketChannel doesn’t have a bind() method, it’s necessary to fetch the peer socket and use it to bind to a port to begin listening for connections. Also use the peer ServerSocket API to set other socket options as needed. ServerSocketChannel ssc = ServerSocketChannel.open(); ServerSocket serverSocket = ssc.socket(); // Listen on port 1234 serverSocket.bind (new InetSocketAddress (1234)); The ServerSocketChannel has an accept() method, as does its peer java.net.ServerSocket object. Once you’ve created a ServerSocketChannel and used the peer socket to bind it, you can then invoke accept() on either. If you choose to invoke accept() on the ServerSocket, it will behave the same as any other ServerSocket: always blocking and 105

Hint: This post is supported by Gama web hosting hrvatska services

* Test channel transfer. This is a very

Monday, October 30th, 2006

You can see in Figure 3-9 that all three of the socket channel classes (DatagramChannel, SocketChannel, and ServerSocketChannel) extend from AbstractSelectableChannel, which lives in the java.nio.channels.spi package. This means that it’s possible to perform readiness selection of socket channels using a Selector object. Selection and multiplexed I/O are discussed in Chapter 4. Figure 3-9. The socket channel family tree 102
Note: If you are looking for top 10 and very good webhost to host and run your jsp application check Actions jsp hosting services

* Test channel transfer. This is a very

Monday, October 30th, 2006

Notice that DatagramChannel and SocketChannel implement the interfaces that define read and write capabilities, but ServerSocketChannel does not. ServerSocketChannel listens for incoming connects and creates new SocketChannel objects. It never transfers any data itself. Before discussing the individual types of socket channels, you should understand the relationship between sockets and socket channels. As described earlier, a channel is a conduit to an I/O service and provides methods for interacting with that service. In the case of sockets, the decision was made not to reimplement the socket protocol APIs in the corresponding channel classes. The preexisting socket channels in java.net are reused for most protocol operations. All the socket channels (SocketChannel, ServerSocketChannel, and DatagramChannel) create a peer socket object when they are instantiated. These are the familiar classes from java.net ( Socket, ServerSocket, and DatagramSocket), which have been updated to be aware of channels. The peer socket can be obtained from a channel by invoking its socket() method. Additionally, each of the java.net classes now has a getChannel() method. While every socket channel (in java.nio.channels) has an associated java.net socket object, not all sockets have an associated channel. If you create a Socket object in the traditional way, by instantiating it directly, it will not have an associated SocketChannel, and its getChannel() method will always return null. Socket channels delegate protocol operations to the peer socket object. In cases where socket methods seem to be duplicated in the channel class, there is some new or different behavior associated with the method on the channel class. 3.5.1 Nonblocking Mode Socket channels can operate in nonblocking mode. This is a simple statement, but one with far-reaching implications. The blocking nature of traditional Java sockets has traditionally been one of the most significant limitations to Java application scalability. Nonblocking I/O is the basis upon which many sophisticated, high-performance applications are built. To place a socket into nonblocking mode, we look to the common superclass of all the socket channel classes: SelectableChannel. The following methods are concerned with a channel’s blocking mode: public abstract class SelectableChannel extends AbstractChannel implements Channel { // This is a partial API listing public abstract void configureBlocking (boolean block) throws IOException; 103
Note: If you are looking for top 10 and very good webhost to host and run your jsp application check Actions jsp hosting services

* Test channel transfer. This is a very

Monday, October 30th, 2006

* Test channel transfer. This is a very simplistic concatenation * program. It takes a list of file names as arguments, opens each * in turn and transfers (copies) their content to the given * WritableByteChannel (in this case, stdout). * * Created April 2002 * @author Ron Hitchens (ron@ronsoft.com) */ public class ChannelTransfer{ public static void main (String [] argv) throws Exception { if (argv.length == 0) { System.err.println (”Usage: filename …”); return; } catFiles (Channels.newChannel (System.out), argv); } // Concatenate the content of each of the named files to // the given channel. A very dumb version of ‘cat’. private static void catFiles (WritableByteChannel target, String [] files) throws Exception { for (int i = 0; i < files.length; i++) { FileInputStream fis = new FileInputStream (files [i]); FileChannel channel = fis.getChannel(); channel.transferTo (0, channel.size(), target); channel.close(); fis.close(); } } } 3.5 Socket Channels Let's move on to the channel classes that model network sockets. Socket channels have different characteristics than file channels. The new socket channels can operate in nonblocking mode and are selectable. These two capabilities enable tremendous scalability and flexibility in large applications, such as web servers and middleware components. As we'll see in this section, it's no longer necessary to dedicate a thread to each socket connection (and suffer the context-switching overhead of managing large numbers of threads). Using the new NIO classes, one or a few threads can manage hundreds or even thousands of active socket connections with little or no performance loss. 101
Note: If you are looking for top 10 and very good webhost to host and run your jsp application check Actions jsp hosting services

showBuffers (ro, rw, cow); // Modify the copy-on-write

Monday, October 30th, 2006

public abstract long transferTo (long position, long count, WritableByteChannel target) public abstract long transferFrom (ReadableByteChannel src, long position, long count) } The transferTo() and transferFrom() methods allow you to cross-connect one channel to another, eliminating the need to pass data through an intermediate buffer. These methods exist only on the FileChannel class, so one of the channels involved in a channel-to-channel transfer must be a FileChannel. You can’t do direct transfers between socket channels, but socket channels implement WritableByteChannel and ReadableByteChannel, so the content of a file can be transferred to a socket with transferTo(), or data can be read from a socket directly into a file with transferFrom(). Direct channel transfers do not update the position associated with a FileChannel. The requested data transfer will begin where indicated by the position argument and will be at most count bytes. The number of bytes actually transferred is returned, which may be less than the number you requested. For transferTo(), where the source of the transfer is a file, if position + count is greater than the file size, the transfer will stop at the end of the file. If the target is a socket in nonblocking mode, the transfer may stop when its send queue is filled, possibly sending nothing if the output queue is already full. Likewise for transferFrom(): if src is another FileChannel and its end-of-file is reached, the transfer will stop early. If src is a nonblocking socket, only the data currently queued will be transferred (which may be none). Sockets in blocking mode may also do partial transfers, depending on the operating system, because of the nondeterministic nature of network data transfer. Many socket implementations will provide what they currently have queued rather than waiting for the full amount you asked for. Also, keep in mind that these methods may throw java.io.IOException if trouble is encountered during the transfer. Channel-to-channel transfers can potentially be extremely fast, especially where the underlying operating system provides native support. Some operating systems can perform direct transfers without ever passing the data through user space. This can be a huge win for high-volume data transfer. (See Example 3-6.) Example 3-6. File concatenation using channel transfer package com.ronsoft.books.nio.channels; import java.nio.channels.FileChannel; import java.nio.channels.WritableByteChannel; import java.nio.channels.Channels; import java.io.FileInputStream; /** 100
Note: If you are looking for cheap and inexpensive provider to host and run your tomcat application check Actions tomcat hosting services

showBuffers (ro, rw, cow); // Modify the copy-on-write

Monday, October 30th, 2006

} System.out.print (c); } System.out.println (”‘”); } } Here’s the output from running the preceding program: Begin R/O: ‘This is the file content|[8168 nulls]|This is more file content’ R/W: ‘This is the file content|[8168 nulls]|This is more file content’ COW: ‘This is the file content|[8168 nulls]|This is more file content’ Change to COW buffer R/O: ‘This is the file content|[8168 nulls]|This is more file content’ R/W: ‘This is the file content|[8168 nulls]|This is more file content’ COW: ‘This is COW file content|[8168 nulls]|This is more file content’ Change to R/W buffer R/O: ‘This is t R/W le content|[8168 nulls]|Th R/W more file content’ R/W: ‘This is t R/W le content|[8168 nulls]|Th R/W more file content’ COW: ‘This is COW file content|[8168 nulls]|Th R/W more file content’ Write on channel R/O: ‘Channel write le content|[8168 nulls]|Th R/W moChannel write t’ R/W: ‘Channel write le content|[8168 nulls]|Th R/W moChannel write t’ COW: ‘This is COW file content|[8168 nulls]|Th R/W moChannel write t’ Second change to COW buffer R/O: ‘Channel write le content|[8168 nulls]|Th R/W moChannel write t’ R/W: ‘Channel write le content|[8168 nulls]|Th R/W moChannel write t’ COW: ‘This is COW file content|[8168 nulls]|Th R/W moChann COW2 te t’ Second change to R/W buffer R/O: ‘ R/W2 l write le content|[8168 nulls]|Th R/W moChannel R/W2 t’ R/W: ‘ R/W2 l write le content|[8168 nulls]|Th R/W moChannel R/W2 t’ COW: ‘This is COW file content|[8168 nulls]|Th R/W moChann COW2 te t’ 3.4.1 Channel-to-Channel Transfers Bulk transfers of file data from one place to another is so common that a couple of optimization methods have been added to the FileChannel class to make it even more efficient: public abstract class FileChannel extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel { // This is a partial API listing 99
Note: If you are looking for cheap and inexpensive provider to host and run your tomcat application check Actions tomcat hosting services

showBuffers (ro, rw, cow); // Modify the copy-on-write

Monday, October 30th, 2006

showBuffers (ro, rw, cow); // Modify the copy-on-write buffer again cow.position (8207); cow.put (” COW2 “.getBytes()); System.out.println (”Second change to COW buffer”); showBuffers (ro, rw, cow); // Modify the read/write buffer rw.position (0); rw.put (” R/W2 “.getBytes()); rw.position (8210); rw.put (” R/W2 “.getBytes()); rw.force(); System.out.println (”Second change to R/W buffer”); showBuffers (ro, rw, cow); // cleanup channel.close(); file.close(); tempFile.delete(); } // Show the current content of the three buffers public static void showBuffers (ByteBuffer ro, ByteBuffer rw, ByteBuffer cow) throws Exception { dumpBuffer (”R/O”, ro); dumpBuffer (”R/W”, rw); dumpBuffer (”COW”, cow); System.out.println (”"); } // Dump buffer content, counting and skipping nulls public static void dumpBuffer (String prefix, ByteBuffer buffer) throws Exception { System.out.print (prefix + “: ‘”); int nulls = 0; int limit = buffer.limit(); for (int i = 0; i < limit; i++) { char c = (char) buffer.get (i); if (c == 'u0000') { nulls++; continue; } if (nulls != 0) { System.out.print ("|[" + nulls + " nulls]|"); nulls = 0; 98
Note: If you are looking for cheap and inexpensive provider to host and run your tomcat application check Actions tomcat hosting services

private static final String LINE_SEP = “rn”; private

Monday, October 30th, 2006

File tempFile = File.createTempFile (”mmaptest”, null); RandomAccessFile file = new RandomAccessFile (tempFile, “rw”); FileChannel channel = file.getChannel(); ByteBuffer temp = ByteBuffer.allocate (100); // Put something in the file, starting at location 0 temp.put (”This is the file content”.getBytes()); temp.flip(); channel.write (temp, 0); // Put something else in the file, starting at location 8192. // 8192 is 8 KB, almost certainly a different memory/FS page. // This may cause a file hole, depending on the // filesystem page size. temp.clear(); temp.put (”This is more file content”.getBytes()); temp.flip(); channel.write (temp, 8192); // Create three types of mappings to the same file MappedByteBuffer ro = channel.map ( FileChannel.MapMode.READ_ONLY, 0, channel.size()); MappedByteBuffer rw = channel.map ( FileChannel.MapMode.READ_WRITE, 0, channel.size()); MappedByteBuffer cow = channel.map ( FileChannel.MapMode.PRIVATE, 0, channel.size()); // the buffer states before any modifications System.out.println (”Begin”); showBuffers (ro, rw, cow); // Modify the copy-on-write buffer cow.position (8); cow.put (”COW”.getBytes()); System.out.println (”Change to COW buffer”); showBuffers (ro, rw, cow); // Modify the read/write buffer rw.position (9); rw.put (” R/W “.getBytes()); rw.position (8194); rw.put (” R/W “.getBytes()); rw.force(); System.out.println (”Change to R/W buffer”); showBuffers (ro, rw, cow); // Write to the file through the channel; hit both pages temp.clear(); temp.put (”Channel write “.getBytes()); temp.flip(); channel.write (temp, 0); temp.rewind(); channel.write (temp, 8202); System.out.println (”Write on channel”); 97
Note: If you are looking for inexpensive but high quality provider to host and run your jsp application check Astra jsp hosting services

private static final String LINE_SEP = “rn”; private

Monday, October 30th, 2006

FileOutputStream fos = new FileOutputStream (OUTPUT_FILE); FileChannel out = fos.getChannel(); // All the buffers have been prepared; write ‘em out while (out.write (gather) > 0) { // Empty body; loop until all buffers are empty } out.close(); System.out.println (”output written to ” + OUTPUT_FILE); } // Convert a string to its constituent bytes // from the ASCII character set private static byte [] bytes (String string) throws Exception { return (string.getBytes (”US-ASCII”)); } } Example 3-5 illustrates how the various modes of memory mapping interact. In particular, this code illustrates how copy-on-write is page-oriented. When a change is made by calling put() on a MappedByteBuffer object created with the MAP_MODE.PRIVATE mode, a copy of the affected page is made. This private copy not only holds local changes, it also insulates the buffer from external changes to that page. However, changes made to other areas of the mapped file will be seen. Example 3-5. Three types of memory-mapped buffers package com.ronsoft.books.nio.channels; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.io.File; import java.io.RandomAccessFile; /** * Test behavior of Memory mapped buffer types. Create a file, write * some data to it, then create three different types of mappings * to it. Observe the effects of changes through the buffer APIs * and updating the file directly. The data spans page boundaries * to illustrate the page-oriented nature of Copy-On-Write mappings. * * @author Ron Hitchens (ron@ronsoft.com) */ public class MapFile{ public static void main (String [] argv) throws Exception { // Create a temp file and get a channel connected to it 96
Note: If you are looking for inexpensive but high quality provider to host and run your jsp application check Astra jsp hosting services

private static final String LINE_SEP = “rn”; private

Monday, October 30th, 2006

private static final String LINE_SEP = “rn”; private static final String SERVER_ID = “Server: Ronsoft Dummy Server”; private static final String HTTP_HDR = “HTTP/1.0 200 OK” + LINE_SEP + SERVER_ID + LINE_SEP; private static final String HTTP_404_HDR = “HTTP/1.0 404 Not Found” + LINE_SEP + SERVER_ID + LINE_SEP; private static final String MSG_404 = “Could not open file: “; public static void main (String [] argv) throws Exception { if (argv.length < 1) { System.err.println ("Usage: filename"); return; } String file = argv [0]; ByteBuffer header = ByteBuffer.wrap (bytes (HTTP_HDR)); ByteBuffer dynhdrs = ByteBuffer.allocate (128); ByteBuffer [] gather = { header, dynhdrs, null }; String contentType = "unknown/unknown"; long contentLength = -1; try { FileInputStream fis = new FileInputStream (file); FileChannel fc = fis.getChannel(); MappedByteBuffer filedata = fc.map (MapMode.READ_ONLY, 0, fc.size()); gather [2] = filedata; contentLength = fc.size(); contentType = URLConnection.guessContentTypeFromName (file); } catch (IOException e) { // file could not be opened; report problem ByteBuffer buf = ByteBuffer.allocate (128); String msg = MSG_404 + e + LINE_SEP; buf.put (bytes (msg)); buf.flip(); // Use the HTTP error response gather [0] = ByteBuffer.wrap (bytes (HTTP_404_HDR)); gather [2] = buf; contentLength = msg.length(); contentType = "text/plain"; } StringBuffer sb = new StringBuffer(); sb.append ("Content-Length: " + contentLength); sb.append (LINE_SEP); sb.append ("Content-Type: ").append (contentType); sb.append (LINE_SEP).append (LINE_SEP); dynhdrs.put (bytes (sb.toString())); dynhdrs.flip(); 95
Note: If you are looking for inexpensive but high quality provider to host and run your jsp application check Astra jsp hosting services

created with this mode, unless the buffer has

Monday, October 30th, 2006

// This is a partial API listing public final MappedByteBuffer load() public final boolean isLoaded() public final MappedByteBuffer force() } When a virtual memory mapping is established to a file, it does not usually (depending on the operating system) cause any of the file data to be read in from disk. It’s like opening a file: the file is located and a handle is established through which you can access the data when you’re ready. For mapped buffers, the virtual memory system will cause chunks of the file to be brought in, on demand, as you touch them. This page validation, or faulting-in, takes time because one or more disk accesses are usually required to bring the data into memory. In some scenarios, you may want to bring all the pages into memory first to minimize buffer-access latency. If all the pages of the file are memory-resident, access speed will be identical to a memory-based buffer. The load() method will attempt to touch the entire file so that all of it is memory-resident. As discussed in Chapter 1, a memory-mapped buffer establishes a virtual memory mapping to a file. This mapping enables the low-level virtual memory subsystem of the operating system to copy chunks of the file into memory on an as-needed basis. The in-memory, or validated, pages consume real memory and can squeeze out other, less recently used memory pages as they are brought into RAM. Calling load() on a mapped buffer can be an expensive operation because it can generate a large number of page-ins, depending on the size of the mapped area of the file. There is, however, no guarantee that the file will be fully memory-resident upon return from load() because of the dynamic nature of demand paging. Results will vary by operating system, filesystem, available JVM memory, maximum JVM memory, file size relative to JVM and system memory, garbage-collector implementation, etc. Use load() with care; it may not yield the result you’re hoping for. Its primary use is to pay the penalty of loading a file up front, so subsequent accesses are as fast as possible. For applications in which near-realtime access is required, preloading is the way to go. But remember that there is no guarantee that all those pages will stay in memory, and you may suffer subsequent page-ins anyway. When and how memory pages are stolen is influenced by several factors, many of which are not controlled by the JVM. As of JDK 1.4, NIO does not provide an API for pinning pages in physical memory, although some operating systems support doing so. For most applications, especially interactive or other event-driven applications, it’s not worth paying the penalty upfront. It’s better to amortize the page-in cost across actual accesses. Letting the operating system bring in pages on demand means that untouched pages never need be loaded. This can easily result in less total I/O activity than preloading the mapped file. The operating system has a sophisticated memory-management system in place. Let it do the job for you. 93
Note: If you are looking for reliable and quality webspace company to host and run your servlet application check Actions servlet hosting services

created with this mode, unless the buffer has

Monday, October 30th, 2006

The isLoaded() method can be called to determine if a mapped file is fully memory-resident. If it returns true, odds are that the mapped buffer can be accessed with little or no latency. But again, there is no guarantee. Likewise, a return of false does not necessarily imply that access to the buffer will be slow or that the file isn’t fully memory-resident. This method is a hint; the asynchronous nature of garbage collection, the underlying operating system, and the dynamics of the running system make it impossible to determine the exact state of all the mapped pages at any given point in time. The last method listed above, force(), is similar to the method of the same name in the FileChannel class (see Section 3.3.1). It forces any changes made to the mapped buffer to be flushed out to permanent disk storage. When updating a file through a MappedByteBuffer object, you should always use MappedByteBuffer.force() rather than FileChannel.force(). The channel object may not be aware of all file updates made through the mapped buffer. MappedByteBuffer doesn’t give you the option of not flushing file metadata it’s always flushed too. Note that the same considerations regarding nonlocal filesystems apply here as they do for FileChannel.force(). (See Section 3.3.1.) If the mapping was established with MapMode.READ_ONLY or MAP_MODE.PRIVATE, then calling force() has no effect, since there will never be any changes to flush to disk (but doing so is harmless). Example 3-4 illustrates the case of memory-mapped buffers and scatter/gather. Example 3-4. Composing HTTP replies with mapped files and gathering writes package com.ronsoft.books.nio.channels; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.channels.GatheringByteChannel; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URLConnection; /** * Dummy HTTP server using MappedByteBuffers. * Given a filename on the command line, pretend to be * a web server and generate an HTTP response containing * the file content preceded by appropriate headers. The * data is sent with a gathering write. * * @author Ron Hitchens (ron@ronsoft.com) */ public class MappedHttp{ private static final String OUTPUT_FILE = “MappedHttp.out”; 94
Note: If you are looking for reliable and quality webspace company to host and run your servlet application check Actions servlet hosting services

created with this mode, unless the buffer has

Monday, October 30th, 2006

created with this mode, unless the buffer has already modified the same area of the file. As described in Chapter 1, memory and filesystems are segmented into pages. When put() is invoked on a copy-on-write buffer, the affected page(s) is duplicated, and changes are made to the copy. The specific page size is implementation-dependent but will usually be the same as the underlying filesystem page size. If the buffer has not made changes to a given page, its content will reflect the corresponding location in the mapped file. Once a page has been copied as a result of a write, the copy will be used thereafter, and that page cannot be modified by other buffers or updates to the file. See Example 3-5 for code that illustrates this behavior. You’ll notice that there is no unmap() method. Once established, a mapping remains in effect until the MappedByteBuffer object is garbage collected. Unlike locks, mapped buffers are not tied to the channel that created them. Closing the associated FileChannel does not destroy the mapping; only disposal of the buffer object itself breaks the mapping. The NIO designers made this decision because destroying a mapping when a channel is closed raises security concerns, and solving the security problem would have introduced a performance problem. They recommend using phantom references (see java.lang.ref.PhantomReference) and a cleanup thread if you need to know positively when a mapping has been destroyed. Odds are that this will rarely be necessary. A MemoryMappedBuffer directly reflects the disk file with which it is associated. If the file is structurally modified while the mapping is in effect, strange behavior can result (exact behaviors are, of course, operating system- and filesystem-dependent). A MemoryMappedBuffer has a fixed size, but the file it’s mapped to is elastic. Specifically, if a file’s size changes while the mapping is in effect, some or all of the buffer may become inaccessible, undefined data could be returned, or unchecked exceptions could be thrown. Be careful about how files are manipulated by other threads or external processes when they are memory-mapped. All MappedByteBuffer objects are direct. This means that the memory space they occupy lives outside the JVM heap (and may not be counted in the JVM’s memory footprint, depending on the operating system’s virtual memory model). Because they’re ByteBuffers, MappedByteBuffers can be passed to the read() or write() method of a channel, such as a SocketChannel, to transfer data efficiently to or from the mapped file. When combined with scatter/gather, it becomes easy to compose data from memory buffers and mapped file content. See Example 3-4 for an example of composing HTTP responses this way. An even more efficient way of transferring file data to and from other channels is described in Section 3.4.1. So far, we’ve been discussing mapped buffers as if they were just like other buffers, which is how you would use them most of the time. But MappedByteBuffer also defines a few unique methods of its own: public abstract class MappedByteBuffer extends ByteBuffer { 92
Note: If you are looking for reliable and quality webspace company to host and run your servlet application check Actions servlet hosting services

// “indexBuffer” is an int view of “buffer”

Monday, October 30th, 2006

buffer = fileChannel.map (FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); Unlike ranges for file locks, mapped file ranges should not extend beyond the actual size of the file. If you request a mapping larger than the file, the file will be made larger to match the size of the mapping you request. If you pass Integer.MAX_VALUE for the size parameter, your file size would balloon to more than 2.1 gigabytes. The map() method will try to do this even if you request a read-only mapping but will throw an IOException in most cases because the underlying file cannot be modified. This behavior is consistent with the behavior of file holes discussed earlier. See Section 3.3.1 for details. The FileChannel class defines constants to represent the mapping modes and uses the convention of a type-safe enumeration rather than numeric values to define these constants. The constants are static fields of an inner class defined inside FileChannel. Being object references, they can be type-checked at compile time, but you use them as you would a numeric constant. Like conventional file handles, file mappings can be writable or read-only. The first two mapping modes, MapMode.READ_ONLY and MapMode.READ_WRITE, are fairly obvious. They indicate whether you want the mapping to be read-only or to allow modification of the mapped file. The requested mapping mode will be constrained by the access permissions of the FileChannel object on which map() is called. If the channel was opened as read-only, map() will throw a NonWritableChannelException if you ask for MapMode.READ_WRITE mode. NonReadableChannelException will be thrown if you request MapMode.READ_ONLY on a channel without read permission. It is permissible to request a MapMode.READ_ONLY mapping on a channel opened for read/write. The mutability of a MappedByteBuffer object can be checked by invoking isReadOnly() on it. The third mode, MapMode.PRIVATE, indicates that you want a copy-on-write mapping. This means that any modifications you make via put() will result in a private copy of the data that only the MappedByteBuffer instance can see. No changes will be made to the underlying file, and any changes made will be lost when the buffer is garbage collected. Even though a copy-on-write mapping prevents any changes to the underlying file, you must have opened the file for read/write to set up a MapMode.PRIVATE mapping. This is necessary for the returned MappedByteBuffer object to allow put()s. Copy-on-write is a technique commonly used by operating systems to manage virtual address spaces when one process spawns another. Using copy-on-write allows the parent and child processes to share memory pages until one of them actually makes changes. The same advantages can accrue for multiple mappings of the same file (depending on underlying operating-system support, of course). If a large file is mapped by several MappedByteBuffer objects, each using MapMode.PRIVATE, then most of the file can be shared among all mappings. Choosing the MapMode.PRIVATE mode does not insulate your buffer from changes made to the file by other means. Changes made to an area of the file will be reflected in a buffer 91

Note: If you are looking for good and affordable webspace to host and run your servlet application check Virtualwebstudio servlet hosting services

// “indexBuffer” is an int view of “buffer”

Monday, October 30th, 2006

// “indexBuffer” is an int view of “buffer” indexBuffer.clear(); for (int i = 0; i < INDEX_COUNT; i++) { idxval++; println ("Updating index " + i + "=" + idxval); indexBuffer.put (idxval); // Pretend that this is really hard work Thread.sleep (500); } // leaves position and limit correct for whole buffer buffer.clear(); fc.write (buffer, INDEX_START); } // ---------------------------------------------------------------- private int lastLineLen = 0; // Specialized println that repaints the current line private void println (String msg) { System.out.print ("r "); System.out.print (msg); for (int i = msg.length(); i < lastLineLen; i++) { System.out.print (" "); } System.out.print ("r"); System.out.flush(); lastLineLen = msg.length(); } } This code blithely ignores the advice I gave about using try/catch/finally to release locks. It's demo code; don't be so lazy in your real code. 3.4 Memory-Mapped Files The new FileChannel class provides a method, map(), that establishes a virtual memory mapping between an open file and a special type of ByteBuffer. (Memory-mapped files and how they interact with virtual memory were summarized in Chapter 1.) Calling map() on a FileChannel creates a virtual memory mapping backed by a disk file and wraps a MappedByteBuffer object around that virtual memory space. (See Figure 1-6.) The MappedByteBuffer object returned from map() behaves like a memory-based buffer in most respects, but its data elements are stored in a file on disk. Calling get() will fetch data from the disk file, and this data reflects the current content of the file, even if the file has been modified by an external process since the mapping was established. The data 89

Note: If you are looking for good and affordable webspace to host and run your servlet application check Virtualwebstudio servlet hosting services

// “indexBuffer” is an int view of “buffer”

Monday, October 30th, 2006

visible through a file mapping is exactly the same as you would see by reading the file conventionally. Likewise, doing a put() to the mapped buffer will update the file on disk (assuming you have write permission), and your changes will be visible to other readers of the file. Accessing a file through the memory-mapping mechanism can be far more efficient than reading or writing data by conventional means, even when using channels. No explicit system calls need to be made, which can be time-consuming. More importantly, the virtual memory system of the operating system automatically caches memory pages. These pages will be cached using system memory and will not consume space from the JVM’s memory heap. Once a memory page has been made valid (brought in from disk), it can be accessed again at full hardware speed without the need to make another system call to get the data. Large, structured files that contain indexes or other sections that are referenced or updated frequently can benefit tremendously from memory mapping. When combined with file locking to protect critical sections and control transactional atomicity, you begin to see how memory mapped buffers can be put to good use. Let’s take a look at how to use memory mapping: public abstract class FileChannel extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel { // This is a partial API listing public abstract MappedByteBuffer map (MapMode mode, long position, long size) public static class MapMode { public static final MapMode READ_ONLY public static final MapMode READ_WRITE public static final MapMode PRIVATE } } As you can see, there is only one map() method to establish a file mapping. It takes a mode, a position and a size. The position and size arguments are the same as lock()’s (discussed in the previous section). It’s possible to create a MappedByteBuffer that represents a subrange of the bytes in a file. For example, to map bytes 100 through 299 (inclusive), do the following: buffer = fileChannel.map (FileChannel.MapMode.READ_ONLY, 100, 200); To map an entire file: 90

Note: If you are looking for good and affordable webspace to host and run your servlet application check Virtualwebstudio servlet hosting services

A FileLock object is valid when created and

Sunday, October 29th, 2006

// holding a shared lock on the index area void doQueries (FileChannel fc) throws Exception { while (true) { println (”trying for shared lock…”); FileLock lock = fc.lock (INDEX_START, INDEX_SIZE, true); int reps = rand.nextInt (60) + 20; for (int i= 0; i < reps; i++) { int n = rand.nextInt (INDEX_COUNT); int position = INDEX_START + (n * SIZEOF_INT); buffer.clear(); fc.read (buffer, position); int value = indexBuffer.get (n); println ("Index entry " + n + "=" + value); // Pretend to be doing some work Thread.sleep (100); } lock.release(); println ("“); Thread.sleep (rand.nextInt (3000) + 500); } } // Simulate a series of updates to the index area // while holding an exclusive lock void doUpdates (FileChannel fc) throws Exception { while (true) { println (”trying for exclusive lock…”); FileLock lock = fc.lock (INDEX_START, INDEX_SIZE, false); updateIndex (fc); lock.release(); println (”“); Thread.sleep (rand.nextInt (2000) + 500); } } // Write new values to the index slots private int idxval = 1; private void updateIndex (FileChannel fc) throws Exception { 88
Note: If you are looking for best hosting provider to host and run your tomcat application check Astra tomcat hosting services

A FileLock object is valid when created and

Sunday, October 29th, 2006

A FileLock object is valid when created and remains so until its release() method is called, the channel it’s associated with is closed, or the JVM shuts down. The validity of a lock can be tested by invoking its isValid() boolean method. A lock’s validity may change over time, but its other properties position, size, and exclusivity are set at creation time and are immutable. You can test a lock to determine if it is shared or exclusive by invoking isShared(). If shared locks are not supported by the underlying operating system or filesystem, this method will always return false, even if you passed true when requesting the lock. If your application depends on shared-locking behavior, test the returned lock to be sure you got the type you requested. FileLock objects are thread-safe; multiple threads may access a lock object concurrently. Finally, a FileLock object can be queried to determine if it overlaps a given file region by calling its overlaps() method. This will let you quickly determine if a lock you hold intersects with a region of interest. A return of false does not guarantee that you can obtain a lock on the desired region. One or more locks may be held elsewhere in the JVM or by external processes. Use tryLock() to be sure. Although a FileLock object is associated with a specific FileChannel instance, the lock it represents is associated with an underlying file, not the channel. This can cause conflicts, or possibly deadlocks, if you don’t release a lock when you’re finished with it. Carefully manage file locks to avoid such problems. Once you’ve successfully obtained a file lock, be sure to release it if subsequent errors occur on the channel. A code pattern similar to the following is recommended: FileLock lock = fileChannel.lock() try {
} catch (IOException) [ } finally { lock.release() } The code in Example 3-3 implements reader processes using shared locks and writers using exclusive locks, as illustrated in Figures Figure 1-7 and Figure 1-8. Because locks are associated with processes and not with Java threads, you will need to run multiple copies of this program. Start one writer and two or more readers to see how the different types of locks interact with each other. Example 3-3. Shared-and exclusive-lock interaction package com.ronsoft.books.nio.channels; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.FileChannel; 86
Note: If you are looking for best hosting provider to host and run your tomcat application check Astra tomcat hosting services

A FileLock object is valid when created and

Sunday, October 29th, 2006

import java.nio.channels.FileLock; import java.io.RandomAccessFile; import java.util.Random; /** * Test locking with FileChannel. * Run one copy of this code with arguments “-w /tmp/locktest.dat” * and one or more copies with “-r /tmp/locktest.dat” to see the * interactions of exclusive and shared locks. Note how too many * readers can starve out the writer. * Note: The filename you provide will be overwritten. Substitute * an appropriate temp filename for your favorite OS. * * Created April, 2002 * @author Ron Hitchens (ron@ronsoft.com) */ public class LockTest{ private static final int SIZEOF_INT = 4; private static final int INDEX_START = 0; private static final int INDEX_COUNT = 10; private static final int INDEX_SIZE = INDEX_COUNT * SIZEOF_INT; private ByteBuffer buffer = ByteBuffer.allocate (INDEX_SIZE); private IntBuffer indexBuffer = buffer.asIntBuffer(); private Random rand = new Random(); public static void main (String [] argv) throws Exception { boolean writer = false; String filename; if (argv.length != 2) { System.out.println (”Usage: [ -r | -w ] filename”); return; } writer = argv [0].equals (”-w”); filename = argv [1]; RandomAccessFile raf = new RandomAccessFile (filename, (writer) ? “rw” : “r”); FileChannel fc = raf.getChannel(); LockTest lockTest = new LockTest(); if (writer) { lockTest.doUpdates (fc); } else { lockTest.doQueries (fc); } } // —————————————————————- // Simulate a series of read-only queries while 87
Note: If you are looking for best hosting provider to host and run your tomcat application check Astra tomcat hosting services

If the file resides on a local filesystem,

Sunday, October 29th, 2006

If the file resides on a local filesystem, then upon returning from force(), it’s guaranteed that all modifications to the file since the channel was created (or the last call to force()) have been written to disk. This is important for critical operations, such as transaction processing, to insure data integrity and reliable recovery. However, this guarantee of synchronization to permanent storage cannot be made if the file resides on a remote filesystem, such as NFS. The same may be true for other filesystems, depending on implementation. The JVM can’t make promises the operating system or filesystem won’t keep. If your application must maintain data integrity in the face of system failures, verify that the operating system and/or filesystem you’re using are dependable in that regard. For applications in which confidence in data integrity are essential, verify the capabilities of the operating environment on which you plan to deploy. The boolean argument to force() indicates whether metadata about the file should also be synchronized to disk before returning. Metadata represents things such as file ownership, access permissions, last modification time, etc. In most cases, this information is not critical for data recovery. Passing false to force() indicates that only the file data need be synchronized before returning. In most cases, synchronizing the metadata will require at least one additional low-level I/O operation by the operating system. Some high-volume transactional applications may gain a moderate performance increase without sacrificing data integrity by not requiring metadata updates on each call to force(). 3.3.2 File Locking Until 1.4, a feature sorely lacking from the Java I/O model was file locking. While most modern operating systems have long had file-locking capabilities of one form or another, file locks have not been available to Java programmers until the JDK 1.4 release. File locking is essential for integration with many non-Java applications. It can also be valuable for arbitrating access among multiple Java components of a large system. As discussed in Chapter 1, locks can be shared or exclusive. The file-locking features described in this section depend heavily on the native operating-system implementation. Not all operating systems and filesystems support shared file locks. For those that don’t, a request for a shared lock will be silently promoted to an exclusive-lock request. This guarantees correctness but may impact performance considerably. For example, employing only exclusive locks would serialize all the reader processes in Figure 1-7. Be sure you understand file-locking behavior on the operating system and filesystem(s) on which you plan to deploy; it could seriously affect your design choices. Additionally, not all platforms implement basic file locking in the same way. File-locking semantics may vary between operating systems and even different filesystems on the same operating system. Some operating systems provide only advisory locking, some only exclusive locks, and some may provide both. You should always manage file locks as if they were advisory, which is the safest approach. But it’s also wise to be aware of 83
Note: If you are looking for cheapest and affordable webspace to host and run your servlet application check Astra servlet hosting services