KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > Database


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: Database.java,v 1.216 2006/12/04 15:00:47 cwl Exp $
7  */

8
9 package com.sleepycat.je;
10
11 import java.util.ArrayList JavaDoc;
12 import java.util.Comparator JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.List JavaDoc;
15 import java.util.logging.Level JavaDoc;
16 import java.util.logging.Logger JavaDoc;
17
18 import com.sleepycat.je.dbi.DatabaseImpl;
19 import com.sleepycat.je.dbi.EnvironmentImpl;
20 import com.sleepycat.je.dbi.GetMode;
21 import com.sleepycat.je.dbi.PutMode;
22 import com.sleepycat.je.dbi.TruncateResult;
23 import com.sleepycat.je.dbi.CursorImpl.SearchMode;
24 import com.sleepycat.je.txn.Locker;
25 import com.sleepycat.je.txn.LockerFactory;
26 import com.sleepycat.je.utilint.DatabaseUtil;
27 import com.sleepycat.je.utilint.TinyHashSet;
28 import com.sleepycat.je.utilint.Tracer;
29
30 public class Database {
31
32     /*
33      * DbState embodies the Database handle state.
34      */

35     static class DbState {
36         private String JavaDoc stateName;
37
38         DbState(String JavaDoc stateName) {
39             this.stateName = stateName;
40         }
41
42         public String JavaDoc toString() {
43             return "DbState." + stateName;
44         }
45     }
46
47     static DbState OPEN = new DbState("OPEN");
48     static DbState CLOSED = new DbState("CLOSED");
49     static DbState INVALID = new DbState("INVALID");
50
51     /* The current state of the handle. */
52     private DbState state;
53
54     /* Handles onto the owning environment and the databaseImpl object. */
55     Environment envHandle; // used by subclasses
56
private DatabaseImpl databaseImpl;
57
58     DatabaseConfig configuration; // properties used at execution
59

60     /* True if this handle permits write operations; */
61     private boolean isWritable;
62
63     /* Transaction that owns the db lock held while the Database is open. */
64     Locker handleLocker;
65
66     /* Set of cursors open against this db handle. */
67     private TinyHashSet cursors = new TinyHashSet();
68
69     /*
70      * DatabaseTrigger list. The list is null if empty, and is checked for
71      * null to avoiding read locking overhead when no triggers are present.
72      * Access to this list is protected by the shared trigger latch in
73      * EnvironmentImpl.
74      */

75     private List JavaDoc triggerList;
76
77     private Logger JavaDoc logger;
78
79     /**
80      * Creates a database but does not open or fully initialize it.
81      * Is protected for use in compat package.
82      */

83     protected Database(Environment env) {
84         this.envHandle = env;
85         handleLocker = null;
86     logger = envHandle.getEnvironmentImpl().getLogger();
87     }
88
89     /**
90      * Create a database, called by Environment.
91      */

92     void initNew(Environment env,
93                  Locker locker,
94                  String JavaDoc databaseName,
95                  DatabaseConfig dbConfig)
96         throws DatabaseException {
97
98         dbConfig.validateForNewDb();
99
100         init(env, dbConfig);
101
102         /* Make the databaseImpl. */
103         EnvironmentImpl environmentImpl =
104             DbInternal.envGetEnvironmentImpl(envHandle);
105         databaseImpl = environmentImpl.createDb(locker,
106                                                 databaseName,
107                                                 dbConfig,
108                                                 this);
109         databaseImpl.addReferringHandle(this);
110     }
111
112     /**
113      * Open a database, called by Environment.
114      */

115     void initExisting(Environment env,
116                       Locker locker,
117                       DatabaseImpl databaseImpl,
118                       DatabaseConfig dbConfig)
119         throws DatabaseException {
120
121         /*
122          * Make sure the configuration used for the open is compatible with the
123          * existing databaseImpl.
124          */

125         validateConfigAgainstExistingDb(dbConfig, databaseImpl);
126
127         init(env, dbConfig);
128         this.databaseImpl = databaseImpl;
129         databaseImpl.addReferringHandle(this);
130
131         /*
132          * Copy the duplicates and transactional properties of the underlying
133          * database, in case the useExistingConfig property is set.
134          */

135         configuration.setSortedDuplicates(databaseImpl.getSortedDuplicates());
136         configuration.setTransactional(databaseImpl.isTransactional());
137     }
138
139     private void init(Environment env,
140                       DatabaseConfig config)
141         throws DatabaseException {
142
143         handleLocker = null;
144
145         envHandle = env;
146         configuration = config.cloneConfig();
147         isWritable = !configuration.getReadOnly();
148         state = OPEN;
149     }
150
151     /**
152      * See if this new handle's configuration is compatible with the
153      * pre-existing database.
154      */

155     private void validateConfigAgainstExistingDb(DatabaseConfig config,
156                                                  DatabaseImpl databaseImpl)
157         throws DatabaseException {
158
159         /*
160          * The allowDuplicates property is persistent and immutable. It does
161          * not need to be specified if the useExistingConfig property is set.
162          */

163         if (!config.getUseExistingConfig()) {
164             if (databaseImpl.getSortedDuplicates() !=
165                 config.getSortedDuplicates()) {
166                 throw new DatabaseException
167             ("You can't open a Database with a duplicatesAllowed " +
168              "configuration of " +
169              config.getSortedDuplicates() +
170              " if the underlying database was created with a " +
171              "duplicatesAllowedSetting of " +
172              databaseImpl.getSortedDuplicates() + ".");
173             }
174         }
175
176         /*
177          * The transactional property is kept constant while any handles are
178          * open, and set when the first handle is opened. It does not need to
179          * be specified if the useExistingConfig property is set.
180          */

181         if (databaseImpl.hasOpenHandles()) {
182             if (!config.getUseExistingConfig()) {
183                 if (config.getTransactional() !=
184                     databaseImpl.isTransactional()) {
185                     throw new DatabaseException
186                         ("You can't open a Database with a transactional " +
187                          "configuration of " + config.getTransactional() +
188                          " if the underlying database was created with a " +
189                          "transactional configuration of " +
190                          databaseImpl.isTransactional() + ".");
191                 }
192             }
193         } else {
194             databaseImpl.setTransactional(config.getTransactional());
195         }
196
197         /*
198          * The deferredWrite property is kept constant while any handles are
199          * open, and set when the first handle is opened. It does not need to
200          * be specified if the useExistingConfig property is set.
201          */

202         if (databaseImpl.hasOpenHandles()) {
203             if (!config.getUseExistingConfig()) {
204                 if (config.getDeferredWrite() !=
205                     databaseImpl.isDeferredWrite()) {
206                     throw new DatabaseException
207                         ("You can't open a Database with a deferredWrite " +
208                          "configuration of " + config.getDeferredWrite() +
209                          " if the underlying database was created with a " +
210                          "deferredWrite configuration of " +
211                          databaseImpl.isDeferredWrite() + ".");
212                 }
213             }
214         } else {
215             databaseImpl.setDeferredWrite(config.getDeferredWrite());
216         }
217
218         /*
219          * Only re-set the comparators if the override is allowed.
220          */

221         if (config.getOverrideBtreeComparator()) {
222             databaseImpl.setBtreeComparator
223                 (config.getBtreeComparator(),
224                  config.getBtreeComparatorByClassName());
225         }
226
227         if (config.getOverrideDuplicateComparator()) {
228             databaseImpl.setDuplicateComparator
229         (config.getDuplicateComparator(),
230          config.getDuplicateComparatorByClassName());
231         }
232     }
233
234     public synchronized void close()
235         throws DatabaseException {
236
237     try {
238         closeInternal();
239     } catch (Error JavaDoc E) {
240         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
241         throw E;
242     }
243     }
244
245     private void closeInternal()
246     throws DatabaseException {
247
248         StringBuffer JavaDoc errors = null;
249
250         checkEnv();
251         checkProhibitedDbState(CLOSED, "Can't close Database:");
252
253         trace(Level.FINEST, "Database.close: ", null, null);
254
255         /* Disassociate triggers before closing. */
256         removeAllTriggers();
257
258         envHandle.removeReferringHandle(this);
259         if (cursors.size() > 0) {
260             errors = new StringBuffer JavaDoc
261                 ("There are open cursors against the database.\n");
262             errors.append("They will be closed.\n");
263
264             /*
265              * Copy the cursors set before iterating since the dbc.close()
266              * mutates the set.
267              */

268             Iterator JavaDoc iter = cursors.copy().iterator();
269             while (iter.hasNext()) {
270                 Cursor dbc = (Cursor) iter.next();
271
272                 try {
273                     dbc.close();
274                 } catch (DatabaseException DBE) {
275                     errors.append("Exception while closing cursors:\n");
276                     errors.append(DBE.toString());
277                 }
278             }
279         }
280
281         if (databaseImpl != null) {
282             databaseImpl.removeReferringHandle(this);
283             databaseImpl = null;
284
285             /*
286              * Tell our protecting txn that we're closing. If this type
287              * of transaction doesn't live beyond the life of the handle,
288              * it will release the db handle lock.
289              */

290             handleLocker.setHandleLockOwner(true, this, true);
291             handleLocker.operationEnd(true);
292             state = CLOSED;
293         }
294
295         if (errors != null) {
296             throw new DatabaseException(errors.toString());
297         }
298     }
299
300     /**
301      * Javadoc for this public method is generated via
302      * the doc templates in the doc_src directory.
303      */

304     public void sync()
305         throws DatabaseException {
306
307         checkEnv();
308         checkRequiredDbState(OPEN, "Can't call Database.sync:");
309         checkWritable("sync");
310         trace(Level.FINEST, "Database.sync", null, null, null, null);
311
312         databaseImpl.sync(true);
313     }
314
315     /**
316      * Javadoc for this public method is generated via
317      * the doc templates in the doc_src directory.
318      */

319     public Sequence openSequence(Transaction txn,
320                                  DatabaseEntry key,
321                                  SequenceConfig config)
322         throws DatabaseException {
323
324     try {
325         checkEnv();
326         DatabaseUtil.checkForNullDbt(key, "key", true);
327         checkRequiredDbState(OPEN, "Can't call Database.openSequence:");
328         checkWritable("openSequence");
329         trace(Level.FINEST, "Database.openSequence", txn, key, null, null);
330
331         return new Sequence(this, txn, key, config);
332     } catch (Error JavaDoc E) {
333         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
334         throw E;
335     }
336     }
337
338     /**
339      * Javadoc for this public method is generated via
340      * the doc templates in the doc_src directory.
341      */

342     public void removeSequence(Transaction txn, DatabaseEntry key)
343         throws DatabaseException {
344
345     try {
346         delete(txn, key);
347     } catch (Error JavaDoc E) {
348         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
349         throw E;
350     }
351     }
352
353     public synchronized Cursor openCursor(Transaction txn,
354                                           CursorConfig cursorConfig)
355         throws DatabaseException {
356
357     try {
358         checkEnv();
359         checkRequiredDbState(OPEN, "Can't open a cursor");
360         CursorConfig useConfig =
361         (cursorConfig == null) ? CursorConfig.DEFAULT : cursorConfig;
362
363         if (useConfig.getReadUncommitted() &&
364         useConfig.getReadCommitted()) {
365         throw new IllegalArgumentException JavaDoc
366             ("Only one may be specified: " +
367              "ReadCommitted or ReadUncommitted");
368         }
369
370         trace(Level.FINEST, "Database.openCursor", txn, cursorConfig);
371         Cursor ret = newDbcInstance(txn, useConfig);
372
373         return ret;
374     } catch (Error JavaDoc E) {
375         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
376         throw E;
377     }
378     }
379  
380     /**
381      * Is overridden by SecondaryDatabase.
382      */

383     Cursor newDbcInstance(Transaction txn,
384                           CursorConfig cursorConfig)
385         throws DatabaseException {
386
387         return new Cursor(this, txn, cursorConfig);
388     }
389
390     public OperationStatus delete(Transaction txn, DatabaseEntry key)
391         throws DatabaseException {
392
393     try {
394         checkEnv();
395         DatabaseUtil.checkForNullDbt(key, "key", true);
396         checkRequiredDbState(OPEN, "Can't call Database.delete:");
397         checkWritable("delete");
398         trace(Level.FINEST, "Database.delete", txn, key, null, null);
399
400         OperationStatus commitStatus = OperationStatus.NOTFOUND;
401         Locker locker = null;
402         try {
403         locker = LockerFactory.getWritableLocker
404             (envHandle, txn, isTransactional());
405         commitStatus = deleteInternal(locker, key, null);
406         return commitStatus;
407         } finally {
408         if (locker != null) {
409             locker.operationEnd(commitStatus);
410         }
411         }
412     } catch (Error JavaDoc E) {
413         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
414         throw E;
415     }
416     }
417
418     /*
419      * This is commented out until we agree on whether this should even be in
420      * the API. See [14264].
421     private OperationStatus delete(Transaction txn,
422                    DatabaseEntry key,
423                    DatabaseEntry data)
424         throws DatabaseException {
425
426     try {
427         checkEnv();
428         DatabaseUtil.checkForNullDbt(key, "key", true);
429         DatabaseUtil.checkForNullDbt(data, "data", true);
430         checkRequiredDbState(OPEN, "Can't call Database.delete:");
431         checkWritable("delete");
432         trace(Level.FINEST, "Database.delete", txn, key, data, null);
433
434         OperationStatus commitStatus = OperationStatus.NOTFOUND;
435         Locker locker = null;
436         try {
437         locker = LockerFactory.getWritableLocker
438             (envHandle, txn, isTransactional());
439         commitStatus = deleteInternal(locker, key, data);
440         return commitStatus;
441         } finally {
442         if (locker != null) {
443             locker.operationEnd(commitStatus);
444         }
445         }
446     } catch (Error E) {
447         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
448         throw E;
449     }
450     }
451     */

452
453     /**
454      * Internal version of delete() that does no parameter checking. Notify
455      * triggers. Deletes all duplicates.
456      */

457     OperationStatus deleteInternal(Locker locker,
458                    DatabaseEntry key,
459                    DatabaseEntry data)
460         throws DatabaseException {
461
462         Cursor cursor = null;
463         try {
464         cursor = new Cursor(this, locker, null);
465         cursor.setNonCloning(true);
466             OperationStatus commitStatus = OperationStatus.NOTFOUND;
467
468             /* Position a cursor at the specified data record. */
469             DatabaseEntry oldData;
470             OperationStatus searchStatus;
471         if (data == null) {
472         oldData = new DatabaseEntry();
473                 searchStatus =
474             cursor.search(key, oldData, LockMode.RMW, SearchMode.SET);
475         } else {
476         oldData = data;
477                 searchStatus =
478             cursor.search(key, oldData, LockMode.RMW, SearchMode.BOTH);
479         }
480
481             /* Delete all records with that key. */
482             if (searchStatus == OperationStatus.SUCCESS) {
483                 do {
484
485                     /*
486                      * Notify triggers before the actual deletion so that a
487                      * primary record never exists while secondary keys refer
488                      * to it. This is relied on by secondary read-uncommitted.
489                      */

490                     if (hasTriggers()) {
491                         notifyTriggers(locker, key, oldData, null);
492                     }
493                     /* The actual deletion. */
494                     commitStatus = cursor.deleteNoNotify();
495                     if (commitStatus != OperationStatus.SUCCESS) {
496                         return commitStatus;
497                     }
498
499             if (data != null) {
500             /* delete(key, data) called so only delete one item. */
501             break;
502             }
503
504                     /* Get another duplicate. */
505                     if (databaseImpl.getSortedDuplicates()) {
506                         searchStatus =
507                             cursor.retrieveNext(key, oldData,
508                                                 LockMode.RMW,
509                                                 GetMode.NEXT_DUP);
510                     } else {
511                         searchStatus = OperationStatus.NOTFOUND;
512                     }
513                 } while (searchStatus == OperationStatus.SUCCESS);
514                 commitStatus = OperationStatus.SUCCESS;
515             }
516             return commitStatus;
517         } finally {
518         if (cursor != null) {
519         cursor.close();
520         }
521         }
522     }
523
524     public OperationStatus get(Transaction txn,
525                                DatabaseEntry key,
526                                DatabaseEntry data,
527                                LockMode lockMode)
528         throws DatabaseException {
529
530     try {
531         checkEnv();
532         DatabaseUtil.checkForNullDbt(key, "key", true);
533         DatabaseUtil.checkForNullDbt(data, "data", false);
534         checkRequiredDbState(OPEN, "Can't call Database.get:");
535         trace(Level.FINEST, "Database.get", txn, key, null, lockMode);
536
537         CursorConfig cursorConfig = CursorConfig.DEFAULT;
538         if (lockMode == LockMode.READ_COMMITTED) {
539         cursorConfig = CursorConfig.READ_COMMITTED;
540         lockMode = null;
541         }
542
543         Cursor cursor = null;
544         try {
545         cursor = new Cursor(this, txn, cursorConfig);
546         cursor.setNonCloning(true);
547         return cursor.search(key, data, lockMode, SearchMode.SET);
548         } finally {
549         if (cursor != null) {
550             cursor.close();
551         }
552         }
553     } catch (Error JavaDoc E) {
554         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
555         throw E;
556     }
557     }
558
559     public OperationStatus getSearchBoth(Transaction txn,
560                                          DatabaseEntry key,
561                                          DatabaseEntry data,
562                                          LockMode lockMode)
563         throws DatabaseException {
564
565     try {
566         checkEnv();
567         DatabaseUtil.checkForNullDbt(key, "key", true);
568         DatabaseUtil.checkForNullDbt(data, "data", true);
569         checkRequiredDbState(OPEN, "Can't call Database.getSearchBoth:");
570         trace(Level.FINEST, "Database.getSearchBoth", txn, key, data,
571           lockMode);
572
573         CursorConfig cursorConfig = CursorConfig.DEFAULT;
574         if (lockMode == LockMode.READ_COMMITTED) {
575         cursorConfig = CursorConfig.READ_COMMITTED;
576         lockMode = null;
577         }
578
579         Cursor cursor = null;
580         try {
581         cursor = new Cursor(this, txn, cursorConfig);
582         cursor.setNonCloning(true);
583         return cursor.search(key, data, lockMode, SearchMode.BOTH);
584         } finally {
585         if (cursor != null) {
586             cursor.close();
587         }
588         }
589     } catch (Error JavaDoc E) {
590         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
591         throw E;
592     }
593     }
594
595     public OperationStatus put(Transaction txn,
596                                DatabaseEntry key,
597                                DatabaseEntry data)
598         throws DatabaseException {
599
600         checkEnv();
601         DatabaseUtil.checkForNullDbt(key, "key", true);
602         DatabaseUtil.checkForNullDbt(data, "data", true);
603         DatabaseUtil.checkForPartialKey(key);
604         checkRequiredDbState(OPEN, "Can't call Database.put");
605         checkWritable("put");
606         trace(Level.FINEST, "Database.put", txn, key, data, null);
607
608         return putInternal(txn, key, data, PutMode.OVERWRITE);
609     }
610
611     public OperationStatus putNoOverwrite(Transaction txn,
612                                           DatabaseEntry key,
613                                           DatabaseEntry data)
614         throws DatabaseException {
615
616         checkEnv();
617         DatabaseUtil.checkForNullDbt(key, "key", true);
618         DatabaseUtil.checkForNullDbt(data, "data", true);
619         DatabaseUtil.checkForPartialKey(key);
620         checkRequiredDbState(OPEN, "Can't call Database.putNoOverWrite");
621         checkWritable("putNoOverwrite");
622         trace(Level.FINEST, "Database.putNoOverwrite", txn, key, data, null);
623
624         return putInternal(txn, key, data, PutMode.NOOVERWRITE);
625     }
626
627     public OperationStatus putNoDupData(Transaction txn,
628                                         DatabaseEntry key,
629                                         DatabaseEntry data)
630         throws DatabaseException {
631
632         checkEnv();
633         DatabaseUtil.checkForNullDbt(key, "key", true);
634         DatabaseUtil.checkForNullDbt(data, "data", true);
635         DatabaseUtil.checkForPartialKey(key);
636         checkRequiredDbState(OPEN, "Can't call Database.putNoDupData");
637         checkWritable("putNoDupData");
638         trace(Level.FINEST, "Database.putNoDupData", txn, key, data, null);
639
640         return putInternal(txn, key, data, PutMode.NODUP);
641     }
642
643     /**
644      * Internal version of put() that does no parameter checking.
645      */

646     OperationStatus putInternal(Transaction txn,
647                                 DatabaseEntry key,
648                 DatabaseEntry data,
649                 PutMode putMode)
650         throws DatabaseException {
651
652     try {
653         Locker locker = null;
654         Cursor cursor = null;
655         OperationStatus commitStatus = OperationStatus.KEYEXIST;
656         try {
657         locker = LockerFactory.getWritableLocker
658             (envHandle, txn, isTransactional());
659
660         cursor = new Cursor(this, locker, null);
661         cursor.setNonCloning(true);
662         commitStatus = cursor.putInternal(key, data, putMode);
663         return commitStatus;
664         } finally {
665         if (cursor != null) {
666             cursor.close();
667         }
668         if (locker != null) {
669             locker.operationEnd(commitStatus);
670         }
671         }
672     } catch (Error JavaDoc E) {
673         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
674         throw E;
675     }
676     }
677
678     /**
679      */

680     public JoinCursor join(Cursor[] cursors, JoinConfig config)
681         throws DatabaseException {
682
683     try {
684         checkEnv();
685         checkRequiredDbState(OPEN, "Can't call Database.join");
686         DatabaseUtil.checkForNullParam(cursors, "cursors");
687         if (cursors.length == 0) {
688         throw new IllegalArgumentException JavaDoc
689                     ("At least one cursor is required.");
690         }
691
692         /*
693          * Check that all cursors use the same locker, if any cursor is
694          * transactional. And if non-transactional, that all databases are
695          * in the same environment.
696          */

697         Locker locker = cursors[0].getCursorImpl().getLocker();
698         if (!locker.isTransactional()) {
699         EnvironmentImpl env = envHandle.getEnvironmentImpl();
700         for (int i = 1; i < cursors.length; i += 1) {
701             Locker locker2 = cursors[i].getCursorImpl().getLocker();
702             if (locker2.isTransactional()) {
703             throw new IllegalArgumentException JavaDoc
704                             ("All cursors must use the same transaction.");
705             }
706             EnvironmentImpl env2 = cursors[i].getDatabaseImpl()
707             .getDbEnvironment();
708             if (env != env2) {
709             throw new IllegalArgumentException JavaDoc
710                             ("All cursors must use the same environment.");
711             }
712         }
713         locker = null; /* Don't reuse a non-transactional locker. */
714         } else {
715         for (int i = 1; i < cursors.length; i += 1) {
716             Locker locker2 = cursors[i].getCursorImpl().getLocker();
717             if (locker.getTxnLocker() != locker2.getTxnLocker()) {
718             throw new IllegalArgumentException JavaDoc
719                             ("All cursors must use the same transaction.");
720             }
721         }
722         }
723
724         /* Create the join cursor. */
725         return new JoinCursor(locker, this, cursors, config);
726     } catch (Error JavaDoc E) {
727         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
728         throw E;
729     }
730     }
731
732     /**
733      * @deprecated It has not been possible to implement this method with
734      * correct transactional semantics without incurring a performance penalty
735      * on all Database operations. Truncate functionality has been moved to
736      * Environment.truncateDatabase(), which requires that all Database handles
737      * on the database are closed before the truncate operation can execute.
738      */

739     public int truncate(Transaction txn, boolean countRecords)
740         throws DatabaseException {
741
742     try {
743         checkEnv();
744         checkRequiredDbState(OPEN, "Can't call Database.truncate");
745         checkWritable("truncate");
746         Tracer.trace(Level.FINEST,
747              envHandle.getEnvironmentImpl(),
748              "Database.truncate: txnId=" +
749              ((txn == null) ?
750               "null" :
751               Long.toString(txn.getId())));
752
753         Locker locker = null;
754         boolean triggerLock = false;
755         boolean operationOk = false;
756
757         try {
758         locker = LockerFactory.getWritableLocker
759             (envHandle, txn, isTransactional(), true /*retainLocks*/,
760              null);
761
762         /*
763          * Pass true to always get a read lock on the triggers, so we
764          * are sure that no secondaries are added during truncation.
765          */

766         acquireTriggerListReadLock();
767         triggerLock = true;
768
769         /* Truncate primary. */
770         int count = truncateInternal(locker, countRecords);
771
772         /* Truncate secondaries. */
773         for (int i = 0; i < triggerList.size(); i += 1) {
774             Object JavaDoc obj = triggerList.get(i);
775             if (obj instanceof SecondaryTrigger) {
776             SecondaryDatabase secDb =
777                 ((SecondaryTrigger) obj).getDb();
778             secDb.truncateInternal(locker, false);
779             }
780         }
781
782         operationOk = true;
783         return count;
784         } finally {
785         if (locker != null) {
786             locker.operationEnd(operationOk);
787         }
788         if (triggerLock) {
789             releaseTriggerListReadLock();
790         }
791         }
792     } catch (Error JavaDoc E) {
793         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
794         throw E;
795     }
796     }
797             
798     /**
799      * Internal unchecked truncate that optionally counts records.
800      * @deprecated
801      */

802     int truncateInternal(Locker locker, boolean countRecords)
803         throws DatabaseException {
804
805         if (databaseImpl == null) {
806             throw new DatabaseException
807                 ("couldn't find database - truncate");
808         }
809         databaseImpl.checkIsDeleted("truncate");
810
811         /*
812          * Truncate must obtain a write lock. In order to do so, it assumes
813          * ownership for the handle lock and transfers it from this Database
814          * object to the txn.
815          */

816         if (handleLocker.isHandleLockTransferrable()) {
817             handleLocker.transferHandleLock(this, locker, false);
818         }
819
820         boolean operationOk = false;
821         try {
822
823             /*
824              * truncate clones the existing database and returns a new one to
825              * replace it with. The old databaseImpl object is marked
826              * 'deleted'.
827              */

828             TruncateResult result =
829                 envHandle.getEnvironmentImpl().truncate(locker, databaseImpl);
830             databaseImpl = result.getDatabase();
831
832             operationOk = true;
833             return countRecords ? result.getRecordCount() : -1;
834         } finally {
835
836             /*
837              * The txn will know if it's living past the end of this operation,
838              * and if it needs to transfer the handle lock. operationEnd()
839              * will be called one level up by the public truncate() method.
840              */

841             locker.setHandleLockOwner(operationOk, this, false);
842         }
843     }
844
845     /*
846      * @deprecated As of JE 2.0.55, replaced by
847      * {@link Database#preload(PreloadConfig)}.
848      */

849     public void preload(long maxBytes)
850         throws DatabaseException {
851
852         checkEnv();
853         checkRequiredDbState(OPEN, "Can't call Database.preload");
854         databaseImpl.checkIsDeleted("preload");
855
856     PreloadConfig config = new PreloadConfig();
857     config.setMaxBytes(maxBytes);
858         databaseImpl.preload(config);
859     }
860
861     /*
862      * @deprecated As of JE 2.1.1, replaced by
863      * {@link Database#preload(PreloadConfig)}.
864      */

865     public void preload(long maxBytes, long maxMillisecs)
866         throws DatabaseException {
867
868         checkEnv();
869         checkRequiredDbState(OPEN, "Can't call Database.preload");
870         databaseImpl.checkIsDeleted("preload");
871
872     PreloadConfig config = new PreloadConfig();
873     config.setMaxBytes(maxBytes);
874     config.setMaxMillisecs(maxMillisecs);
875         databaseImpl.preload(config);
876     }
877
878     public PreloadStats preload(PreloadConfig config)
879         throws DatabaseException {
880
881         checkEnv();
882         checkRequiredDbState(OPEN, "Can't call Database.preload");
883         databaseImpl.checkIsDeleted("preload");
884
885         return databaseImpl.preload(config);
886     }
887
888     public long count()
889         throws DatabaseException {
890
891         checkEnv();
892         checkRequiredDbState(OPEN, "Can't call Database.count");
893         databaseImpl.checkIsDeleted("count");
894
895         return databaseImpl.count();
896     }
897
898     public DatabaseStats getStats(StatsConfig config)
899         throws DatabaseException {
900
901         checkEnv();
902         checkRequiredDbState(OPEN, "Can't call Database.stat");
903         StatsConfig useConfig =
904             (config == null) ? StatsConfig.DEFAULT : config;
905
906         if (databaseImpl != null) {
907             databaseImpl.checkIsDeleted("stat");
908             return databaseImpl.stat(useConfig);
909         }
910         return null;
911     }
912
913     public DatabaseStats verify(VerifyConfig config)
914         throws DatabaseException {
915
916     try {
917         checkEnv();
918         checkRequiredDbState(OPEN, "Can't call Database.verify");
919         databaseImpl.checkIsDeleted("verify");
920         VerifyConfig useConfig =
921         (config == null) ? VerifyConfig.DEFAULT : config;
922
923         DatabaseStats stats = databaseImpl.getEmptyStats();
924         databaseImpl.verify(useConfig, stats);
925         return stats;
926     } catch (Error JavaDoc E) {
927         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
928         throw E;
929     }
930     }
931
932     public String JavaDoc getDatabaseName()
933         throws DatabaseException {
934
935     try {
936         checkEnv();
937         if (databaseImpl != null) {
938         return databaseImpl.getName();
939         } else {
940         return null;
941         }
942     } catch (Error JavaDoc E) {
943         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
944         throw E;
945     }
946     }
947
948     /*
949      * Non-transactional database name, safe to access when creating error
950      * messages.
951      */

952     String JavaDoc getDebugName() {
953         if (databaseImpl != null) {
954             return databaseImpl.getDebugName();
955         } else {
956             return null;
957         }
958     }
959
960     public DatabaseConfig getConfig()
961         throws DatabaseException {
962
963     try {
964         DatabaseConfig showConfig = configuration.cloneConfig();
965
966         /*
967          * Set the comparators from the database impl, they might have
968          * changed from another handle.
969          */

970         Comparator JavaDoc btComp = null;
971         Comparator JavaDoc dupComp = null;
972         boolean btCompByClass = false;
973         boolean dupCompByClass = false;
974         if (databaseImpl != null) {
975         btComp = databaseImpl.getBtreeComparator();
976         dupComp = databaseImpl.getDuplicateComparator();
977         btCompByClass = databaseImpl.getBtreeComparatorByClass();
978         dupCompByClass = databaseImpl.getDuplicateComparatorByClass();
979         }
980         showConfig.setBtreeComparatorInternal(btComp, btCompByClass);
981         showConfig.setDuplicateComparatorInternal(dupComp, dupCompByClass);
982         return showConfig;
983     } catch (Error JavaDoc E) {
984         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
985         throw E;
986     }
987     }
988
989     /**
990      * Equivalent to getConfig().getTransactional() but cheaper.
991      */

992     boolean isTransactional()
993         throws DatabaseException {
994
995         return databaseImpl.isTransactional();
996     }
997
998     public Environment getEnvironment()
999         throws DatabaseException {
1000
1001        return envHandle;
1002    }
1003
1004    public List JavaDoc getSecondaryDatabases()
1005        throws DatabaseException {
1006
1007    try {
1008        List JavaDoc list = new ArrayList JavaDoc();
1009        if (hasTriggers()) {
1010        acquireTriggerListReadLock();
1011        try {
1012            for (int i = 0; i < triggerList.size(); i += 1) {
1013            Object JavaDoc obj = triggerList.get(i);
1014            if (obj instanceof SecondaryTrigger) {
1015                list.add(((SecondaryTrigger) obj).getDb());
1016            }
1017            }
1018        } finally {
1019            releaseTriggerListReadLock();
1020        }
1021        }
1022        return list;
1023    } catch (Error JavaDoc E) {
1024        DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
1025        throw E;
1026    }
1027    }
1028
1029    /*
1030     * Helpers, not part of the public API
1031     */

1032
1033    /**
1034     * @return true if the Database was opened read/write.
1035     */

1036    boolean isWritable() {
1037        return isWritable;
1038    }
1039
1040    /**
1041     * Return the databaseImpl object instance.
1042     */

1043    DatabaseImpl getDatabaseImpl() {
1044        return databaseImpl;
1045    }
1046
1047    /**
1048     * The handleLocker is the one that holds the db handle lock.
1049     */

1050    void setHandleLocker(Locker locker) {
1051        handleLocker = locker;
1052    }
1053
1054    synchronized void removeCursor(Cursor dbc) {
1055        cursors.remove(dbc);
1056    }
1057
1058    synchronized void addCursor(Cursor dbc) {
1059        cursors.add(dbc);
1060    }
1061
1062    /**
1063     * @throws DatabaseException if the Database state is not this value.
1064     */

1065    void checkRequiredDbState(DbState required, String JavaDoc msg)
1066        throws DatabaseException {
1067
1068        if (state != required) {
1069            throw new DatabaseException
1070                (msg + " Database state can't be " + state +
1071                 " must be " + required);
1072        }
1073    }
1074
1075    /**
1076     * @throws DatabaseException if the Database state is this value.
1077     */

1078    void checkProhibitedDbState(DbState prohibited, String JavaDoc msg)
1079        throws DatabaseException {
1080
1081        if (state == prohibited) {
1082            throw new DatabaseException
1083                (msg + " Database state must not be " + prohibited);
1084        }
1085    }
1086
1087    /**
1088     * @throws RunRecoveryException if the underlying environment is
1089     * invalid
1090     */

1091    void checkEnv()
1092        throws RunRecoveryException {
1093
1094        EnvironmentImpl env = envHandle.getEnvironmentImpl();
1095        if (env != null) {
1096            env.checkIfInvalid();
1097        }
1098    }
1099
1100    /**
1101     * Invalidate the handle, called by txn.abort by way of DbInternal.
1102     */

1103    synchronized void invalidate() {
1104        state = INVALID;
1105        envHandle.removeReferringHandle(this);
1106        if (databaseImpl != null) {
1107            databaseImpl.removeReferringHandle(this);
1108        }
1109    }
1110
1111    /**
1112     * Check that write operations aren't used on a readonly Database.
1113     */

1114    private void checkWritable(String JavaDoc operation)
1115        throws DatabaseException {
1116
1117        if (!isWritable) {
1118            throw new DatabaseException
1119                ("Database is Read Only: " + operation);
1120        }
1121    }
1122
1123    /**
1124     * Send trace messages to the java.util.logger. Don't rely on the logger
1125     * alone to conditionalize whether we send this message, we don't even want
1126     * to construct the message if the level is not enabled.
1127     */

1128    void trace(Level JavaDoc level,
1129               String JavaDoc methodName,
1130               Transaction txn,
1131               DatabaseEntry key,
1132               DatabaseEntry data,
1133               LockMode lockMode)
1134        throws DatabaseException {
1135
1136        if (logger.isLoggable(level)) {
1137            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1138            sb.append(methodName);
1139            if (txn != null) {
1140                sb.append(" txnId=").append(txn.getId());
1141            }
1142            sb.append(" key=").append(key.dumpData());
1143            if (data != null) {
1144                sb.append(" data=").append(data.dumpData());
1145            }
1146            if (lockMode != null) {
1147                sb.append(" lockMode=").append(lockMode);
1148            }
1149            logger.log(level, sb.toString());
1150        }
1151    }
1152
1153    /**
1154     * Send trace messages to the java.util.logger. Don't rely on the logger
1155     * alone to conditionalize whether we send this message, we don't even want
1156     * to construct the message if the level is not enabled.
1157     */

1158    void trace(Level JavaDoc level,
1159               String JavaDoc methodName,
1160               Transaction txn,
1161               CursorConfig config)
1162        throws DatabaseException {
1163
1164        if (logger.isLoggable(level)) {
1165            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1166            sb.append(methodName);
1167            sb.append(" name=" + getDebugName());
1168            if (txn != null) {
1169                sb.append(" txnId=").append(txn.getId());
1170            }
1171            if (config != null) {
1172                sb.append(" config=").append(config);
1173            }
1174            logger.log(level, sb.toString());
1175        }
1176    }
1177
1178    /*
1179     * Manage triggers.
1180     */

1181
1182    /**
1183     * Returns whether any triggers are currently associated with this primary.
1184     * Note that an update of the trigger list may be in progress and this
1185     * method does not wait for that update to be completed.
1186     */

1187    boolean hasTriggers() {
1188
1189        return triggerList != null;
1190    }
1191
1192    /**
1193     * Gets a read-lock on the list of triggers. releaseTriggerListReadLock()
1194     * must be called to release the lock. Called by all primary put and
1195     * delete operations.
1196     */

1197    private void acquireTriggerListReadLock()
1198        throws DatabaseException {
1199
1200        EnvironmentImpl env = envHandle.getEnvironmentImpl();
1201        env.getTriggerLatch().acquireShared();
1202        if (triggerList == null) {
1203            triggerList = new ArrayList JavaDoc();
1204        }
1205    }
1206
1207    /**
1208     * Releases a lock acquired by calling acquireTriggerListReadLock().
1209     */

1210    private void releaseTriggerListReadLock()
1211        throws DatabaseException {
1212
1213        EnvironmentImpl env = envHandle.getEnvironmentImpl();
1214        env.getTriggerLatch().release();
1215    }
1216
1217    /**
1218     * Gets a write lock on the list of triggers. An empty list is created if
1219     * necessary, so null is never returned. releaseTriggerListWriteLock()
1220     * must always be called to release the lock.
1221     */

1222    private void acquireTriggerListWriteLock()
1223        throws DatabaseException {
1224
1225        EnvironmentImpl env = envHandle.getEnvironmentImpl();
1226        env.getTriggerLatch().acquireExclusive();
1227        if (triggerList == null) {
1228            triggerList = new ArrayList JavaDoc();
1229        }
1230    }
1231
1232    /**
1233     * Releases a lock acquired by calling acquireTriggerListWriteLock(). If
1234     * the list is now empty then it is set to null, that is, hasTriggers()
1235     * will subsequently return false.
1236     */

1237    private void releaseTriggerListWriteLock()
1238        throws DatabaseException {
1239
1240        if (triggerList.size() == 0) {
1241            triggerList = null;
1242        }
1243        EnvironmentImpl env = envHandle.getEnvironmentImpl();
1244        env.getTriggerLatch().release();
1245    }
1246
1247    /**
1248     * Adds a given trigger to the list of triggers. Called while opening
1249     * a SecondaryDatabase.
1250     *
1251     * @param insertAtFront true to insert at the front, or false to append.
1252     */

1253    void addTrigger(DatabaseTrigger trigger, boolean insertAtFront)
1254        throws DatabaseException {
1255
1256        acquireTriggerListWriteLock();
1257        try {
1258            if (insertAtFront) {
1259                triggerList.add(0, trigger);
1260            } else {
1261                triggerList.add(trigger);
1262            }
1263            trigger.triggerAdded(this);
1264        } finally {
1265            releaseTriggerListWriteLock();
1266        }
1267    }
1268
1269    /**
1270     * Removes a given trigger from the list of triggers. Called by
1271     * SecondaryDatabase.close().
1272     */

1273    void removeTrigger(DatabaseTrigger trigger)
1274        throws DatabaseException {
1275
1276        acquireTriggerListWriteLock();
1277        try {
1278            triggerList.remove(trigger);
1279            trigger.triggerRemoved(this);
1280        } finally {
1281            releaseTriggerListWriteLock();
1282        }
1283    }
1284
1285    /**
1286     * Clears the list of triggers. Called by close(), this allows closing the
1287     * primary before its secondaries, although we document that secondaries
1288     * should be closed first.
1289     */

1290    private void removeAllTriggers()
1291        throws DatabaseException {
1292
1293        acquireTriggerListWriteLock();
1294        try {
1295            for (int i = 0; i < triggerList.size(); i += 1) {
1296                DatabaseTrigger trigger = (DatabaseTrigger) triggerList.get(i);
1297                trigger.triggerRemoved(this);
1298            }
1299            triggerList.clear();
1300        } finally {
1301            releaseTriggerListWriteLock();
1302        }
1303    }
1304
1305    /**
1306     * Notifies associated triggers when a put() or delete() is performed on
1307     * the primary. This method is normally called only if hasTriggers() has
1308     * returned true earlier. This avoids acquiring a shared latch for
1309     * primaries with no triggers. If a trigger is added during the update
1310     * process, there is no requirement to immediately start updating it.
1311     *
1312     * @param locker the internal locker.
1313     *
1314     * @param priKey the primary key.
1315     *
1316     * @param oldData the primary data before the change, or null if the record
1317     * did not previously exist.
1318     *
1319     * @param newData the primary data after the change, or null if the record
1320     * has been deleted.
1321     */

1322    void notifyTriggers(Locker locker,
1323                        DatabaseEntry priKey,
1324                        DatabaseEntry oldData,
1325                        DatabaseEntry newData)
1326        throws DatabaseException {
1327
1328        acquireTriggerListReadLock();
1329        try {
1330            for (int i = 0; i < triggerList.size(); i += 1) {
1331                DatabaseTrigger trigger = (DatabaseTrigger) triggerList.get(i);
1332
1333                /* Notify trigger. */
1334                trigger.databaseUpdated
1335                    (this, locker, priKey, oldData, newData);
1336            }
1337        } finally {
1338            releaseTriggerListReadLock();
1339        }
1340    }
1341}
1342
Popular Tags