Source Code Cross Referenced for MDB.java in  » Project-Management » borg » net » sf » borg » model » db » file » mdb » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Project Management » borg » net.sf.borg.model.db.file.mdb 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * This file is part of BORG.
0003:         * 
0004:         * BORG is free software; you can redistribute it and/or modify it under the
0005:         * terms of the GNU General Public License as published by the Free Software
0006:         * Foundation; either version 2 of the License, or (at your option) any later
0007:         * version.
0008:         * 
0009:         * BORG is distributed in the hope that it will be useful, but WITHOUT ANY
0010:         * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
0011:         * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
0012:         * 
0013:         * You should have received a copy of the GNU General Public License along with
0014:         * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
0015:         * Suite 330, Boston, MA 02111-1307 USA
0016:         * 
0017:         * Copyright 2003 by Mike Berger
0018:         */
0019:        package net.sf.borg.model.db.file.mdb;
0020:
0021:        import java.io.ByteArrayOutputStream;
0022:        import java.io.EOFException;
0023:        import java.io.File;
0024:        import java.io.IOException;
0025:        import java.io.RandomAccessFile;
0026:        import java.nio.channels.FileChannel;
0027:        import java.nio.channels.FileLock;
0028:        import java.util.ArrayList;
0029:        import java.util.HashMap;
0030:        import java.util.Vector;
0031:
0032:        import javax.swing.filechooser.FileFilter;
0033:
0034:        import net.sf.borg.common.Errmsg;
0035:
0036:        // MDB - a random access DB (or perhaps 1 table in a DB)
0037:        // MDB stores text data of unlimited size using an integer key
0038:        // So, the schema for MDB is simply: int key; short userflags; - some boolean
0039:        // flags that are indexed text userdata
0040:        // Supports true random access. Records are keyed using a
0041:        // user supplied key - no duplicates allowed. When DB object
0042:        // is constructed, the file is quickly scanned and an index (table of contents)
0043:        // is created in memory. After that, insert, delete, update operations
0044:        // are done at a record level.
0045:        // Record size is fixed. Data larger than record size is stored
0046:        // under a chain of records. Freed records are left in place
0047:        // and marked as free for later reuse - file "holes" are not eliminated.
0048:        // Data is ASCII strings of any size. DB is not sensitive
0049:        // to the data content. DB is not sensitive to key values.
0050:        // Each record can contain 2 bytes of user boolean flags. These flags
0051:        // are read during the initial indexing scan - when no text is read
0052:        // These flags can give the user info about record without the need to
0053:        // perform the more expensive text read
0054:        // File structure
0055:        // |=========================================|
0056:        // | Superblock - contains info about the DB |
0057:        // | esp. size of the DB records |
0058:        // |-----------------------------------------|
0059:        // | Block |
0060:        // |-----------------------------------------|
0061:        // | Block |
0062:        // |-----------------------------------------|
0063:        // | Block |
0064:        // |-----------------------------------------|
0065:        // 
0066:        // Block structure
0067:        // |=========================================|
0068:        // | Header - contains the user KEY, optional|
0069:        // | link to the next block if the record |
0070:        // | is part of a chain, and flags |
0071:        // |-----------------------------------------|
0072:        // | Data |
0073:        // |=========================================|
0074:
0075:        public class MDB {
0076:
0077:            /**
0078:             * a file chooser filter that selects JDB files
0079:             */
0080:            public static class DBFilter extends FileFilter {
0081:                public boolean accept(File f) {
0082:                    if (f.isDirectory()) {
0083:                        return true;
0084:                    }
0085:                    String ext = null;
0086:                    String s = f.getName();
0087:                    int i = s.lastIndexOf('.');
0088:                    if (i > 0 && i < s.length() - 1) {
0089:                        ext = s.substring(i + 1).toLowerCase();
0090:                    }
0091:                    if (ext != null) {
0092:                        if (ext.equals("jdb")) {
0093:                            return true;
0094:                        }
0095:
0096:                        return false;
0097:                    }
0098:                    return false;
0099:                }
0100:
0101:                // The description of this filter
0102:                public String getDescription() {
0103:                    return "JDB Files";
0104:                }
0105:
0106:            }
0107:
0108:            // Block Flags
0109:            private static final int BH_SIZE = 4 + 4 + 4; // 3 integers
0110:
0111:            private static final int F_DELETE = 0x01; // Block is unused (available)
0112:
0113:            private static final int F_EXTEND = 0x02; // Block is part of a chain of
0114:
0115:            // blocks - not the first
0116:
0117:            private static final int F_CRYPT = 0x04; // Block is encrypted
0118:
0119:            private static final int F_SYSTEM = 0x08; // system block
0120:
0121:            private static final int F_MAX = 0x0F;
0122:
0123:            // system keys - user cannot use keys this large - reserved for internal MDB
0124:            // use
0125:            // protected static final int LOG_KEY = 2000000001; // key to the record
0126:            // that
0127:            // holds the
0128:
0129:            // log file name
0130:
0131:            private static final int VERSION = 8; // 6 - add user flags, 7 = separate
0132:
0133:            // SYS blocks
0134:
0135:            // 8 - add update count to superblock for shared access
0136:
0137:            // lock mode for constructor and for persistent lock flag
0138:            public static final int UNLOCKED = 0; // no lock
0139:
0140:            public static final int READ_ONLY = 1; // exclusive read lock, allow no
0141:
0142:            // updates
0143:
0144:            public static final int READ_WRITE = 2; // exclusive read/write
0145:
0146:            public static final int READ_DIRTY = 3; // gets no lock, can read only, no
0147:
0148:            // guarantee that
0149:
0150:            public static final int ADMIN = 5; // unconditional access, used to remove
0151:
0152:            // other locks
0153:
0154:            // Header data contained in each block
0155:            static private class BlockHeader {
0156:                public int key_; // user key
0157:
0158:                public int next_; // number of next "extension" block if
0159:
0160:                // there is chaining
0161:                public int flags_; // flags (deleted, extension, crypt, system)
0162:
0163:                public int userflags_; // user defined flags
0164:
0165:                static public BlockHeader read(RandomAccessFile fp)
0166:                        throws Exception, EOFException {
0167:                    BlockHeader bh = new BlockHeader();
0168:                    try {
0169:
0170:                        // read the 3 ints that make up the block header
0171:                        bh.key_ = fp.readInt();
0172:                        bh.next_ = fp.readInt();
0173:                        int allflags = fp.readInt();
0174:
0175:                        // the third int is half system flags and half user flags
0176:                        bh.flags_ = allflags & 0xFFFF;
0177:                        bh.userflags_ = (allflags & 0xFFFF0000) >>> 16;
0178:                    } catch (EOFException eo) {
0179:                        throw eo;
0180:                    } catch (Exception e) {
0181:                        throw e;
0182:                    }
0183:
0184:                    return (bh);
0185:                }
0186:
0187:                public void write(RandomAccessFile fp) throws Exception {
0188:                    try {
0189:
0190:                        // write out the 3 ints that make up the block header
0191:                        fp.writeInt(key_);
0192:                        fp.writeInt(next_);
0193:
0194:                        // the flags int must be combined from the system and user flags
0195:                        int allflags = (flags_ + ((userflags_ & 0xFFFF) << 16));
0196:                        fp.writeInt(allflags);
0197:
0198:                    }
0199:
0200:                    catch (Exception e2) {
0201:                        throw (new Exception("Cannot write superblock"));
0202:                    }
0203:                }
0204:            }
0205:
0206:            // the superblock describes info about the entire DB file
0207:            private static class SuperBlock {
0208:                static final int NAME_SIZE = 20; // Size of DB name
0209:
0210:                static final int FILLSIZE = 8; // filler space - not used yet
0211:
0212:                // sizes of superblock is 3 ints (blocksize, version, lock) + name +
0213:                // filler
0214:                // Java int size is fixed to 4 bytes by definition!!
0215:                static final int SB_SIZE = 4 + 4 + 4 + NAME_SIZE + 4 + FILLSIZE;
0216:
0217:                public String dbname_;
0218:
0219:                public int blocksize_;
0220:
0221:                public int version_;
0222:
0223:                public int locktype_;
0224:
0225:                public int updateCount_;
0226:
0227:                private SuperBlock(int blocksize, int version, int locktype) {
0228:                    dbname_ = "MDB Database"; // default name
0229:                    blocksize_ = blocksize;
0230:                    version_ = version;
0231:                    locktype_ = locktype;
0232:                    updateCount_ = 0;
0233:                }
0234:
0235:                // write superblock
0236:                public void write(RandomAccessFile fp) throws Exception {
0237:                    if (dbname_.length() > NAME_SIZE)
0238:                        throw (new Exception("DB name too long"));
0239:
0240:                    // create byte array for name + padding to get to NAME_SIZE + filler
0241:                    byte b[] = dbname_.getBytes();
0242:                    byte pad[] = new byte[NAME_SIZE - b.length];
0243:                    byte fill[] = new byte[FILLSIZE];
0244:
0245:                    try {
0246:                        // super block always at file start
0247:                        fp.seek(0);
0248:
0249:                        // write it
0250:                        fp.writeInt(blocksize_);
0251:                        fp.writeInt(version_);
0252:                        fp.writeInt(locktype_);
0253:                        fp.write(b);
0254:                        fp.write(pad);
0255:                        fp.writeInt(updateCount_);
0256:                        fp.write(fill);
0257:                    }
0258:
0259:                    catch (Exception e2) {
0260:                        throw (new Exception("Cannot write superblock"));
0261:                    }
0262:                }
0263:
0264:                // read superblock
0265:                static public SuperBlock read(RandomAccessFile fp)
0266:                        throws Exception {
0267:                    SuperBlock nsb = new SuperBlock(100, VERSION, READ_WRITE);
0268:                    try {
0269:                        // always at file start
0270:                        fp.seek(0);
0271:                        nsb.blocksize_ = fp.readInt();
0272:                        nsb.version_ = fp.readInt();
0273:                        nsb.locktype_ = fp.readInt();
0274:                        fp.seek(4 + 4 + 4 + NAME_SIZE);
0275:                        nsb.updateCount_ = fp.readInt();
0276:
0277:                        // don't need to read name for anything
0278:                    } catch (Exception e) {
0279:                        throw new Exception("Error reading superblock fields");
0280:                    }
0281:
0282:                    if (nsb.blocksize_ < BH_SIZE + 2)
0283:                        throw new Exception("Blocksize is too small");
0284:
0285:                    return (nsb);
0286:                }
0287:
0288:                public int readCounter(RandomAccessFile fp) throws Exception {
0289:                    try {
0290:                        fp.seek(4 + 4 + 4 + NAME_SIZE);
0291:                        updateCount_ = fp.readInt();
0292:                        return (updateCount_);
0293:                    } catch (Exception e) {
0294:                        throw new Exception("Error reading update counter");
0295:                    }
0296:
0297:                }
0298:
0299:                public void writeCounter(RandomAccessFile fp) throws Exception {
0300:                    try {
0301:                        // super block always at file start
0302:                        fp.seek(4 + 4 + 4 + NAME_SIZE);
0303:
0304:                        // write it
0305:                        fp.writeInt(updateCount_);
0306:                    } catch (Exception e2) {
0307:                        throw (new Exception("Cannot write update counter"));
0308:                    }
0309:                }
0310:            }
0311:
0312:            //
0313:            // fields
0314:            //
0315:
0316:            private String filename_; // the actual DB filename
0317:
0318:            private SuperBlock sb; // the superblock read from the db
0319:
0320:            private int locktype_; // locktype that we opened the db with
0321:
0322:            private int maxkey_; // highest key in the db
0323:
0324:            private boolean sharedDB_ = false;
0325:
0326:            private FileLock rwlock_ = null;
0327:
0328:            private HashMap index_;
0329:
0330:            private HashMap sysindex_;
0331:
0332:            // mapping of keys to user flags
0333:            private HashMap flagmap_;
0334:
0335:            private int cur_key_idx_; // currency pointer into cur_keys_ array to
0336:
0337:            // hold current key across first/next calls
0338:            private Object cur_keys_[]; // array of all keys to speed up first/next
0339:
0340:            // traversal
0341:
0342:            private boolean cur_crypt_; // flag to indicate if the current record is
0343:
0344:            // encrypted
0345:
0346:            // List of free blocks in the DB file
0347:            private ArrayList freelist_;
0348:
0349:            // the file itself
0350:            private RandomAccessFile fp_;
0351:
0352:            // last block in file - keeps track of where to add new blocks
0353:            private int lastblock_;
0354:
0355:            // key for weak-encryption for private records
0356:            static private String ckey_ = "543216789192837465";
0357:
0358:            // the encryption object - just does weak encryption - but prevents casual
0359:            // string scan on db file for encrypted records.
0360:            private Crypt mc_;
0361:
0362:            //
0363:            // end fields
0364:            //
0365:
0366:            // add a mapping of a key to a block into either the user or system
0367:            // maps
0368:            private void add_kpair(int key, int bnum, boolean system) {
0369:
0370:                if (system)
0371:                    sysindex_.put(new Integer(key), new Integer(bnum));
0372:                else
0373:                    index_.put(new Integer(key), new Integer(bnum));
0374:            }
0375:
0376:            // look up the block number corresponding to a key
0377:            // in the system or user maps
0378:            private int find_block(int key, boolean system) {
0379:                Integer bnum;
0380:                if (system) {
0381:                    bnum = (Integer) sysindex_.get(new Integer(key));
0382:                } else {
0383:                    bnum = (Integer) index_.get(new Integer(key));
0384:                }
0385:
0386:                if (bnum != null)
0387:                    return (bnum.intValue());
0388:
0389:                return (-1);
0390:            }
0391:
0392:            // find a free block in the DB or return the last block + 1 if the
0393:            // DB needs to grow
0394:            private int get_free_block() {
0395:                Integer bnum;
0396:                int i = freelist_.size();
0397:                if (i > 0) {
0398:                    bnum = (Integer) freelist_.remove(i - 1);
0399:                    return (bnum.intValue());
0400:                }
0401:
0402:                lastblock_++;
0403:                return (lastblock_);
0404:            }
0405:
0406:            // move a block from one of the maps to the freelist
0407:            private int free_entry(int key, boolean system) throws Exception {
0408:
0409:                // reset currency pointer when changing the DB
0410:                cur_keys_ = null;
0411:                Integer k = new Integer(key);
0412:                Integer bnum;
0413:                if (system)
0414:                    bnum = (Integer) sysindex_.remove(k);
0415:                else
0416:                    bnum = (Integer) index_.remove(k);
0417:
0418:                if (bnum != null) {
0419:                    freelist_.add(bnum);
0420:                    return (bnum.intValue());
0421:                }
0422:
0423:                throw new Exception("free_entry - block not found");
0424:            }
0425:
0426:            // add a new key to the DB and associate a block with it
0427:            private int add_new_entry(int key, boolean system) throws Exception {
0428:
0429:                // reset currency pointer when changing the DB
0430:                cur_keys_ = null;
0431:
0432:                int bnum = find_block(key, system);
0433:                if (bnum != -1) {
0434:                    throw (new Exception(
0435:                            "add_new_entry - duplicate key already exists"));
0436:                }
0437:
0438:                int fb = get_free_block();
0439:                add_kpair(key, fb, system);
0440:                return (fb);
0441:
0442:            }
0443:
0444:            // create a new MDB database file
0445:            static public void create(String dbname, String filename,
0446:                    int blocksize) throws Exception {
0447:                RandomAccessFile fp;
0448:
0449:                // blocksize is the size of a block - header + text space
0450:                // make sure the user allocates 2 bytes text space for a block
0451:                // this is way too small anyway - should be around 80-100 text bytes
0452:                // per block - depends on the average text size for a DB
0453:                if (blocksize < BH_SIZE + 2)
0454:                    throw (new Exception("Blocksize is too small"));
0455:
0456:                // create the file
0457:                File f = new File(filename);
0458:                if (f.exists())
0459:                    throw (new Exception("File Already Exists"));
0460:                fp = new RandomAccessFile(filename, "rw");
0461:
0462:                // write out a superblock
0463:                SuperBlock sb = new SuperBlock(blocksize, VERSION, UNLOCKED);
0464:                sb.dbname_ = dbname;
0465:                sb.write(fp);
0466:
0467:            }
0468:
0469:            // remove lock files when destroyed
0470:            protected void finalize() throws Throwable {
0471:                close();
0472:                super .finalize();
0473:            }
0474:
0475:            // try to get a shared lock to lock for multi-user usage
0476:            private void getSharedLock() throws Exception {
0477:                try {
0478:                    FileChannel fc = fp_.getChannel();
0479:
0480:                    FileLock lock;
0481:                    try {
0482:                        lock = fc.tryLock(0, 1, true);
0483:                    } catch (IOException e) {
0484:                        throw new Exception("Cannot lock DB " + filename_);
0485:                    }
0486:                    if (lock == null) {
0487:                        throw new Exception("Cannot lock DB " + filename_);
0488:                    }
0489:                } catch (NoSuchMethodError nsm) {
0490:                    // running 1.3 - from Jsync conduit
0491:                    return;
0492:                }
0493:
0494:            }
0495:
0496:            // try to get an exclusive lock for single user usage
0497:            private void getExclusiveLock() throws Exception {
0498:                try {
0499:                    FileChannel fc = fp_.getChannel();
0500:                    FileLock lock;
0501:                    try {
0502:                        lock = fc.tryLock(0, 1, false);
0503:                    } catch (IOException e) {
0504:                        e.printStackTrace();
0505:                        throw new Exception("Cannot lock DB " + filename_);
0506:                    }
0507:                    if (lock == null) {
0508:                        throw new Exception("Cannot lock DB " + filename_);
0509:                    }
0510:                } catch (NoSuchMethodError nsm) {
0511:                    // running 1.3 - from Jsync conduit
0512:                    return;
0513:                }
0514:
0515:            }
0516:
0517:            // wait for a shared read lock
0518:            public void getReadLock() throws Exception {
0519:                if (!sharedDB_)
0520:                    return;
0521:                FileChannel fc = fp_.getChannel();
0522:                try {
0523:                    if (rwlock_ != null)
0524:                        return;
0525:
0526:                    rwlock_ = fc.lock(1, 1, true);
0527:                } catch (IOException e) {
0528:                    throw new Exception("Cannot lock DB " + filename_);
0529:                }
0530:                if (rwlock_ == null) {
0531:                    throw new Exception("Cannot lock DB " + filename_);
0532:                }
0533:
0534:            }
0535:
0536:            // wait for an exclusive write lock
0537:            public void getWriteLock() throws Exception {
0538:                if (!sharedDB_)
0539:                    return;
0540:
0541:                FileChannel fc = fp_.getChannel();
0542:                try {
0543:                    // check if we already have this lock
0544:                    if (rwlock_ != null && !rwlock_.isShared())
0545:                        return;
0546:
0547:                    // release any read lock to upgrade to write
0548:                    if (rwlock_ != null)
0549:                        rwlock_.release();
0550:                    rwlock_ = fc.lock(1, 1, false);
0551:                } catch (IOException e) {
0552:                    throw new Exception("Cannot lock DB " + filename_);
0553:                }
0554:
0555:                if (rwlock_ == null) {
0556:                    throw new Exception("Cannot lock DB " + filename_);
0557:                }
0558:            }
0559:
0560:            // release a read or write lock
0561:            public void releaseLock() throws Exception {
0562:                if (!sharedDB_)
0563:                    return;
0564:                if (rwlock_ != null) {
0565:                    try {
0566:                        rwlock_.release();
0567:                    } catch (IOException e) {
0568:                        throw new Exception("Cannot lock DB " + filename_);
0569:                    } catch (NoSuchMethodError nsm) {
0570:                        // running 1.3 - from Jsync conduit
0571:                        return;
0572:                    }
0573:                }
0574:                rwlock_ = null;
0575:            }
0576:
0577:            public boolean haveLock() {
0578:                if (rwlock_ != null)
0579:                    return (true);
0580:
0581:                return (false);
0582:            }
0583:
0584:            // Are we out of sync?
0585:            // if we're not locked before entering this method, we attempt to acquire
0586:            // a read lock
0587:            public boolean isDirty() throws Exception {
0588:
0589:                // if we have exclusive access - don't bother doing anything
0590:                if (!sharedDB_)
0591:                    return false;
0592:
0593:                boolean gotlock = false;
0594:
0595:                try {
0596:                    if (!haveLock()) {
0597:                        getReadLock();
0598:                        gotlock = true;
0599:                    }
0600:
0601:                    int count = sb.updateCount_;
0602:                    int newcount = sb.readCounter(fp_);
0603:                    if (count == newcount) {
0604:                        // if the last count for this object matches the count read
0605:                        // from the file - nothing is needed, db is in sync
0606:                        return false;
0607:                    }
0608:                } finally {
0609:                    if (gotlock)
0610:                        releaseLock();
0611:                }
0612:                return true;
0613:            }
0614:
0615:            // flush all cached data and recreate
0616:            // if we're not locked before entering this method, we attempt to acquire
0617:            // a read lock
0618:            public void sync() {
0619:
0620:                // if we have exclusive access - don't bother doing anything
0621:                if (!sharedDB_)
0622:                    return;
0623:
0624:                boolean gotlock = false;
0625:
0626:                try {
0627:                    if (!haveLock()) {
0628:                        getReadLock();
0629:                        gotlock = true;
0630:                    }
0631:
0632:                    int count = sb.updateCount_;
0633:                    int newcount = sb.readCounter(fp_);
0634:                    if (count == newcount) {
0635:                        // if the last count for this object matches the count read
0636:                        // from the file - nothing is needed, db is in sync
0637:                        return;
0638:                    }
0639:
0640:                    // flush all cached data and re-sync
0641:                    index_.clear();
0642:                    sysindex_.clear();
0643:                    flagmap_.clear();
0644:                    cur_key_idx_ = -1;
0645:                    cur_keys_ = null;
0646:                    freelist_.clear();
0647:                    lastblock_ = -1;
0648:                    build_index();
0649:                } catch (Exception e) {
0650:                    System.out.println(e);
0651:                } finally {
0652:                    if (gotlock)
0653:                        try {
0654:                            releaseLock();
0655:                        } catch (Exception e) {
0656:
0657:                        }
0658:                }
0659:            }
0660:
0661:            /**
0662:             * constructor - opens DB of given name with given lock type
0663:             * 
0664:             * @param file
0665:             *                filename of DB
0666:             * @param locktype
0667:             *                locking mode
0668:             * @throws Exception
0669:             *                 errors
0670:             */
0671:            public MDB(String file, int locktype, boolean shared)
0672:                    throws Exception {
0673:                filename_ = file;
0674:                lastblock_ = -1;
0675:                sharedDB_ = shared;
0676:
0677:                // allocate an encrpytion object
0678:                try {
0679:                    mc_ = new Crypt(ckey_);
0680:                } catch (Exception e) {
0681:                    throw new Exception(
0682:                            "failed to initialize encryption object");
0683:                }
0684:
0685:                // init some stuff
0686:                locktype_ = locktype;
0687:                maxkey_ = 0;
0688:
0689:                index_ = new HashMap();
0690:                sysindex_ = new HashMap(10);
0691:                flagmap_ = new HashMap();
0692:                freelist_ = new ArrayList();
0693:                cur_keys_ = null;
0694:                cur_key_idx_ = -1;
0695:
0696:                // open the DB file
0697:                File f = new File(filename_);
0698:                if (!f.exists())
0699:                    throw new Exception("DB does not exist");
0700:
0701:                // open mode based on lock type
0702:                try {
0703:                    if (locktype == READ_ONLY || locktype == READ_DIRTY) {
0704:                        fp_ = new RandomAccessFile(filename_, "r");
0705:                    } else if (locktype == READ_WRITE || locktype == ADMIN) {
0706:                        fp_ = new RandomAccessFile(filename_, "rw");
0707:                    } else {
0708:                        throw new Exception("Invalid Locking Mode");
0709:                    }
0710:                } catch (Exception e) {
0711:                    throw new Exception(e.toString());
0712:                }
0713:
0714:                // lock the database
0715:                try {
0716:                    if (sharedDB_) {
0717:                        getSharedLock();
0718:                        getReadLock();
0719:                    } else
0720:                        getExclusiveLock();
0721:                } catch (Exception e) {
0722:                    throw new Exception(e.toString());
0723:                }
0724:
0725:                // read the superblock
0726:                sb = SuperBlock.read(fp_);
0727:
0728:                // transition to version 8
0729:                if (sb.version_ < 8) {
0730:                    sb.updateCount_ = 0;
0731:                    sb.version_ = VERSION;
0732:                    sb.write(fp_);
0733:                }
0734:
0735:                if (VERSION != sb.version_)
0736:                    throw new Exception("DB version != software version: "
0737:                            + sb.version_ + "!=" + VERSION);
0738:
0739:                // check persistent SB lock - not really used
0740:                // the persistent lock has nothing to do with the temporary lock files
0741:                // it was used to lock a DB so that it could not be opened without
0742:                // a special close process. it was meant to indicate that a DB was
0743:                // probably
0744:                // a secondary copy and perhaps should not be used unless the primary
0745:                // was lost
0746:                // didn't want to accidentally start making real changes to a throw-away
0747:                // backup copy
0748:                // obsolete
0749:                if (((sb.locktype_ == READ_WRITE) && ((locktype == READ_WRITE) || (locktype == READ_ONLY)))
0750:                        || ((sb.locktype_ == READ_ONLY) && (locktype == READ_WRITE))) {
0751:                    throw new Exception(
0752:                            "Cannot lock DB. Superblock indicates a lock");
0753:                }
0754:
0755:                // scan the whole DB file jumping from block-header to block-header to
0756:                // build
0757:                // a map of keys vs. blocks. does not read throwse text fields
0758:                build_index();
0759:
0760:                releaseLock(); // release read lock
0761:
0762:            }
0763:
0764:            public void close() {
0765:                try {
0766:                    fp_.close();
0767:                } catch (Exception e) {
0768:                    System.out.println(e);
0769:                }
0770:
0771:            }
0772:
0773:            // duh
0774:            public int nextkey() {
0775:                try {
0776:                    sync();
0777:                } catch (Exception e) {
0778:                    Errmsg.errmsg(e);
0779:                }
0780:                return (++maxkey_);
0781:            }
0782:
0783:            /**
0784:             * Add a new DB row
0785:             * 
0786:             * @param key
0787:             *                integer key ( > 0 and < MAX_KEY)
0788:             * @param st
0789:             *                data to store
0790:             * @throws Exception
0791:             *                 errors
0792:             */
0793:
0794:            // add a user row to the db - with optional encryption
0795:            public void add(int key, int flags, String st, boolean crypt)
0796:                    throws Exception {
0797:                add(key, flags, st, crypt, false);
0798:            }
0799:
0800:            // add a system row to the DB
0801:            protected void addSys(int key, String st) throws Exception {
0802:                add(key, 0, st, false, true);
0803:            }
0804:
0805:            // add an unencrypted user row
0806:            public void add(int key, int flags, String st) throws Exception {
0807:                add(key, flags, st, false, false);
0808:            }
0809:
0810:            // main add function
0811:            // add will store a record with a key, flags, and text string
0812:            // it will break up the string as needed and create extended blocks if the
0813:            // string is too large to fit in a single block
0814:            private void add(int key, int flags, String st, boolean crypt,
0815:                    boolean system) throws Exception {
0816:                if (locktype_ != READ_WRITE && locktype_ != ADMIN)
0817:                    throw new Exception("add: Cannot lock DB for write");
0818:
0819:                getWriteLock();
0820:                sync();
0821:
0822:                int bnum, main_bnum;
0823:
0824:                // add the first block - get free block number
0825:                main_bnum = add_new_entry(key, system);
0826:
0827:                // figure out how much text can fit per block
0828:                int text_per_block = sb.blocksize_ - BH_SIZE - 1;
0829:
0830:                // allocate a new header
0831:                BlockHeader header = new BlockHeader();
0832:
0833:                // set the header flags
0834:                header.flags_ = 0;
0835:                header.userflags_ = flags;
0836:
0837:                // encrypt the string if needed
0838:                if (crypt) {
0839:                    st = mc_.encrypt(st);
0840:                }
0841:
0842:                byte[] utf;
0843:
0844:                // UTF-8 encoding is used in the DB
0845:                try {
0846:                    utf = st.getBytes("UTF-8");
0847:                } catch (Exception e) {
0848:                    throw new Exception("UTF-8 not supported");
0849:                }
0850:
0851:                int ulen = utf.length;
0852:
0853:                // build blocks as needed - IN REVERSE ORDER so that each can be stored
0854:                // with the proper next value. So blocks are stored with the end of the
0855:                // string
0856:                // stored first (timewise). Each new block will point to the one stored
0857:                // before it. The final linked chain will start at the beginning of the
0858:                // string and end with block holding the end of the string
0859:                int last_block = -1;
0860:                for (int extnum = (ulen - 1) / text_per_block; extnum >= 0; extnum--) {
0861:
0862:                    // fill in key and next in header
0863:                    header.key_ = key;
0864:                    header.next_ = last_block;
0865:
0866:                    // Write an EXTENSION Block
0867:                    if (extnum > 0) {
0868:                        // get a free block - can't use main_bnum - thats for the first
0869:                        // block in the chain that holds the start of the text string
0870:                        bnum = get_free_block();
0871:                        header.flags_ = F_EXTEND;
0872:                    }
0873:                    // Write the first block using main_bnum
0874:                    else {
0875:                        header.flags_ = 0;
0876:
0877:                        // the SYSTEM and CRYPT flags are only set in the header of
0878:                        // the first block and aren't needed in any extension blocks
0879:                        if (crypt) {
0880:                            header.flags_ |= F_CRYPT;
0881:                        } else if (system) {
0882:                            header.flags_ |= F_SYSTEM;
0883:                        }
0884:
0885:                        bnum = main_bnum;
0886:                    }
0887:
0888:                    try {
0889:                        // seek to the beginning of the block in the file
0890:                        // which is sizeof(bnum blocks) after the superblock ends
0891:                        fp_.seek(SuperBlock.SB_SIZE + (sb.blocksize_ * bnum));
0892:                    } catch (Exception e) {
0893:                        throw new Exception("add: seek failed");
0894:                    }
0895:
0896:                    // write the record
0897:                    try {
0898:
0899:                        // write the header
0900:                        header.write(fp_);
0901:
0902:                        // figure out which portion of the string goes in this
0903:                        // block and write it
0904:                        int endindex = ((extnum + 1) * text_per_block);
0905:
0906:                        if (endindex > ulen) {
0907:                            endindex = ulen;
0908:                        }
0909:
0910:                        // String subs = st.substring(extnum * text_per_block, endindex
0911:                        // );
0912:                        // fp_.writeBytes(subs);
0913:                        int sidx = extnum * text_per_block;
0914:                        int sublen = endindex - sidx;
0915:                        // System.out.println( utf + " " + sidx + " " + sublen );
0916:                        fp_.write(utf, sidx, sublen);
0917:
0918:                        // if the string does not fill up all of the space in the block
0919:                        // -
0920:                        // write '\0' bytes to fill the block
0921:                        for (int i = 0; i <= (text_per_block - sublen); i++) {
0922:                            fp_.writeByte(0);
0923:                        }
0924:
0925:                    } catch (Exception e) {
0926:                        throw new Exception("add: write failed");
0927:                    }
0928:
0929:                    // set last_block in case we loop around and have to store
0930:                    // another that points to this one
0931:                    last_block = bnum;
0932:
0933:                    // if we have stored a user key larger that the max we already
0934:                    // have, then update the max.
0935:                    if (key > maxkey_ && !system)
0936:                        maxkey_ = key;
0937:
0938:                }
0939:
0940:                // write a log record and update the map of user flags with the user's
0941:                // flags
0942:                if (!system) {
0943:                    flagmap_.put(new Integer(key), new Integer(flags));
0944:                }
0945:
0946:                if (sharedDB_) {
0947:                    sb.updateCount_++;
0948:                    sb.writeCounter(fp_);
0949:                }
0950:                releaseLock();
0951:            }
0952:
0953:            /** delete a row */
0954:            public void delete(int key) throws Exception {
0955:                delete(key, false);
0956:            }
0957:
0958:            // delete a system row
0959:            protected void deleteSys(int key) throws Exception {
0960:                delete(key, true);
0961:            }
0962:
0963:            // main delete function
0964:            private void delete(int key, boolean system) throws Exception {
0965:
0966:                int bnum;
0967:
0968:                // check if DB is open for R/W
0969:                if (locktype_ != READ_WRITE && locktype_ != ADMIN)
0970:                    throw new Exception("delete: cannot lock DB");
0971:
0972:                getWriteLock();
0973:                sync();
0974:
0975:                // remove the user flags data from the map
0976:                if (!system)
0977:                    flagmap_.remove(new Integer(key));
0978:
0979:                // free the entry from the map of keys/block nums
0980:                bnum = free_entry(key, system);
0981:
0982:                // build a new block header to mark the record as deleted
0983:                BlockHeader inheader;
0984:                BlockHeader delheader = new BlockHeader();
0985:                delheader.key_ = -1;
0986:                delheader.next_ = -1;
0987:                delheader.flags_ = F_DELETE;
0988:
0989:                // write deleted block headers to all records in the chain
0990:                while (bnum >= 0) {
0991:
0992:                    try {
0993:                        // seek to block
0994:                        fp_.seek(SuperBlock.SB_SIZE + (sb.blocksize_ * bnum));
0995:                    } catch (Exception e) {
0996:                        throw new Exception("delete: seek failed");
0997:                    }
0998:
0999:                    try {
1000:                        // read existing header - to get next ptr
1001:                        inheader = BlockHeader.read(fp_);
1002:                    } catch (Exception e) {
1003:                        throw new Exception("delete: cannot read header");
1004:                    }
1005:
1006:                    try {
1007:                        // seek back to beginning of header
1008:                        fp_.seek(SuperBlock.SB_SIZE + (sb.blocksize_ * bnum));
1009:                    } catch (Exception e) {
1010:                        throw new Exception("delete: seek2 failed");
1011:                    }
1012:
1013:                    // overwrite the header with a deleted header
1014:                    try {
1015:                        delheader.write(fp_);
1016:                    } catch (Exception e) {
1017:                        throw new Exception("delete: write failed");
1018:                    }
1019:
1020:                    // add the block to the free list if it is an extended block
1021:                    // the main block is added to the free list in free_entry()
1022:                    if ((inheader.flags_ & F_EXTEND) != 0) {
1023:                        freelist_.add(new Integer(bnum));
1024:                    }
1025:
1026:                    // go to next block in chain
1027:                    bnum = inheader.next_;
1028:
1029:                    // sanity check - check if next block in chain has same key as
1030:                    // one before. if not, it may be different data so error, DB is
1031:                    // corrupted
1032:                    if (key != inheader.key_) {
1033:                        throw new Exception("delete: different key found");
1034:                    }
1035:
1036:                }
1037:
1038:                if (sharedDB_) {
1039:                    sb.updateCount_++;
1040:                    sb.writeCounter(fp_);
1041:                }
1042:                releaseLock();
1043:            }
1044:
1045:            public Vector keys() {
1046:                Vector v = new Vector(index_.keySet());
1047:                return (v);
1048:            }
1049:
1050:            protected Vector syskeys() {
1051:                Vector v = new Vector(sysindex_.keySet());
1052:                return (v);
1053:            }
1054:
1055:            // get the flags for the current record where current is the last record
1056:            // accessed using first/next
1057:            // in this way, the flags can be retrieved without accessing the DB text
1058:            public int getFlags() throws Exception {
1059:
1060:                if (cur_keys_ == null)
1061:                    throw new Exception("getFlags() called with no currency");
1062:                Integer key = (Integer) cur_keys_[cur_key_idx_];
1063:                Integer flags = (Integer) flagmap_.get(key);
1064:                if (flags == null)
1065:                    throw new Exception("key not found in flagmap");
1066:                return (flags.intValue());
1067:            }
1068:
1069:            // look up the flags for a certain record by key
1070:            public int getFlags(int key) throws Exception {
1071:
1072:                Integer flags = (Integer) flagmap_.get(new Integer(key));
1073:                if (flags == null)
1074:                    throw new Exception("key not found in flagmap");
1075:                return (flags.intValue());
1076:            }
1077:
1078:            /**
1079:             * retrieve record text with given key
1080:             * 
1081:             * @param key
1082:             *                key of record
1083:             * @throws Exception
1084:             *                 not found/lock error/system error
1085:             * @return text of record if found
1086:             */
1087:            public String read(int key) throws Exception {
1088:                return (read(key, false));
1089:            }
1090:
1091:            // get system record text
1092:            protected String readSys(int key) throws Exception {
1093:                return (read(key, true));
1094:            }
1095:
1096:            // main read function
1097:            private String read(int key, boolean system) throws Exception {
1098:
1099:                getReadLock();
1100:                sync();
1101:
1102:                // need to go to the DB - so get the block number
1103:                int bnum = find_block(key, system);
1104:                if (bnum == -1)
1105:                    return null;
1106:
1107:                ByteArrayOutputStream bao = new ByteArrayOutputStream();
1108:
1109:                cur_crypt_ = false;
1110:                BlockHeader header = null;
1111:                while (bnum >= 0) {
1112:
1113:                    // seek to the block
1114:                    fp_.seek(SuperBlock.SB_SIZE + (sb.blocksize_ * bnum));
1115:
1116:                    // read the header
1117:                    header = BlockHeader.read(fp_);
1118:
1119:                    // set flag if block is encrypted
1120:                    if ((header.flags_ & F_CRYPT) != 0)
1121:                        cur_crypt_ = true;
1122:
1123:                    // read the text bytes until we hit the max text in block or a 0
1124:                    // byte
1125:                    // and keep appending to the StringBuffer
1126:
1127:                    for (int i = 0; i < sb.blocksize_ - BH_SIZE; i++) {
1128:                        int c = fp_.readUnsignedByte();
1129:
1130:                        if (c == 0)
1131:                            break;
1132:
1133:                        bao.write(c);
1134:                    }
1135:
1136:                    // check if flags look valid
1137:                    if (header.flags_ < 0 || header.flags_ > F_MAX) {
1138:                        throw new Exception("read: invalid flags");
1139:                    }
1140:
1141:                    // check if key in block header matches the one we were looking for
1142:                    // never should fail unless DB file corrupted
1143:                    if (key != header.key_) {
1144:                        throw new Exception("read: key mismatch");
1145:                    }
1146:
1147:                    // go to next block in chain
1148:                    bnum = header.next_;
1149:                }
1150:
1151:                // get String from StringBuffer
1152:                String s;
1153:                try {
1154:                    s = new String(bao.toByteArray(), "UTF-8");
1155:                    // System.out.println("read: " + s );
1156:                } catch (Exception e) {
1157:                    throw new Exception("UTF-8 not supported");
1158:                }
1159:
1160:                // decrypt if needed
1161:                if (cur_crypt_) {
1162:                    s = mc_.decrypt(s);
1163:                }
1164:
1165:                releaseLock();
1166:                return (s);
1167:
1168:            }
1169:
1170:            // update an unencrypted user record
1171:            public void update(int key, int flags, String st) throws Exception {
1172:                update(key, flags, st, false, false);
1173:            }
1174:
1175:            // update a user record with crypt option
1176:            public void update(int key, int flags, String st, boolean crypt)
1177:                    throws Exception {
1178:                update(key, flags, st, crypt, false);
1179:            }
1180:
1181:            // update a system record
1182:            protected void updateSys(int key, String st) throws Exception {
1183:                update(key, 0, st, false, true);
1184:            }
1185:
1186:            // main update function
1187:            private void update(int key, int flags, String st, boolean crypt,
1188:                    boolean system) throws Exception {
1189:
1190:                // check if DB opened for R/W
1191:                if (locktype_ != READ_WRITE && locktype_ != ADMIN)
1192:                    throw new Exception("update: cannot lock db");
1193:
1194:                // update is simply delete then add
1195:                // update in place would be messy because the number of blocks in the
1196:                // chain might change - so just delete and add
1197:                delete(key, system);
1198:                add(key, flags, st, crypt, system);
1199:
1200:            }
1201:
1202:            // scan all block headers and build the index of keys to blocks
1203:            // done on startup
1204:            private void build_index() throws Exception {
1205:                BlockHeader header;
1206:
1207:                int bnum = 0;
1208:
1209:                // seek past superblock
1210:                fp_.seek(SuperBlock.SB_SIZE);
1211:
1212:                // read all block headers
1213:                while (true) {
1214:                    try {
1215:                        // read a header
1216:                        header = BlockHeader.read(fp_);
1217:                    } catch (EOFException e) {
1218:                        break;
1219:                    }
1220:
1221:                    // validate that flags look ok
1222:                    if (header.flags_ < 0 || header.flags_ > F_MAX) {
1223:                        System.out
1224:                                .println("build_index(): Bad flags found for record ("
1225:                                        + header.key_ + ") - ignoring record");
1226:                    }
1227:                    // if block is deleted - add to freelist
1228:                    else if ((header.flags_ & F_DELETE) != 0) {
1229:                        freelist_.add(new Integer(bnum));
1230:                    }
1231:                    // if block is not an extended block - i.e. it is a single block
1232:                    // or first in a chain, then save it to the user or system indexes
1233:                    else if ((header.flags_ & F_EXTEND) == 0) {
1234:                        if ((header.flags_ & F_SYSTEM) != 0) {
1235:                            add_kpair(header.key_, bnum, true);
1236:                        } else {
1237:                            add_kpair(header.key_, bnum, false);
1238:
1239:                            // for user blocks, add the user flags to an index
1240:                            flagmap_.put(new Integer(header.key_), new Integer(
1241:                                    header.userflags_));
1242:
1243:                            // keep track of the highest key
1244:                            if (header.key_ > maxkey_)
1245:                                maxkey_ = header.key_;
1246:                        }
1247:
1248:                    }
1249:
1250:                    // go to next block and update total number of blocks
1251:                    lastblock_ = bnum;
1252:                    bnum++;
1253:
1254:                    try {
1255:                        // seek to next block
1256:                        fp_.seek(SuperBlock.SB_SIZE + bnum * sb.blocksize_);
1257:                    } catch (Exception e) {
1258:                        break;
1259:                    }
1260:                }
1261:
1262:            }
1263:
1264:        }
w___w_w_.___j__a_v___a___2__s_.___c__o___m_ | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.