KickJava   Java API By Example, From Geeks To Geeks.

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


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

8
9 package com.sleepycat.je.dbi;
10
11 import java.io.File JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.PrintStream JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.Collection JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.logging.ConsoleHandler JavaDoc;
18 import java.util.logging.FileHandler JavaDoc;
19 import java.util.logging.Handler JavaDoc;
20 import java.util.logging.Level JavaDoc;
21 import java.util.logging.Logger JavaDoc;
22 import java.util.logging.SimpleFormatter JavaDoc;
23
24 import com.sleepycat.je.CheckpointConfig;
25 import com.sleepycat.je.Database;
26 import com.sleepycat.je.DatabaseConfig;
27 import com.sleepycat.je.DatabaseException;
28 import com.sleepycat.je.DbInternal;
29 import com.sleepycat.je.Environment;
30 import com.sleepycat.je.EnvironmentConfig;
31 import com.sleepycat.je.EnvironmentMutableConfig;
32 import com.sleepycat.je.EnvironmentStats;
33 import com.sleepycat.je.ExceptionListener;
34 import com.sleepycat.je.LockStats;
35 import com.sleepycat.je.RunRecoveryException;
36 import com.sleepycat.je.StatsConfig;
37 import com.sleepycat.je.Transaction;
38 import com.sleepycat.je.TransactionConfig;
39 import com.sleepycat.je.TransactionStats;
40 import com.sleepycat.je.VerifyConfig;
41 import com.sleepycat.je.cleaner.Cleaner;
42 import com.sleepycat.je.cleaner.UtilizationProfile;
43 import com.sleepycat.je.cleaner.UtilizationTracker;
44 import com.sleepycat.je.config.EnvironmentParams;
45 import com.sleepycat.je.evictor.Evictor;
46 import com.sleepycat.je.incomp.INCompressor;
47 import com.sleepycat.je.latch.Latch;
48 import com.sleepycat.je.latch.LatchSupport;
49 import com.sleepycat.je.latch.SharedLatch;
50 import com.sleepycat.je.log.FileManager;
51 import com.sleepycat.je.log.LatchedLogManager;
52 import com.sleepycat.je.log.LogManager;
53 import com.sleepycat.je.log.SyncedLogManager;
54 import com.sleepycat.je.log.TraceLogHandler;
55 import com.sleepycat.je.recovery.Checkpointer;
56 import com.sleepycat.je.recovery.RecoveryInfo;
57 import com.sleepycat.je.recovery.RecoveryManager;
58 import com.sleepycat.je.tree.BIN;
59 import com.sleepycat.je.tree.BINReference;
60 import com.sleepycat.je.tree.IN;
61 import com.sleepycat.je.tree.Key;
62 import com.sleepycat.je.txn.Locker;
63 import com.sleepycat.je.txn.Txn;
64 import com.sleepycat.je.txn.TxnManager;
65 import com.sleepycat.je.utilint.DbLsn;
66 import com.sleepycat.je.utilint.NotImplementedYetException;
67 import com.sleepycat.je.utilint.PropUtil;
68 import com.sleepycat.je.utilint.TestHook;
69 import com.sleepycat.je.utilint.TestHookExecute;
70 import com.sleepycat.je.utilint.Tracer;
71
72 /**
73  * Underlying Environment implementation. There is a single instance for any
74  * database environment opened by the application.
75  */

76 public class EnvironmentImpl implements EnvConfigObserver {
77    
78     /*
79      * Set true and run unit tests for NO_LOCKING_MODE test.
80      * EnvironmentConfigTest.testInconsistentParams will fail. [#13788]
81      */

82     private static final boolean TEST_NO_LOCKING_MODE = false;
83
84     /* Attributes of the entire environment */
85     private DbEnvState envState;
86     private boolean closing; // true if close has begun
87
private File JavaDoc envHome;
88     private int referenceCount; // count of opened Database and DbTxns
89
private boolean isTransactional; // true if env opened with DB_INIT_TRANS
90
private boolean isNoLocking; // true if env has no locking
91
private boolean isReadOnly; // true if env opened with the read only flag.
92
private boolean isMemOnly; // true if je.log.memOnly=true
93
private boolean directNIO; // true to use direct NIO buffers
94
private static boolean fairLatches;// true if user wants fair latches
95
private static boolean useSharedLatchesForINs;
96     /* true if offset tracking should be used for deferred write dbs. */
97     private boolean deferredWriteTemp;
98
99     private MemoryBudget memoryBudget;
100     private static int adler32ChunkSize;
101
102     /* Save so we don't have to look it up in the config manager frequently. */
103     private long lockTimeout;
104     private long txnTimeout;
105
106     /* DatabaseImpl */
107     private DbTree dbMapTree;
108     private long mapTreeRootLsn = DbLsn.NULL_LSN;
109     private Latch mapTreeRootLatch;
110     private INList inMemoryINs;
111
112     /* Services */
113     private DbConfigManager configManager;
114     private List JavaDoc configObservers;
115     private Logger JavaDoc envLogger;
116     protected LogManager logManager;
117     private FileManager fileManager;
118     private TxnManager txnManager;
119     
120     /* Daemons */
121     private Evictor evictor;
122     private INCompressor inCompressor;
123     private Checkpointer checkpointer;
124     private Cleaner cleaner;
125
126     /* Replication */
127     private boolean isReplicated;
128     private ReplicatorInstance repInstance;
129
130     /* Stats, debug information */
131     private RecoveryInfo lastRecoveryInfo;
132     private RunRecoveryException savedInvalidatingException;
133
134     /* If true, call Thread.yield() at strategic points (stress test aid) */
135     private static boolean forcedYield = false;
136
137     /*
138      * Used by Database to protect access to the trigger list. A single latch
139      * for all databases is used to prevent deadlocks.
140      */

141     private SharedLatch triggerLatch;
142
143     /**
144      * The exception listener for this envimpl, if any has been specified.
145      */

146     private ExceptionListener exceptionListener = null;
147
148     /*
149      * Configuration and tracking of background IO limits. Managed by the
150      * updateBackgroundReads, updateBackgroundWrites and sleepAfterBackgroundIO
151      * methods. The limits and the backlog are volatile because we check them
152      * outside the synchronized block. Other fields are updated and checked
153      * while synchronized on the tracking mutex object. The sleep mutex is
154      * used to block multiple background threads while sleeping.
155      */

156     private volatile int backgroundSleepBacklog;
157     private volatile int backgroundReadLimit;
158     private volatile int backgroundWriteLimit;
159     private long backgroundSleepInterval;
160     private int backgroundReadCount;
161     private long backgroundWriteBytes;
162     private TestHook backgroundSleepHook;
163     private Object JavaDoc backgroundTrackingMutex = new Object JavaDoc();
164     private Object JavaDoc backgroundSleepMutex = new Object JavaDoc();
165
166     /*
167      * ThreadLocal.get() is not cheap so we want to minimize calls to it. We
168      * only use ThreadLocals for the TreeStatsAccumulator which are only called
169      * in limited circumstances. Use this reference count to indicate that a
170      * thread has set a TreeStatsAccumulator. When it's done, it decrements
171      * the counter. It's static so that we don't have to pass around the
172      * EnvironmentImpl.
173      */

174     private static int threadLocalReferenceCount = 0;
175
176     /**
177      * DbPrintLog doesn't need btree and dup comparators to function properly
178      * don't require any instantiations. This flag, if true, indicates that
179      * we've been called from DbPrintLog.
180      */

181     private static boolean noComparators = false;
182     
183     /*
184      * A preallocated RunRecoveryException that is used in OOME and other
185      * java.lang.Error situations so that allocation does not need to be done
186      * in the OOME context.
187      */

188     public final RunRecoveryException SAVED_RRE = DbInternal.makeNoArgsRRE();
189
190     public static final boolean JAVA5_AVAILABLE;
191
192     private static final String JavaDoc DISABLE_JAVA_ADLER32 =
193     "je.disable.java.adler32";
194
195     static {
196     boolean ret = false;
197     if (System.getProperty(DISABLE_JAVA_ADLER32) == null) {
198
199         /*
200          * Use this to determine if we're in J5.
201          */

202         String JavaDoc javaVersion = System.getProperty("java.version");
203         if (javaVersion != null &&
204         !javaVersion.startsWith("1.4.")) {
205         ret = true;
206         }
207     }
208     JAVA5_AVAILABLE = ret;
209     }
210
211     /**
212      * Create a database environment to represent the data in envHome.
213      * dbHome. Properties from the je.properties file in that directory are
214      * used to initialize the system wide property bag. Properties passed to
215      * this method are used to influence the open itself.
216      *
217      * @param envHome absolute path of the database environment
218      * home directory
219      *
220      * @param envConfig
221      *
222      * @throws DatabaseException on all other failures
223      */

224     public EnvironmentImpl(File JavaDoc envHome, EnvironmentConfig envConfig)
225         throws DatabaseException {
226
227         try {
228             this.envHome = envHome;
229             envState = DbEnvState.INIT;
230             mapTreeRootLatch = LatchSupport.makeLatch("MapTreeRoot", this);
231
232             /* Set up configuration parameters */
233             configManager = new DbConfigManager(envConfig);
234             configObservers = new ArrayList JavaDoc();
235             addConfigObserver(this);
236
237             /*
238              * Decide on memory budgets based on environment config params and
239              * memory available to this process.
240              */

241             memoryBudget = new MemoryBudget(this, configManager);
242
243             /*
244              * Set up debug logging. Depending on configuration, add handlers,
245              * set logging level.
246              */

247             envLogger = initLogger(envHome);
248
249             /*
250              * Essential services. These must exist before recovery.
251              */

252         forcedYield =
253         configManager.getBoolean(EnvironmentParams.ENV_FORCED_YIELD);
254             isTransactional =
255         configManager.getBoolean(EnvironmentParams.ENV_INIT_TXN);
256             isNoLocking = !(configManager.getBoolean
257                 (EnvironmentParams.ENV_INIT_LOCKING));
258         if (isTransactional && isNoLocking) {
259         if (TEST_NO_LOCKING_MODE) {
260             isNoLocking = !isTransactional;
261         } else {
262             throw new IllegalArgumentException JavaDoc
263             ("Can't set 'je.env.isNoLocking' and " +
264              "'je.env.isTransactional';");
265         }
266         }
267
268         directNIO =
269         configManager.getBoolean(EnvironmentParams.LOG_DIRECT_NIO);
270         fairLatches =
271         configManager.getBoolean(EnvironmentParams.ENV_FAIR_LATCHES);
272             isReadOnly =
273         configManager.getBoolean(EnvironmentParams.ENV_RDONLY);
274             isMemOnly =
275                 configManager.getBoolean(EnvironmentParams.LOG_MEMORY_ONLY);
276         useSharedLatchesForINs =
277         configManager.getBoolean(EnvironmentParams.ENV_SHARED_LATCHES);
278         adler32ChunkSize =
279         configManager.getInt(EnvironmentParams.ADLER32_CHUNK_SIZE);
280
281         exceptionListener = envConfig.getExceptionListener();
282
283             /*
284              * This property indicates that we should use obsolete offset
285              * tracking for deferred write dbs. Very likely to be a temporary
286              * property.
287              */

288             deferredWriteTemp =
289         configManager.getBoolean(
290                                   EnvironmentParams.LOG_DEFERREDWRITE_TEMP);
291
292             fileManager = new FileManager(this, envHome, isReadOnly);
293             if (!envConfig.getAllowCreate() && !fileManager.filesExist()) {
294                 throw new DatabaseException
295             ("Environment.setAllowCreate is false so environment " +
296                      " creation is not permitted, but there is no " +
297                      " pre-existing environment in " + envHome);
298             }
299
300             if (fairLatches) {
301                 logManager = new LatchedLogManager(this, isReadOnly);
302             } else {
303                 logManager = new SyncedLogManager(this, isReadOnly);
304             }
305
306             inMemoryINs = new INList(this);
307             txnManager = new TxnManager(this);
308
309             /*
310              * Daemons are always made here, but only started after recovery.
311              * We want them to exist so we can call them programatically even
312              * if the daemon thread is not started.
313              */

314             createDaemons();
315
316             /*
317          * Recovery will recreate the dbMapTree from the log if it exists.
318          */

319             dbMapTree = new DbTree(this);
320
321             referenceCount = 0;
322
323             triggerLatch = LatchSupport.makeSharedLatch("TriggerLatch", this);
324
325             /*
326              * Do not do recovery and start daemons if this environment is for
327              * a utility.
328              */

329             if (configManager.getBoolean(EnvironmentParams.ENV_RECOVERY)) {
330
331                 /*
332                  * Run recovery. Note that debug logging to the database log
333                  * is disabled until recovery is finished.
334                  */

335                 try {
336                     RecoveryManager recoveryManager =
337             new RecoveryManager(this);
338                     lastRecoveryInfo = recoveryManager.recover(isReadOnly);
339                 } finally {
340                     try {
341                         /* Flush to get all exception tracing out to the log.*/
342                         logManager.flush();
343                         fileManager.clear();
344                     } catch (IOException JavaDoc e) {
345                         throw new DatabaseException(e.getMessage());
346                     }
347                 }
348             } else {
349                 isReadOnly = true;
350         noComparators = true;
351             }
352
353             /* Initialize mutable properties and start daemons. */
354             envConfigUpdate(configManager);
355
356             /*
357              * Cache a few critical values. We keep our timeout in millis
358              * instead of microseconds because Object.wait takes millis.
359              */

360             lockTimeout =
361         PropUtil.microsToMillis(configManager.getLong
362                     (EnvironmentParams.LOCK_TIMEOUT));
363             txnTimeout =
364         PropUtil.microsToMillis(configManager.getLong
365                     (EnvironmentParams.TXN_TIMEOUT));
366
367             /* Initialize the environment memory usage number. */
368             memoryBudget.initCacheMemoryUsage();
369
370             /* Mark as open. */
371             open();
372         } catch (DatabaseException e) {
373
374             /* Release any environment locks if there was a problem. */
375             if (fileManager != null) {
376                 try {
377                     /*
378                      * Clear again, in case an exception in logManager.flush()
379                      * caused us to skip the earlier call to clear().
380                      */

381                     fileManager.clear();
382                     fileManager.close();
383                 } catch (IOException JavaDoc IOE) {
384
385             /*
386              * Klockwork - ok
387              * Eat it, we want to throw the original exception.
388              */

389                 }
390             }
391             throw e;
392         }
393     }
394
395     /**
396      * Respond to config updates.
397      */

398     public void envConfigUpdate(DbConfigManager mgr)
399         throws DatabaseException {
400
401         runOrPauseDaemons(mgr);
402         
403         backgroundReadLimit = mgr.getInt
404             (EnvironmentParams.ENV_BACKGROUND_READ_LIMIT);
405         backgroundWriteLimit = mgr.getInt
406             (EnvironmentParams.ENV_BACKGROUND_WRITE_LIMIT);
407         backgroundSleepInterval = PropUtil.microsToMillis(mgr.getLong
408             (EnvironmentParams.ENV_BACKGROUND_SLEEP_INTERVAL));
409     }
410     
411     /**
412      * Read configurations for daemons, instantiate.
413      */

414     private void createDaemons()
415         throws DatabaseException {
416
417         /* Evictor */
418         evictor = new Evictor(this, "Evictor");
419
420         /* Checkpointer */
421
422         /*
423          * Make sure that either log-size-based or time-based checkpointing
424          * is enabled.
425          */

426         long checkpointerWakeupTime =
427             Checkpointer.getWakeupPeriod(configManager);
428         checkpointer = new Checkpointer(this,
429                                         checkpointerWakeupTime,
430                                         Environment.CHECKPOINTER_NAME);
431
432         /* INCompressor */
433         long compressorWakeupInterval =
434             PropUtil.microsToMillis
435         (configManager.getLong
436          (EnvironmentParams.COMPRESSOR_WAKEUP_INTERVAL));
437         inCompressor = new INCompressor(this, compressorWakeupInterval,
438                                         Environment.INCOMP_NAME);
439
440     /* The cleaner is not time-based so no wakeup interval is used. */
441     cleaner = new Cleaner(this, Environment.CLEANER_NAME);
442     }
443
444     /**
445      * Run or pause daemons, depending on config properties.
446      */

447     private void runOrPauseDaemons(DbConfigManager mgr)
448         throws DatabaseException {
449
450         if (!isReadOnly) {
451             /* INCompressor */
452             inCompressor.runOrPause
453                 (mgr.getBoolean(EnvironmentParams.ENV_RUN_INCOMPRESSOR));
454
455             /* Cleaner. Do not start it if running in-memory */
456             cleaner.runOrPause
457                 (mgr.getBoolean(EnvironmentParams.ENV_RUN_CLEANER) &&
458                  !isMemOnly);
459
460             /*
461              * Checkpointer. Run in both transactional and non-transactional
462              * environments to guarantee recovery time.
463              */

464             checkpointer.runOrPause
465                 (mgr.getBoolean(EnvironmentParams.ENV_RUN_CHECKPOINTER));
466         }
467
468         /* Evictor */
469         evictor.runOrPause
470             (mgr.getBoolean(EnvironmentParams.ENV_RUN_EVICTOR));
471     }
472         
473     /**
474      * Return the incompressor. In general, don't use this directly because
475      * it's easy to forget that the incompressor can be null at times (i.e
476      * during the shutdown procedure. Instead, wrap the functionality within
477      * this class, like lazyCompress.
478      */

479     public INCompressor getINCompressor() {
480     return inCompressor;
481     }
482
483     /**
484      * Returns the UtilizationTracker.
485      */

486     public UtilizationTracker getUtilizationTracker() {
487         return cleaner.getUtilizationTracker();
488     }
489
490     /**
491      * Returns the UtilizationProfile.
492      */

493     public UtilizationProfile getUtilizationProfile() {
494         return cleaner.getUtilizationProfile();
495     }
496
497     /**
498      * If a background read limit has been configured and that limit is
499      * exceeded when the cumulative total is incremented by the given number of
500      * reads, increment the sleep backlog to cause a sleep to occur. Called by
501      * background activities such as the cleaner after performing a file read
502      * operation.
503      *
504      * @see #sleepAfterBackgroundIO
505      */

506     public void updateBackgroundReads(int nReads) {
507
508         /*
509          * Make a copy of the volatile limit field since it could change
510          * between the time we check it and the time we use it below.
511          */

512         int limit = backgroundReadLimit;
513         if (limit > 0) {
514             synchronized (backgroundTrackingMutex) {
515                 backgroundReadCount += nReads;
516                 if (backgroundReadCount >= limit) {
517                     backgroundSleepBacklog += 1;
518                     /* Remainder is rolled forward. */
519                     backgroundReadCount -= limit;
520                     assert backgroundReadCount >= 0;
521                 }
522             }
523         }
524     }
525
526     /**
527      * If a background write limit has been configured and that limit is
528      * exceeded when the given amount written is added to the cumulative total,
529      * increment the sleep backlog to cause a sleep to occur. Called by
530      * background activities such as the checkpointer and evictor after
531      * performing a file write operation.
532      *
533      * <p>The number of writes is estimated by dividing the bytes written by
534      * the log buffer size. Since the log write buffer is shared by all
535      * writers, this is the best approximation possible.</p>
536      *
537      * @see #sleepAfterBackgroundIO
538      */

539     public void updateBackgroundWrites(int writeSize, int logBufferSize) {
540
541         /*
542          * Make a copy of the volatile limit field since it could change
543          * between the time we check it and the time we use it below.
544          */

545         int limit = backgroundWriteLimit;
546         if (limit > 0) {
547             synchronized (backgroundTrackingMutex) {
548                 backgroundWriteBytes += writeSize;
549                 int writeCount = (int) (backgroundWriteBytes / logBufferSize);
550                 if (writeCount >= limit) {
551                     backgroundSleepBacklog += 1;
552                     /* Remainder is rolled forward. */
553                     backgroundWriteBytes -= (limit * logBufferSize);
554                     assert backgroundWriteBytes >= 0;
555                 }
556             }
557         }
558     }
559
560     /**
561      * If the sleep backlog is non-zero (set by updateBackgroundReads or
562      * updateBackgroundWrites), sleep for the configured interval and decrement
563      * the backlog.
564      *
565      * <p>If two threads call this method and the first call causes a sleep,
566      * the call by the second thread will block until the first thread's sleep
567      * interval is over. When the call by the second thread is unblocked, if
568      * another sleep is needed then the second thread will sleep again. In
569      * other words, when lots of sleeps are needed, background threads may
570      * backup. This is intended to give foreground threads a chance to "catch
571      * up" when background threads are doing a lot of IO.</p>
572      */

573     public void sleepAfterBackgroundIO() {
574         if (backgroundSleepBacklog > 0) {
575             synchronized (backgroundSleepMutex) {
576                 /* Sleep. Rethrow interrupts if they occur. */
577                 try {
578             /* FindBugs: OK that we're sleeping with a mutex held. */
579                     Thread.sleep(backgroundSleepInterval);
580                 } catch (InterruptedException JavaDoc e) {
581                     Thread.currentThread().interrupt();
582                 }
583                 /* Assert has intentional side effect for unit testing. */
584                 assert TestHookExecute.doHookIfSet(backgroundSleepHook);
585             }
586             synchronized (backgroundTrackingMutex) {
587                 /* Decrement backlog last to make other threads wait. */
588                 if (backgroundSleepBacklog > 0) {
589                     backgroundSleepBacklog -= 1;
590                 }
591             }
592         }
593     }
594
595     /* For unit testing only. */
596     public void setBackgroundSleepHook(TestHook hook) {
597         backgroundSleepHook = hook;
598     }
599
600     /**
601      * Log the map tree root and save the LSN.
602      */

603     public void logMapTreeRoot()
604         throws DatabaseException {
605
606         mapTreeRootLatch.acquire();
607         try {
608             mapTreeRootLsn = logManager.log(dbMapTree);
609         } finally {
610             mapTreeRootLatch.release();
611         }
612     }
613
614     /**
615      * Force a rewrite of the map tree root if required.
616      */

617     public void rewriteMapTreeRoot(long cleanerTargetLsn)
618         throws DatabaseException {
619
620         mapTreeRootLatch.acquire();
621         try {
622             if (DbLsn.compareTo(cleanerTargetLsn, mapTreeRootLsn) == 0) {
623
624                 /*
625          * The root entry targetted for cleaning is in use. Write a
626          * new copy.
627                  */

628                 mapTreeRootLsn = logManager.log(dbMapTree);
629             }
630         } finally {
631             mapTreeRootLatch.release();
632         }
633     }
634
635     /**
636      * @return the mapping tree root LSN.
637      */

638     public long getRootLsn() {
639         return mapTreeRootLsn;
640     }
641         
642     /**
643      * Set the mapping tree from the log. Called during recovery.
644      */

645     public void readMapTreeFromLog(long rootLsn)
646         throws DatabaseException {
647
648         dbMapTree = (DbTree) logManager.get(rootLsn);
649         dbMapTree.setEnvironmentImpl(this);
650
651         /* Set the map tree root */
652         mapTreeRootLatch.acquire();
653         try {
654             mapTreeRootLsn = rootLsn;
655         } finally {
656             mapTreeRootLatch.release();
657         }
658     }
659
660     /**
661      * Tells the asynchronous IN compressor thread about a BIN with a deleted
662      * entry.
663      */

664     public void addToCompressorQueue(BIN bin,
665                                      Key deletedKey,
666                                      boolean doWakeup)
667         throws DatabaseException {
668         
669         /*
670          * May be called by the cleaner on its last cycle, after the compressor
671          * is shut down.
672          */

673         if (inCompressor != null) {
674             inCompressor.addBinKeyToQueue(bin, deletedKey, doWakeup);
675         }
676     }
677
678     /**
679      * Tells the asynchronous IN compressor thread about a BINReference with a
680      * deleted entry.
681      */

682     public void addToCompressorQueue(BINReference binRef,
683                                  boolean doWakeup)
684         throws DatabaseException {
685         
686         /*
687          * May be called by the cleaner on its last cycle, after the compressor
688          * is shut down.
689          */

690         if (inCompressor != null) {
691             inCompressor.addBinRefToQueue(binRef, doWakeup);
692         }
693     }
694
695     /**
696      * Tells the asynchronous IN compressor thread about a collections of
697      * BINReferences with deleted entries.
698      */

699     public void addToCompressorQueue(Collection JavaDoc binRefs,
700                                      boolean doWakeup)
701         throws DatabaseException {
702
703         /*
704          * May be called by the cleaner on its last cycle, after the compressor
705          * is shut down.
706          */

707         if (inCompressor != null) {
708             inCompressor.addMultipleBinRefsToQueue(binRefs, doWakeup);
709         }
710     }
711
712     /**
713      * Do lazy compression at opportune moments.
714      */

715     public void lazyCompress(IN in)
716         throws DatabaseException {
717
718         /*
719          * May be called by the cleaner on its last cycle, after the compressor
720          * is shut down.
721          */

722         if (inCompressor != null) {
723             inCompressor.lazyCompress(in);
724         }
725     }
726
727     /**
728      * Initialize the debugging logging system. Note that publishing to the
729      * database log is not permitted until we've initialized the file manager
730      * in recovery. We can't log safely before that.
731      */

732     private Logger JavaDoc initLogger(File JavaDoc envHome)
733         throws DatabaseException {
734
735         /* XXX, this creates problems in unit tests, not sure why yet
736            Logger logger = Logger.getLogger(EnvironmentImpl.class.getName() +
737            "." + envNum); */

738         Logger JavaDoc logger = Logger.getAnonymousLogger();
739
740         /*
741          * Disable handlers inherited from parents, we want JE to control its
742          * own behavior. Add our handlers based on configuration
743          */

744         logger.setUseParentHandlers(false);
745
746         /* Set the logging level. */
747         Level JavaDoc level =
748         Tracer.parseLevel(this, EnvironmentParams.JE_LOGGING_LEVEL);
749         logger.setLevel(level);
750
751         /* Log to console. */
752         if (configManager.getBoolean(EnvironmentParams.JE_LOGGING_CONSOLE)) {
753             Handler JavaDoc consoleHandler = new ConsoleHandler JavaDoc();
754             consoleHandler.setLevel(level);
755             logger.addHandler(consoleHandler);
756         }
757
758         /* Log to text file. */
759         Handler JavaDoc fileHandler = null;
760         try {
761             if (configManager.getBoolean(EnvironmentParams.JE_LOGGING_FILE)) {
762
763                 /* Log with a rotating set of files, use append mode. */
764                 int limit =
765                     configManager.getInt(EnvironmentParams.
766                      JE_LOGGING_FILE_LIMIT);
767                 int count =
768                     configManager.getInt(EnvironmentParams.
769                      JE_LOGGING_FILE_COUNT);
770                 String JavaDoc logFilePattern = envHome + "/" + Tracer.INFO_FILES;
771
772                 fileHandler = new FileHandler JavaDoc(logFilePattern,
773                                               limit, count, true);
774                 fileHandler.setFormatter(new SimpleFormatter JavaDoc());
775                 fileHandler.setLevel(level);
776                 logger.addHandler(fileHandler);
777             }
778         } catch (IOException JavaDoc e) {
779             throw new DatabaseException(e.getMessage());
780         }
781
782         return logger;
783     }
784
785     /**
786      * Add the database log as one of the debug logging destinations when the
787      * logging system is sufficiently initialized.
788      */

789     public void enableDebugLoggingToDbLog()
790         throws DatabaseException {
791
792         if (configManager.getBoolean(EnvironmentParams.JE_LOGGING_DBLOG)) {
793             Handler JavaDoc dbLogHandler = new TraceLogHandler(this);
794             Level JavaDoc level =
795                 Level.parse(configManager.get(EnvironmentParams.
796                           JE_LOGGING_LEVEL));
797             dbLogHandler.setLevel(level);
798             envLogger.addHandler(dbLogHandler);
799         }
800     }
801
802     /**
803      * Close down the logger.
804      */

805     public void closeLogger() {
806         Handler JavaDoc [] handlers = envLogger.getHandlers();
807         for (int i = 0; i < handlers.length; i++) {
808             handlers[i].close();
809         }
810     }
811
812     /**
813      * Not much to do, mark state.
814      */

815     public void open() {
816         envState = DbEnvState.OPEN;
817     }
818
819     /**
820      * Invalidate the environment. Done when a fatal exception
821      * (RunRecoveryException) is thrown.
822      */

823     public void invalidate(RunRecoveryException e) {
824
825         /*
826          * Remember the fatal exception so we can redisplay it if the
827          * environment is called by the application again. Set some state in
828          * the exception so the exception message will be clear that this was
829          * an earlier exception.
830          */

831         savedInvalidatingException = e;
832         envState = DbEnvState.INVALID;
833     requestShutdownDaemons();
834     }
835
836     public void invalidate(Error JavaDoc e) {
837     savedInvalidatingException = (RunRecoveryException)
838         SAVED_RRE.initCause(e);
839     envState = DbEnvState.INVALID;
840     requestShutdownDaemons();
841     }
842
843     /**
844      * @return true if environment is open.
845      */

846     public boolean isOpen() {
847         return (envState == DbEnvState.OPEN);
848     }
849