KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > quadcap > sql > file > Datafile


1 package com.quadcap.sql.file;
2
3 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
4  *
5  * This software is distributed under the Quadcap Free Software License.
6  * This software may be used or modified for any purpose, personal or
7  * commercial. Open Source redistributions are permitted. Commercial
8  * redistribution of larger works derived from, or works which bundle
9  * this software requires a "Commercial Redistribution License"; see
10  * http://www.quadcap.com/purchase.
11  *
12  * Redistributions qualify as "Open Source" under one of the following terms:
13  *
14  * Redistributions are made at no charge beyond the reasonable cost of
15  * materials and delivery.
16  *
17  * Redistributions are accompanied by a copy of the Source Code or by an
18  * irrevocable offer to provide a copy of the Source Code for up to three
19  * years at the cost of materials and delivery. Such redistributions
20  * must allow further use, modification, and redistribution of the Source
21  * Code under substantially the same terms as this license.
22  *
23  * Redistributions of source code must retain the copyright notices as they
24  * appear in each source code file, these license terms, and the
25  * disclaimer/limitation of liability set forth as paragraph 6 below.
26  *
27  * Redistributions in binary form must reproduce this Copyright Notice,
28  * these license terms, and the disclaimer/limitation of liability set
29  * forth as paragraph 6 below, in the documentation and/or other materials
30  * provided with the distribution.
31  *
32  * The Software is provided on an "AS IS" basis. No warranty is
33  * provided that the Software is free of defects, or fit for a
34  * particular purpose.
35  *
36  * Limitation of Liability. Quadcap Software shall not be liable
37  * for any damages suffered by the Licensee or any third party resulting
38  * from use of the Software.
39  */

40
41 import java.io.ByteArrayOutputStream JavaDoc;
42 import java.io.BufferedInputStream JavaDoc;
43 import java.io.File JavaDoc;
44 import java.io.FileOutputStream JavaDoc;
45 import java.io.IOException JavaDoc;
46 import java.io.InputStream JavaDoc;
47 import java.io.OutputStream JavaDoc;
48 import java.io.RandomAccessFile JavaDoc;
49
50 //#ifdef JDK14
51
import java.nio.channels.FileChannel JavaDoc;
52 import java.nio.channels.FileLock JavaDoc;
53 //#endif
54

55 import java.util.HashMap JavaDoc;
56 import java.util.Iterator JavaDoc;
57 import java.util.Properties JavaDoc;
58
59 import com.quadcap.sql.lock.Lock;
60 import com.quadcap.sql.lock.LockManager;
61 import com.quadcap.sql.lock.LockMode;
62 import com.quadcap.sql.lock.Transaction;
63 import com.quadcap.sql.lock.TransactionObserver;
64
65 import com.quadcap.sql.types.Value;
66
67 import com.quadcap.util.collections.LongIterator;
68 import com.quadcap.util.collections.LongMap;
69
70 import com.quadcap.util.ConfigNumber;
71 import com.quadcap.util.ConfigString;
72 import com.quadcap.util.Debug;
73 import com.quadcap.util.Util;
74  
75 /**
76  * This class brings together the various file-level components of the
77  * structured (SQL) database.
78  *
79  * @author Stan Bailes
80  */

81 abstract public class Datafile {
82     protected boolean inMemory = false;
83     String JavaDoc fileName;
84     
85     protected DatafileRoot root;
86     File JavaDoc dbRootDir;
87     File JavaDoc tempDir;
88     String JavaDoc url;
89     protected String JavaDoc origFileName;
90
91     // "datafile"
92
protected BlockFile file;
93
94     // "scratch"
95
protected BlockFile tempFile;
96
97
98     // "log" file
99
Log log;
100
101     // read/write table locks
102
LockManager locks = null;
103
104     // Original connection properties
105
Properties JavaDoc props;
106
107     static {
108         try {
109             Class.forName("com.quadcap.sql.io.Extern");
110         } catch (Throwable JavaDoc t) {}
111     }
112
113     /** True if we've locked the lockfile */
114     private FileChannel JavaDoc lockChannel = null;
115     private FileLock JavaDoc lockLock = null;
116     
117     protected boolean readOnly = false;
118     protected byte[] temp = new byte[256];
119
120     protected Object JavaDoc fileLock = new Object JavaDoc();
121
122     private int tempFileRefCount = 0;
123
124     abstract public void maybeBackup() throws IOException JavaDoc;
125     abstract public void readRoot() throws IOException JavaDoc;
126     abstract public void createRoot(String JavaDoc fileName,
127                                     Properties JavaDoc props) throws IOException JavaDoc;
128     abstract public void flushRoot() throws IOException JavaDoc;
129     abstract public long getNextTransId() throws IOException JavaDoc;
130     abstract public void bootFromRoot(boolean restart) throws IOException JavaDoc;
131
132     /**
133      * Minimum cache size: 256K, with 8K blocks. The minimum is
134      * dependent on various factors, including query type (joins
135      * need more cache, for example) and concurrency level.
136      *
137      * Simple applications which are primarily single threaded will
138      * be able to get by with 32 blocks or so.
139      */

140     static final int MIN_CACHE = 32;
141
142     /**
143      * Reasonable cache size: 4MB (8K blocks). This cache size should
144      * support a moderate load; 8-10 busy threads...
145      */

146     static final int GOOD_CACHE = 256;
147
148     /**
149      * Reasonable scratch cache default
150      */

151     static final int SCRATCH_CACHE_DEFAULT = 64;
152
153     /*{com.quadcap.util.Config-vars.xml-1000}
154      * <config-var>
155      * <config-name>qed.blockSize</config-name>
156      * <config-dflt>8192</config-dflt>
157      * <config-desc>The database block size.</config-desc>
158      * </config-var>
159      */

160     static final int BLOCK_SIZE
161     = ConfigNumber.find("qed.blockSize", Integer.toString(8192)).intValue();
162     int blockSize = BLOCK_SIZE;
163
164     /*{com.quadcap.util.Config-vars.xml-1001}
165      * <config-var>
166      * <config-name>qed.cacheSize</config-name>
167      * <config-dflt>256 blocks</config-dflt>
168      * <config-desc>The database cache size. A certain number of blocks
169      * are cached in an LRU cache; this parameter controls the size
170      * of this cache. 32 is probably a the minimum for a single-threaded
171      * application; more threads increase cache requirements.
172      * </config-desc>
173      * </config-var>
174      */

175     final ConfigNumber cacheSize
176     = ConfigNumber.find("qed.cacheSize",
177             Integer.toString(GOOD_CACHE * BLOCK_SIZE));
178     int CACHE_SIZE =
179     Math.max(MIN_CACHE, cacheSize.intValue() / BLOCK_SIZE);
180
181     /*{com.quadcap.util.Config-vars.xml-1002}
182      * <config-var>
183      * <config-name>qed.scratchCacheSize</config-name>
184      * <config-dflt>32 blocks</config-dflt>
185      * <config-desc>The "temporary" file cache size. A certain number of
186      * blocks in the scratch file (used for temporary tables and
187      * indexes) are cached in an LRU cache; this parameter controls
188      * the size of this cache.
189      * </config-desc>
190      * </config-var>
191      */

192     final ConfigNumber scratchCacheSize
193     = ConfigNumber.find("qed.scratchCacheSize",
194             Integer.toString(SCRATCH_CACHE_DEFAULT * BLOCK_SIZE));
195     int SCRATCH_CACHE_SIZE =
196     Math.max(MIN_CACHE, scratchCacheSize.intValue() / BLOCK_SIZE);
197
198     /*{com.quadcap.util.Config-vars.xml-1003}
199      * <config-var>
200      * <config-name>qed.defaultMode</config-name>
201      * <config-dflt>rw</config-dflt>
202      * <config-desc>The default database 'mode'. Either 'r' or 'rw'
203      * </config-desc>
204      * </config-var>
205      */

206     final ConfigString defaultMode
207     = ConfigString.find("qed.defaultMode", "rw");
208
209     /*{com.quadcap.util.Config-vars.xml-1004}
210      * <config-var>
211      * <config-name>qed.isCaseSensitive</config-name>
212      * <config-dflt>true</config-dflt>
213      * <config-desc>A boolean flag specifying whether string (VARCHAR)
214      * items are case-sensitive.
215      * </config-desc>
216      * </config-var>
217      */

218     static final ConfigString caseSensitive
219     = ConfigString.find("qed.isCaseSensitive", "true");
220     // XXX This should *NOT* be static, but expediency forces it
221
// XXX to be so for now.
222
public static boolean isCaseSensitive =
223     caseSensitive.toString().equalsIgnoreCase("true");
224
225     /**
226      * Construct a new Datafile object, from the specified filename and mode.
227      */

228
229     /*{com.quadcap.sql.Datafile-conn.xml-0}
230      *
231      * <config>
232      */

233
234     /*{com.quadcap.sql.Datafile-conn.xml-999999}
235      *
236      * </config>
237      */

238
239     public Datafile() {
240     }
241
242     public void init(String JavaDoc url, String JavaDoc fileName, Properties JavaDoc props)
243         throws IOException JavaDoc
244     {
245         this.fileName = fileName;
246         this.inMemory =
247             props.getProperty("memoryOnly", "").equalsIgnoreCase("true");
248         boolean nullStore =
249             props.getProperty("cacheOnly", "").equalsIgnoreCase("true");
250         if (inMemory || nullStore) {
251             initMemoryDatabase(url, fileName, props);
252         } else {
253             initFileDatabase(url, fileName, props);
254         }
255     }
256     
257     public void initMemoryDatabase(String JavaDoc url, String JavaDoc fileName, Properties JavaDoc props)
258     throws IOException JavaDoc
259     {
260         this.inMemory = true;
261         this.props = (Properties JavaDoc)props.clone();
262     this.url = url;
263     this.origFileName = fileName;
264         this.readOnly = false;
265
266         String JavaDoc ics = props.getProperty("isCaseSensitive",
267                                       caseSensitive.toString());
268         isCaseSensitive = ics.equalsIgnoreCase("true");
269         Value.isCaseSensitive = isCaseSensitive;
270         String JavaDoc bs = props.getProperty("blockSize");
271         if (bs != null) {
272             blockSize = Integer.parseInt(bs);
273         }
274         String JavaDoc cs = props.getProperty("cacheSize");
275         if (cs != null) {
276             CACHE_SIZE = Integer.parseInt(cs) / blockSize;
277         }
278         this.locks = new LockManager();
279
280         this.file = new BlockFile(fileName, "rw", props,
281                                   blockSize, CACHE_SIZE);
282         this.fileLock = this.file.getLock();
283         createRoot(fileName, props);
284         try {
285             this.log = new Log3();
286         } catch (Throwable JavaDoc t) {
287             throw new DatafileException("Error creating logger", t);
288         }
289         log.init(this, true, props);
290         file.setLog(log);
291
292         //log.restoreBlocks();
293
bootFromRoot(false);
294     }
295
296     public void initFileDatabase(String JavaDoc url, String JavaDoc fileName, Properties JavaDoc props)
297     throws IOException JavaDoc
298     {
299         this.props = (Properties JavaDoc)props.clone();
300     this.url = url;
301     this.origFileName = fileName;
302     fileName = new File JavaDoc(fileName).getAbsolutePath();
303     this.dbRootDir = new File JavaDoc(fileName);
304     boolean newDb = false;
305         /*{com.quadcap.sql.Datafile-conn.xml-10}
306          * <config-var>
307          * <config-name>mode</config-name>
308          * <config-dflt>from Config: qed.defaultMode</config-dflt>
309          * <config-desc>Read-only or read-write access to the database:
310          * <ul>
311          * <li><code><b>rw</b></code>: Read-write (the default)</li>
312          * <li><code><b>r</b></code>:
313          * Read-only, intended for deployment on read-only media
314          * </li>
315          * </ul>
316          * </config-desc>
317          * </config-var>
318          */

319     String JavaDoc mode = props.getProperty("mode", defaultMode.toString());
320     this.readOnly = mode.equalsIgnoreCase("r");
321
322         /*{com.quadcap.sql.Datafile-conn.xml-11}
323          * <config-var>
324          * <config-name>isCaseSensitive</config-name>
325          * <config-dflt>from Config: qed.isCaseSensitive</config-dflt>
326          * <config-desc>A boolean flag specifying whether string (VARCHAR)
327          * items are case-sensitive.
328          * </config-desc>
329          * </config-var>
330          */

331         String JavaDoc ics = props.getProperty("isCaseSensitive",
332                                       caseSensitive.toString());
333         isCaseSensitive = ics.equalsIgnoreCase("true");
334         Value.isCaseSensitive = isCaseSensitive;
335
336         
337         /*{com.quadcap.sql.Datafile-conn.xml-12}
338          * <config-var>
339          * <config-name>create</config-name>
340          * <config-dflt>false</config-dflt>
341          * <config-desc>If <code>"true"</code>, the database will be
342          * created if it doesn't already exist.
343          * </config-desc>
344          * </config-var>
345          */

346         String JavaDoc create = props.getProperty("create", "false");
347     if (!dbRootDir.isDirectory()) {
348         if (readOnly) {
349         throw new IOException JavaDoc("Can't open database: " + fileName);
350         }
351         if (!create.equalsIgnoreCase("true")) {
352         throw new IOException JavaDoc("Can't open database: " + fileName);
353         }
354         newDb = true;
355         if (!dbRootDir.mkdirs()) {
356         throw new IOException JavaDoc("Can't create directory: " + fileName);
357         }
358     }
359
360         /*{com.quadcap.sql.Datafile-conn.xml-13}
361          * <config-var>
362          * <config-name>blockSize</config-name>
363          * <config-dflt>from Config:
364          * qed.blockSize</config-dflt>
365          * <config-desc>The database block size, which must be a
366          * power of two.
367          * Maximum size: 32768, Minimum recommended size: 4096.
368          * </config-desc>
369          * </config-var>
370          */

371         String JavaDoc bs = props.getProperty("blockSize");
372         if (bs != null) {
373             blockSize = Integer.parseInt(bs);
374         }
375
376
377         /*{com.quadcap.sql.Datafile-conn.xml-14}
378          * <config-var>
379          * <config-name>cacheSize</config-name>
380          * <config-dflt>from Config:
381          * qed.cacheSize</config-dflt>
382          * <config-desc>The database cache size. A certain number of blocks
383          * are cached in an LRU cache; this parameter controls the size
384          * of this cache.</config-desc>
385          * </config-var>
386          */

387         String JavaDoc cs = props.getProperty("cacheSize");
388         if (cs != null) {
389             CACHE_SIZE = Integer.parseInt(cs) / blockSize;
390         }
391
392         /*{com.quadcap.sql.Datafile-conn.xml-15}
393          * <config-var>
394          * <config-name>scratchCacheSize</config-name>
395          * <config-dflt>from Config:
396          * qed.scratchCacheSize</config-dflt>
397          * <config-desc>The database scratch cache size, used for caching access
398          * to the database temporary (or "scratch") file.
399          * A certain number of blocks are cached in an LRU cache;
400          * this parameter controls the size
401          * of this cache.</config-desc>
402          * </config-var>
403          */

404         String JavaDoc scs = props.getProperty("scratchCacheSize");
405         if (scs != null) {
406             SCRATCH_CACHE_SIZE = Integer.parseInt(scs) / blockSize;
407         }
408
409         /*{com.quadcap.sql.Datafile-conn.xml-16}
410          * <config-var>
411          * <config-name>useLockFile</config-name>
412          * <config-dflt>true</config-dflt>
413          * <config-desc>A boolean. If <b>true</b>, a lock file will
414          * be used to protect from database corruption which might
415          * result from multiple processes accessing the same database
416          * simultaneously.</config-desc>
417          * </config-var>
418          */

419         String JavaDoc lf = props.getProperty("useLockFile", "true");
420         boolean useLockFile = lf.equalsIgnoreCase("true");
421
422     try {
423         File JavaDoc g = new File JavaDoc(dbRootDir, "datafile");
424         if (!g.exists()) {
425         if (readOnly) {
426             throw new IOException JavaDoc("Can't open database: " + fileName);
427         }
428         newDb = true;
429         }
430         
431         if (!readOnly && useLockFile) {
432                 File JavaDoc k = new File JavaDoc(dbRootDir, "lockfile");
433                 //#ifdef JDK14
434
lockChannel = new RandomAccessFile JavaDoc(k, "rw").getChannel();
435                 lockLock = lockChannel.tryLock();
436                 if (lockLock == null) {
437                     /*{com.quadcap.sql.Datafile-conn.xml-18}
438                      * <config-var>
439                      * <config-name>force</config-name>
440                      * <config-dflt>false</config-dflt>
441                      * <config-desc>QED uses a lockfile to prevent multiple
442                      * JVMs from accessing the database files
443                      * simultaneously, which could lead to database
444                      * corruption. When the JVM exits normally, this
445                      * lockfile is deleted. If the JVM exits abnormally
446                      * the lockfile may be left behind, and subsequent
447                      * attempts to access the database will fail because
448                      * of the old lockfile. If <b>force</b> is
449                      * <code>true</code>, the database connection will
450                      * succeed even if the lockfile is present.
451                      * This option should be used with extreme care,
452                      * since if there really is another process accessing
453                      * the database, corruption may result.</config-desc>
454                      * </config-var>
455                      */

456             String JavaDoc force = props.getProperty("force", "false");
457             if (!force.equalsIgnoreCase("true")) {
458             throw new IOException JavaDoc(
459                 "lockfile already exists. Maybe another " +
460                 "process is accessing the database?");
461             }
462                 }
463                 //#endif
464
}
465             makeTempDirectory(props);
466         this.locks = new LockManager();
467
468         String JavaDoc gFileName = g.getAbsolutePath();
469             this.file = new BlockFile(gFileName, mode, props,
470                                       blockSize, CACHE_SIZE);
471             this.fileLock = this.file.getLock();
472         if (!newDb) {
473                 try {
474                     readRoot();
475                 } catch (IOException JavaDoc ex) {
476                     if (ex.toString().indexOf("magic") > 0) {
477                         throw new IOException JavaDoc("Database Connection Failed: Possible bad authorization");
478                     } else {
479                         throw ex;
480                     }
481                 }
482         } else {
483                 createRoot(fileName, props);
484         }
485         if (!readOnly) {
486                 /*{com.quadcap.sql.Datafile-conn.xml-20}
487                  * <config-var>
488                  * <config-name>logger</config-name>
489                  * <config-dflt>2</config-dflt>
490                  * <config-desc>If no transaction logging is required
491                  * (i.e., no rollback or recovery), then use '0'.
492                  * Otherwise, use '2', (the default)</config-desc>
493                  * </config-var>
494                  */

495                 String JavaDoc ls = props.getProperty("logger");
496                 if (ls == null) ls = "2";
497                 this.log = (Log)(
498                     Class.forName("com.quadcap.sql.file.Log" + ls).
499                     newInstance());
500                 log.init(this, newDb, props);
501         file.setLog(log);
502                 if (!newDb) {
503                     log.restoreBlocks();
504                     file.revert();
505                 }
506         }
507             bootFromRoot(!newDb);
508             if (!newDb) {
509                 log.restart();
510             }
511             if (!readOnly) {
512                 log.start();
513             }
514             log.checkpoint();
515     } catch (IOException JavaDoc e) {
516         if (newDb && !readOnly) deleteHalfBakedDir();
517         throw e;
518     } catch (RuntimeException JavaDoc e) {
519         if (newDb && !readOnly) deleteHalfBakedDir();
520         throw new DatafileException(e);
521     } catch (Exception JavaDoc e) {
522         if (newDb && !readOnly) deleteHalfBakedDir();
523         throw new DatafileException(e);
524     }
525     }
526
527     // i am a factor. but at least I have a name!
528
final private void deleteHalfBakedDir() {
529         new File JavaDoc(dbRootDir, "datafile").delete();
530     new File JavaDoc(dbRootDir, "lockfile").delete();
531     }
532
533     /**
534      * Accessor for my underlying file
535      */

536     public BlockFile getFile() { return file; }
537
538     /**
539      * Return the JDBC URL used to connect to this database.
540      */

541     public String JavaDoc getURL() { return url; }
542
543     /**
544      * Return a File representing the database root directory
545      */

546     File JavaDoc getDbRootDir() { return dbRootDir; }
547
548     /**
549      * Return a File representing the database scratch/temp root directory
550      */

551     File JavaDoc getScratchDir() { return tempDir; }
552
553     /**
554      * Accessor for the datbase's lock manager
555      */

556     public LockManager getLockManager() { return locks; }
557
558     /**
559      * Accessor for file lock
560      */

561     public Object JavaDoc getFileLock() { return fileLock; }
562
563     /**
564      * Is db read only?
565      */

566     public boolean isReadOnly() { return readOnly; }
567
568     /**
569      * Return the specified persistent object from the store
570      * @param ref the block number of the object's root
571      * @return the object
572      * @exception IOException may be thrown
573      */

574     public Object JavaDoc getObject(long ref) throws IOException JavaDoc {
575         return file.getObject(ref);
576     }
577
578
579     /**
580      * Write a new object to the store and return its reference
581      * @param obj the object
582      * @return the block number of the object's root
583      * @exception IOException may be thrown
584      */

585     public long putObject(Object JavaDoc obj) throws IOException JavaDoc {
586         return file.putObject(obj);
587     }
588
589     /**
590      * Write a new version of a persistent object to the store.
591      * @param blockNum the address of the object's root page in the store
592      * @param obj the new object value
593      *
594      * @exception IOException may be thrown
595      */

596     public void updateObject(long seg, Object JavaDoc obj) throws IOException JavaDoc {
597         file.updateObject(seg, obj);
598     }
599
600     /**
601      * Remove an object from the store
602      * @param ref the block number of the object's root
603      *
604      * @exception IOException may be thrown
605      */

606     public void removeObject(long ref) throws IOException JavaDoc {
607         file.removeObject(ref);
608     }
609
610
611     /**
612      * Hook which allows the scratch files to be written to a separate
613      * directory. Useful when you want to mount the data files read only
614      */

615     final void makeTempDirectory(Properties JavaDoc props) throws IOException JavaDoc {
616         /*{com.quadcap.sql.Datafile-conn.xml-22}
617          * <config-var>
618          * <config-name>scratchDir</config-name>
619          * <config-dflt><i>database directory</i></config-dflt>
620          * <config-desc>
621          * Specify the directory where scratch files are written. This
622          * option is most useful in conjunction with <code>mode=r</code>,
623          * where the database files can be located on read-only media,
624          * and the scratch directory can be located separately.
625          * </config-desc>
626          * </config-var>
627          */

628         if (!inMemory) {
629             String JavaDoc tmpDirName = props.getProperty("scratchDir",
630                                                   dbRootDir.getAbsolutePath());
631             this.tempDir = new File JavaDoc(tmpDirName);
632             if (!tempDir.isDirectory()) {
633                 if (!tempDir.mkdirs()) {
634                     throw new IOException JavaDoc("Can't create temp directory: " +
635                                           tmpDirName);
636                 }
637             }
638         }
639     }
640
641     /**
642      * The temporary file can be used for storage of non-transactionally-secure
643      * data
644      */

645     public BlockFile getTempFile() throws IOException JavaDoc {
646         return getTempFile(true);
647     }
648     
649     public BlockFile getTempFile(boolean incr) throws IOException JavaDoc {
650         synchronized (fileLock) {
651             if (tempFile == null) {
652                 if (inMemory) {
653                     this.tempFile = new BlockFile(fileName + ".scratch", "rw",
654                                                   props, blockSize, SCRATCH_CACHE_SIZE);
655                 } else {
656                     File JavaDoc f = new File JavaDoc(tempDir, "scratch");
657                     this.tempFile = new BlockFile(f.getAbsolutePath(), "rw",
658                                                   props,
659                                                   blockSize,
660                                                   SCRATCH_CACHE_SIZE);
661                 }
662                 this.tempFileRefCount = 0;
663             }
664             //Debug.println("Datafile.getTempFile(" + tempFileRefCount +
665
// (incr ? (" -> " + (tempFileRefCount+1)) : "") +
666
// ") @\n" + Util.stackTrace());
667
if (incr) tempFileRefCount++;
668         }
669     return tempFile;
670     }
671
672     public void releaseTempFile() {
673         synchronized (fileLock) {
674             //Debug.println("Datafile.releaseTempFile(" + tempFileRefCount +
675
// " -> " + (tempFileRefCount-1) + ") @\n" +
676
// Util.stackTrace());
677
if (--tempFileRefCount == 0) {
678                 clearTempFile();
679             }
680         }
681     }
682
683     /**
684      * When nothing is happening, get rid of this guy. Who needs him, eh?
685      */

686     void clearTempFile() {
687         if (tempFile != null) {
688             //Debug.println("clearTempFile"); // XXX
689
if (inMemory) {
690                 tempFile = null;
691             } else if (true) {
692                 try {
693                     tempFile.close();
694                 } catch (Throwable JavaDoc e8) {
695                 } finally {
696                     tempFile = null;
697                 }
698                 try {
699                     new File JavaDoc(dbRootDir, "scratch").delete();
700                 } catch (Throwable JavaDoc e9) {
701                 }
702             }
703         }
704     }
705
706     /**
707      * Close the database. Sync and close the physical file(s),
708      * delete the tempfile(s) and lockfile. Kill the database
709      * reference monitor thread.
710      */

711     public void close() {
712     if (file != null) {
713             //#ifdef TRACE
714
if (Trace.bit(12)) {
715                 Debug.println("Datafile.close(" + dbRootDir.getPath() + ")");
716             }
717             //#endif
718
try {
719                 if (log != null) {
720                     log.checkpoint();
721                     log.sync();
722                     log.close();
723                     log.remove();
724                 }
725             } catch (Throwable JavaDoc e8) {
726                 Debug.print(e8);
727             } finally {
728                 log = null;
729             }
730
731             clearTempFile();
732             
733             try {
734         if (file != null) file.close();
735         } catch (Throwable JavaDoc e2) {
736         }
737
738         file = null;
739             try {
740                 //#ifdef JDK14
741
if (lockLock != null) {
742                     lockLock.release();
743                     if (lockChannel != null) lockChannel.close();
744                     //new File(dbRootDir, "lockfile").delete();
745
}
746                 //#endif
747
} catch (Throwable JavaDoc t) {
748             } finally {
749                 //#ifdef JDK14
750
lockLock = null;
751                 lockChannel = null;
752                 //#endif
753
}
754         }
755     }
756
757     /**
758      * Helper to create a new transaction, and optionally to write the
759      * BEGIN_TRANSACTION log entry.
760      */

761     public final Transaction makeTransaction(boolean writeLog)
762         throws IOException JavaDoc
763     {
764         Transaction trans = null;
765         long transId = -1;
766         synchronized (fileLock) {
767             transId = getNextTransId();
768             trans = locks.getTransaction(transId);
769         }
770         if (writeLog) {
771             log.addEntry(new LogEntry(transId,
772                                       LogEntry.BEGIN_TRANSACTION));
773         }
774         return trans;
775     }
776
777     /**
778      * Commit the transaction
779      */

780     public void commitTransaction(Transaction trans) throws IOException JavaDoc {
781         long transId = trans.getTransactionId();
782         log.addEntry(new LogEntry(transId, LogEntry.COMMIT));
783         log.flushLog();
784         releaseLocks(trans);
785     }
786     
787     /**
788      * Rollback the transaction
789      */

790     public void rollbackTransaction(Transaction trans)
791         throws IOException JavaDoc
792     {
793         log.rollbackTransaction(trans);
794         commitTransaction(trans);
795     }
796     
797     /**
798      * Rollback the specified statement.
799      */

800     public void rollbackStatement(Transaction trans, int stmtId)
801         throws IOException JavaDoc
802     {
803         log.rollbackStatement(trans, stmtId);
804     }
805     
806     /**
807      * Remove the transaction for the given connection from the
808      * 'active transactions' table. Flush the log?
809      */

810     public void releaseLocks(Transaction trans) {
811         locks.releaseTransaction(trans);
812     }
813
814     /**
815      * Find the specified transaction
816      */

817     public Transaction findTransaction(long transId) throws IOException JavaDoc {
818         Transaction t = locks.findTransaction(transId);
819         return t;
820     }
821
822     /**
823      * Return the log for this database
824      */

825     public final Log getLog() { return log; }
826
827     /**
828      * If we get finalized, and haven't closed yet, then we should try.
829      */

830     public void finalize() throws Throwable JavaDoc {
831     close();
832         super.finalize();
833     }
834
835     /**
836      * Return the total size of the datafile, in bytes
837      */

838     public long getSize() {
839     return file.getSize();
840     }
841
842     /**
843      * Reset the temp file (on checkpoint)
844      */

845     public void checkpoint(boolean truncate, boolean fastSync)
846         throws IOException JavaDoc
847     {
848         synchronized (fileLock) {
849             if (tempFile != null) {
850                 tempFile.flush(fastSync);
851             }
852         }
853     }
854
855     public void checkpointHandler(LongMap activeTransactions)
856         throws IOException JavaDoc
857     {
858     }
859     
860     //#ifdef DEBUG
861
/**
862      * Return a displayable representation for debugging purposes
863      */

864     public String JavaDoc toString() {
865         String JavaDoc u = url;
866         if (u.startsWith("jdbc:qed:")) u = u.substring(9);
867         int idx = u.indexOf(';');
868         if (idx > 0) u = u.substring(0, idx);
869         return u;
870     }
871     //#endif
872

873     public void doStep(Transaction t, LogEntry e)
874         throws IOException JavaDoc, DatafileException
875     {
876         log.addEntry(e);
877         e.redo(t, this);
878     }
879
880     public boolean inRecovery() throws IOException JavaDoc {
881         return log != null && log.inRecovery();
882     }
883
884 }
885
Popular Tags