View Javadoc

1   package simpledb.file;
2   
3   import static simpledb.file.Page.BLOCK_SIZE;
4   import java.io.*;
5   import java.nio.ByteBuffer;
6   import java.nio.channels.FileChannel;
7   import java.util.*;
8   
9   /**
10   * The SimpleDB file manager.
11   * The database system stores its data as files within a specified directory.
12   * The file manager provides methods for reading the contents of
13   * a file block to a Java byte buffer,
14   * writing the contents of a byte buffer to a file block,
15   * and appending the contents of a byte buffer to the end of a file.
16   * These methods are called exclusively by the class {@link simpledb.file.Page Page},
17   * and are thus package-private.
18   * The class also contains two public methods:
19   * Method {@link #isNew() isNew} is called during system initialization by {@link simpledb.server.SimpleDB#init}.
20   * Method {@link #size(String) size} is called by the log manager and transaction manager to
21   * determine the end of the file.
22   * @author Edward Sciore
23   */
24  public class FileMgr {
25     private File dbDirectory;
26     private boolean isNew;
27     private Map<String,FileChannel> openFiles = new HashMap<String,FileChannel>();
28  
29     /**
30      * Creates a file manager for the specified database.
31      * The database will be stored in a folder of that name
32      * in the user's home directory.
33      * If the folder does not exist, then a folder containing
34      * an empty database is created automatically.
35      * Files for all temporary tables (i.e. tables beginning with "temp") are deleted.
36      * @param dbname the name of the directory that holds the database
37      */
38     public FileMgr(String dbname) {
39        String homedir = System.getProperty("user.home");
40        dbDirectory = new File(homedir, dbname);
41        isNew = !dbDirectory.exists();
42  
43        // create the directory if the database is new
44        if (isNew && !dbDirectory.mkdir())
45           throw new RuntimeException("cannot create " + dbname);
46  
47        // remove any leftover temporary tables
48        for (String filename : dbDirectory.list())
49           if (filename.startsWith("temp"))
50           new File(dbDirectory, filename).delete();
51     }
52  
53     /**
54      * Reads the contents of a disk block into a bytebuffer.
55      * @param blk a reference to a disk block
56      * @param bb  the bytebuffer
57      */
58     synchronized void read(Block blk, ByteBuffer bb) {
59        try {
60           bb.clear();
61           FileChannel fc = getFile(blk.fileName());
62           fc.read(bb, blk.number() * BLOCK_SIZE);
63        }
64        catch (IOException e) {
65           throw new RuntimeException("cannot read block " + blk);
66        }
67     }
68  
69     /**
70      * Writes the contents of a bytebuffer into a disk block.
71      * @param blk a reference to a disk block
72      * @param bb  the bytebuffer
73      */
74     synchronized void write(Block blk, ByteBuffer bb) {
75        try {
76           bb.rewind();
77           FileChannel fc = getFile(blk.fileName());
78           fc.write(bb, blk.number() * BLOCK_SIZE);
79        }
80        catch (IOException e) {
81           throw new RuntimeException("cannot write block" + blk);
82        }
83     }
84  
85     /**
86      * Appends the contents of a bytebuffer to the end
87      * of the specified file.
88      * @param filename the name of the file
89      * @param bb  the bytebuffer
90      * @return a reference to the newly-created block.
91      */
92     synchronized Block append(String filename, ByteBuffer bb) {
93        int newblknum = size(filename);
94        Block blk = new Block(filename, newblknum);
95        write(blk, bb);
96        return blk;
97     }
98  
99     /**
100     * Returns the number of blocks in the specified file.
101     * @param filename the name of the file
102     * @return the number of blocks in the file
103     */
104    public synchronized int size(String filename) {
105       try {
106          FileChannel fc = getFile(filename);
107          return (int)(fc.size() / BLOCK_SIZE);
108       }
109       catch (IOException e) {
110          throw new RuntimeException("cannot access " + filename);
111       }
112    }
113 
114    /**
115     * Returns a boolean indicating whether the file manager
116     * had to create a new database directory.
117     * @return true if the database is new
118     */
119    public boolean isNew() {
120       return isNew;
121    }
122 
123    /**
124     * Returns the file channel for the specified filename.
125     * The file channel is stored in a map keyed on the filename.
126     * If the file is not open, then it is opened and the file channel
127     * is added to the map.
128     * @param filename the specified filename
129     * @return the file channel associated with the open file.
130     * @throws IOException
131     */
132    private FileChannel getFile(String filename) throws IOException {
133       FileChannel fc = openFiles.get(filename);
134       if (fc == null) {
135          File dbTable = new File(dbDirectory, filename);
136          RandomAccessFile f = new RandomAccessFile(dbTable, "rws");
137          fc = f.getChannel();
138          openFiles.put(filename, fc);
139       }
140       return fc;
141    }
142 }