KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > dbi > DatabaseImpl


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: DatabaseImpl.java,v 1.156 2006/11/27 22:44:19 mark Exp $
7  */

8
9 package com.sleepycat.je.dbi;
10
11 import java.io.ByteArrayInputStream JavaDoc;
12 import java.io.ByteArrayOutputStream JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.io.ObjectInputStream JavaDoc;
15 import java.io.ObjectOutputStream JavaDoc;
16 import java.io.PrintStream JavaDoc;
17 import java.nio.ByteBuffer JavaDoc;
18 import java.util.Collections JavaDoc;
19 import java.util.Comparator JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Set JavaDoc;
25
26 import com.sleepycat.je.BtreeStats;
27 import com.sleepycat.je.Cursor;
28 import com.sleepycat.je.Database;
29 import com.sleepycat.je.DatabaseConfig;
30 import com.sleepycat.je.DatabaseEntry;
31 import com.sleepycat.je.DatabaseException;
32 import com.sleepycat.je.DatabaseStats;
33 import com.sleepycat.je.DbInternal;
34 import com.sleepycat.je.LockMode;
35 import com.sleepycat.je.OperationStatus;
36 import com.sleepycat.je.PreloadConfig;
37 import com.sleepycat.je.PreloadStats;
38 import com.sleepycat.je.PreloadStatus;
39 import com.sleepycat.je.SecondaryDatabase;
40 import com.sleepycat.je.StatsConfig;
41 import com.sleepycat.je.VerifyConfig;
42 import com.sleepycat.je.cleaner.UtilizationTracker;
43 import com.sleepycat.je.config.EnvironmentParams;
44 import com.sleepycat.je.dbi.SortedLSNTreeWalker.ExceptionPredicate;
45 import com.sleepycat.je.dbi.SortedLSNTreeWalker.TreeNodeProcessor;
46 import com.sleepycat.je.latch.LatchSupport;
47 import com.sleepycat.je.log.LogEntryType;
48 import com.sleepycat.je.log.LogException;
49 import com.sleepycat.je.log.LogFileNotFoundException;
50 import com.sleepycat.je.log.LogReadable;
51 import com.sleepycat.je.log.LogUtils;
52 import com.sleepycat.je.log.LogWritable;
53 import com.sleepycat.je.recovery.Checkpointer;
54 import com.sleepycat.je.tree.BIN;
55 import com.sleepycat.je.tree.ChildReference;
56 import com.sleepycat.je.tree.DBIN;
57 import com.sleepycat.je.tree.DIN;
58 import com.sleepycat.je.tree.DupCountLN;
59 import com.sleepycat.je.tree.IN;
60 import com.sleepycat.je.tree.LN;
61 import com.sleepycat.je.tree.Node;
62 import com.sleepycat.je.tree.Tree;
63 import com.sleepycat.je.tree.TreeUtils;
64 import com.sleepycat.je.tree.TreeWalkerStatsAccumulator;
65 import com.sleepycat.je.tree.WithRootLatched;
66 import com.sleepycat.je.txn.Locker;
67 import com.sleepycat.je.txn.ThreadLocker;
68 import com.sleepycat.je.utilint.CmdUtil;
69 import com.sleepycat.je.utilint.DbLsn;
70 import com.sleepycat.je.utilint.TestHook;
71
72 /**
73  * The underlying object for a given database.
74  */

75 public class DatabaseImpl
76     implements LogWritable, LogReadable, Cloneable JavaDoc {
77
78     /*
79      * Delete processing states. See design note on database deletion and
80      * truncation
81      */

82     private static final short NOT_DELETED = 1;
83     private static final short DELETED_CLEANUP_INLIST_HARVEST = 2;
84     private static final short DELETED_CLEANUP_LOG_HARVEST = 3;
85     private static final short DELETED = 4;
86
87     private DatabaseId id; // unique id
88
private Tree tree;
89     private EnvironmentImpl envImpl; // Tree operations find the env this way
90
private boolean duplicatesAllowed; // duplicates allowed
91
private boolean transactional; // All open handles are transactional
92
private boolean deferredWrite; // deferred write mode set
93
private Set JavaDoc referringHandles; // Set of open Database handles
94
private BtreeStats stats; // most recent btree stats w/ !DB_FAST_STAT
95
private long eofNodeId; // Logical EOF node for range locking
96
private short deleteState; // one of four delete states.
97

98     /*
99      * The user defined Btree and duplicate comparison functions, if specified.
100      */

101     private Comparator JavaDoc btreeComparator = null;
102     private Comparator JavaDoc duplicateComparator = null;
103     private byte[] btreeComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
104     private byte[] duplicateComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
105     private boolean btreeComparatorByClassName = false;
106     private boolean duplicateComparatorByClassName = false;
107
108     /*
109      * Cache some configuration values.
110      */

111     private int binDeltaPercent;
112     private int binMaxDeltas;
113     private int maxMainTreeEntriesPerNode;
114     private int maxDupTreeEntriesPerNode;
115
116     /*
117      * The debugDatabaseName is used for error messages only, to avoid
118      * accessing the db mapping tree in error situations. Currently it's not
119      * guaranteed to be transactionally correct, nor is it updated by rename.
120      */

121     private String JavaDoc debugDatabaseName;
122
123     /* For unit tests */
124     private TestHook pendingDeletedHook;
125
126     /**
127      * Create a database object for a new database.
128      */

129     public DatabaseImpl(String JavaDoc dbName,
130             DatabaseId id,
131             EnvironmentImpl envImpl,
132             DatabaseConfig dbConfig)
133         throws DatabaseException {
134
135         this.id = id;
136         this.envImpl = envImpl;
137         setBtreeComparator(dbConfig.getBtreeComparator(),
138                            dbConfig.getBtreeComparatorByClassName());
139         setDuplicateComparator(dbConfig.getDuplicateComparator(),
140                                dbConfig.getDuplicateComparatorByClassName());
141         duplicatesAllowed = dbConfig.getSortedDuplicates();
142         transactional = dbConfig.getTransactional();
143         deferredWrite = dbConfig.getDeferredWrite();
144     maxMainTreeEntriesPerNode = dbConfig.getNodeMaxEntries();
145     maxDupTreeEntriesPerNode = dbConfig.getNodeMaxDupTreeEntries();
146
147         initDefaultSettings();
148
149         deleteState = NOT_DELETED;
150
151         /*
152          * The tree needs the env, make sure we assign it before
153          * allocating the tree.
154          */

155         tree = new Tree(this);
156         referringHandles = Collections.synchronizedSet(new HashSet JavaDoc());
157
158         eofNodeId = Node.getNextNodeId();
159
160         /* For error messages only. */
161         debugDatabaseName = dbName;
162     }
163
164     /**
165      * Create an empty database object for initialization from the log. Note
166      * that the rest of the initialization comes from readFromLog(), except
167      * for the debugDatabaseName, which is set by the caller.
168      */

169     public DatabaseImpl()
170         throws DatabaseException {
171
172         id = new DatabaseId();
173         envImpl = null;
174
175         deleteState = NOT_DELETED;
176
177         tree = new Tree();
178         referringHandles = Collections.synchronizedSet(new HashSet JavaDoc());
179
180         /* initDefaultSettings is called after envImpl is set. */
181
182         eofNodeId = Node.getNextNodeId();
183     }
184
185     public void setDebugDatabaseName(String JavaDoc debugName) {
186         debugDatabaseName = debugName;
187     }
188
189     public String JavaDoc getDebugName() {
190         return debugDatabaseName;
191     }
192
193     /* For unit testing only. */
194     public void setPendingDeletedHook(TestHook hook) {
195         pendingDeletedHook = hook;
196     }
197
198     /**
199      * Initialize configuration settings when creating a new instance or after
200      * reading an instance from the log. The envImpl field must be set before
201      * calling this method.
202      */

203     private void initDefaultSettings()
204         throws DatabaseException {
205
206         DbConfigManager configMgr = envImpl.getConfigManager();
207
208         binDeltaPercent =
209             configMgr.getInt(EnvironmentParams.BIN_DELTA_PERCENT);
210         binMaxDeltas =
211             configMgr.getInt(EnvironmentParams.BIN_MAX_DELTAS);
212
213     if (maxMainTreeEntriesPerNode == 0) {
214             maxMainTreeEntriesPerNode =
215         configMgr.getInt(EnvironmentParams.NODE_MAX);
216     }
217
218     if (maxDupTreeEntriesPerNode == 0) {
219             maxDupTreeEntriesPerNode =
220         configMgr.getInt(EnvironmentParams.NODE_MAX_DUPTREE);
221     }
222     }
223
224     /**
225      * Clone. For now just pass off to the super class for a field-by-field
226      * copy.
227      */

228     public Object JavaDoc clone()
229         throws CloneNotSupportedException JavaDoc {
230
231         return super.clone();
232     }
233
234     /**
235      * @return the database tree.
236      */

237     public Tree getTree() {
238         return tree;
239     }
240
241     void setTree(Tree tree) {
242         this.tree = tree;
243     }
244
245     /**
246      * @return the database id.
247      */

248     public DatabaseId getId() {
249         return id;
250     }
251
252     void setId(DatabaseId id) {
253         this.id = id;
254     }
255
256     public long getEofNodeId() {
257         return eofNodeId;
258     }
259
260     /**
261      * @return true if this database is transactional.
262      */

263     public boolean isTransactional() {
264         return transactional;
265     }
266
267     /**
268      * Sets the transactional property for the first opened handle.
269      */

270     public void setTransactional(boolean transactional) {
271         this.transactional = transactional;
272     }
273
274     /**
275      * @return true if this database is in deferred write mode.
276      */

277     public boolean isDeferredWrite() {
278         return deferredWrite;
279     }
280
281     /*
282      * Set the deferred write property for the first opened handle.
283      */

284     public void setDeferredWrite(boolean deferredWrite) {
285         this.deferredWrite = deferredWrite;
286     }
287
288     /**
289      * @return true if duplicates are allowed in this database.
290      */

291     public boolean getSortedDuplicates() {
292         return duplicatesAllowed;
293     }
294
295     public int getNodeMaxEntries() {
296     return maxMainTreeEntriesPerNode;
297     }
298
299     public int getNodeMaxDupTreeEntries() {
300     return maxDupTreeEntriesPerNode;
301     }
302
303     /**
304      * Returns the memory size that should be added to MAPLN_OVERHEAD.
305      *
306      * This is a start at budgeting per-Database memory. For future reference,
307      * other things that could be budgeted are:
308      * - debugDatabaseName as it is set
309      * - Database handles as they are added/removed in referringHandles
310      */

311     public int getAdditionalMemorySize() {
312
313         int val = 0;
314         
315         /*
316          * If the comparator object is non-null we double the size of the
317          * serialized form to account for the approximate size of the user's
318          * comparator object. This is only an approximation of course, and is
319          * not a very good one if we have serialized the class name, but we
320          * have no way to know the size of the user's object.
321          */

322         if (btreeComparator != null) {
323             val += 2 * MemoryBudget.byteArraySize
324                 (btreeComparatorBytes.length);
325         }
326         if (duplicateComparator != null) {
327             val += 2 * MemoryBudget.byteArraySize
328                 (duplicateComparatorBytes.length);
329         }
330         return val;
331     }
332
333     /**
334      * Set the duplicate comparison function for this database.
335      *
336      * @param duplicateComparator - The Duplicate Comparison function.
337      */

338     public void setDuplicateComparator(Comparator JavaDoc comparator,
339                                        boolean byClassName)
340         throws DatabaseException {
341
342         duplicateComparator = comparator;
343         duplicateComparatorBytes =
344             comparatorToBytes(comparator, byClassName, "Duplicate");
345         duplicateComparatorByClassName = byClassName;
346     }
347
348     /**
349      * Set the btree comparison function for this database.
350      *
351      * @param btreeComparator - The btree Comparison function.
352      */

353     public void setBtreeComparator(Comparator JavaDoc comparator,
354                                    boolean byClassName)
355         throws DatabaseException {
356
357         btreeComparator = comparator;
358         btreeComparatorBytes =
359             comparatorToBytes(comparator, byClassName, "Btree");
360         btreeComparatorByClassName = byClassName;
361     }
362
363     /**
364      * @return the btree Comparator object.
365      */

366     public Comparator JavaDoc getBtreeComparator() {
367         return btreeComparator;
368     }
369
370     /**
371      * @return the duplicate Comparator object.
372      */

373     public Comparator JavaDoc getDuplicateComparator() {
374         return duplicateComparator;
375     }
376
377     /**
378      * @return whether Comparator is set by class name, not by serializable
379      * Comparator object.
380      */

381     public boolean getBtreeComparatorByClass() {
382         return btreeComparatorByClassName;
383     }
384
385     /**
386      * @return whether Comparator is set by class name, not by serializable
387      * Comparator object.
388      */

389     public boolean getDuplicateComparatorByClass() {
390         return duplicateComparatorByClassName;
391     }
392
393     /**
394      * Set the db environment during recovery, after instantiating the database
395      * from the log
396      */

397     public void setEnvironmentImpl(EnvironmentImpl envImpl)
398         throws DatabaseException {
399
400         this.envImpl = envImpl;
401         initDefaultSettings();
402         tree.setDatabase(this);
403     }
404
405     /**
406      * @return the database environment.
407      */

408     public EnvironmentImpl getDbEnvironment() {
409         return envImpl;
410     }
411
412     /**
413      * Returns whether one or more handles are open.
414      */

415     public boolean hasOpenHandles() {
416         return referringHandles.size() > 0;
417     }
418
419     /**
420      * Add a referring handle
421      */

422     public void addReferringHandle(Database db) {
423         referringHandles.add(db);
424     }
425
426     /**
427      * Decrement the reference count.
428      */

429     public void removeReferringHandle(Database db) {
430         referringHandles.remove(db);
431     }
432
433     /**
434      * @return the referring handle count.
435      */

436     synchronized int getReferringHandleCount() {
437         return referringHandles.size();
438     }
439
440     /**
441      * Flush all dirty nodes for this database to disk.
442      */

443     public synchronized void sync(boolean flushLog)
444         throws DatabaseException {
445
446         if (!isDeferredWrite()) {
447             throw new DatabaseException("Database.sync() is only supported " +
448                                         "for deferred-write databases");
449         }
450
451     if (tree.rootExists()) {
452         Checkpointer.syncDatabase(envImpl, this, flushLog);
453     }
454     }
455
456     /**
457      * For this secondary database return the primary that it is associated
458      * with, or null if not associated with any primary. Note that not all
459      * handles need be associated with a primary.
460      */

461     public Database findPrimaryDatabase()
462         throws DatabaseException {
463
464         for (Iterator JavaDoc i = referringHandles.iterator(); i.hasNext();) {
465             Object JavaDoc obj = i.next();
466             if (obj instanceof SecondaryDatabase) {
467                 return ((SecondaryDatabase) obj).getPrimaryDatabase();
468             }
469         }
470         return null;
471     }
472
473     public String JavaDoc getName()
474         throws DatabaseException {
475
476         return envImpl.getDbMapTree().getDbName(id);
477     }
478
479     /*
480      * @return true if this database is deleted. Delete cleanup
481      * may still be in progress.
482      */

483     public boolean isDeleted() {
484         return !(deleteState == NOT_DELETED);
485     }
486
487     /*
488      * @return true if this database is deleted and all cleanup is finished.
489      */

490     public boolean isDeleteFinished() {
491         return (deleteState == DELETED);
492     }
493
494     /*
495      * The delete cleanup is starting. Set this before releasing any
496      * write locks held for a db operation.
497      */

498     public void startDeleteProcessing() {
499         assert (deleteState == NOT_DELETED);
500
501         deleteState = DELETED_CLEANUP_INLIST_HARVEST;
502     }
503
504     /*
505      * Should be called by the SortedLSNTreeWalker when it is finished with
506      * the INList.
507      */

508     void finishedINListHarvest() {
509         assert (deleteState == DELETED_CLEANUP_INLIST_HARVEST);
510
511         deleteState = DELETED_CLEANUP_LOG_HARVEST;
512     }
513
514     /**
515      * Purge a DatabaseImpl and corresponding MapLN in the db mapping tree.
516      * Purging consists of removing all related INs from the db mapping tree
517      * and deleting the related MapLN.
518      * Used at the transaction end in these cases:
519      * - purge the deleted database after a commit of
520      * Environment.removeDatabase
521      * - purge the deleted database after a commit of
522      * Environment.truncateDatabase
523      * - purge the newly created database after an abort of
524      * Environment.truncateDatabase
525      */

526     public void deleteAndReleaseINs()
527         throws DatabaseException {
528         
529         startDeleteProcessing();
530         releaseDeletedINs();
531     }
532
533     public void releaseDeletedINs()
534         throws DatabaseException {
535
536         if (pendingDeletedHook != null) {
537             pendingDeletedHook.doHook();
538         }
539
540         try {
541
542             /*
543              * Get the root LSN before deleting the MapLN, as that will null
544              * out the root.
545              */

546             long rootLsn = tree.getRootLsn();
547             if (rootLsn == DbLsn.NULL_LSN) {
548
549                 /*
550                  * There's nothing in this database. (It might be the abort of
551                  * a truncation, where we are trying to clean up the new, blank
552                  * database. Do delete the MapLN.
553                  */

554                 envImpl.getDbMapTree().deleteMapLN(id);
555
556             } else {
557
558                 UtilizationTracker snapshot = new UtilizationTracker(envImpl);
559
560                 /*
561                  * Start by recording the lsn of the root IN as obsolete. A
562                  * zero size is passed for the last parameter because it is too
563                  * expensive to fetch the node.
564                  */

565                 snapshot.countObsoleteNodeInexact
566                     (rootLsn, LogEntryType.LOG_IN, 0);
567
568                 /* Fetch LNs to count LN sizes only if so configured. */
569                 boolean fetchLNSize =
570                     envImpl.getCleaner().getFetchObsoleteSize();
571
572                 /* Use the tree walker to visit every child lsn in the tree. */
573                 ObsoleteProcessor obsoleteProcessor =
574                     new ObsoleteProcessor(snapshot);
575                 SortedLSNTreeWalker walker = new ObsoleteTreeWalker
576                     (this, rootLsn, fetchLNSize, obsoleteProcessor);
577
578                 /*
579                  * Delete MapLN before the walk. Note that the processing of
580                  * the naming tree means this MapLN is never actually
581                  * accessible from the current tree, but deleting the MapLN
582                  * will do two things:
583                  * (a) mark it properly obsolete
584                  * (b) null out the database tree, leaving the INList the only
585                  * reference to the INs.
586                  */

587                 envImpl.getDbMapTree().deleteMapLN(id);
588
589                 /*
590                  * At this point, it's possible for the evictor to find an IN
591                  * for this database on the INList. It should be ignored.
592                  */

593                 walker.walk();
594
595                 /*
596                  * Count obsolete nodes for a deleted database at transaction
597                  * end time. Write out the modified file summaries for
598                  * recovery.
599                  */

600                 envImpl.getUtilizationProfile().countAndLogSummaries
601                     (snapshot.getTrackedFiles());
602             }
603         } finally {
604             deleteState = DELETED;
605         }
606     }
607
608     public void checkIsDeleted(String JavaDoc operation)
609         throws DatabaseException {
610
611         if (isDeleted()) {
612             throw new DatabaseException
613                 ("Attempt to " + operation + " a deleted database");
614         }
615     }
616
617     private static class ObsoleteTreeWalker extends SortedLSNTreeWalker {
618
619         private ObsoleteTreeWalker(DatabaseImpl dbImpl,
620                                    long rootLsn,
621                                    boolean fetchLNSize,
622                                    TreeNodeProcessor callback)
623             throws DatabaseException {
624
625             super(dbImpl,
626                   true, // remove INs from INList
627
true, // set INList finish harvest
628
rootLsn,
629                   callback,
630                   null, /* savedException */
631                   null); /* exception predicate */
632
633         accumulateLNs = fetchLNSize;
634         }
635     }
636
637     /* Mark each LSN obsolete in the utilization tracker. */
638     private static class ObsoleteProcessor implements TreeNodeProcessor {
639
640         private UtilizationTracker tracker;
641
642         ObsoleteProcessor(UtilizationTracker tracker) {
643             this.tracker = tracker;
644         }
645
646         public void processLSN(long childLsn,
647                    LogEntryType childType,
648                    Node node,
649                                byte[] lnKey)
650         throws DatabaseException {
651
652             assert childLsn != DbLsn.NULL_LSN;
653             
654             /* Count the LN log size if an LN node and key are available. */
655             int size = 0;
656             if (lnKey != null && node instanceof LN) {
657                 size = ((LN) node).getTotalLastLoggedSize(lnKey);
658             }
659
660             tracker.countObsoleteNodeInexact(childLsn, childType, size);
661         }
662
663     public void processDupCount(long ignore) {
664     }
665     }
666
667     public DatabaseStats stat(StatsConfig config)
668         throws DatabaseException {
669
670         if (stats == null) {
671
672             /*
673              * Called first time w/ FAST_STATS so just give them an
674              * empty one.
675              */

676             stats = new BtreeStats();
677         }
678
679         if (!config.getFast()) {
680             if (tree == null) {
681                 return new BtreeStats();
682             }
683
684             PrintStream JavaDoc out = config.getShowProgressStream();
685             if (out == null) {
686                 out = System.err;
687             }
688
689         StatsAccumulator statsAcc =
690         new StatsAccumulator(out,
691                      config.getShowProgressInterval(),
692                                      getEmptyStats());
693         walkDatabaseTree(statsAcc, out, true);
694             statsAcc.copyToStats(stats);
695         }
696
697         return stats;
698     }
699
700     /*
701      * @param config verify configuration
702      * @param emptyStats empty database stats, to be filled by this method
703      * @return true if the verify saw no errors.
704      */

705     public boolean verify(VerifyConfig config, DatabaseStats emptyStats)
706         throws DatabaseException {
707
708     if (tree == null) {
709         return true;
710     }
711
712     PrintStream JavaDoc out = config.getShowProgressStream();
713     if (out == null) {
714         out = System.err;
715     }
716
717     StatsAccumulator statsAcc =
718         new StatsAccumulator(out,
719                                  config.getShowProgressInterval(),
720                                  emptyStats) {
721             void verifyNode(Node node) {
722
723             try {
724                 node.verify(null);
725             } catch (DatabaseException INE) {
726                 progressStream.println(INE);
727             }
728             }
729         };
730     boolean ok = walkDatabaseTree(statsAcc, out, config.getPrintInfo());
731     statsAcc.copyToStats(emptyStats);
732         return ok;
733     }
734
735     /* @return the right kind of stats object for this database. */
736     public DatabaseStats getEmptyStats() {
737         return new BtreeStats();
738     }
739
740     /*
741      * @return true if no errors.
742      */

743     private boolean walkDatabaseTree(TreeWalkerStatsAccumulator statsAcc,
744                                      PrintStream JavaDoc out,
745                                      boolean verbose)
746         throws DatabaseException {
747
748         boolean ok = true;
749         Locker locker = new ThreadLocker(envImpl);
750         Cursor cursor = null;
751     CursorImpl impl = null;
752         try {
753         EnvironmentImpl.incThreadLocalReferenceCount();
754             cursor = DbInternal.newCursor(this, locker, null);
755         impl = DbInternal.getCursorImpl(cursor);
756         tree.setTreeStatsAccumulator(statsAcc);
757
758         /*
759          * This will only be used on the first call for the position()
760          * call.
761          */

762         impl.setTreeStatsAccumulator(statsAcc);
763             DatabaseEntry foundData = new DatabaseEntry();
764             DatabaseEntry key = new DatabaseEntry();
765             OperationStatus status = DbInternal.position
766                 (cursor, key, foundData, LockMode.READ_UNCOMMITTED, true);
767             while (status == OperationStatus.SUCCESS) {
768         try {
769             status = DbInternal.retrieveNext
770             (cursor, key, foundData, LockMode.READ_UNCOMMITTED,
771              GetMode.NEXT);
772         } catch (DatabaseException DBE) {
773                     ok = false;
774             if (DbInternal.advanceCursor(cursor, key, foundData)) {
775                         if (verbose) {
776                             out.println("Error encountered (continuing):");
777                             out.println(DBE);
778                             printErrorRecord(out, key, foundData);
779                         }
780                     } else {
781                         throw DBE;
782                     }
783         }
784             }
785         } finally {
786         if (impl != null) {
787         impl.setTreeStatsAccumulator(null);
788         }
789         tree.setTreeStatsAccumulator(null);
790         EnvironmentImpl.decThreadLocalReferenceCount();
791             if (cursor != null) {
792                 cursor.close();
793             }
794         }
795
796         return ok;
797     }
798
799     /**
800      * Prints the key and data, if available, for a BIN entry that could not be
801      * read/verified. Uses the same format as DbDump and prints both the hex
802      * and printable versions of the entries.
803      */

804     private void printErrorRecord(PrintStream JavaDoc out,
805                                   DatabaseEntry key,
806                                   DatabaseEntry data) {
807
808         byte[] bytes = key.getData();
809         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Error Key ");
810         if (bytes == null) {
811             sb.append("UNKNOWN");
812         } else {
813             CmdUtil.formatEntry(sb, bytes, false);
814             sb.append(' ');
815             CmdUtil.formatEntry(sb, bytes, true);
816         }
817         out.println(sb);
818
819         bytes = data.getData();
820         sb = new StringBuffer JavaDoc("Error Data ");
821         if (bytes == null) {
822             sb.append("UNKNOWN");
823         } else {
824             CmdUtil.formatEntry(sb, bytes, false);
825             sb.append(' ');
826             CmdUtil.formatEntry(sb, bytes, true);
827         }
828         out.println(sb);
829     }
830
831     static class StatsAccumulator implements TreeWalkerStatsAccumulator {
832     private Set JavaDoc inNodeIdsSeen = new HashSet JavaDoc();
833     private Set JavaDoc binNodeIdsSeen = new HashSet JavaDoc();
834     private Set JavaDoc dinNodeIdsSeen = new HashSet JavaDoc();
835     private Set JavaDoc dbinNodeIdsSeen = new HashSet JavaDoc();
836     private Set JavaDoc dupCountLNsSeen = new HashSet JavaDoc();
837     private long[] insSeenByLevel = null;
838     private long[] binsSeenByLevel = null;
839     private long[] dinsSeenByLevel = null;
840     private long[] dbinsSeenByLevel = null;
841     private long lnCount = 0;
842     private long deletedLNCount = 0;
843     private int mainTreeMaxDepth = 0;
844     private int duplicateTreeMaxDepth = 0;
845     private DatabaseStats useStats;
846
847     PrintStream JavaDoc progressStream;
848     int progressInterval;
849
850     /* The max levels we ever expect to see in a tree. */
851     private static final int MAX_LEVELS = 100;
852
853     StatsAccumulator(PrintStream JavaDoc progressStream,
854              int progressInterval,
855                          DatabaseStats useStats) {
856
857         this.progressStream = progressStream;
858         this.progressInterval = progressInterval;
859
860         insSeenByLevel = new long[MAX_LEVELS];
861         binsSeenByLevel = new long[MAX_LEVELS];
862         dinsSeenByLevel = new long[MAX_LEVELS];
863         dbinsSeenByLevel = new long[MAX_LEVELS];
864         
865         this.useStats = useStats;
866     }
867
868     void verifyNode(Node node) {
869
870     }
871
872     public void processIN(IN node, Long JavaDoc nid, int level) {
873         if (inNodeIdsSeen.add(nid)) {
874         tallyLevel(level, insSeenByLevel);
875         verifyNode(node);
876         }
877     }
878
879     public void processBIN(BIN node, Long JavaDoc nid, int level) {
880         if (binNodeIdsSeen.add(nid)) {
881         tallyLevel(level, binsSeenByLevel);
882         verifyNode(node);
883         }
884     }
885
886     public void processDIN(DIN node, Long JavaDoc nid, int level) {
887         if (dinNodeIdsSeen.add(nid)) {
888         tallyLevel(level, dinsSeenByLevel);
889         verifyNode(node);
890         }
891     }
892
893     public void processDBIN(DBIN node, Long JavaDoc nid, int level) {
894         if (dbinNodeIdsSeen.add(nid)) {
895         tallyLevel(level, dbinsSeenByLevel);
896         verifyNode(node);
897         }
898     }
899
900     public void processDupCountLN(DupCountLN node, Long JavaDoc nid) {
901         dupCountLNsSeen.add(nid);
902         verifyNode(node);
903     }
904
905     private void tallyLevel(int levelArg, long[] nodesSeenByLevel) {
906         int level = levelArg;
907         if (level >= IN.DBMAP_LEVEL) {
908         return;
909         }
910         if (level >= IN.MAIN_LEVEL) {
911         level &= ~IN.MAIN_LEVEL;
912         if (level > mainTreeMaxDepth) {
913             mainTreeMaxDepth = level;
914         }
915         } else {
916         if (level > duplicateTreeMaxDepth) {
917             duplicateTreeMaxDepth = level;
918         }
919         }
920
921         nodesSeenByLevel[level]++;
922     }
923
924     public void incrementLNCount() {
925         lnCount++;
926         if (progressInterval != 0) {
927         if ((lnCount % progressInterval) == 0) {
928                     copyToStats(useStats);
929             progressStream.println(useStats);
930         }
931         }
932     }
933
934     public void incrementDeletedLNCount() {
935         deletedLNCount++;
936     }
937
938     Set JavaDoc getINNodeIdsSeen() {
939         return inNodeIdsSeen;
940     }
941
942     Set JavaDoc getBINNodeIdsSeen() {
943         return binNodeIdsSeen;
944     }
945
946     Set JavaDoc getDINNodeIdsSeen() {
947         return dinNodeIdsSeen;
948     }
949
950     Set JavaDoc getDBINNodeIdsSeen() {
951         return dbinNodeIdsSeen;
952     }
953
954     long[] getINsByLevel() {
955         return insSeenByLevel;
956     }
957
958     long[] getBINsByLevel() {
959         return binsSeenByLevel;
960     }
961
962     long[] getDINsByLevel() {
963         return dinsSeenByLevel;
964     }
965
966     long[] getDBINsByLevel() {
967         return dbinsSeenByLevel;
968     }
969
970     long getLNCount() {
971         return lnCount;
972     }
973
974     Set JavaDoc getDupCountLNCount() {
975         return dupCountLNsSeen;
976     }
977
978     long getDeletedLNCount() {
979         return deletedLNCount;
980     }
981
982     int getMainTreeMaxDepth() {
983         return mainTreeMaxDepth;
984     }
985
986     int getDuplicateTreeMaxDepth() {
987         return duplicateTreeMaxDepth;
988     }
989
990     private void copyToStats(DatabaseStats stats) {
991             BtreeStats bStats = (BtreeStats) stats;
992         bStats.setInternalNodeCount(getINNodeIdsSeen().size());
993         bStats.setBottomInternalNodeCount
994         (getBINNodeIdsSeen().size());
995         bStats.setDuplicateInternalNodeCount
996         (getDINNodeIdsSeen().size());
997         bStats.setDuplicateBottomInternalNodeCount
998         (getDBINNodeIdsSeen().size());
999         bStats.setLeafNodeCount(getLNCount());
1000        bStats.setDeletedLeafNodeCount(getDeletedLNCount());
1001        bStats.setDupCountLeafNodeCount
1002        (getDupCountLNCount().size());
1003        bStats.setMainTreeMaxDepth(getMainTreeMaxDepth());
1004        bStats.setDuplicateTreeMaxDepth(getDuplicateTreeMaxDepth());
1005        bStats.setINsByLevel(getINsByLevel());
1006        bStats.setBINsByLevel(getBINsByLevel());
1007        bStats.setDINsByLevel(getDINsByLevel());
1008        bStats.setDBINsByLevel(getDBINsByLevel());
1009    }
1010    }
1011
1012    /**
1013     * Preload exceptions, classes, callbacks.
1014     */

1015
1016    /**
1017     * Undeclared exception used to throw through SortedLSNTreeWalker code
1018     * when preload has either filled the user's max byte or time request.
1019     */

1020    private static class HaltPreloadException extends RuntimeException JavaDoc {
1021
1022    private PreloadStatus status;
1023
1024    HaltPreloadException(PreloadStatus status) {
1025        super(status.toString());
1026        this.status = status;
1027    }
1028
1029    PreloadStatus getStatus() {
1030        return status;
1031    }
1032    }
1033
1034    private static final HaltPreloadException
1035    TIME_EXCEEDED_PRELOAD_EXCEPTION =
1036    new HaltPreloadException(PreloadStatus.EXCEEDED_TIME);
1037
1038    private static final HaltPreloadException
1039    MEMORY_EXCEEDED_PRELOAD_EXCEPTION =
1040    new HaltPreloadException(PreloadStatus.FILLED_CACHE);
1041
1042    /**
1043     * The processLSN() code for PreloadLSNTreeWalker.
1044     */

1045    private static class PreloadProcessor implements TreeNodeProcessor {
1046
1047    private EnvironmentImpl envImpl;
1048    private long maxBytes;
1049    private long targetTime;
1050    private PreloadStats stats;
1051
1052    PreloadProcessor(EnvironmentImpl envImpl,
1053             long maxBytes,
1054             long targetTime,
1055             PreloadStats stats) {
1056        this.envImpl = envImpl;
1057        this.maxBytes = maxBytes;
1058        this.targetTime = targetTime;
1059        this.stats = stats;
1060    }
1061
1062    /**
1063     * Called for each LSN that the SortedLSNTreeWalker encounters.
1064     */

1065        public void processLSN(long childLsn,
1066                   LogEntryType childType,
1067                   Node ignore,
1068                   byte[] ignore2)
1069        throws DatabaseException {
1070
1071            assert childLsn != DbLsn.NULL_LSN;
1072
1073        /*
1074         * Check if we've exceeded either the max time or max bytes
1075         * allowed for this preload() call.
1076         */

1077        if (System.currentTimeMillis() > targetTime) {
1078        throw TIME_EXCEEDED_PRELOAD_EXCEPTION;
1079        }
1080
1081        if (envImpl.getMemoryBudget().getCacheMemoryUsage() > maxBytes) {
1082        throw MEMORY_EXCEEDED_PRELOAD_EXCEPTION;
1083        }
1084
1085        /* Count entry types to return in the PreloadStats. */
1086        if (childType.equals(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL) ||
1087        childType.equals(LogEntryType.LOG_DUPCOUNTLN)) {
1088        stats.nDupCountLNsLoaded++;
1089        } else if (childType.equals(LogEntryType.LOG_LN_TRANSACTIONAL) ||
1090               childType.equals(LogEntryType.LOG_LN)) {
1091        stats.nLNsLoaded++;
1092            } else if (childType.equals(LogEntryType.LOG_DBIN)) {
1093        stats.nDBINsLoaded++;
1094        } else if (childType.equals(LogEntryType.LOG_BIN)) {
1095        stats.nBINsLoaded++;
1096        } else if (childType.equals(LogEntryType.LOG_DIN)) {
1097        stats.nDINsLoaded++;
1098        } else if (childType.equals(LogEntryType.LOG_IN)) {
1099        stats.nINsLoaded++;
1100        }
1101        }
1102
1103    public void processDupCount(long ignore) {
1104    }
1105    }
1106
1107    /*
1108     * An extension of SortedLSNTreeWalker that provides an LSN to IN/index
1109     * map. When an LSN is processed by the tree walker, the map is used to
1110     * lookup the parent IN and child entry index of each LSN processed by the
1111     * tree walker.
1112     */

1113    private static class PreloadLSNTreeWalker extends SortedLSNTreeWalker {
1114
1115    /* LSN -> INEntry */
1116    private Map JavaDoc lsnINMap = new HashMap JavaDoc();
1117
1118    /* struct to hold IN/entry-index pair. */
1119    private static class INEntry {
1120        INEntry(IN in, int index) {
1121        this.in = in;
1122        this.index = index;
1123        }
1124
1125        IN in;
1126        int index;
1127    }
1128
1129    PreloadLSNTreeWalker(DatabaseImpl db,
1130                 TreeNodeProcessor callback,
1131                 PreloadConfig conf)
1132        throws DatabaseException {
1133
1134        super(db, false, false, db.tree.getRootLsn(), callback,
1135          null, null); /* savedException, exception predicate */
1136        accumulateLNs = conf.getLoadLNs();
1137    }
1138
1139    private final class PreloadWithRootLatched
1140        implements WithRootLatched {
1141
1142        public IN doWork(ChildReference root)
1143        throws DatabaseException {
1144
1145        walkInternal();
1146        return null;
1147        }
1148    }
1149
1150    public void walk()
1151        throws DatabaseException {
1152
1153        WithRootLatched preloadWRL = new PreloadWithRootLatched();
1154        dbImpl.getTree().withRootLatchedExclusive(preloadWRL);
1155    }
1156
1157    /*
1158     * Method to get the Root IN for this DatabaseImpl's tree. Latches
1159     * the root IN.
1160     */

1161    protected IN getRootIN(long rootLsn)
1162        throws DatabaseException {
1163
1164        return dbImpl.getTree().getRootIN(false);
1165    }
1166
1167    /*
1168     * Release the latch on the root IN.
1169     */

1170    protected void releaseRootIN(IN root)
1171        throws DatabaseException {
1172
1173        root.releaseLatch();
1174    }
1175
1176    /*
1177     * Add an LSN -> IN/index entry to the map.
1178     */

1179    protected void addToLsnINMap(Long JavaDoc lsn, IN in, int index) {
1180        assert in.getDatabase() != null;
1181        lsnINMap.put(lsn, new INEntry(in, index));
1182    }
1183
1184    /*
1185     * Process an LSN. Get & remove its INEntry from the map, then fetch
1186     * the target at the INEntry's IN/index pair. This method will be
1187     * called in sorted LSN order.
1188         *
1189         * We do not bother to set the lnkeyEntry because we never use the
1190         * lnKey parameter in the processLSN method.
1191     */

1192    protected Node fetchLSN(long lsn, DatabaseEntry lnKeyEntry)
1193        throws DatabaseException {
1194
1195        INEntry inEntry = (INEntry) lsnINMap.remove(new Long JavaDoc(lsn));
1196        assert (inEntry != null);
1197        IN in = inEntry.in;
1198        in.latch();
1199        try {
1200        int index = inEntry.index;
1201        if (in.isEntryKnownDeleted(index) ||
1202            in.getLsn(index) != lsn) {
1203            return null;
1204        }
1205        return in.fetchTarget(index);
1206        } finally {
1207        in.releaseLatch();
1208        }
1209    }
1210    }
1211
1212    /**
1213     * Preload the cache, using up to maxBytes bytes or maxMillsecs msec.
1214     */

1215    public PreloadStats preload(PreloadConfig config)
1216    throws DatabaseException {
1217
1218    try {
1219        long maxBytes = config.getMaxBytes();
1220        long maxMillisecs = config.getMaxMillisecs();
1221        long targetTime = Long.MAX_VALUE;
1222        if (maxMillisecs > 0) {
1223        targetTime = System.currentTimeMillis() + maxMillisecs;
1224        }
1225
1226        long cacheBudget = envImpl.getMemoryBudget().getCacheBudget();
1227        if (maxBytes == 0) {
1228        maxBytes = cacheBudget;
1229        } else if (maxBytes > cacheBudget) {
1230        throw new IllegalArgumentException JavaDoc
1231            ("maxBytes parameter to Database.preload() was " +
1232             "specified as " +
1233             maxBytes + " bytes \nbut the cache is only " +
1234             cacheBudget + " bytes.");
1235        }
1236
1237        PreloadStats ret = new PreloadStats();
1238        PreloadProcessor callback =
1239        new PreloadProcessor(envImpl, maxBytes, targetTime, ret);
1240        SortedLSNTreeWalker walker =
1241        new PreloadLSNTreeWalker(this, callback, config);
1242        try {
1243        walker.walk();
1244        } catch (HaltPreloadException HPE) {
1245        ret.status = HPE.getStatus();
1246        }
1247
1248        assert LatchSupport.countLatchesHeld() == 0;
1249        return ret;
1250    } catch (Error JavaDoc E) {
1251        envImpl.invalidate(E);
1252        throw E;
1253    }
1254    }
1255
1256    /**
1257     * The processLSN() code for PreloadLSNTreeWalker.
1258     */

1259    private static class CountProcessor implements TreeNodeProcessor {
1260
1261    private EnvironmentImpl envImpl;
1262    /* Use PreloadStats in case we ever want to count more than LNs. */
1263    private PreloadStats stats;
1264
1265    CountProcessor(EnvironmentImpl envImpl,
1266               PreloadStats stats) {
1267        this.envImpl = envImpl;
1268        this.stats = stats;
1269    }
1270
1271    /**
1272     * Called for each LSN that the SortedLSNTreeWalker encounters.
1273     */

1274        public void processLSN(long childLsn,
1275                   LogEntryType childType,
1276                   Node ignore,
1277                               byte[] ignore2)
1278        throws DatabaseException {
1279
1280        /* Count entry types to return in the PreloadStats. */
1281        if (childType.equals(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL) ||
1282        childType.equals(LogEntryType.LOG_DUPCOUNTLN)) {
1283        /* Don't descend down into the dup tree -- just use the DCL. */
1284        long dupCount = 0;
1285        DupCountLN dcl = (DupCountLN)
1286            envImpl.getLogManager().get(childLsn);
1287        dupCount = dcl.getDupCount();
1288        stats.nLNsLoaded += dupCount;
1289        } else if (childType.equals(LogEntryType.LOG_LN_TRANSACTIONAL) ||
1290               childType.equals(LogEntryType.LOG_LN)) {
1291        stats.nLNsLoaded++;
1292        }
1293        }
1294
1295    /* Used when processing Deferred Write dbs and there are no LSNs. */
1296    public void processDupCount(long count) {
1297        stats.nLNsLoaded += count;
1298    }
1299    }
1300
1301    private static class CountExceptionPredicate
1302    implements ExceptionPredicate {
1303
1304    /*
1305     * Return true if the exception can be ignored.
1306     * LogFileNotFoundException is the only one so far.
1307     */

1308    public boolean ignoreException(Exception JavaDoc e) {
1309        if (e instanceof LogFileNotFoundException) {
1310        return true;
1311        }
1312        return false;
1313    }
1314    }
1315
1316    /**
1317     * Count entries in the database including dups, but don't dirty the cache.
1318     */

1319    public long count()
1320    throws DatabaseException {
1321
1322    try {
1323        PreloadStats ret = new PreloadStats();
1324
1325        CountProcessor callback = new CountProcessor(envImpl, ret);
1326        ExceptionPredicate excPredicate = new CountExceptionPredicate();
1327        SortedLSNTreeWalker walker =
1328        new SortedLSNTreeWalker(this, false, false,
1329                    tree.getRootLsn(), callback, null,
1330                    excPredicate);
1331        /* Don't descend down into the dup tree. Use the DupCountLN. */
1332        walker.setProcessDupTree(false);
1333        if (deferredWrite) {
1334        walker.setPassNullLSNNodes(true);
1335        }
1336        walker.walk();
1337
1338        assert LatchSupport.countLatchesHeld() == 0;
1339        return ret.nLNsLoaded;
1340    } catch (Error JavaDoc E) {
1341        envImpl.invalidate(E);
1342        throw E;
1343    }
1344    }
1345
1346    /*
1347     * Dumping
1348     */

1349    public String JavaDoc dumpString(int nSpaces) {
1350        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1351        sb.append(TreeUtils.indent(nSpaces));
1352        sb.append("<database id=\"" );
1353        sb.append(id.toString());
1354        sb.append("\"");
1355        if (btreeComparator != null) {
1356            sb.append(" btc=\"");
1357            sb.append(getComparatorClassName(btreeComparator));
1358            sb.append("\"");
1359        }
1360        if (duplicateComparator != null) {
1361            sb.append(" dupc=\"");
1362            sb.append(getComparatorClassName(duplicateComparator));
1363            sb.append("\"");
1364        }
1365        sb.append("/>");
1366        return sb.toString();
1367    }
1368
1369    /*
1370     * Logging support
1371     */

1372
1373    /**
1374     * Returns the true last logged size by calling Tree.getLastLoggedSize().
1375     *
1376     * @see MapLN#getLastLoggedSize
1377     * @see Tree#getLastLoggedSize
1378     */

1379    public int getLastLoggedSize() {
1380        return getLogSizeInternal(true);
1381    }
1382
1383    /**
1384     * @see LogWritable#getLogSize
1385     */

1386    public int getLogSize() {
1387        return getLogSizeInternal(false);
1388    }
1389
1390    private int getLogSizeInternal(boolean lastLogged) {
1391        return
1392            id.getLogSize() +
1393            (lastLogged ? tree.getLastLoggedSize() : tree.getLogSize()) +
1394            LogUtils.getBooleanLogSize() +
1395            LogUtils.getByteArrayLogSize(btreeComparatorBytes) +
1396            LogUtils.getByteArrayLogSize(duplicateComparatorBytes) +
1397        (LogUtils.getIntLogSize() * 2);
1398    }
1399
1400    /**
1401     * @see LogWritable#writeToLog
1402     */

1403    public void writeToLog(ByteBuffer JavaDoc logBuffer) {
1404        id.writeToLog(logBuffer);
1405        tree.writeToLog(logBuffer);
1406        LogUtils.writeBoolean(logBuffer, duplicatesAllowed);
1407        LogUtils.writeByteArray(logBuffer, btreeComparatorBytes);
1408        LogUtils.writeByteArray(logBuffer, duplicateComparatorBytes);
1409    LogUtils.writeInt(logBuffer, maxMainTreeEntriesPerNode);
1410    LogUtils.writeInt(logBuffer, maxDupTreeEntriesPerNode);
1411    }
1412
1413    /**
1414     * @see LogReadable#readFromLog
1415     */

1416    public void readFromLog(ByteBuffer JavaDoc itemBuffer, byte entryTypeVersion)
1417        throws LogException {
1418
1419        id.readFromLog(itemBuffer, entryTypeVersion);
1420        tree.readFromLog(itemBuffer, entryTypeVersion);
1421        duplicatesAllowed = LogUtils.readBoolean(itemBuffer);
1422
1423    if (entryTypeVersion >= 2) {
1424            btreeComparatorBytes = LogUtils.readByteArray(itemBuffer);
1425            duplicateComparatorBytes = LogUtils.readByteArray(itemBuffer);
1426        } else {
1427            String JavaDoc btreeClassName = LogUtils.readString(itemBuffer);
1428            String JavaDoc dupClassName = LogUtils.readString(itemBuffer);
1429            if (btreeClassName.length() == 0) {
1430                btreeComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
1431            } else {
1432                btreeComparatorBytes =
1433                    objectToBytes(btreeClassName, "Btree");
1434            }
1435            if (dupClassName.length() == 0) {
1436                duplicateComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
1437            } else {
1438                duplicateComparatorBytes =
1439                    objectToBytes(dupClassName, "Duplicate");
1440            }
1441        }
1442
1443        /* Don't instantiate if comparators are unnecessary (DbPrintLog). */
1444        if (!EnvironmentImpl.getNoComparators()) {
1445            try {
1446        if (btreeComparatorBytes.length != 0) {
1447                    Object JavaDoc obj = bytesToObject
1448                        (btreeComparatorBytes, "Btree");
1449                    if (obj instanceof String JavaDoc) {
1450                        Class JavaDoc cls = Class.forName((String JavaDoc) obj);
1451                        btreeComparator =
1452                            instantiateComparator(cls, "Btree");
1453                        btreeComparatorByClassName = true;
1454                    } else if (obj instanceof Comparator JavaDoc) {
1455                        btreeComparator = (Comparator JavaDoc) obj;
1456                        btreeComparatorByClassName = false;
1457                    } else {
1458                        assert false : obj.getClass().getName();
1459                    }
1460        } else {
1461                    btreeComparator = null;
1462                    btreeComparatorByClassName = false;
1463                }
1464        if (duplicateComparatorBytes.length != 0) {
1465                    Object JavaDoc obj = bytesToObject
1466                        (duplicateComparatorBytes, "Duplicate");
1467                    if (obj instanceof String JavaDoc) {
1468                        Class JavaDoc cls = Class.forName((String JavaDoc) obj);
1469                        duplicateComparator =
1470                            instantiateComparator(cls, "Duplicate");
1471                        duplicateComparatorByClassName = true;
1472                    } else if (obj instanceof Comparator JavaDoc) {
1473                        duplicateComparator = (Comparator JavaDoc) obj;
1474                        duplicateComparatorByClassName = false;
1475                    } else {
1476                        assert false : obj.getClass().getName();
1477                    }
1478                } else {
1479                    duplicateComparator = null;
1480                    duplicateComparatorByClassName = false;
1481        }
1482            } catch (ClassNotFoundException JavaDoc CNFE) {
1483                throw new LogException("couldn't instantiate class comparator",
1484                                       CNFE);
1485            }
1486        }
1487
1488    if (entryTypeVersion >= 1) {
1489        maxMainTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
1490        maxDupTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
1491        }
1492    }
1493
1494    /**
1495     * @see LogReadable#dumpLog
1496     */

1497    public void dumpLog(StringBuffer JavaDoc sb, boolean verbose) {
1498        sb.append("<database>");
1499        id.dumpLog(sb, verbose);
1500        tree.dumpLog(sb, verbose);
1501        sb.append("<dupsort v=\"").append(duplicatesAllowed);
1502        sb.append("\"/>");
1503        sb.append("<btcf name=\"");
1504        sb.append(getComparatorClassName(btreeComparator));
1505        sb.append("\"/>");
1506        sb.append("<dupcf name=\"");
1507        sb.append(getComparatorClassName(duplicateComparator));
1508        sb.append("\"/>");
1509        sb.append("</database>");
1510    }
1511
1512    /**
1513     * @see LogReadable#logEntryIsTransactional
1514     */

1515    public boolean logEntryIsTransactional() {
1516    return false;
1517    }
1518
1519    /**
1520     * @see LogReadable#getTransactionId
1521     */

1522    public long getTransactionId() {
1523    return 0;
1524    }
1525
1526    /**
1527     * Used for log dumping.
1528     */

1529    private static String JavaDoc getComparatorClassName(Comparator JavaDoc comparator) {
1530        if (comparator != null) {
1531            return comparator.getClass().getName();
1532        } else {
1533            return "";
1534        }
1535    }
1536
1537    /**
1538     * Used both to read from the log and to validate a comparator when set in
1539     * DatabaseConfig.
1540     */

1541    public static Comparator JavaDoc instantiateComparator(Class JavaDoc comparator,
1542                                                   String JavaDoc comparatorType)
1543        throws LogException {
1544
1545    if (comparator == null) {
1546        return null;
1547    }
1548
1549        try {
1550        return (Comparator JavaDoc) comparator.newInstance();
1551        } catch (InstantiationException JavaDoc IE) {
1552            throw new LogException
1553                ("Exception while trying to load " + comparatorType +
1554                 " Comparator class: " + IE);
1555        } catch (IllegalAccessException JavaDoc IAE) {
1556            throw new LogException
1557                ("Exception while trying to load " + comparatorType +
1558                 " Comparator class: " + IAE);
1559        }
1560    }
1561
1562    /**
1563     * Used to validate a comparator when set in DatabaseConfig.
1564     */

1565    public static Comparator JavaDoc instantiateComparator(Comparator JavaDoc comparator,
1566                                                   String JavaDoc comparatorType)
1567        throws DatabaseException {
1568
1569    if (comparator == null) {
1570        return null;
1571    }
1572
1573        return (Comparator JavaDoc) bytesToObject
1574            (objectToBytes(comparator, comparatorType), comparatorType);
1575    }
1576
1577    /**
1578     * Converts a comparator object to a serialized byte array, converting to
1579     * a class name String object if byClassName is true.
1580     *
1581     * @throws LogException if the object cannot be serialized.
1582     */

1583    private static byte[] comparatorToBytes(Comparator JavaDoc comparator,
1584                                            boolean byClassName,
1585                                            String JavaDoc comparatorType)
1586        throws DatabaseException {
1587
1588        if (comparator == null) {
1589            return LogUtils.ZERO_LENGTH_BYTE_ARRAY;
1590        } else {
1591            Object JavaDoc obj;
1592            if (byClassName) {
1593                obj = comparator.getClass().getName();
1594            } else {
1595                obj = comparator;
1596            }
1597            return objectToBytes(obj, comparatorType);
1598        }
1599    }
1600
1601    /**
1602     * Converts an arbitrary object to a serialized byte array. Assumes that
1603     * the object given is non-null.
1604     */

1605    public static byte[] objectToBytes(Object JavaDoc obj,
1606                                       String JavaDoc comparatorType)
1607        throws LogException {
1608
1609        try {
1610            ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
1611            ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(baos);
1612            oos.writeObject(obj);
1613            return baos.toByteArray();
1614        } catch (IOException JavaDoc e) {
1615            throw new LogException
1616                ("Exception while trying to load " + comparatorType +
1617                 ": " + e);
1618        }
1619    }
1620
1621    /**
1622     * Converts an arbitrary serialized byte array to an object. Assumes that
1623     * the byte array given is non-null and has a non-zero length.
1624     */

1625    private static Object JavaDoc bytesToObject(byte[] bytes,
1626                                        String JavaDoc comparatorType)
1627        throws LogException {
1628
1629        try {
1630            ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(bytes);
1631            ObjectInputStream JavaDoc ois = new ObjectInputStream JavaDoc(bais);
1632            return ois.readObject();
1633        } catch (IOException JavaDoc e) {
1634            throw new LogException
1635                ("Exception while trying to load " + comparatorType +
1636                 ": " + e);
1637        } catch (ClassNotFoundException JavaDoc e) {
1638            throw new LogException
1639                ("Exception while trying to load " + comparatorType +
1640                 ": " + e);
1641        }
1642    }
1643
1644    public int getBinDeltaPercent() {
1645        return binDeltaPercent;
1646    }
1647
1648    public int getBinMaxDeltas() {
1649        return binMaxDeltas;
1650    }
1651}
1652
Popular Tags