View Javadoc

1   /**
2    * JDBM LICENSE v1.00
3    *
4    * Redistribution and use of this software and associated documentation
5    * ("Software"), with or without modification, are permitted provided
6    * that the following conditions are met:
7    *
8    * 1. Redistributions of source code must retain copyright
9    *    statements and notices.  Redistributions must also contain a
10   *    copy of this document.
11   *
12   * 2. Redistributions in binary form must reproduce the
13   *    above copyright notice, this list of conditions and the
14   *    following disclaimer in the documentation and/or other
15   *    materials provided with the distribution.
16   *
17   * 3. The name "JDBM" must not be used to endorse or promote
18   *    products derived from this Software without prior written
19   *    permission of Cees de Groot.  For written permission,
20   *    please contact cg@cdegroot.com.
21   *
22   * 4. Products derived from this Software may not be called "JDBM"
23   *    nor may "JDBM" appear in their names without prior written
24   *    permission of Cees de Groot. 
25   *
26   * 5. Due credit should be given to the JDBM Project
27   *    (http://jdbm.sourceforge.net/).
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
30   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
31   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
33   * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
34   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
36   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
38   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
40   * OF THE POSSIBILITY OF SUCH DAMAGE.
41   *
42   * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
43   * Contributions are Copyright (C) 2000 by their associated contributors.
44   *
45   * $Id: PageManager.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
46   */
47  
48  package jdbm.recman;
49  
50  import java.io.*;
51  
52  /**
53   *  This class manages the linked lists of pages that make up a file.
54   */
55  final class PageManager {
56      // our record file
57      private RecordFile file;
58      // header data
59      private FileHeader header;
60      private BlockIo headerBuf;
61      
62      /**
63       *  Creates a new page manager using the indicated record file.
64       */
65      PageManager(RecordFile file) throws IOException {
66          this.file = file;
67          
68          // check the file header. If the magic is 0, we assume a new
69          // file. Note that we hold on to the file header node.
70          headerBuf = file.get(0);
71          if (headerBuf.readShort(0) == 0)
72              header = new FileHeader(headerBuf, true);
73          else
74              header = new FileHeader(headerBuf, false);
75      }
76      
77      /**
78       *  Allocates a page of the indicated type. Returns recid of the
79       *  page.
80       */
81      long allocate(short type) throws IOException {
82          
83          if (type == Magic.FREE_PAGE)
84              throw new Error("allocate of free page?");
85          
86          // do we have something on the free list?
87          long retval = header.getFirstOf(Magic.FREE_PAGE);
88          boolean isNew = false;
89          if (retval != 0) {
90              // yes. Point to it and make the next of that page the
91              // new first free page.
92              header.setFirstOf(Magic.FREE_PAGE, getNext(retval));
93          }
94          else {
95              // nope. make a new record
96              retval = header.getLastOf(Magic.FREE_PAGE);
97              if (retval == 0)
98                  // very new file - allocate record #1
99                  retval = 1;
100             header.setLastOf(Magic.FREE_PAGE, retval + 1);
101             isNew = true;
102         }
103         
104         // Cool. We have a record, add it to the correct list
105         BlockIo buf = file.get(retval);
106         PageHeader pageHdr = isNew ? new PageHeader(buf, type) 
107             : PageHeader.getView(buf);
108         long oldLast = header.getLastOf(type);
109         
110         // Clean data.
111         System.arraycopy(RecordFile.cleanData, 0, 
112                          buf.getData(), 0, 
113                          RecordFile.BLOCK_SIZE);
114         pageHdr.setType(type);
115         pageHdr.setPrev(oldLast);
116         pageHdr.setNext(0);
117         
118         
119         if (oldLast == 0)
120             // This was the first one of this type
121             header.setFirstOf(type, retval);
122         header.setLastOf(type, retval);
123         file.release(retval, true);
124         
125         // If there's a previous, fix up its pointer
126         if (oldLast != 0) {
127             buf = file.get(oldLast);
128             pageHdr = PageHeader.getView(buf);
129             pageHdr.setNext(retval);
130             file.release(oldLast, true);
131         }
132         
133         // remove the view, we have modified the type.
134         buf.setView(null);
135         
136         return retval;
137     }
138     
139     /**
140      *  Frees a page of the indicated type.
141      */
142     void free(short type, long recid) throws IOException {
143         if (type == Magic.FREE_PAGE)
144             throw new Error("free free page?");
145         if (recid == 0)
146             throw new Error("free header page?");
147         
148         // get the page and read next and previous pointers
149         BlockIo buf = file.get(recid);
150         PageHeader pageHdr = PageHeader.getView(buf);
151         long prev = pageHdr.getPrev();
152         long next = pageHdr.getNext();
153         
154         // put the page at the front of the free list.
155         pageHdr.setType(Magic.FREE_PAGE);
156         pageHdr.setNext(header.getFirstOf(Magic.FREE_PAGE));
157         pageHdr.setPrev(0);
158         
159         header.setFirstOf(Magic.FREE_PAGE, recid);
160         file.release(recid, true);
161         
162         // remove the page from its old list
163         if (prev != 0) {
164             buf = file.get(prev);
165             pageHdr = PageHeader.getView(buf);
166             pageHdr.setNext(next);
167             file.release(prev, true);
168         }
169         else {
170             header.setFirstOf(type, next);
171         }
172         if (next != 0) {
173             buf = file.get(next);
174             pageHdr = PageHeader.getView(buf);
175             pageHdr.setPrev(prev);
176             file.release(next, true);
177         }
178         else {
179             header.setLastOf(type, prev);
180         }
181         
182     }
183     
184     
185     /**
186      *  Returns the page following the indicated block
187      */
188     long getNext(long block) throws IOException {
189         try {
190             return PageHeader.getView(file.get(block)).getNext();
191         } finally {
192             file.release(block, false);
193         }
194     }
195     
196     /**
197      *  Returns the page before the indicated block
198      */
199     long getPrev(long block) throws IOException {
200         try {
201             return PageHeader.getView(file.get(block)).getPrev();
202         } finally {
203             file.release(block, false);
204         }
205     }
206     
207     /**
208      *  Returns the first page on the indicated list.
209      */
210     long getFirst(short type) throws IOException {
211         return header.getFirstOf(type);
212     }
213 
214     /**
215      *  Returns the last page on the indicated list.
216      */
217     long getLast(short type) throws IOException {
218         return header.getLastOf(type);
219     }
220     
221     
222     /**
223      *  Commit all pending (in-memory) data by flushing the page manager.
224      *  This forces a flush of all outstanding blocks (this it's an implicit
225      *  {@link RecordFile#commit} as well).
226      */
227     void commit() throws IOException {
228         // write the header out
229         file.release(headerBuf);
230         file.commit();
231 
232         // and obtain it again
233         headerBuf = file.get(0);
234         header = new FileHeader(headerBuf, false);
235     }
236 
237     /**
238      *  Flushes the page manager. This forces a flush of all outstanding
239      *  blocks (this it's an implicit {@link RecordFile#commit} as well).
240      */
241     void rollback() throws IOException {
242         // release header
243         file.discard(headerBuf);
244         file.rollback();
245         // and obtain it again
246         headerBuf = file.get(0);
247         if (headerBuf.readShort(0) == 0)
248             header = new FileHeader(headerBuf, true);
249         else
250             header = new FileHeader(headerBuf, false);
251     }
252     
253     /**
254      *  Closes the page manager. This flushes the page manager and releases
255      *  the lock on the header.
256      */
257     void close() throws IOException {   
258         file.release(headerBuf);
259         file.commit();
260         headerBuf = null;
261         header = null;
262         file = null;
263     }
264     
265     /**
266      *  Returns the file header.
267      */
268     FileHeader getFileHeader() {
269         return header;
270     }
271     
272 }