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 FileChannel channel;

    private long length = 0;

    private Closer closer = new Closer();

    private File file;

    private int fd;

    public File getFile() {
        return file;
    }

    public int getFd() {
        return fd;
    }

    public boolean isClosed() {
        return closer.isClosed();
    }

    @Override
    public String toString() {
        return file.toString();
    }

    private static final Logger log = Logger.getLogger(MappedFileReader.class);

    private FileInputStream in;

    private MappedByteBuffer mappedByteBuffer = null;

    public MappedFileReader(String path) throws IOException {
        this(new File(path));
    }

    public MappedFileReader(File file) throws IOException {

        init(file);

    }

    private void init(File file) throws IOException {

        this.file = file;

        this.in = new FileInputStream(file);
        this.channel = in.getChannel();
        this.fd = Native.getFd(in.getFD());

        this.length = file.length();

    }

    /**
     * Read from this mapped file.
     */
    public MappedByteBuffer map(boolean setAutoLock) throws IOException {

        try {

            if(mappedByteBuffer == null) {

                if(setAutoLock) {
                    closer.add(new MemLock(file, in.getFD(), 0, length));
                }

                mappedByteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);

                closer.add(new MappedByteBufferCloser(mappedByteBuffer));

            }

            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.add(channel);
        closer.add(in);

        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();

        }

    }

}

@@ -465,72 +465,8 @@
  %7B%0A%0A
-    private FileChannel channel;%0A%0A    private long length = 0;%0A%0A
     
@@ -532,29 +532,8 @@
 e;%0A%0A
-    private int fd;%0A%0A
     
@@ -588,59 +588,8 @@
  %7D%0A%0A
-    public int getFd() %7B%0A        return fd;%0A    %7D%0A%0A
     
@@ -826,41 +826,8 @@
 );%0A%0A
-    private FileInputStream in;%0A%0A
     
@@ -917,35 +917,16 @@
 ng path)
- throws IOException
  %7B%0A     
@@ -1000,50 +1000,35 @@
 le) 
-throws IOException %7B%0A%0A        init(
+%7B%0A        this.file = 
 file
-);%0A
+;
 %0A   
@@ -1039,302 +1039,247 @@
     
-private void init(File file) throws IOException %7B%0A%0A        this.file = file;%0A%0A        this.in = new FileInputStream(file);%0A        this.channel = in.getChannel();%0A        this.fd = Native.getFd(in.getFD());%0A%0A        this.length = file.length();%0A%0A    %7D%0A%0A    /**%0A     * Read from this mapped file
+/**%0A     * Return a new MappedByteBuffer which can be used to read%0A     * data from the file.  If setAutoLock is true, attempt to%0A     * lock the file's data in memory.  Calling map() multiple%0A     * times returns the same MappedByteBuffer
 .%0A  
@@ -1351,32 +1351,51 @@
  IOException %7B%0A%0A
+%09long length = -1;%0A
         try %7B%0A%0A 
@@ -1433,24 +1433,416 @@
 == null) %7B%0A%0A
+%09%09/* Sample the file length just before we mmap() the%0A%09%09 * file, as it may have changed size since the ctor. We%0A%09%09 * do need to remember the exact length we mmap()ed for%0A%09%09 * later when we unmap() to avoid leaking mappings, but%0A%09%09 * MappedByteBuffer does that for us.%0A%09%09 */%0A%09%09length = file.length();%0A%09%09FileInputStream in = new FileInputStream(file);%0A%09%09FileChannel channel = in.getChannel();%0A%0A
             
@@ -2118,24 +2118,191 @@
 eBuffer));%0A%0A
+%09%09/* On Unix-like systems we don't need to keep the file%0A%09%09 * descriptor around after we mmap()ed the file.  So%0A%09%09 * close it now. */%0A%09%09channel.close();%0A%09%09in.close();%0A
             
@@ -2853,62 +2853,8 @@
 n;%0A%0A
-        closer.add(channel);%0A        closer.add(in);%0A%0A
     

Simplify MappedFileReader, close Unix fd early