package voldemort.store.readonly.io; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import org.apache.log4j.Logger; /** * Facade around a MappedByteBuffer but we also support mlock on the mapped * pages, and closing all dependent resources. * */ public class MappedFileReader implements Closeable { private Closer closer = new Closer(); private File file; public File getFile() { return file; } public boolean isClosed() { return closer.isClosed(); } @Override public String toString() { return file.toString(); } private static final Logger log = Logger.getLogger(MappedFileReader.class); private MappedByteBuffer mappedByteBuffer = null; public MappedFileReader(File file) { this.file = file; } /** * Return a new MappedByteBuffer which can be used to read * data from the file. If setAutoLock is true, attempt to * lock the file's data in memory. Calling map() multiple * times returns the same MappedByteBuffer. */ public MappedByteBuffer map() throws IOException { long length = -1; try { if(mappedByteBuffer == null) { /* Sample the file length just before we mmap() the * file, as it may have changed size since the ctor. We * do need to remember the exact length we mmap()ed for * later when we unmap() to avoid leaking mappings, but * MappedByteBuffer does that for us. */ length = file.length(); FileInputStream in = new FileInputStream(file); FileChannel channel = in.getChannel(); mappedByteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length); closer.add(new MappedByteBufferCloser(mappedByteBuffer)); /* On Unix-like systems we don't need to keep the file * descriptor around after we mmap()ed the file. So * close it now. */ channel.close(); in.close(); } return mappedByteBuffer; } catch(IOException e) { log.error(String.format("Failed to map %s of length %,d", file.getPath(), length), e); throw new IOException(String.format("Failed to map %s of length %,d", file.getPath(), length), e); } } @Override public void close() throws IOException { if(closer.isClosed()) return; closer.close(); } /** * A closeable which is smart enough to work on mapped byte buffers. */ class MappedByteBufferCloser extends ByteBufferCloser { public MappedByteBufferCloser(ByteBuffer buff) { super(buff); } @Override public void close() throws IOException { super.close(); } } }