Source Code Cross Referenced for kelondroAbstractRecords.java in  » Search-Engine » yacy » de » anomic » kelondro » 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 » Search Engine » yacy » de.anomic.kelondro 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        // kelondroTray.java
0002:        // (C) 2003 - 2007 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
0003:        // first published 2003 on http://yacy.net
0004:        //
0005:        // This is a part of YaCy, a peer-to-peer based web search engine
0006:        //
0007:        // $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $
0008:        // $LastChangedRevision: 1986 $
0009:        // $LastChangedBy: orbiter $
0010:        //
0011:        // LICENSE
0012:        // 
0013:        // This program is free software; you can redistribute it and/or modify
0014:        // it under the terms of the GNU General Public License as published by
0015:        // the Free Software Foundation; either version 2 of the License, or
0016:        // (at your option) any later version.
0017:        //
0018:        // This program is distributed in the hope that it will be useful,
0019:        // but WITHOUT ANY WARRANTY; without even the implied warranty of
0020:        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021:        // GNU General Public License for more details.
0022:        //
0023:        // You should have received a copy of the GNU General Public License
0024:        // along with this program; if not, write to the Free Software
0025:        // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0026:
0027:        package de.anomic.kelondro;
0028:
0029:        import java.io.File;
0030:        import java.io.FileNotFoundException;
0031:        import java.io.IOException;
0032:        import java.util.HashSet;
0033:        import java.util.Iterator;
0034:        import java.util.Random;
0035:        import java.util.Set;
0036:        import java.util.StringTokenizer;
0037:        import java.util.TreeSet;
0038:        import java.util.logging.Level;
0039:        import java.util.logging.Logger;
0040:
0041:        import de.anomic.kelondro.kelondroRow.EntryIndex;
0042:        import de.anomic.server.logging.serverLog;
0043:
0044:        public abstract class kelondroAbstractRecords implements 
0045:                kelondroRecords {
0046:
0047:            // static seek pointers
0048:            private static int LEN_DESCR = 60;
0049:            private static long POS_MAGIC = 0; // 1 byte, byte: file type magic
0050:            private static long POS_BUSY = POS_MAGIC + 1; // 1 byte, byte: marker for synchronization
0051:            private static long POS_PORT = POS_BUSY + 1; // 2 bytes, short: hint for remote db access
0052:            private static long POS_DESCR = POS_PORT + 2; // 60 bytes, string: any description string
0053:            private static long POS_COLUMNS = POS_DESCR + LEN_DESCR; // 2 bytes, short: number of columns in one entry
0054:            private static long POS_OHBYTEC = POS_COLUMNS + 2; // 2 bytes, number of extra bytes on each Node
0055:            private static long POS_OHHANDLEC = POS_OHBYTEC + 2; // 2 bytes, number of Handles on each Node
0056:            private static long POS_USEDC = POS_OHHANDLEC + 2; // 4 bytes, int: used counter
0057:            private static long POS_FREEC = POS_USEDC + 4; // 4 bytes, int: free counter
0058:            private static long POS_FREEH = POS_FREEC + 4; // 4 bytes, int: free pointer (to free chain start)
0059:            private static long POS_MD5PW = POS_FREEH + 4; // 16 bytes, string (encrypted password to this file)
0060:            private static long POS_ENCRYPTION = POS_MD5PW + 16; // 16 bytes, string (method description)
0061:            private static long POS_OFFSET = POS_ENCRYPTION + 16; // 8 bytes, long (seek position of first record)
0062:            private static long POS_INTPROPC = POS_OFFSET + 8; // 4 bytes, int: number of INTPROP elements
0063:            private static long POS_TXTPROPC = POS_INTPROPC + 4; // 4 bytes, int: number of TXTPROP elements
0064:            private static long POS_TXTPROPW = POS_TXTPROPC + 4; // 4 bytes, int: width of TXTPROP elements
0065:            private static long POS_COLWIDTHS = POS_TXTPROPW + 4; // array of 4 bytes, int[]: sizes of columns
0066:            // after this configuration field comes:
0067:            // POS_HANDLES: INTPROPC * 4 bytes  : INTPROPC Integer properties, randomly accessible
0068:            // POS_TXTPROPS: TXTPROPC * TXTPROPW : an array of TXTPROPC byte arrays of width TXTPROPW that can hold any string
0069:            // POS_NODES : (USEDC + FREEC) * (overhead + sum(all: COLWIDTHS)) : Node Objects
0070:
0071:            // values that are only present at run-time
0072:            protected String filename; // the database's file name
0073:            protected kelondroIOChunks entryFile; // the database file
0074:            protected int overhead; // OHBYTEC + 4 * OHHANDLEC = size of additional control bytes
0075:            protected int headchunksize;// overheadsize + key element column size
0076:            protected int tailchunksize;// sum(all: COLWIDTHS) minus the size of the key element colum
0077:            protected int recordsize; // (overhead + sum(all: COLWIDTHS)) = the overall size of a record
0078:            private byte[] spaceChunk; // a chunk of data that is used to reserve space within the file
0079:
0080:            // dynamic run-time seek pointers
0081:            private long POS_HANDLES = 0; // starts after end of POS_COLWIDHS which is POS_COLWIDTHS + COLWIDTHS.length * 4
0082:            private long POS_TXTPROPS = 0; // starts after end of POS_HANDLES which is POS_HANDLES + HANDLES.length * 4
0083:            protected long POS_NODES = 0; // starts after end of POS_TXTPROPS which is POS_TXTPROPS + TXTPROPS.length * TXTPROPW
0084:
0085:            // dynamic variables that are back-ups of stored values in file; read/defined on instantiation
0086:            protected usageControl USAGE; // counter for used and re-use records and pointer to free-list
0087:            protected short OHBYTEC; // number of extra bytes in each node
0088:            protected short OHHANDLEC; // number of handles in each node
0089:            protected kelondroRow ROW; // array with widths of columns
0090:            private kelondroHandle HANDLES[]; // array with handles
0091:            private byte[] TXTPROPS[]; // array with text properties
0092:            private int TXTPROPW; // size of a single TXTPROPS element
0093:
0094:            // optional logger
0095:            protected Logger theLogger = Logger.getLogger("KELONDRO"); // default logger
0096:
0097:            // tracking of file cration
0098:            protected boolean fileExisted;
0099:
0100:            // Random. This is used to shift flush-times of write-buffers to differrent time
0101:            private static Random random = new Random(System
0102:                    .currentTimeMillis());
0103:
0104:            // check for debug mode
0105:            public static boolean debugmode = false;
0106:            static {
0107:                assert debugmode = true;
0108:            }
0109:
0110:            protected final class usageControl {
0111:                protected int USEDC; // counter of used elements
0112:                protected int FREEC; // counter of free elements in list of free Nodes
0113:                protected kelondroHandle FREEH; // pointer to first element in list of free Nodes, empty = NUL
0114:
0115:                protected usageControl(boolean init) throws IOException {
0116:                    if (init) {
0117:                        this .USEDC = 0;
0118:                        this .FREEC = 0;
0119:                        this .FREEH = new kelondroHandle(kelondroHandle.NUL);
0120:                    } else {
0121:                        readfree();
0122:                        readused();
0123:                        try {
0124:                            int rest = (int) ((entryFile.length() - POS_NODES) % recordsize);// == 0 : "rest = " + ((entryFile.length()  - POS_NODES) % ((long) recordsize)) + ", USEDC = " + this.USEDC + ", FREEC = " + this.FREEC  + ", recordsize = " + recordsize + ", file = " + filename;
0125:                            int calculated_used = (int) ((entryFile.length() - POS_NODES) / recordsize);
0126:                            if ((rest != 0)
0127:                                    || (calculated_used != this .USEDC
0128:                                            + this .FREEC)) {
0129:                                theLogger.log(Level.WARNING,
0130:                                        "USEDC inconsistency at startup: calculated_used = "
0131:                                                + calculated_used
0132:                                                + ", USEDC = " + this .USEDC
0133:                                                + ", FREEC = " + this .FREEC
0134:                                                + ", recordsize = "
0135:                                                + recordsize + ", file = "
0136:                                                + filename);
0137:                                this .USEDC = calculated_used - this .FREEC;
0138:                                if (this .USEDC < 0) {
0139:                                    theLogger.log(Level.WARNING,
0140:                                            "USEDC inconsistency at startup: cannot recover "
0141:                                                    + filename);
0142:                                    throw new kelondroException(
0143:                                            "cannot recover inconsistency in "
0144:                                                    + filename);
0145:                                }
0146:                                writeused(true);
0147:                            }
0148:                        } catch (IOException e) {
0149:                            assert false;
0150:                        }
0151:                    }
0152:                }
0153:
0154:                private synchronized void writeused(boolean finalwrite)
0155:                        throws IOException {
0156:                    // we write only at close time, not in between. othervise, the read/write head
0157:                    // needs to run up and own all the way between the beginning and the end of the
0158:                    // file for each record. We check consistency beteen file size and
0159:                    if (finalwrite)
0160:                        synchronized (entryFile) {
0161:                            assert this .USEDC >= 0 : "this.USEDC = "
0162:                                    + this .USEDC;
0163:                            entryFile.writeInt(POS_USEDC, this .USEDC);
0164:                            entryFile.commit();
0165:                        }
0166:                }
0167:
0168:                private synchronized void writefree() throws IOException {
0169:                    //synchronized (entryFile) {
0170:                    entryFile.writeInt(POS_FREEC, FREEC);
0171:                    entryFile.writeInt(POS_FREEH, FREEH.index);
0172:                    entryFile.commit();
0173:                    checkConsistency();
0174:                    //}
0175:                }
0176:
0177:                private synchronized void readused() throws IOException {
0178:                    //synchronized (entryFile) {
0179:                    this .USEDC = entryFile.readInt(POS_USEDC);
0180:                    assert this .USEDC >= 0 : "this.USEDC = " + this .USEDC
0181:                            + ", filename = " + filename;
0182:                    //}
0183:                }
0184:
0185:                private synchronized void readfree() throws IOException {
0186:                    //synchronized (entryFile) {
0187:                    this .FREEC = entryFile.readInt(POS_FREEC);
0188:                    this .FREEH = new kelondroHandle(entryFile
0189:                            .readInt(POS_FREEH));
0190:                    //}
0191:                }
0192:
0193:                protected synchronized int allCount() {
0194:                    checkConsistency();
0195:                    return this .USEDC + this .FREEC;
0196:                }
0197:
0198:                private synchronized int used() {
0199:                    checkConsistency();
0200:                    return this .USEDC;
0201:                }
0202:
0203:                protected synchronized void dispose(kelondroHandle h)
0204:                        throws IOException {
0205:                    // delete element with handle h
0206:                    // this element is then connected to the deleted-chain and can be
0207:                    // re-used change counter
0208:                    assert (h.index >= 0);
0209:                    assert (h.index != kelondroHandle.NUL);
0210:                    //synchronized (USAGE) {
0211:                    synchronized (entryFile) {
0212:                        assert (h.index < USEDC + FREEC) : "USEDC = " + USEDC
0213:                                + ", FREEC = " + FREEC + ", h.index = "
0214:                                + h.index;
0215:                        long sp = seekpos(h);
0216:                        assert (sp <= entryFile.length() + ROW.objectsize) : h.index
0217:                                + "/"
0218:                                + sp
0219:                                + " exceeds file size "
0220:                                + entryFile.length();
0221:                        USEDC--;
0222:                        FREEC++;
0223:                        // change pointer
0224:                        entryFile.writeInt(sp, FREEH.index); // extend free-list
0225:                        // write new FREEH Handle link
0226:                        FREEH = h;
0227:                        writefree();
0228:                        writeused(false);
0229:                    }
0230:                    //}
0231:                }
0232:
0233:                protected synchronized int allocatePayload(byte[] chunk)
0234:                        throws IOException {
0235:                    // reserves a new record and returns index of record
0236:                    // the return value is not a seek position
0237:                    // the seek position can be retrieved using the seekpos() function
0238:                    if (chunk == null) {
0239:                        chunk = spaceChunk;
0240:                    }
0241:                    assert (chunk.length == ROW.objectsize) : "chunk.length = "
0242:                            + chunk.length + ", ROW.objectsize() = "
0243:                            + ROW.objectsize;
0244:                    //synchronized (USAGE) {
0245:                    synchronized (entryFile) {
0246:                        if (USAGE.FREEC == 0) {
0247:                            // generate new entry
0248:                            int index = USAGE.allCount();
0249:                            entryFile.write(seekpos(index) + overhead, chunk,
0250:                                    0, ROW.objectsize); // occupy space, othervise the USAGE computaton does not work
0251:                            USAGE.USEDC++;
0252:                            writeused(false);
0253:                            return index;
0254:                        } else {
0255:                            // re-use record from free-list
0256:                            USAGE.USEDC++;
0257:                            USAGE.FREEC--;
0258:                            // take link
0259:                            int index = USAGE.FREEH.index;
0260:                            if (index == kelondroHandle.NUL) {
0261:                                serverLog.logSevere("kelondroTray/" + filename,
0262:                                        "INTERNAL ERROR (DATA INCONSISTENCY): re-use of records failed, lost "
0263:                                                + (USAGE.FREEC + 1)
0264:                                                + " records.");
0265:                                // try to heal..
0266:                                USAGE.USEDC = USAGE.allCount() + 1;
0267:                                USAGE.FREEC = 0;
0268:                                index = USAGE.USEDC - 1;
0269:                            } else {
0270:                                //System.out.println("*DEBUG* ALLOCATED DELETED INDEX " + index);
0271:                                // check for valid seek position
0272:                                long seekp = seekpos(USAGE.FREEH);
0273:                                if (seekp >= entryFile.length()) {
0274:                                    // this is a severe inconsistency. try to heal..
0275:                                    serverLog
0276:                                            .logSevere(
0277:                                                    "kelondroTray/" + filename,
0278:                                                    "new Handle: lost "
0279:                                                            + USAGE.FREEC
0280:                                                            + " marked nodes; seek position "
0281:                                                            + seekp
0282:                                                            + "/"
0283:                                                            + USAGE.FREEH.index
0284:                                                            + " out of file size "
0285:                                                            + entryFile
0286:                                                                    .length()
0287:                                                            + "/"
0288:                                                            + ((entryFile
0289:                                                                    .length() - POS_NODES) / recordsize));
0290:                                    index = USAGE.allCount(); // a place at the end of the file
0291:                                    USAGE.USEDC += USAGE.FREEC; // to avoid that non-empty records at the end are overwritten
0292:                                    USAGE.FREEC = 0; // discard all possible empty nodes
0293:                                    USAGE.FREEH.index = kelondroHandle.NUL;
0294:                                } else {
0295:                                    // read link to next element of FREEH chain
0296:                                    USAGE.FREEH.index = entryFile
0297:                                            .readInt(seekp);
0298:                                    assert ((USAGE.FREEH.index == kelondroHandle.NUL) && (USAGE.FREEC == 0))
0299:                                            || seekpos(USAGE.FREEH) < entryFile
0300:                                                    .length() : "allocatePayload: USAGE.FREEH.index = "
0301:                                            + USAGE.FREEH.index
0302:                                            + ", seekp = "
0303:                                            + seekp;
0304:                                }
0305:                            }
0306:                            USAGE.writeused(false);
0307:                            USAGE.writefree();
0308:                            entryFile.write(seekpos(index) + overhead, chunk,
0309:                                    0, ROW.objectsize); // overwrite space
0310:                            return index;
0311:                        }
0312:                    }
0313:                    //}
0314:                }
0315:
0316:                protected synchronized void allocateRecord(int index,
0317:                        byte[] bulkchunk, int offset) throws IOException {
0318:                    // in case that the handle index was created outside this class,
0319:                    // this method ensures that the USAGE counters are consistent with the
0320:                    // new handle index
0321:                    if (bulkchunk == null) {
0322:                        bulkchunk = new byte[recordsize];
0323:                        offset = 0;
0324:                    }
0325:                    //assert (chunk.length == ROW.objectsize()) : "chunk.length = " + chunk.length + ", ROW.objectsize() = " + ROW.objectsize();
0326:                    //synchronized (USAGE) {
0327:                    synchronized (entryFile) {
0328:                        if (index < USAGE.allCount()) {
0329:                            // write within the file
0330:                            // this can be critical, if we simply overwrite fields that are marked
0331:                            // as deleted. This case should be avoided. There is no other way to check
0332:                            // that the field is not occupied than looking at the FREEC counter
0333:                            assert (USAGE.FREEC == 0) : "FREEC = "
0334:                                    + USAGE.FREEC;
0335:                            // simply overwrite the cell
0336:                            entryFile.write(seekpos(index), bulkchunk, offset,
0337:                                    recordsize);
0338:                            // no changes of counter necessary
0339:                        } else {
0340:                            // write beyond the end of the file
0341:                            // records that are in between are marked as deleted
0342:                            kelondroHandle h;
0343:                            while (index > USAGE.allCount()) {
0344:                                h = new kelondroHandle(USAGE.allCount());
0345:                                USAGE.FREEC++;
0346:                                entryFile.write(seekpos(h), spaceChunk); // occupy space, othervise the USAGE computaton does not work
0347:                                entryFile.writeInt(seekpos(h),
0348:                                        USAGE.FREEH.index);
0349:                                USAGE.FREEH = h;
0350:                                assert ((USAGE.FREEH.index == kelondroHandle.NUL) && (USAGE.FREEC == 0))
0351:                                        || seekpos(USAGE.FREEH) < entryFile
0352:                                                .length() : "allocateRecord: USAGE.FREEH.index = "
0353:                                        + USAGE.FREEH.index;
0354:                                USAGE.writefree();
0355:                                entryFile.commit();
0356:                            }
0357:                            assert (index <= USAGE.allCount());
0358:
0359:                            // adopt USAGE.USEDC
0360:                            if (USAGE.allCount() == index) {
0361:                                entryFile.write(seekpos(index), bulkchunk,
0362:                                        offset, recordsize); // write chunk and occupy space
0363:                                USAGE.USEDC++;
0364:                                USAGE.writeused(false);
0365:                                entryFile.commit();
0366:                            }
0367:                        }
0368:                    }
0369:                    //}
0370:                }
0371:
0372:                private synchronized void checkConsistency() {
0373:                    if ((debugmode) && (entryFile != null))
0374:                        try { // in debug mode
0375:                            long efl = entryFile.length();
0376:                            assert ((efl - POS_NODES) % ((long) recordsize)) == 0 : "rest = "
0377:                                    + ((entryFile.length() - POS_NODES) % ((long) recordsize))
0378:                                    + ", USEDC = "
0379:                                    + this .USEDC
0380:                                    + ", FREEC = "
0381:                                    + this .FREEC
0382:                                    + ", recordsize = "
0383:                                    + recordsize + ", file = " + filename;
0384:                            long calculated_used = (efl - POS_NODES)
0385:                                    / ((long) recordsize);
0386:                            if (calculated_used != this .USEDC + this .FREEC)
0387:                                logFailure("INCONSISTENCY in USED computation: calculated_used = "
0388:                                        + calculated_used
0389:                                        + ", USEDC = "
0390:                                        + this .USEDC
0391:                                        + ", FREEC = "
0392:                                        + this .FREEC
0393:                                        + ", recordsize = "
0394:                                        + recordsize + ", file = " + filename);
0395:                        } catch (IOException e) {
0396:                            assert false;
0397:                        }
0398:                }
0399:            }
0400:
0401:            public static int staticsize(File file) {
0402:                if (!(file.exists()))
0403:                    return 0;
0404:                try {
0405:                    kelondroRA ra = new kelondroFileRA(file.getCanonicalPath());
0406:                    kelondroIOChunks entryFile = new kelondroRAIOChunks(ra, ra
0407:                            .name());
0408:
0409:                    int used = entryFile.readInt(POS_USEDC); // works only if consistency with file size is given
0410:                    entryFile.close();
0411:                    ra.close();
0412:                    return used;
0413:                } catch (FileNotFoundException e) {
0414:                    return 0;
0415:                } catch (IOException e) {
0416:                    return 0;
0417:                }
0418:            }
0419:
0420:            public kelondroAbstractRecords(File file, boolean useNodeCache,
0421:                    short ohbytec, short ohhandlec, kelondroRow rowdef,
0422:                    int FHandles, int txtProps, int txtPropWidth)
0423:                    throws IOException {
0424:                // opens an existing file or creates a new file
0425:                // file: the file that shall be created
0426:                // oha : overhead size array of four bytes: oha[0]=# of bytes, oha[1]=# of shorts, oha[2]=# of ints, oha[3]=# of longs, 
0427:                // columns: array with size of column width; columns.length is number of columns
0428:                // FHandles: number of integer properties
0429:                // txtProps: number of text properties
0430:
0431:                this .fileExisted = file.exists(); // can be used by extending class to track if this class created the file
0432:                this .OHBYTEC = ohbytec;
0433:                this .OHHANDLEC = ohhandlec;
0434:                this .ROW = rowdef; // create row
0435:                this .TXTPROPW = txtPropWidth;
0436:
0437:                if (file.exists()) {
0438:                    // opens an existing tree
0439:                    this .filename = file.getCanonicalPath();
0440:                    kelondroRA raf = new kelondroFileRA(this .filename);
0441:                    //kelondroRA raf = new kelondroBufferedRA(new kelondroFileRA(this.filename), 1024, 100);
0442:                    //kelondroRA raf = new kelondroCachedRA(new kelondroFileRA(this.filename), 5000000, 1000);
0443:                    //kelondroRA raf = new kelondroNIOFileRA(this.filename, (file.length() < 4000000), 10000);
0444:                    initExistingFile(raf, useNodeCache);
0445:                } else {
0446:                    this .filename = file.getCanonicalPath();
0447:                    kelondroRA raf = new kelondroFileRA(this .filename);
0448:                    // kelondroRA raf = new kelondroBufferedRA(new kelondroFileRA(this.filename), 1024, 100);
0449:                    // kelondroRA raf = new kelondroNIOFileRA(this.filename, false, 10000);
0450:                    initNewFile(raf, FHandles, txtProps);
0451:                }
0452:                assignRowdef(rowdef);
0453:                if (fileExisted) {
0454:                    kelondroByteOrder oldOrder = readOrderType();
0455:                    if ((oldOrder != null)
0456:                            && (!(oldOrder.equals(rowdef.objectOrder)))) {
0457:                        writeOrderType(); // write new order type
0458:                        //throw new IOException("wrong object order upon initialization. new order is " + rowdef.objectOrder.toString() + ", old order was " + oldOrder.toString());
0459:                    }
0460:                } else {
0461:                    // create new file structure
0462:                    writeOrderType();
0463:                }
0464:            }
0465:
0466:            public kelondroAbstractRecords(kelondroRA ra, String filename,
0467:                    boolean useCache, short ohbytec, short ohhandlec,
0468:                    kelondroRow rowdef, int FHandles, int txtProps,
0469:                    int txtPropWidth, boolean exitOnFail) {
0470:                // this always creates a new file
0471:                this .fileExisted = false;
0472:                this .filename = filename;
0473:                this .OHBYTEC = ohbytec;
0474:                this .OHHANDLEC = ohhandlec;
0475:                this .ROW = rowdef; // create row
0476:                this .TXTPROPW = txtPropWidth;
0477:
0478:                try {
0479:                    initNewFile(ra, FHandles, txtProps);
0480:                } catch (IOException e) {
0481:                    logFailure("cannot create / " + e.getMessage());
0482:                    if (exitOnFail)
0483:                        System.exit(-1);
0484:                }
0485:                assignRowdef(rowdef);
0486:                writeOrderType();
0487:            }
0488:
0489:            public void reset() throws IOException {
0490:                kelondroRA ra = this .entryFile.getRA();
0491:                File f = new File(ra.name());
0492:                this .entryFile.close();
0493:                f.delete();
0494:                ra = new kelondroFileRA(f);
0495:                initNewFile(ra, this .HANDLES.length, this .TXTPROPS.length);
0496:            }
0497:
0498:            private void initNewFile(kelondroRA ra, int FHandles, int txtProps)
0499:                    throws IOException {
0500:
0501:                // create new Chunked IO
0502:                this .entryFile = new kelondroRAIOChunks(ra, ra.name());
0503:
0504:                // store dynamic run-time data
0505:                this .overhead = this .OHBYTEC + 4 * this .OHHANDLEC;
0506:                this .recordsize = this .overhead + ROW.objectsize;
0507:                this .headchunksize = overhead + ROW.width(0);
0508:                this .tailchunksize = this .recordsize - this .headchunksize;
0509:                this .spaceChunk = fillSpaceChunk(recordsize);
0510:
0511:                // store dynamic run-time seek pointers
0512:                POS_HANDLES = POS_COLWIDTHS + ROW.columns() * 4;
0513:                POS_TXTPROPS = POS_HANDLES + FHandles * 4;
0514:                POS_NODES = POS_TXTPROPS + txtProps * this .TXTPROPW;
0515:                //System.out.println("*** DEBUG: POS_NODES = " + POS_NODES + " for " + filename);
0516:
0517:                // store dynamic back-up variables
0518:                USAGE = new usageControl(true);
0519:                HANDLES = new kelondroHandle[FHandles];
0520:                for (int i = 0; i < FHandles; i++)
0521:                    HANDLES[i] = new kelondroHandle(kelondroHandle.NUL);
0522:                TXTPROPS = new byte[txtProps][];
0523:                for (int i = 0; i < txtProps; i++)
0524:                    TXTPROPS[i] = new byte[0];
0525:
0526:                // write data to file
0527:                entryFile.writeByte(POS_MAGIC, 4); // magic marker for this file type
0528:                entryFile.writeByte(POS_BUSY, 0); // unlock: default
0529:                entryFile.writeShort(POS_PORT, 4444); // default port (not used yet)
0530:                entryFile.write(POS_DESCR, "--AnomicRecords file structure--"
0531:                        .getBytes());
0532:                entryFile.writeShort(POS_COLUMNS, this .ROW.columns());
0533:                entryFile.writeShort(POS_OHBYTEC, OHBYTEC);
0534:                entryFile.writeShort(POS_OHHANDLEC, OHHANDLEC);
0535:                entryFile.writeInt(POS_USEDC, 0);
0536:                entryFile.writeInt(POS_FREEC, 0);
0537:                entryFile.writeInt(POS_FREEH, this .USAGE.FREEH.index);
0538:                entryFile.write(POS_MD5PW, "PASSWORDPASSWORD".getBytes());
0539:                entryFile.write(POS_ENCRYPTION, "ENCRYPTION!#$%&?".getBytes());
0540:                entryFile.writeLong(POS_OFFSET, POS_NODES);
0541:                entryFile.writeInt(POS_INTPROPC, FHandles);
0542:                entryFile.writeInt(POS_TXTPROPC, txtProps);
0543:                entryFile.writeInt(POS_TXTPROPW, this .TXTPROPW);
0544:
0545:                // write configuration arrays
0546:                for (int i = 0; i < this .ROW.columns(); i++) {
0547:                    entryFile
0548:                            .writeInt(POS_COLWIDTHS + 4 * i, this .ROW.width(i));
0549:                }
0550:                for (int i = 0; i < this .HANDLES.length; i++) {
0551:                    entryFile.writeInt(POS_HANDLES + 4 * i, kelondroHandle.NUL);
0552:                    HANDLES[i] = new kelondroHandle(kelondroHandle.NUL);
0553:                }
0554:                byte[] ea = new byte[TXTPROPW];
0555:                for (int j = 0; j < TXTPROPW; j++)
0556:                    ea[j] = 0;
0557:                for (int i = 0; i < this .TXTPROPS.length; i++) {
0558:                    entryFile.write(POS_TXTPROPS + TXTPROPW * i, ea);
0559:                }
0560:
0561:                this .entryFile.commit();
0562:            }
0563:
0564:            private static final byte[] fillSpaceChunk(int size) {
0565:                byte[] chunk = new byte[size];
0566:                while (--size >= 0)
0567:                    chunk[size] = (byte) 0xff;
0568:                return chunk;
0569:            }
0570:
0571:            public void setDescription(byte[] description) throws IOException {
0572:                if (description.length > LEN_DESCR)
0573:                    entryFile.write(POS_DESCR, description, 0, LEN_DESCR);
0574:                else
0575:                    entryFile.write(POS_DESCR, description);
0576:            }
0577:
0578:            public byte[] getDescription() throws IOException {
0579:                byte[] b = new byte[LEN_DESCR];
0580:                entryFile.readFully(POS_DESCR, b, 0, LEN_DESCR);
0581:                return b;
0582:            }
0583:
0584:            public void setLogger(Logger newLogger) {
0585:                this .theLogger = newLogger;
0586:            }
0587:
0588:            public void logWarning(String message) {
0589:                if (this .theLogger == null)
0590:                    System.err.println("KELONDRO WARNING " + this .filename
0591:                            + ": " + message);
0592:                else
0593:                    this .theLogger.warning("KELONDRO WARNING " + this .filename
0594:                            + ": " + message);
0595:            }
0596:
0597:            public void logFailure(String message) {
0598:                if (this .theLogger == null)
0599:                    System.err.println("KELONDRO FAILURE " + this .filename
0600:                            + ": " + message);
0601:                else
0602:                    this .theLogger.severe("KELONDRO FAILURE " + this .filename
0603:                            + ": " + message);
0604:            }
0605:
0606:            public void logFine(String message) {
0607:                if (this .theLogger == null)
0608:                    System.out.println("KELONDRO DEBUG " + this .filename + ": "
0609:                            + message);
0610:                else
0611:                    this .theLogger.fine("KELONDRO DEBUG " + this .filename
0612:                            + ": " + message);
0613:            }
0614:
0615:            public kelondroAbstractRecords(kelondroRA ra, String filename,
0616:                    boolean useNodeCache) throws IOException {
0617:                this .fileExisted = false;
0618:                this .filename = filename;
0619:                initExistingFile(ra, useNodeCache);
0620:                readOrderType();
0621:            }
0622:
0623:            private void initExistingFile(kelondroRA ra, boolean useBuffer)
0624:                    throws IOException {
0625:                // read from Chunked IO
0626:                if (useBuffer) {
0627:                    this .entryFile = new kelondroBufferedIOChunks(ra,
0628:                            ra.name(), 0, 30000 + random.nextLong() % 30000);
0629:                } else {
0630:                    this .entryFile = new kelondroRAIOChunks(ra, ra.name());
0631:                }
0632:
0633:                // read dynamic variables that are back-ups of stored values in file;
0634:                // read/defined on instantiation
0635:
0636:                this .OHBYTEC = entryFile.readShort(POS_OHBYTEC);
0637:                this .OHHANDLEC = entryFile.readShort(POS_OHHANDLEC);
0638:
0639:                kelondroColumn[] COLDEFS = new kelondroColumn[entryFile
0640:                        .readShort(POS_COLUMNS)];
0641:                this .HANDLES = new kelondroHandle[entryFile
0642:                        .readInt(POS_INTPROPC)];
0643:                this .TXTPROPS = new byte[entryFile.readInt(POS_TXTPROPC)][];
0644:                this .TXTPROPW = entryFile.readInt(POS_TXTPROPW);
0645:
0646:                if (COLDEFS.length == 0)
0647:                    throw new kelondroException(filename,
0648:                            "init: zero columns; strong failure");
0649:
0650:                // calculate dynamic run-time seek pointers
0651:                POS_HANDLES = POS_COLWIDTHS + COLDEFS.length * 4;
0652:                POS_TXTPROPS = POS_HANDLES + HANDLES.length * 4;
0653:                POS_NODES = POS_TXTPROPS + TXTPROPS.length * TXTPROPW;
0654:                //System.out.println("*** DEBUG: POS_NODES = " + POS_NODES + " for " + filename);
0655:
0656:                // read configuration arrays
0657:                for (int i = 0; i < COLDEFS.length; i++) {
0658:                    COLDEFS[i] = new kelondroColumn("col-" + i,
0659:                            kelondroColumn.celltype_binary,
0660:                            kelondroColumn.encoder_bytes, entryFile
0661:                                    .readInt(POS_COLWIDTHS + 4 * i), "");
0662:                }
0663:                for (int i = 0; i < HANDLES.length; i++) {
0664:                    HANDLES[i] = new kelondroHandle(entryFile
0665:                            .readInt(POS_HANDLES + 4 * i));
0666:                }
0667:                for (int i = 0; i < TXTPROPS.length; i++) {
0668:                    TXTPROPS[i] = new byte[TXTPROPW];
0669:                    entryFile.readFully(POS_TXTPROPS + TXTPROPW * i,
0670:                            TXTPROPS[i], 0, TXTPROPS[i].length);
0671:                }
0672:                this .ROW = new kelondroRow(COLDEFS, readOrderType(), 0);
0673:
0674:                // assign remaining values that are only present at run-time
0675:                this .overhead = OHBYTEC + 4 * OHHANDLEC;
0676:                this .recordsize = this .overhead + ROW.objectsize;
0677:                this .headchunksize = this .overhead + this .ROW.width(0);
0678:                this .tailchunksize = this .recordsize - this .headchunksize;
0679:                this .spaceChunk = fillSpaceChunk(recordsize);
0680:
0681:                // init USAGE, must be done at the end because it needs the recordsize value
0682:                this .USAGE = new usageControl(false);
0683:            }
0684:
0685:            private void writeOrderType() {
0686:                try {
0687:                    setDescription((this .ROW.objectOrder == null) ? "__"
0688:                            .getBytes() : this .ROW.objectOrder.signature()
0689:                            .getBytes());
0690:                } catch (IOException e) {
0691:                }
0692:            }
0693:
0694:            private kelondroByteOrder readOrderType() {
0695:                try {
0696:                    byte[] d = getDescription();
0697:                    String s = new String(d).substring(0, 2);
0698:                    return orderBySignature(s);
0699:                } catch (IOException e) {
0700:                    return null;
0701:                }
0702:            }
0703:
0704:            public static kelondroByteOrder orderBySignature(String signature) {
0705:                kelondroByteOrder oo = null;
0706:                if (oo == null)
0707:                    oo = kelondroNaturalOrder.bySignature(signature);
0708:                if (oo == null)
0709:                    oo = kelondroBase64Order.bySignature(signature);
0710:                if (oo == null)
0711:                    oo = new kelondroNaturalOrder(true);
0712:                return oo;
0713:            }
0714:
0715:            public String filename() {
0716:                return filename;
0717:            }
0718:
0719:            public synchronized final byte[] bulkRead(int start, int end)
0720:                    throws IOException {
0721:                // a bulk read simply reads a piece of memory from the record file
0722:                // this makes only sense if there are no overhead bytes or pointer
0723:                // the end value is OUTSIDE the record interval
0724:                assert OHBYTEC == 0;
0725:                assert OHHANDLEC == 0;
0726:                assert start >= 0;
0727:                assert end <= USAGE.allCount();
0728:                byte[] bulk = new byte[(end - start) * recordsize];
0729:                long bulkstart = POS_NODES + ((long) recordsize * (long) start);
0730:                entryFile.readFully(bulkstart, bulk, 0, bulk.length);
0731:                return bulk;
0732:            }
0733:
0734:            protected synchronized void deleteNode(kelondroHandle handle)
0735:                    throws IOException {
0736:                USAGE.dispose(handle);
0737:            }
0738:
0739:            protected void printChunk(kelondroRow.Entry chunk) {
0740:                for (int j = 0; j < chunk.columns(); j++)
0741:                    System.out.print(new String(chunk.getColBytes(j)) + ", ");
0742:            }
0743:
0744:            public final kelondroRow row() {
0745:                return this .ROW;
0746:            }
0747:
0748:            private final void assignRowdef(kelondroRow rowdef) {
0749:                // overwrites a given rowdef
0750:                // the new rowdef must be compatible
0751:                /*
0752:                if ((rowdef.columns() != ROW.columns()) &&
0753:                    ((rowdef.columns() + 1 != ROW.columns()) ||
0754:                     (rowdef.column(rowdef.columns() - 1).cellwidth() != (ROW.column(ROW.columns() - 1).cellwidth() + ROW.column(ROW.columns() - 2).cellwidth()))))
0755:                    throw new kelondroException(this.filename,
0756:                            "new rowdef '" + rowdef.toString() + "' is not compatible with old rowdef '" + ROW.toString() + "', they have a different number of columns");
0757:                 */
0758:                // adopt encoder and cell type
0759:                /*
0760:                kelondroColumn col;
0761:                for (int i = 0; i < ROW.columns(); i++) {
0762:                    col = rowdef.column(i);
0763:                    ROW.column(i).setAttributes(col.nickname(), col.celltype(), col.encoder());
0764:                }
0765:                 */
0766:                this .ROW = rowdef;
0767:            }
0768:
0769:            protected final long seekpos(kelondroHandle handle) {
0770:                assert (handle.index >= 0) : "handle index too low: "
0771:                        + handle.index;
0772:                return POS_NODES + ((long) recordsize * (long) handle.index);
0773:            }
0774:
0775:            protected final long seekpos(int index) {
0776:                assert (index >= 0) : "handle index too low: " + index;
0777:                return POS_NODES + ((long) recordsize * index);
0778:            }
0779:
0780:            // additional properties
0781:            public final synchronized int handles() {
0782:                return this .HANDLES.length;
0783:            }
0784:
0785:            protected final void setHandle(int pos, kelondroHandle handle)
0786:                    throws IOException {
0787:                if (pos >= HANDLES.length)
0788:                    throw new IllegalArgumentException(
0789:                            "setHandle: handle array exceeded");
0790:                if (handle == null)
0791:                    handle = new kelondroHandle(kelondroHandle.NUL);
0792:                HANDLES[pos] = handle;
0793:                entryFile.writeInt(POS_HANDLES + 4 * pos, handle.index);
0794:            }
0795:
0796:            protected final kelondroHandle getHandle(int pos) {
0797:                if (pos >= HANDLES.length)
0798:                    throw new IllegalArgumentException(
0799:                            "getHandle: handle array exceeded");
0800:                return (HANDLES[pos].index == kelondroHandle.NUL) ? null
0801:                        : HANDLES[pos];
0802:            }
0803:
0804:            // custom texts
0805:            public final void setText(int pos, byte[] text) throws IOException {
0806:                if (pos >= TXTPROPS.length)
0807:                    throw new IllegalArgumentException(
0808:                            "setText: text array exceeded");
0809:                if (text.length > TXTPROPW)
0810:                    throw new IllegalArgumentException(
0811:                            "setText: text lemgth exceeded");
0812:                if (text == null)
0813:                    text = new byte[0];
0814:                TXTPROPS[pos] = text;
0815:                entryFile.write(POS_TXTPROPS + TXTPROPW * pos, text);
0816:            }
0817:
0818:            public final byte[] getText(int pos) {
0819:                if (pos >= TXTPROPS.length)
0820:                    throw new IllegalArgumentException(
0821:                            "getText: text array exceeded");
0822:                return TXTPROPS[pos];
0823:            }
0824:
0825:            // Returns true if this map contains no key-value mappings.
0826:            public final boolean isEmpty() {
0827:                return (USAGE.used() == 0);
0828:            }
0829:
0830:            // Returns the number of key-value mappings in this map.
0831:            public synchronized int size() {
0832:                return USAGE.used();
0833:            }
0834:
0835:            protected synchronized final int free() {
0836:                return USAGE.FREEC;
0837:            }
0838:
0839:            protected final Set<kelondroHandle> deletedHandles(long maxTime)
0840:                    throws kelondroException, IOException {
0841:                // initialize set with deleted nodes; the set contains Handle-Objects
0842:                // this may last only the given maxInitTime
0843:                // if the initTime is exceeded, the method throws an kelondroException
0844:                TreeSet<kelondroHandle> markedDeleted = new TreeSet<kelondroHandle>();
0845:                long timeLimit = (maxTime < 0) ? Long.MAX_VALUE : System
0846:                        .currentTimeMillis()
0847:                        + maxTime;
0848:                long seekp;
0849:                synchronized (USAGE) {
0850:                    if (USAGE.FREEC != 0) {
0851:                        kelondroHandle h = USAGE.FREEH;
0852:                        long repair_position = POS_FREEH;
0853:                        while (h.index != kelondroHandle.NUL) {
0854:                            // check handle
0855:                            seekp = seekpos(h);
0856:                            if (seekp > entryFile.length()) {
0857:                                // repair last hande store position
0858:                                this .theLogger
0859:                                        .severe("KELONDRO WARNING "
0860:                                                + this .filename
0861:                                                + ": seek position "
0862:                                                + seekp
0863:                                                + "/"
0864:                                                + h.index
0865:                                                + " out of file size "
0866:                                                + entryFile.length()
0867:                                                + "/"
0868:                                                + ((entryFile.length() - POS_NODES) / recordsize)
0869:                                                + " after "
0870:                                                + markedDeleted.size()
0871:                                                + " iterations; patched wrong node");
0872:                                entryFile.writeInt(repair_position,
0873:                                        kelondroHandle.NUL);
0874:                                return markedDeleted;
0875:                            }
0876:
0877:                            // handle seems to be correct. store handle
0878:                            markedDeleted.add(h);
0879:
0880:                            // move to next handle
0881:                            repair_position = seekp;
0882:                            h = new kelondroHandle(entryFile.readInt(seekp));
0883:                            if (h.index == kelondroHandle.NUL)
0884:                                break;
0885:
0886:                            // double-check for already stored handles: detect loops
0887:                            if (markedDeleted.contains(h)) {
0888:                                // loop detection
0889:                                this .theLogger.severe("KELONDRO WARNING "
0890:                                        + this .filename
0891:                                        + ": FREE-Queue contains loops");
0892:                                entryFile.writeInt(repair_position,
0893:                                        kelondroHandle.NUL);
0894:                                return markedDeleted;
0895:                            }
0896:
0897:                            // this appears to be correct. go on.
0898:                            if (System.currentTimeMillis() > timeLimit)
0899:                                throw new kelondroException(filename,
0900:                                        "time limit of " + maxTime
0901:                                                + " exceeded; > "
0902:                                                + markedDeleted.size()
0903:                                                + " deleted entries");
0904:                        }
0905:                        System.out.println("\nDEBUG: " + markedDeleted.size()
0906:                                + " deleted entries in " + entryFile.name());
0907:                    }
0908:                }
0909:                return markedDeleted;
0910:            }
0911:
0912:            public synchronized void close() {
0913:                if (entryFile == null) {
0914:                    theLogger.severe("close(): file '" + this .filename
0915:                            + "' was closed before close was called.");
0916:                } else
0917:                    try {
0918:                        USAGE.writeused(true);
0919:                        this .entryFile.close();
0920:                        theLogger.fine("file '" + this .filename + "' closed.");
0921:                    } catch (IOException e) {
0922:                        theLogger.severe("file '" + this .filename
0923:                                + "': failed to close.");
0924:                        e.printStackTrace();
0925:                    }
0926:                this .entryFile = null;
0927:            }
0928:
0929:            public void finalize() {
0930:                if (entryFile != null)
0931:                    close();
0932:                this .entryFile = null;
0933:            }
0934:
0935:            protected final static String[] line2args(String line) {
0936:                // parse the command line
0937:                if ((line == null) || (line.length() == 0))
0938:                    return null;
0939:                String args[];
0940:                StringTokenizer st = new StringTokenizer(line);
0941:
0942:                args = new String[st.countTokens()];
0943:                for (int i = 0; st.hasMoreTokens(); i++) {
0944:                    args[i] = st.nextToken();
0945:                }
0946:                st = null;
0947:                return args;
0948:            }
0949:
0950:            protected final static boolean equals(byte[] a, byte[] b) {
0951:                if (a == b)
0952:                    return true;
0953:                if ((a == null) || (b == null))
0954:                    return false;
0955:                if (a.length != b.length)
0956:                    return false;
0957:                for (int n = 0; n < a.length; n++)
0958:                    if (a[n] != b[n])
0959:                        return false;
0960:                return true;
0961:            }
0962:
0963:            public final static void NUL2bytes(byte[] b, int offset) {
0964:                b[offset] = (byte) (0XFF & (kelondroHandle.NUL >> 24));
0965:                b[offset + 1] = (byte) (0XFF & (kelondroHandle.NUL >> 16));
0966:                b[offset + 2] = (byte) (0XFF & (kelondroHandle.NUL >> 8));
0967:                b[offset + 3] = (byte) (0XFF & kelondroHandle.NUL);
0968:            }
0969:
0970:            public final static void int2bytes(long i, byte[] b, int offset) {
0971:                b[offset] = (byte) (0XFF & (i >> 24));
0972:                b[offset + 1] = (byte) (0XFF & (i >> 16));
0973:                b[offset + 2] = (byte) (0XFF & (i >> 8));
0974:                b[offset + 3] = (byte) (0XFF & i);
0975:            }
0976:
0977:            public final static int bytes2int(byte[] b, int offset) {
0978:                return (((b[offset] & 0xff) << 24)
0979:                        | ((b[offset + 1] & 0xff) << 16)
0980:                        | ((b[offset + 2] & 0xff) << 8) | (b[offset + 3] & 0xff));
0981:            }
0982:
0983:            public void print() throws IOException {
0984:                System.out.println("REPORT FOR FILE '" + this .filename + "':");
0985:                System.out.println("--");
0986:                System.out.println("CONTROL DATA");
0987:                System.out.print("  HANDLES    : " + HANDLES.length
0988:                        + " int-values");
0989:                if (HANDLES.length == 0)
0990:                    System.out.println();
0991:                else {
0992:                    System.out.print("  {" + HANDLES[0].toString());
0993:                    for (int i = 1; i < HANDLES.length; i++)
0994:                        System.out.print(", " + HANDLES[i].toString());
0995:                    System.out.println("}");
0996:                }
0997:                System.out.print("  TXTPROPS   : " + TXTPROPS.length
0998:                        + " strings, max length " + TXTPROPW + " bytes");
0999:                if (TXTPROPS.length == 0)
1000:                    System.out.println();
1001:                else {
1002:                    System.out.print("  {'" + (new String(TXTPROPS[0])).trim());
1003:                    System.out.print("'");
1004:                    for (int i = 1; i < TXTPROPS.length; i++)
1005:                        System.out.print(", '"
1006:                                + (new String(TXTPROPS[i])).trim() + "'");
1007:                    System.out.println("}");
1008:                }
1009:                System.out.println("  USEDC      : " + USAGE.used());
1010:                System.out.println("  FREEC      : " + USAGE.FREEC);
1011:                System.out.println("  FREEH      : " + USAGE.FREEH.toString());
1012:                System.out.println("  NUL repres.: 0x"
1013:                        + Integer.toHexString(kelondroHandle.NUL));
1014:                System.out.println("  Data Offset: 0x"
1015:                        + Long.toHexString(POS_NODES));
1016:                System.out.println("--");
1017:                System.out.println("RECORDS");
1018:                System.out.print("  Columns    : " + row().columns()
1019:                        + " columns  {" + ROW.width(0));
1020:                for (int i = 1; i < row().columns(); i++)
1021:                    System.out.print(", " + ROW.width(i));
1022:                System.out.println("}");
1023:                System.out.println("  Overhead   : " + this .overhead
1024:                        + " bytes  (" + OHBYTEC + " OH bytes, " + OHHANDLEC
1025:                        + " OH Handles)");
1026:                System.out.println("  Recordsize : " + this .recordsize
1027:                        + " bytes");
1028:                System.out.println("--");
1029:                System.out.println("DELETED HANDLES");
1030:                Set<kelondroHandle> dh = deletedHandles(-1);
1031:                Iterator<kelondroHandle> dhi = dh.iterator();
1032:                kelondroHandle h;
1033:                while (dhi.hasNext()) {
1034:                    h = (kelondroHandle) dhi.next();
1035:                    System.out.print(h.index + ", ");
1036:                }
1037:                System.out.println("\n--");
1038:            }
1039:
1040:            public String toString() {
1041:                return size() + " RECORDS IN FILE " + filename;
1042:            }
1043:
1044:            public final Iterator<EntryIndex> contentRows(long maxInitTime)
1045:                    throws kelondroException {
1046:                return new contentRowIterator(maxInitTime);
1047:            }
1048:
1049:            public final class contentRowIterator implements 
1050:                    Iterator<EntryIndex> {
1051:                // iterator that iterates all kelondroRow.Entry-objects in the file
1052:                // all records that are marked as deleted are omitted
1053:
1054:                private Iterator<kelondroNode> nodeIterator;
1055:
1056:                public contentRowIterator(long maxInitTime) {
1057:                    nodeIterator = contentNodes(maxInitTime);
1058:                }
1059:
1060:                public boolean hasNext() {
1061:                    return nodeIterator.hasNext();
1062:                }
1063:
1064:                public EntryIndex next() {
1065:                    try {
1066:                        kelondroNode n = (kelondroNode) nodeIterator.next();
1067:                        return row().newEntryIndex(n.getValueRow(),
1068:                                n.handle().index);
1069:                    } catch (IOException e) {
1070:                        throw new kelondroException(filename, e.getMessage());
1071:                    }
1072:                }
1073:
1074:                public void remove() {
1075:                    throw new UnsupportedOperationException();
1076:                }
1077:
1078:            }
1079:
1080:            protected final Iterator<kelondroNode> contentNodes(long maxInitTime)
1081:                    throws kelondroException {
1082:                // returns an iterator of Node-objects that are not marked as 'deleted'
1083:                try {
1084:                    return new contentNodeIterator(maxInitTime);
1085:                } catch (IOException e) {
1086:                    return new HashSet<kelondroNode>().iterator();
1087:                }
1088:            }
1089:
1090:            protected final class contentNodeIterator implements 
1091:                    Iterator<kelondroNode> {
1092:                // iterator that iterates all Node-objects in the file
1093:                // all records that are marked as deleted are ommitted
1094:                // this is probably also the fastest way to iterate all objects
1095:
1096:                private Set<kelondroHandle> markedDeleted;
1097:                private kelondroHandle pos;
1098:                private byte[] bulk;
1099:                private int bulksize;
1100:                private int bulkstart; // the offset of the bulk array to the node position
1101:                private boolean fullyMarked;
1102:                private kelondroNode next;
1103:
1104:                public contentNodeIterator(long maxInitTime)
1105:                        throws IOException, kelondroException {
1106:                    // initialize markedDeleted set of deleted Handles
1107:                    markedDeleted = deletedHandles(maxInitTime);
1108:                    fullyMarked = (maxInitTime < 0);
1109:
1110:                    // seek first position according the delete node set
1111:                    pos = new kelondroHandle(0);
1112:                    while ((markedDeleted.contains(pos))
1113:                            && (pos.index < USAGE.allCount()))
1114:                        pos.index++;
1115:
1116:                    // initialize bulk
1117:                    bulksize = Math.max(1, Math.min(65536 / recordsize, USAGE
1118:                            .allCount()));
1119:                    bulkstart = -bulksize;
1120:                    bulk = new byte[bulksize * recordsize];
1121:                    next = (hasNext0()) ? next0() : null;
1122:                }
1123:
1124:                public kelondroNode next() {
1125:                    kelondroNode n = next;
1126:                    next = next0();
1127:                    return n;
1128:                }
1129:
1130:                public boolean hasNext() {
1131:                    return next != null;
1132:                }
1133:
1134:                public boolean hasNext0() {
1135:                    return pos.index < USAGE.allCount();
1136:                }
1137:
1138:                public kelondroNode next0() {
1139:                    // read Objects until a non-deleted Node appears
1140:                    while (hasNext0()) {
1141:                        kelondroNode nn;
1142:                        try {
1143:                            nn = next00();
1144:                        } catch (IOException e) {
1145:                            serverLog
1146:                                    .logSevere("kelondroCachedRecords",
1147:                                            filename + " failed with "
1148:                                                    + e.getMessage(), e);
1149:                            return null;
1150:                        }
1151:                        byte[] key = nn.getKey();
1152:                        if ((key == null)
1153:                                || ((key.length == 1) && (key[0] == (byte) 0x80))
1154:                                || // the NUL pointer ('lost' chain terminator)
1155:                                (key.length < 3)
1156:                                || ((key.length > 3) && (key[2] == 0) && (key[3] == 0))
1157:                                || ((key.length > 3) && (key[0] == (byte) 0x80)
1158:                                        && (key[1] == 0) && (key[2] == 0) && (key[3] == 0))
1159:                                || ((key.length > 0) && (key[0] == 0)) // a 'lost' pointer within a deleted-chain
1160:                        ) {
1161:                            // this is a deleted node; probably not commited with dispose
1162:                            if (fullyMarked)
1163:                                try {
1164:                                    USAGE.dispose(nn.handle());
1165:                                } catch (IOException e) {
1166:                                } // mark this now as deleted
1167:                            continue;
1168:                        }
1169:                        return nn;
1170:                    }
1171:                    return null;
1172:                }
1173:
1174:                public kelondroNode next00() throws IOException {
1175:                    // see if the next record is in the bulk, and if not re-fill the bulk
1176:                    if (pos.index >= (bulkstart + bulksize)) {
1177:                        bulkstart = pos.index;
1178:                        int maxlength = Math.min(USAGE.allCount() - bulkstart,
1179:                                bulksize);
1180:                        if ((((long) POS_NODES) + ((long) bulkstart)
1181:                                * ((long) recordsize)) < 0)
1182:                            serverLog.logSevere("kelondroCachedRecords",
1183:                                    "DEBUG: negative offset. POS_NODES = "
1184:                                            + POS_NODES + ", bulkstart = "
1185:                                            + bulkstart + ", recordsize = "
1186:                                            + recordsize);
1187:                        if ((maxlength * recordsize) < 0)
1188:                            serverLog.logSevere("kelondroCachedRecords",
1189:                                    "DEBUG: negative length. maxlength = "
1190:                                            + maxlength + ", recordsize = "
1191:                                            + recordsize);
1192:                        entryFile.readFully(((long) POS_NODES)
1193:                                + ((long) bulkstart) * ((long) recordsize),
1194:                                bulk, 0, maxlength * recordsize);
1195:                    }
1196:                    /* POS_NODES = 302, bulkstart = 3277, recordsize = 655386
1197:                       POS_NODES = 302, bulkstart = 820, recordsize = 2621466
1198:                       POS_NODES = 302, bulkstart = 13106, recordsize = 163866 */
1199:                    // read node from bulk
1200:                    kelondroNode n = newNode(new kelondroHandle(pos.index),
1201:                            bulk, (pos.index - bulkstart) * recordsize);
1202:                    pos.index++;
1203:                    while ((markedDeleted.contains(pos))
1204:                            && (pos.index < USAGE.allCount()))
1205:                        pos.index++;
1206:                    return n;
1207:                }
1208:
1209:                public void remove() {
1210:                    throw new UnsupportedOperationException();
1211:                }
1212:
1213:            }
1214:
1215:            public static byte[] trimCopy(byte[] a, int offset, int length) {
1216:                if (length > a.length - offset)
1217:                    length = a.length - offset;
1218:                while ((length > 0) && (a[offset + length - 1] == 0))
1219:                    length--;
1220:                if (length == 0)
1221:                    return null;
1222:                byte[] b = new byte[length];
1223:                System.arraycopy(a, offset, b, 0, length);
1224:                return b;
1225:            }
1226:
1227:            public abstract kelondroNode newNode(kelondroHandle handle,
1228:                    byte[] bulk, int offset) throws IOException;
1229:
1230:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.