KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: DbTree.java,v 1.169 2006/11/28 13:52:05 mark Exp $
7  */

8
9 package com.sleepycat.je.dbi;
10
11 import java.io.PrintStream JavaDoc;
12 import java.io.UnsupportedEncodingException JavaDoc;
13 import java.nio.ByteBuffer JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import com.sleepycat.je.Database;
19 import com.sleepycat.je.DatabaseConfig;
20 import com.sleepycat.je.DatabaseEntry;
21 import com.sleepycat.je.DatabaseException;
22 import com.sleepycat.je.DatabaseNotFoundException;
23 import com.sleepycat.je.DeadlockException;
24 import com.sleepycat.je.OperationStatus;
25 import com.sleepycat.je.TransactionConfig;
26 import com.sleepycat.je.VerifyConfig;
27 import com.sleepycat.je.dbi.CursorImpl.SearchMode;
28 import com.sleepycat.je.log.LogEntryType;
29 import com.sleepycat.je.log.LogException;
30 import com.sleepycat.je.log.LogReadable;
31 import com.sleepycat.je.log.LogUtils;
32 import com.sleepycat.je.log.LoggableObject;
33 import com.sleepycat.je.tree.ChildReference;
34 import com.sleepycat.je.tree.IN;
35 import com.sleepycat.je.tree.LN;
36 import com.sleepycat.je.tree.MapLN;
37 import com.sleepycat.je.tree.NameLN;
38 import com.sleepycat.je.tree.Tree;
39 import com.sleepycat.je.tree.TreeUtils;
40 import com.sleepycat.je.tree.WithRootLatched;
41 import com.sleepycat.je.txn.AutoTxn;
42 import com.sleepycat.je.txn.BasicLocker;
43 import com.sleepycat.je.txn.LockType;
44 import com.sleepycat.je.txn.Locker;
45
46 /**
47  * Represents the DatabaseImpl Naming Tree.
48  */

49 public class DbTree implements LoggableObject, LogReadable {
50
51     /* The id->DatabaseImpl tree is always id 0 */
52     public static final DatabaseId ID_DB_ID = new DatabaseId(0);
53     /* The name->id tree is always id 1 */
54     public static final DatabaseId NAME_DB_ID = new DatabaseId(1);
55
56     /* Internal databases - the database mapping tree and utilization info. */
57     private static final String JavaDoc ID_DB_NAME = "_jeIdMap";
58     private static final String JavaDoc NAME_DB_NAME = "_jeNameMap";
59     public static final String JavaDoc UTILIZATION_DB_NAME = "_jeUtilization";
60     public static final String JavaDoc REP_OPERATIONS_NAME = "_jeRepOp";
61
62     /* Reserved database names. */
63     private static final String JavaDoc[] RESERVED_DB_NAMES = {
64         ID_DB_NAME,
65         NAME_DB_NAME,
66         UTILIZATION_DB_NAME,
67         REP_OPERATIONS_NAME
68     };
69
70     /* Database id counter, must be accessed w/synchronization. */
71     private int lastAllocatedDbId;
72
73     private DatabaseImpl idDatabase; // map db ids -> databases
74
private DatabaseImpl nameDatabase; // map names -> dbIds
75
private EnvironmentImpl envImpl;
76
77     /**
78      * Create a dbTree from the log.
79      */

80     public DbTree()
81         throws DatabaseException {
82             
83         this.envImpl = null;
84         idDatabase = new DatabaseImpl();
85         idDatabase.setDebugDatabaseName(ID_DB_NAME);
86         nameDatabase = new DatabaseImpl();
87         nameDatabase.setDebugDatabaseName(NAME_DB_NAME);
88     }
89
90     /**
91      * Create a new dbTree for a new environment.
92      */

93     public DbTree(EnvironmentImpl env)
94         throws DatabaseException {
95
96         this.envImpl = env;
97         idDatabase = new DatabaseImpl(ID_DB_NAME,
98                       new DatabaseId(0),
99                       env,
100                       new DatabaseConfig());
101                                   
102         nameDatabase = new DatabaseImpl(NAME_DB_NAME,
103                     new DatabaseId(1),
104                     env,
105                     new DatabaseConfig());
106                                   
107         lastAllocatedDbId = 1;
108     }
109
110     /**
111      * Get the latest allocated id, for checkpoint info.
112      */

113     public synchronized int getLastDbId() {
114         return lastAllocatedDbId;
115     }
116
117     /**
118      * Get the next available database id.
119      */

120     private synchronized int getNextDbId() {
121         return ++lastAllocatedDbId;
122     }
123
124     /**
125      * Initialize the db id, from recovery.
126      */

127     public synchronized void setLastDbId(int maxDbId) {
128         lastAllocatedDbId = maxDbId;
129     }
130
131     private Locker createMapDbLocker(EnvironmentImpl envImpl)
132     throws DatabaseException {
133
134     if (envImpl.isNoLocking()) {
135         return new BasicLocker(envImpl);
136     } else {
137         return new AutoTxn(envImpl, new TransactionConfig());
138     }
139     }
140
141     /**
142      * Set the db environment during recovery, after instantiating the tree
143      * from the log.
144      */

145     void setEnvironmentImpl(EnvironmentImpl envImpl)
146         throws DatabaseException {
147
148         this.envImpl = envImpl;
149         idDatabase.setEnvironmentImpl(envImpl);
150         nameDatabase.setEnvironmentImpl(envImpl);
151     }
152
153     /**
154      * Create a database.
155      *
156      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
157      * level DbTree operation. [#15176]
158      *
159      * @param locker owning locker
160      * @param databaseName identifier for database
161      * @param dbConfig
162      */

163     public synchronized DatabaseImpl createDb(Locker locker,
164                                               String JavaDoc databaseName,
165                                               DatabaseConfig dbConfig,
166                                               Database databaseHandle)
167         throws DatabaseException {
168
169         /* Create a new database object. */
170         DatabaseId newId = new DatabaseId(getNextDbId());
171         DatabaseImpl newDb = new DatabaseImpl(databaseName,
172                           newId,
173                           envImpl,
174                           dbConfig);
175         CursorImpl idCursor = null;
176         CursorImpl nameCursor = null;
177         boolean operationOk = false;
178         Locker idDbLocker = null;
179         try {
180             /* Insert it into name -> id db. */
181             nameCursor = new CursorImpl(nameDatabase, locker);
182             LN nameLN = new NameLN(newId);
183             nameCursor.putLN(databaseName.getBytes("UTF-8"),
184                  nameLN, false);
185
186             /*
187              * If this is a non-handle use, no need to record any handle locks.
188              */

189             if (databaseHandle != null) {
190                 locker.addToHandleMaps(new Long JavaDoc(nameLN.getNodeId()),
191                                        databaseHandle);
192             }
193
194             /* Insert it into id -> name db, in auto commit mode. */
195             idDbLocker = new BasicLocker(envImpl);
196             idCursor = new CursorImpl(idDatabase, idDbLocker);
197             idCursor.putLN(newId.getBytes(), new MapLN(newDb), false);
198             operationOk = true;
199     } catch (UnsupportedEncodingException JavaDoc UEE) {
200         throw new DatabaseException(UEE);
201         } finally {
202             if (idCursor != null) {
203                 idCursor.close();
204             }
205  
206             if (nameCursor != null) {
207                 nameCursor.close();
208             }
209
210             if (idDbLocker != null) {
211                 idDbLocker.operationEnd(operationOk);
212             }
213         }
214
215         return newDb;
216     }
217     /**
218      * Called by the Tree to propagate a root change. If the tree is a data
219      * database, we will write the MapLn that represents this db to the log. If
220      * the tree is one of the mapping dbs, we'll write the dbtree to the log.
221      *
222      * @param db the target db
223      */

224     public void optionalModifyDbRoot(DatabaseImpl db)
225         throws DatabaseException {
226     
227         if (db.isDeferredWrite()) {
228             return;
229         }
230         
231         modifyDbRoot(db);
232     }
233
234     /**
235      * Called by the Tree to propagate a root change. If the tree is a data
236      * database and it's not a deferred write db, we will write the MapLn that
237      * represents this db to the log. If the tree is one of the mapping dbs,
238      * we'll write the dbtree to the log.
239      *
240      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
241      * level DbTree operation. [#15176]
242      *
243      * @param db the target db
244      */

245     public void modifyDbRoot(DatabaseImpl db)
246         throws DatabaseException {
247
248         if (db.getId().equals(ID_DB_ID) ||
249             db.getId().equals(NAME_DB_ID)) {
250             envImpl.logMapTreeRoot();
251         } else {
252             Locker idDbLocker = new BasicLocker(envImpl);
253             CursorImpl cursor = new CursorImpl(idDatabase, idDbLocker);
254             boolean operationOk = false;
255             try {
256                 DatabaseEntry keyDbt =
257             new DatabaseEntry(db.getId().getBytes());
258         MapLN mapLN = null;
259
260         /*
261          * Retry indefinitely in the face of lock timeouts since the
262          * lock on the MapLN is only supposed to be held for short
263          * periods.
264          */

265         while (true) {
266             try {
267             boolean searchOk = (cursor.searchAndPosition
268                         (keyDbt, new DatabaseEntry(),
269                          SearchMode.SET, LockType.WRITE) &
270                         CursorImpl.FOUND) != 0;
271             if (!searchOk) {
272                             throw new DatabaseException(
273                                 "can't find database " + db.getId());
274                         }
275             mapLN = (MapLN)
276                 cursor.getCurrentLNAlreadyLatched(LockType.WRITE);
277                         assert mapLN != null; /* Should be locked. */
278             } catch (DeadlockException DE) {
279             cursor.close();
280             idDbLocker.operationEnd(false);
281             idDbLocker = new BasicLocker(envImpl);
282             cursor = new CursorImpl(idDatabase, idDbLocker);
283             continue;
284             } finally {
285             cursor.releaseBINs();
286             }
287             break;
288         }
289
290         RewriteMapLN writeMapLN = new RewriteMapLN(cursor);
291         mapLN.getDatabase().getTree().
292             withRootLatchedExclusive(writeMapLN);
293
294                 operationOk = true;
295             } finally {
296                 if (cursor != null) {
297                     cursor.close();
298                 }
299
300                 idDbLocker.operationEnd(operationOk);
301             }
302         }
303     }
304
305     private static class RewriteMapLN implements WithRootLatched {
306         private CursorImpl cursor;
307
308         RewriteMapLN(CursorImpl cursor) {
309             this.cursor = cursor;
310         }
311
312         public IN doWork(ChildReference root)
313             throws DatabaseException {
314             
315         DatabaseEntry dataDbt = new DatabaseEntry(new byte[0]);
316         cursor.putCurrent(dataDbt, null, null);
317             return null;
318         }
319     }
320
321     /*
322      * Helper for database operations. This method positions a cursor
323      * on the NameLN that represents this database and write locks it.
324      *
325      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
326      * level DbTree operation. [#15176]
327      */

328     private NameLockResult lockNameLN(Locker locker,
329                                       String JavaDoc databaseName,
330                                       String JavaDoc action)
331         throws DatabaseException {
332
333         /*
334          * We have to return both a cursor on the nameing tree and a
335          * reference to the found DatabaseImpl.
336          */

337         NameLockResult result = new NameLockResult();
338
339         /* Find the existing DatabaseImpl and establish a cursor. */
340         result.dbImpl = getDb(locker, databaseName, null);
341         if (result.dbImpl == null) {
342             throw new DatabaseNotFoundException
343                 ("Attempted to " + action + " non-existent database " +
344                  databaseName);
345         }
346         result.nameCursor = new CursorImpl(nameDatabase, locker);
347
348         try {
349             /* Position the cursor at the specified NameLN. */
350             DatabaseEntry key =
351                 new DatabaseEntry(databaseName.getBytes("UTF-8"));
352             boolean found =
353                 (result.nameCursor.searchAndPosition(key, null, SearchMode.SET,
354                                                      LockType.WRITE) &
355                  CursorImpl.FOUND) != 0;
356             if (!found) {
357                 result.nameCursor.releaseBIN();
358                 result.nameCursor.close();
359                 result.nameCursor = null;
360                 return result;
361             }
362
363             /* Call getCurrentLN to write lock the nameLN. */
364             result.nameLN = (NameLN)
365                 result.nameCursor.getCurrentLNAlreadyLatched(LockType.WRITE);
366             assert result.nameLN != null; /* Should be locked. */
367
368             /*
369              * Check the open handle count after we have the write lock and no
370              * other transactions can open. XXX, another handle using the same
371              * txn could open ...
372              */

373             int handleCount = result.dbImpl.getReferringHandleCount();
374             if (handleCount > 0) {
375                 throw new DatabaseException("Can't " + action + " database " +
376                                             databaseName + "," + handleCount +
377                                             " open Dbs exist");
378             }
379     } catch (UnsupportedEncodingException JavaDoc UEE) {
380             result.nameCursor.releaseBIN();
381             result.nameCursor.close();
382         throw new DatabaseException(UEE);
383         } catch (DatabaseException e) {
384             result.nameCursor.releaseBIN();
385             result.nameCursor.close();
386         throw e;
387         }
388
389         return result;
390     }
391
392     private static class NameLockResult {
393         CursorImpl nameCursor;
394         DatabaseImpl dbImpl;
395         NameLN nameLN;
396     }
397
398     /**
399      * Return true if the operation succeeded, false otherwise.
400      */

401     boolean dbRename(Locker locker, String JavaDoc databaseName, String JavaDoc newName)
402         throws DatabaseException {
403
404         CursorImpl nameCursor = null;
405         try {
406             NameLockResult result = lockNameLN(locker, databaseName, "rename");
407             nameCursor = result.nameCursor;
408             if (nameCursor == null) {
409                 return false;
410             } else {
411
412                 /*
413                  * Rename simply deletes the one entry in the naming
414                  * tree and replaces it with a new one. Remove the
415                  * oldName->dbId entry and insert newName->dbId.
416                  */

417         nameCursor.latchBIN();
418                 nameCursor.delete();
419                 nameCursor.putLN(newName.getBytes("UTF-8"),
420                                  new NameLN(result.dbImpl.getId()), false);
421                 result.dbImpl.setDebugDatabaseName(newName);
422                 return true;
423             }
424     } catch (UnsupportedEncodingException JavaDoc UEE) {
425         throw new DatabaseException(UEE);
426         } finally {
427             if (nameCursor != null) {
428                 nameCursor.releaseBIN();
429                 nameCursor.close();
430             }
431         }
432     }
433
434     /**
435      * Remove the database by deleting the nameLN.
436      */

437     void dbRemove(Locker locker, String JavaDoc databaseName)
438         throws DatabaseException {
439
440         CursorImpl nameCursor = null;
441         try {
442             NameLockResult result = lockNameLN(locker, databaseName, "remove");
443             nameCursor = result.nameCursor;
444             if (nameCursor == null) {
445                 return;
446             } else {
447
448                 /*
449                  * Delete the NameLN. There's no need to mark any Database
450                  * handle invalid, because the handle must be closed when we
451                  * take action and any further use of the handle will re-look
452                  * up the database.
453                  */

454         nameCursor.latchBIN();
455                 nameCursor.delete();
456
457                 /*
458                  * Schedule database for final deletion during commit. This
459                  * should be the last action taken, since this will take
460                  * effect immediately for non-txnal lockers.
461                  */

462                 locker.markDeleteAtTxnEnd(result.dbImpl, true);
463             }
464         } finally {
465             if (nameCursor != null) {
466                 nameCursor.releaseBIN();
467                 nameCursor.close();
468             }
469         }
470     }
471
472     /**
473      * To truncate, remove the database named by databaseName and
474      * create a new database in its place.
475      *
476      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
477      * level DbTree operation. [#15176]
478      *
479      * @param returnCount if true, must return the count of records in the
480      * database, which can be an expensive option.
481      */

482     long truncate(Locker locker,
483                   String JavaDoc databaseName,
484                   boolean returnCount)
485         throws DatabaseException {
486
487         CursorImpl nameCursor = null;
488         Locker idDbLocker = null;
489         try {
490             NameLockResult result = lockNameLN(locker, databaseName,
491                                                "truncate");
492             nameCursor = result.nameCursor;
493             if (nameCursor == null) {
494                 return 0;
495             } else {
496
497                 /*
498                  * Make a new database with an empty tree. Make the
499                  * nameLN refer to the id of the new database.
500                  */

501                 DatabaseId newId = new DatabaseId(getNextDbId());
502                 DatabaseImpl newDb = (DatabaseImpl) result.dbImpl.clone();
503                 newDb.setId(newId);
504                 newDb.setTree(new Tree(newDb));
505             
506                 /*
507                  * Insert the new MapLN into the id tree. Always use
508                  * an AutoTxn on the id databaase, because we can not
509                  * hold long term locks on the mapLN.
510                  */

511                 CursorImpl idCursor = null;
512                 boolean operationOk = false;
513                 try {
514             idDbLocker = new BasicLocker(envImpl);
515                     idCursor = new CursorImpl(idDatabase, idDbLocker);
516                     idCursor.putLN(newId.getBytes(),
517                                    new MapLN(newDb), false);
518                     operationOk = true;
519                 } finally {
520                     if (idCursor != null) {
521                         idCursor.close();
522                     }
523
524                     if (idDbLocker != null) {
525                         idDbLocker.operationEnd(operationOk);
526                     }
527                 }
528                 result.nameLN.setId(newDb.getId());
529
530                 /* If required, count the number of records in the database. */
531                 long recordCount = 0;
532                 if (returnCount) {
533                     recordCount = result.dbImpl.count();
534                 }
535
536                 /* log the nameLN. */
537                 DatabaseEntry dataDbt = new DatabaseEntry(new byte[0]);
538                 nameCursor.putCurrent(dataDbt, null, null);
539
540                 /*
541                  * Marking the lockers should be the last action, since
542                  * it takes effect immediately for non-txnal lockers.
543                  */

544
545                 /* Schedule old database for deletion if txn commits. */
546                 locker.markDeleteAtTxnEnd(result.dbImpl, true);
547
548                 /* Schedule new database for deletion if txn aborts. */
549                 locker.markDeleteAtTxnEnd(newDb, false);
550
551                 return recordCount;
552             }
553
554     } catch (CloneNotSupportedException JavaDoc CNSE) {
555         throw new DatabaseException(CNSE);
556         } finally {
557             if (nameCursor != null) {
558                 nameCursor.releaseBIN();
559                 nameCursor.close();
560             }
561         }
562     }
563
564     /*
565      * Remove the mapLN that refers to this database.
566      *
567      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
568      * level DbTree operation. [#15176]
569      */

570     void deleteMapLN(DatabaseId id)
571         throws DatabaseException {
572
573         Locker idDbLocker = null;
574         boolean operationOk = false;
575         CursorImpl idCursor = null;
576         
577         try {
578         idDbLocker = new BasicLocker(envImpl);
579             idCursor = new CursorImpl(idDatabase, idDbLocker);
580             boolean found =
581                 (idCursor.searchAndPosition(new DatabaseEntry(id.getBytes()),
582                                             null,
583                                             SearchMode.SET,
584                                             LockType.WRITE) &
585                  CursorImpl.FOUND) != 0;
586             if (found) {
587                 idCursor.delete();
588             }
589
590             operationOk = true;
591         } finally {
592             if (idCursor != null) {
593                 idCursor.close();
594             }
595
596             if (idDbLocker != null) {
597                 idDbLocker.operationEnd(operationOk);
598             }
599         }
600     }
601
602     /**
603      * Truncate a database named by databaseName. Return the new DatabaseImpl
604      * object that represents the truncated database. The old one is marked as
605      * deleted.
606      *
607      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
608      * level DbTree operation. [#15176]
609      *
610      * @deprecated This method used by Database.truncate()
611      */

612     TruncateResult truncate(Locker locker,
613                             DatabaseImpl oldDatabase,
614                             boolean returnCount)
615         throws DatabaseException {
616
617         CursorImpl nameCursor = new CursorImpl(nameDatabase, locker);
618
619         try {
620             String JavaDoc databaseName = getDbName(oldDatabase.getId());
621             DatabaseEntry keyDbt =
622                 new DatabaseEntry(databaseName.getBytes("UTF-8"));
623             boolean found =
624                 (nameCursor.searchAndPosition(keyDbt, null,
625                           SearchMode.SET, LockType.WRITE) &
626          CursorImpl.FOUND) != 0;
627             if (!found) {
628
629                 /*
630                  * Should be found, since truncate is instigated from
631                  * Database.truncate();
632                  */

633                 throw new DatabaseException
634                     ("Database " + databaseName + " not found in map tree");
635             }
636
637             /* Call getCurrentLN to write lock the nameLN. */
638             NameLN nameLN = (NameLN)
639                 nameCursor.getCurrentLNAlreadyLatched(LockType.WRITE);
640             assert nameLN != null; /* Should be locked. */
641
642             /*
643              * Check the open handle count after we have the write lock and no
644              * other transactions can open. XXX, another handle using the same
645              * txn could open ...
646              */

647             int handleCount = oldDatabase.getReferringHandleCount();
648             if (handleCount > 1) {
649                 throw new DatabaseException("Can't truncate database " +
650                         databaseName + "," + handleCount +
651                         " open databases exist");
652             }
653             
654             /*
655              * Make a new database with an empty tree. Make the nameLN refer to
656              * the id of the new database.
657              */

658             DatabaseImpl newDb;
659             DatabaseId newId = new DatabaseId(getNextDbId());
660         newDb = (DatabaseImpl) oldDatabase.clone();
661             newDb.setId(newId);
662             newDb.setTree(new Tree(newDb));
663             
664             /* Insert the new db into id -> name map */
665             CursorImpl idCursor = null;
666             boolean operationOk = false;
667             Locker idDbLocker = null;
668             try {
669         idDbLocker = new BasicLocker(envImpl);
670                 idCursor = new CursorImpl(idDatabase, idDbLocker);
671                 idCursor.putLN(newId.getBytes(),
672                    new MapLN(newDb), false);
673                 operationOk = true;
674             } finally {
675                 if (idCursor != null) {
676                     idCursor.close();
677                 }
678
679                 if (idDbLocker != null) {
680                     idDbLocker.operationEnd(operationOk);
681                 }
682             }
683             nameLN.setId(newDb.getId());
684
685             /* count records for the deleted database. */
686             long count = 0;
687             if (returnCount) {
688                 count = oldDatabase.count();
689             }
690
691             /* Schedule database for final deletion during commit. */
692             locker.markDeleteAtTxnEnd(oldDatabase, true);
693
694             /* log the nameLN. */
695             DatabaseEntry dataDbt = new DatabaseEntry(new byte[0]);
696             nameCursor.putCurrent(dataDbt, null, null);
697
698             return new TruncateResult(newDb, (int) count);
699     } catch (CloneNotSupportedException JavaDoc CNSE) {
700         throw new DatabaseException(CNSE);
701     } catch (UnsupportedEncodingException JavaDoc UEE) {
702         throw new DatabaseException(UEE);
703         } finally {
704             nameCursor.releaseBIN();
705             nameCursor.close();
706         }
707     }
708
709     /**
710      * Get a database object given a database name.
711      *
712      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
713      * level DbTree operation. [#15176]
714      *
715      * @param nameLocker is used to access the NameLN. As always, a NullTxn
716      * is used to access the MapLN.
717      * @param databaseName target database
718      * @return null if database doesn't exist
719      */

720     public DatabaseImpl getDb(Locker nameLocker,
721                               String JavaDoc databaseName,
722                               Database databaseHandle)
723         throws DatabaseException {
724
725         try {
726             if (databaseName.equals(ID_DB_NAME)) {
727                 return idDatabase;
728             } else if (databaseName.equals(NAME_DB_NAME)) {
729                 return nameDatabase;
730             }
731
732             /*
733              * Search the nameDatabase tree for the NameLn for this name.
734              * Release locks before searching the id tree
735              */

736             CursorImpl nameCursor = null;
737             DatabaseId id = null;
738             try {
739
740                 nameCursor = new CursorImpl(nameDatabase, nameLocker);
741                 DatabaseEntry keyDbt =
742             new DatabaseEntry(databaseName.getBytes("UTF-8"));
743                 boolean found =
744                     (nameCursor.searchAndPosition(keyDbt, null,
745                           SearchMode.SET,
746                                                   LockType.READ) &
747                      CursorImpl.FOUND) != 0;
748
749                 if (found) {
750                     NameLN nameLN = (NameLN)
751                         nameCursor.getCurrentLNAlreadyLatched(LockType.READ);
752                     assert nameLN != null; /* Should be locked. */
753                     id = nameLN.getId();
754
755                     /*
756                      * If this is a non-handle use, no need to record any
757                      * handle locks.
758                      */

759                     if (databaseHandle != null) {
760                         nameLocker.addToHandleMaps(new Long JavaDoc(nameLN.getNodeId()),
761                                                    databaseHandle);
762                     }
763                 }
764             } finally {
765                 if (nameCursor != null) {
766                     nameCursor.releaseBIN();
767                     nameCursor.close();
768                 }
769             }
770
771             /*
772              * Now search the id tree.
773              */

774             if (id == null) {
775                 return null;
776             } else {
777                 return getDb(id, -1, databaseName);
778             }
779     } catch (UnsupportedEncodingException JavaDoc UEE) {
780         throw new DatabaseException(UEE);
781         }
782     }
783
784     /**
785      * Get a database object based on an id only. Used by recovery, cleaning
786      * and other clients who have an id in hand, and don't have a resident
787      * node, to find the matching database for a given log entry.
788      */

789     public DatabaseImpl getDb(DatabaseId dbId)
790         throws DatabaseException {
791
792         return getDb(dbId, -1);
793     }
794
795     /**
796      * Get a database object based on an id only. Specify the lock timeout to
797      * use, or -1 to use the default timeout. A timeout should normally only
798      * be specified by daemons with their own timeout configuration. public
799      * for unit tests.
800      */

801     public DatabaseImpl getDb(DatabaseId dbId, long lockTimeout)
802         throws DatabaseException {
803
804         return getDb(dbId, lockTimeout, (String JavaDoc) null);
805     }
806
807     /**
808      * Get a database object based on an id only, caching the id-db mapping in
809      * the given map.
810      */

811     public DatabaseImpl getDb(DatabaseId dbId, long lockTimeout, Map JavaDoc dbCache)
812         throws DatabaseException {
813
814         if (dbCache.containsKey(dbId)) {
815             return (DatabaseImpl) dbCache.get(dbId);
816         } else {
817             DatabaseImpl db = getDb(dbId, lockTimeout, (String JavaDoc) null);
818             dbCache.put(dbId, db);
819             return db;
820         }
821     }
822
823     /**
824      * Get a database object based on an id only. Specify the lock timeout to
825      * use, or -1 to use the default timeout. A timeout should normally only
826      * be specified by daemons with their own timeout configuration. public
827      * for unit tests.
828      *
829      * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
830      * level DbTree operation. [#15176]
831      */

832     public DatabaseImpl getDb(DatabaseId dbId,
833                               long lockTimeout,
834                               String JavaDoc dbNameIfAvailable)
835         throws DatabaseException {
836
837         if (dbId.equals(idDatabase.getId())) {
838             /* We're looking for the id database itself. */
839             return idDatabase;
840         } else if (dbId.equals(nameDatabase.getId())) {
841             /* We're looking for the name database itself. */
842             return nameDatabase;
843         } else {
844             Locker locker = new BasicLocker(envImpl);
845             if (lockTimeout != -1) {
846                 locker.setLockTimeout(lockTimeout);
847             }
848
849             /* Scan the tree for this db. */
850             CursorImpl idCursor = null;
851             DatabaseImpl foundDbImpl = null;
852
853         /*
854          * Retry in the face of lock timeouts. Deadlocks may be due to
855          * conflicts with modifyDbRoot.
856          */

857         while (true) {
858                 idCursor = new CursorImpl(idDatabase, locker);
859         try {
860             DatabaseEntry keyDbt = new DatabaseEntry(dbId.getBytes());
861             boolean found =
862             (idCursor.searchAndPosition
863              (keyDbt, new DatabaseEntry(), SearchMode.SET,
864               LockType.READ) &
865              CursorImpl.FOUND) != 0;
866             if (found) {
867             MapLN mapLN = (MapLN)
868                 idCursor.getCurrentLNAlreadyLatched(LockType.READ);
869                         assert mapLN != null; /* Should be locked. */
870                         foundDbImpl = mapLN.getDatabase();
871                     }
872                     break;
873         } catch (DeadlockException DE) {
874             idCursor.close();
875             locker.operationEnd(false);
876             locker = new BasicLocker(envImpl);
877             if (lockTimeout != -1) {
878             locker.setLockTimeout(lockTimeout);
879             }
880             idCursor = new CursorImpl(idDatabase, locker);
881             continue;
882         } finally {
883             idCursor.releaseBIN();
884             idCursor.close();
885             locker.operationEnd(true);
886         }
887         }
888
889             /*
890              * Set the debugging name in the databaseImpl, but only after
891              * recovery had finished setting up the tree.
892              */

893             if (envImpl.isOpen()) {
894                 setDebugNameForDatabaseImpl(foundDbImpl, dbNameIfAvailable);
895             }
896
897             return foundDbImpl;
898         }
899     }
900
901     /*
902      * We need to cache a database name in the dbImpl for later use in error
903      * messages, when it may be unsafe to walk the mapping tree. Finding a
904      * name by id is slow, so minimize the number of times we must set the
905      * debug name. The debug name will only be uninitialized when an existing
906      * databaseImpl is faulted in.
907      */

908     private void setDebugNameForDatabaseImpl(DatabaseImpl dbImpl,
909                                              String JavaDoc dbName)
910         throws DatabaseException {
911         
912         if (dbImpl != null) {
913             if (dbName != null) {
914                 /* If a name was provided, use that. */
915                 dbImpl.setDebugDatabaseName(dbName);
916             } else if (dbImpl.getDebugName() == null) {
917                 /*
918                  * Only worry about searching for a name if the name
919                  * is uninitialized.
920                  */

921                 dbImpl.setDebugDatabaseName(getDbName(dbImpl.getId()));
922             }
923         }
924     }
925
926     /**
927      * Rebuild the IN list after recovery.
928      */

929     public void rebuildINListMapDb()
930         throws DatabaseException {
931
932         idDatabase.getTree().rebuildINList();
933     }
934
935     /*
936      * Verification, must be run while system is quiescent.
937      */

938     public boolean verify(VerifyConfig config, PrintStream JavaDoc out)
939         throws DatabaseException {
940
941     boolean ret = true;
942     try {
943         /* For now, verify all databases. */
944         boolean ok = idDatabase.verify(config,
945                                            idDatabase.getEmptyStats());
946             if (!ok) {
947                 ret = false;
948             }
949
950         ok = nameDatabase.verify(config,
951                                      nameDatabase.getEmptyStats());
952             if (!ok) {
953                 ret = false;
954             }
955     } catch (DatabaseException DE) {
956         ret = false;
957     }
958
959     synchronized (envImpl.getINCompressor()) {
960
961         /*
962          * Get a cursor on the id tree. Use objects at the dbi layer rather
963          * than at the public api, in order to retrieve objects rather than
964          * Dbts. Note that we don't do cursor cloning here, so any failures
965              * from each db verify invalidate the cursor. Use dirty read
966              * (LockMode.NONE) because locks on the MapLN should never be held
967              * for long, as that will cause deadlocks with splits and
968              * checkpointing.
969          */

970         Locker locker = null;
971         CursorImpl cursor = null;
972             LockType lockType = LockType.NONE;
973         try {
974         locker = new BasicLocker(envImpl);
975         cursor = new CursorImpl(idDatabase, locker);
976                 /* Perform eviction when performing multiple operations. */
977                 cursor.setAllowEviction(true);
978         if (cursor.positionFirstOrLast(true, null)) {
979                     MapLN mapLN = (MapLN) cursor.
980                         getCurrentLNAlreadyLatched(lockType);
981
982                     DatabaseEntry keyDbt = new DatabaseEntry();
983                     DatabaseEntry dataDbt = new DatabaseEntry();
984                     while (true) {
985                         if (mapLN != null && !mapLN.isDeleted()) {
986                             DatabaseImpl dbImpl = mapLN.getDatabase();
987                             boolean ok = dbImpl.verify(config,
988                                                        dbImpl.getEmptyStats());
989                             if (!ok) {
990                                 ret = false;
991                             }
992                         }
993                         /* Go on to the next entry. */
994                         OperationStatus status =
995                             cursor.getNext(keyDbt, dataDbt, lockType,
996                                            true, // go forward
997
false); // do need to latch
998
if (status != OperationStatus.SUCCESS) {
999                             break;
1000                        }
1001                        mapLN = (MapLN) cursor.getCurrentLN(lockType);
1002                    }
1003                }
1004        } catch (DatabaseException e) {
1005                e.printStackTrace(out);
1006        ret = false;
1007        } finally {
1008        if (cursor != null) {
1009            cursor.releaseBINs();
1010            cursor.close();
1011        }
1012        if (locker != null) {
1013            locker.operationEnd();
1014        }
1015        }
1016    }
1017
1018    return ret;
1019    }
1020
1021    /**
1022     * Return the database name for a given db. Slow, must traverse. Used by
1023     * truncate and for debugging.
1024     *
1025     * Do not evict (do not call CursorImpl.setAllowEviction(true)) during low
1026     * level DbTree operation. [#15176]
1027     */

1028    public String JavaDoc getDbName(DatabaseId id)
1029        throws DatabaseException {
1030
1031        if (id.equals(ID_DB_ID)) {
1032            return ID_DB_NAME;
1033        } else if (id.equals(NAME_DB_ID)) {
1034            return NAME_DB_NAME;
1035        }
1036
1037        Locker locker = null;
1038        CursorImpl cursor = null;
1039        try {
1040            locker = new BasicLocker(envImpl);
1041            cursor = new CursorImpl(nameDatabase, locker);
1042            /* Use dirty reads (LockType.NONE). */
1043            DatabaseEntry keyDbt = new DatabaseEntry();
1044            DatabaseEntry dataDbt = new DatabaseEntry();
1045            String JavaDoc name = null;
1046            if (cursor.positionFirstOrLast(true, null)) {
1047                /* Fill in the key DatabaseEntry */
1048                OperationStatus status = cursor.getCurrentAlreadyLatched
1049                    (keyDbt, dataDbt, LockType.NONE, true);
1050                do {
1051                    if (status == OperationStatus.SUCCESS) {
1052                        NameLN nameLN = (NameLN) cursor.getCurrentLN
1053                            (LockType.NONE);
1054                        if (nameLN != null && nameLN.getId().equals(id)) {
1055                            name = new String JavaDoc(keyDbt.getData(), "UTF-8");
1056                            break;
1057                        }
1058                    }
1059
1060                    /* Go on to the next entry. */
1061                    status = cursor.getNext(keyDbt, dataDbt, LockType.NONE,
1062                                            true, // go forward
1063
false); // do need to latch
1064
} while (status == OperationStatus.SUCCESS);
1065            }
1066            return name;
1067    } catch (UnsupportedEncodingException JavaDoc UEE) {
1068        throw new DatabaseException(UEE);
1069        } finally {
1070            if (cursor != null) {
1071                cursor.releaseBINs();
1072                cursor.close();
1073            }
1074            if (locker != null) {
1075                locker.operationEnd();
1076            }
1077        }
1078    }
1079    
1080    /**
1081     * @return a list of database names held in the environment, as strings.
1082     */

1083    public List JavaDoc getDbNames()
1084        throws DatabaseException {
1085        
1086        List JavaDoc nameList = new ArrayList JavaDoc();
1087        Locker locker = null;
1088        CursorImpl cursor = null;
1089        try {
1090            locker = new BasicLocker(envImpl);
1091            cursor = new CursorImpl(nameDatabase, locker);
1092            /* Perform eviction when performing multiple operations. */
1093            cursor.setAllowEviction(true);
1094            DatabaseEntry keyDbt = new DatabaseEntry();
1095            DatabaseEntry dataDbt = new DatabaseEntry();
1096            if (cursor.positionFirstOrLast(true, null)) {
1097                OperationStatus status = cursor.getCurrentAlreadyLatched
1098                    (keyDbt, dataDbt, LockType.READ, true);
1099                do {
1100                    if (status == OperationStatus.SUCCESS) {
1101                        String JavaDoc name = new String JavaDoc(keyDbt.getData(), "UTF-8");
1102                        if (!isReservedDbName(name)) {
1103                            nameList.add(name);
1104                        }
1105                    }
1106
1107                    /* Go on to the next entry. */
1108                    status = cursor.getNext(keyDbt, dataDbt, LockType.READ,
1109                                            true, // go forward
1110
false); // do need to latch
1111
} while (status == OperationStatus.SUCCESS);
1112            }
1113            return nameList;
1114    } catch (UnsupportedEncodingException JavaDoc UEE) {
1115        throw new DatabaseException(UEE);
1116        } finally {
1117            if (cursor != null) {
1118                cursor.close();
1119            }
1120
1121            if (locker != null) {
1122                locker.operationEnd();
1123            }
1124        }
1125    }
1126
1127    /**
1128     * Return a list of the names of internally used databases that
1129     * don't get looked up through the naming tree.
1130     */

1131    public List JavaDoc getInternalNoLookupDbNames() {
1132        List JavaDoc names = new ArrayList JavaDoc();
1133        names.add(ID_DB_NAME);
1134        names.add(NAME_DB_NAME);
1135        return names;
1136    }
1137
1138    /**
1139     * Return a list of the names of internally used databases.
1140     */

1141    public List JavaDoc getInternalDbNames() {
1142        List JavaDoc names = new ArrayList JavaDoc();
1143        names.add(UTILIZATION_DB_NAME);
1144        return names;
1145    }
1146
1147    /**
1148     * Returns true if the name is a reserved JE database name.
1149     */

1150    public boolean isReservedDbName(String JavaDoc name) {
1151        for (int i = 0; i < RESERVED_DB_NAMES.length; i += 1) {
1152            if (RESERVED_DB_NAMES[i].equals(name)) {
1153                return true;
1154            }
1155        }
1156        return false;
1157    }
1158
1159    /**
1160     * @return the higest level node in the environment.
1161     */

1162    public int getHighestLevel()
1163        throws DatabaseException {
1164
1165        /* The highest level in the map side */
1166        int idHighLevel = getHighestLevel(idDatabase);
1167
1168        /* The highest level in the name side */
1169        int nameHighLevel = getHighestLevel(nameDatabase);
1170
1171        return (nameHighLevel > idHighLevel) ? nameHighLevel : idHighLevel;
1172    }
1173
1174    /**
1175     * @return the higest level node for this database.
1176     */

1177    public int getHighestLevel(DatabaseImpl dbImpl)
1178        throws DatabaseException {
1179
1180        /* The highest level in the map side */
1181        RootLevel getLevel = new RootLevel(dbImpl);
1182        dbImpl.getTree().withRootLatchedShared(getLevel);
1183        return getLevel.getRootLevel();
1184    }
1185
1186    /*
1187     * RootLevel lets us fetch the root IN within the root latch.
1188     */

1189    private static class RootLevel implements WithRootLatched {
1190        private DatabaseImpl db;
1191        private int rootLevel;
1192
1193        RootLevel(DatabaseImpl db) {
1194            this.db = db;
1195            rootLevel = 0;
1196        }
1197
1198        public IN doWork(ChildReference root)
1199            throws DatabaseException {
1200            
1201            IN rootIN = (IN) root.fetchTarget(db, null);
1202            rootLevel = rootIN.getLevel();
1203            return null;
1204        }
1205
1206        int getRootLevel() {
1207            return rootLevel;
1208        }
1209    }
1210
1211    /*
1212     * LoggableObject
1213     */

1214
1215    /**
1216     * @see LoggableObject#getLogType
1217     */

1218    public LogEntryType getLogType() {
1219        return LogEntryType.LOG_ROOT;
1220    }
1221
1222    /**
1223     * @see LoggableObject#marshallOutsideWriteLatch
1224     * Can be marshalled outside the log write latch.
1225     */

1226    public boolean marshallOutsideWriteLatch() {
1227        return true;
1228    }
1229
1230    /**
1231     * @see LoggableObject#countAsObsoleteWhenLogged
1232     */

1233    public boolean countAsObsoleteWhenLogged() {
1234        return false;
1235    }
1236
1237    /**
1238     * @see LoggableObject#getLogSize
1239     */

1240    public int getLogSize() {
1241        return
1242            LogUtils.getIntLogSize() + // last allocated id
1243
idDatabase.getLogSize() + // id db
1244
nameDatabase.getLogSize(); // name db
1245
}
1246
1247    /**
1248     * @see LoggableObject#writeToLog
1249     */

1250    public void writeToLog(ByteBuffer JavaDoc logBuffer) {
1251        LogUtils.writeInt(logBuffer,lastAllocatedDbId); // last id
1252
idDatabase.writeToLog(logBuffer); // id db
1253
nameDatabase.writeToLog(logBuffer); // name db
1254
}
1255
1256    /**
1257     * @see LoggableObject#postLogWork
1258     */

1259    public void postLogWork(long justLoggedLsn)
1260        throws DatabaseException {
1261    }
1262
1263    /*
1264     * LogReadable
1265     */

1266
1267    /**
1268     * @see LogReadable#readFromLog
1269     */

1270    public void readFromLog(ByteBuffer JavaDoc itemBuffer, byte entryTypeVersion)
1271        throws LogException {
1272
1273        lastAllocatedDbId = LogUtils.readInt(itemBuffer); // last id
1274
idDatabase.readFromLog(itemBuffer, entryTypeVersion); // id db
1275
nameDatabase.readFromLog(itemBuffer, entryTypeVersion); // name db
1276
}
1277    
1278    /**
1279     * @see LogReadable#dumpLog
1280     */

1281    public void dumpLog(StringBuffer JavaDoc sb, boolean verbose) {
1282        sb.append("<dbtree lastId = \"");
1283        sb.append(lastAllocatedDbId);
1284        sb.append("\">");
1285        sb.append("<idDb>");
1286        idDatabase.dumpLog(sb, verbose);
1287        sb.append("</idDb><nameDb>");
1288        nameDatabase.dumpLog(sb, verbose);
1289        sb.append("</nameDb>");
1290        sb.append("</dbtree>");
1291    }
1292
1293    /**
1294     * @see LogReadable#logEntryIsTransactional.
1295     */

1296    public boolean logEntryIsTransactional() {
1297    return false;
1298    }
1299
1300    /**
1301     * @see LogReadable#getTransactionId
1302     */

1303    public long getTransactionId() {
1304    return 0;
1305    }
1306
1307    /*
1308     * For unit test support
1309     */

1310
1311    String JavaDoc dumpString(int nSpaces) {
1312        StringBuffer JavaDoc self = new StringBuffer JavaDoc();
1313        self.append(TreeUtils.indent(nSpaces));
1314        self.append("<dbTree lastDbId =\"");
1315        self.append(lastAllocatedDbId);
1316        self.append("\">");
1317        self.append('\n');
1318        self.append(idDatabase.dumpString(nSpaces + 1));
1319        self.append('\n');
1320        self.append(nameDatabase.dumpString(nSpaces + 1));
1321        self.append('\n');
1322        self.append("</dbtree>");
1323        return self.toString();
1324    }
1325        
1326    public String JavaDoc toString() {
1327        return dumpString(0);
1328    }
1329
1330    /**
1331     * For debugging.
1332     */

1333    public void dump()
1334        throws DatabaseException {
1335
1336        idDatabase.getTree().dump();
1337        nameDatabase.getTree().dump();
1338    }
1339}
1340
Popular Tags