1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je;
9 
10 import java.io.Closeable;
11 import java.io.File;
12 import java.io.PrintStream;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.logging.Level;
17 
18 import javax.transaction.xa.Xid;
19 
20 import com.sleepycat.je.Durability.ReplicaAckPolicy;
21 import com.sleepycat.je.dbi.DatabaseImpl;
22 import com.sleepycat.je.dbi.DbConfigManager;
23 import com.sleepycat.je.dbi.DbEnvPool;
24 import com.sleepycat.je.dbi.DbTree;
25 import com.sleepycat.je.dbi.DbTree.TruncateDbResult;
26 import com.sleepycat.je.dbi.EnvironmentImpl;
27 import com.sleepycat.je.dbi.RepConfigProxy;
28 import com.sleepycat.je.dbi.StartupTracker.Phase;
29 import com.sleepycat.je.dbi.TriggerManager;
30 import com.sleepycat.je.txn.HandleLocker;
31 import com.sleepycat.je.txn.Locker;
32 import com.sleepycat.je.txn.LockerFactory;
33 import com.sleepycat.je.txn.Txn;
34 import com.sleepycat.je.utilint.DatabaseUtil;
35 import com.sleepycat.je.utilint.LoggerUtils;
36 import com.sleepycat.je.utilint.Pair;
37 
38 /**
39  * A database environment.  Environments include support for some or all of
40  * caching, locking, logging and transactions.
41  *
42  * <p>To open an existing environment with default attributes the application
43  * may use a default environment configuration object or null:
44  * <blockquote>
45  *     <pre>
46  *      // Open an environment handle with default attributes.
47  *     Environment env = new Environment(home, new EnvironmentConfig());
48  *     </pre>
49  * </blockquote>
50  * or
51  * <blockquote><pre>
52  *     Environment env = new Environment(home, null);
53  * </pre></blockquote>
54  * <p>Note that many Environment objects may access a single environment.</p>
55  * <p>To create an environment or customize attributes, the application should
56  * customize the configuration class. For example:</p>
57  * <blockquote><pre>
58  *     EnvironmentConfig envConfig = new EnvironmentConfig();
59  *     envConfig.setTransactional(true);
60  *     envConfig.setAllowCreate(true);
61  *     envConfig.setCacheSize(1000000);
62  *     Environment newlyCreatedEnv = new Environment(home, envConfig);
63  * </pre></blockquote>
64  *
65  * <p>Note that environment configuration parameters can also be set through
66  * the &lt;environment home&gt;/je.properties file. This file takes precedence
67  * over any programmatically specified configuration parameters so that
68  * configuration changes can be made without recompiling. Environment
69  * configuration follows this order of precedence:</p>
70  *
71  * <ol>
72  * <li>Configuration parameters specified in
73  * &lt;environment home&gt;/je.properties take first precedence.
74  * <li> Configuration parameters set in the EnvironmentConfig object used at
75  * Environment construction e   tameters not set by the application are set to
76  * system defaults, described along with the parameter name String constants
77  * in the EnvironmentConfig class.
78  * </ol>
79  *
80  * <p>An <em>environment handle</em> is an Environment instance.  More than one
81  * Environment instance may be created for the same physical directory, which
82  * is the same as saying that more than one Environment handle may be open at
83  * one time for a given environment.</p>
84  *
85  * The Environment handle should not be closed while any other handle remains
86  * open that is using it as a reference (for example, {@link
87  * com.sleepycat.je.Database Database} or {@link com.sleepycat.je.Transaction
88  * Transaction}.  Once {@link com.sleepycat.je.Environment#close
89  * Environment.close} is called, this object may not be accessed again.
90  */
91 public class Environment implements Closeable {
92 
93     /**
94      * @hidden
95      * envImpl is a reference to the shared underlying environment.
96      */
97     protected EnvironmentImpl envImpl;
98     private TransactionConfig defaultTxnConfig;
99     private EnvironmentMutableConfig handleConfig;
100     private final EnvironmentConfig appliedFinalConfig;
101 
102     private final Map<Database, Database> referringDbs;
103     private final Map<Transaction, Transaction> referringDbTxns;
104 
105     /**
106      * @hidden
107      * The name of the cleaner daemon thread.  This constant is passed to an
108      * ExceptionEvent's threadName argument when an exception is thrown in the
109      * cleaner daemon thread.
110      */
111     public static final String CLEANER_NAME = "Cleaner";
112 
113     /**
114      * @hidden
115      * The name of the IN Compressor daemon thread.  This constant is passed to
116      * an ExceptionEvent's threadName argument when an exception is thrown in
117      * the IN Compressor daemon thread.
118      */
119     public static final String INCOMP_NAME = "INCompressor";
120 
121     /**
122      * @hidden
123      * The name of the Checkpointer daemon thread.  This constant is passed to
124      * an ExceptionEvent's threadName argument when an exception is thrown in
125      * the Checkpointer daemon thread.
126      */
127     public static final String CHECKPOINTER_NAME = "Checkpointer";
128 
129     /**
130      * @hidden
131      * The name of the StatCapture daemon thread.  This constant is passed to
132      * an ExceptionEvent's threadName argument when an exception is thrown in
133      * the StatCapture daemon thread.
134      */
135     public static final String STATCAPTURE_NAME = "StatCapture";
136 
137     /**
138      * Creates a database environment handle.
139      *
140      * @param envHome The database environment's home directory.
141      *
142      * @param configuration The database environment attributes.  If null,
143      * default attributes are used.
144      *
145      * @throws EnvironmentNotFoundException if the environment does not exist
146      * (does not contain at least one log file) and the {@code
147      * EnvironmentConfig AllowCreate} parameter is false.
148      *
149      * @throws EnvironmentLockedException when an environment cannot be opened
150      * for write access because another process has the same environment open
151      * for write access.  <strong>Warning:</strong> This exception should be
152      * handled when an environment is opened by more than one process.
153      *
154      * @throws VersionMismatchException when the existing log is not compatible
155      * with the version of JE that is running.  This occurs when a later
156      * version of JE was used to create the log.  <strong>Warning:</strong>
157      * This exception should be handled when more than one version of JE may be
158      * used to access an environment.
159      *
160      * @throws EnvironmentFailureException if an unexpected, internal or
161      * environment-wide failure occurs.
162      *
163      * @throws UnsupportedOperationException if this environment was previously
164      * opened for replication and is not being opened read-only.
165      *
166      * @throws IllegalArgumentException if an invalid parameter is specified,
167      * for example, an invalid {@code EnvironmentConfig} parameter.
168      */
Environment(File envHome, EnvironmentConfig configuration)169     public Environment(File envHome, EnvironmentConfig configuration)
170         throws EnvironmentNotFoundException,
171                EnvironmentLockedException,
172                VersionMismatchException,
173                DatabaseException,
174                IllegalArgumentException {
175 
176         this(envHome, configuration, true /*openIfNeeded*/,
177              null /*repConfigProxy*/, null /*envImplParam*/);
178     }
179 
180     /**
181      * @hidden
182      * Replication support.
183      */
Environment(File envHome, EnvironmentConfig configuration, RepConfigProxy repConfigProxy, EnvironmentImpl envImplParam)184     protected Environment(File envHome,
185                           EnvironmentConfig configuration,
186                           RepConfigProxy repConfigProxy,
187                           EnvironmentImpl envImplParam) {
188         this(envHome, configuration, true /*openIfNeeded*/, repConfigProxy,
189              envImplParam);
190     }
191 
192     /**
193      * Gets an Environment for an existing EnvironmentImpl. Used by utilities
194      * such as the JMX MBean which don't want to open the environment or be
195      * reference counted. The calling application must take care not to retain
196      */
Environment(File envHome)197     Environment(File envHome) {
198         this(envHome, null /*configuration*/, false /*openIfNeeded*/,
199              null /*repConfigProxy*/, null /*envImplParam*/);
200     }
201 
202     /**
203      * @hidden
204      * Internal common constructor.
205      *
206      * @param envImpl is non-null only when used by EnvironmentIml to create an
207      * InternalEnvironment.
208      */
Environment(File envHome, EnvironmentConfig envConfig, boolean openIfNeeded, RepConfigProxy repConfigProxy, EnvironmentImpl envImplParam)209     protected Environment(File envHome,
210                           EnvironmentConfig envConfig,
211                           boolean openIfNeeded,
212                           RepConfigProxy repConfigProxy,
213                           EnvironmentImpl envImplParam) {
214 
215         /* If openIfNeeded is false, then envConfig must be null. */
216         assert openIfNeeded || envConfig == null || envImplParam != null;
217 
218         envImpl = null;
219         referringDbs = new ConcurrentHashMap<Database, Database>();
220         referringDbTxns = new ConcurrentHashMap<Transaction, Transaction>();
221 
222         DatabaseUtil.checkForNullParam(envHome, "envHome");
223 
224         appliedFinalConfig =
225             setupHandleConfig(envHome, envConfig, repConfigProxy);
226 
227         if (envImplParam != null) {
228             /* We're creating an InternalEnvironment in EnvironmentImpl. */
229             envImpl = envImplParam;
230         } else {
231             /* Open a new or existing environment in the shared pool. */
232             envImpl = makeEnvironmentImpl(envHome,
233                                           envConfig,
234                                           openIfNeeded,
235                                           repConfigProxy);
236         }
237     }
238 
239     /**
240      * @hidden
241      * MakeEnvironmentImpl is called both by the Environment constructor and
242      * by the ReplicatedEnvironment constructor when recreating the environment
243      * for a hard recovery.
244      */
makeEnvironmentImpl(File envHome, EnvironmentConfig envConfig, boolean openIfNeeded, RepConfigProxy repConfigProxy)245     protected EnvironmentImpl makeEnvironmentImpl
246         (File envHome,
247          EnvironmentConfig envConfig,
248          boolean openIfNeeded,
249          RepConfigProxy repConfigProxy) {
250 
251         envImpl = DbEnvPool.getInstance().getEnvironment
252             (envHome,
253              appliedFinalConfig,
254              envConfig != null /*checkImmutableParams*/,
255              openIfNeeded,
256              setupRepConfig(envHome, repConfigProxy, envConfig));
257 
258         if (envImpl != null) {
259             envImpl.registerMBean(this);
260         }
261 
262         return envImpl;
263     }
264 
265     /**
266      * Validate the parameters specified in the environment config.  Applies
267      * the configurations specified in the je.properties file to override any
268      * programmatically set configurations.  Create a copy to save in this
269      * handle. The main reason to return a config instead of using the
270      * handleConfig field is to return an EnvironmentConfig instead of a
271      * EnvironmentMutableConfig.
272      */
setupHandleConfig(File envHome, EnvironmentConfig envConfig, RepConfigProxy repConfig)273     private EnvironmentConfig setupHandleConfig(File envHome,
274                                                 EnvironmentConfig envConfig,
275                                                 RepConfigProxy repConfig)
276         throws IllegalArgumentException {
277 
278         /* If the user specified a null object, use the default */
279         EnvironmentConfig baseConfig = (envConfig == null) ?
280             EnvironmentConfig.DEFAULT : envConfig;
281 
282         /* Make a copy, apply je.properties, and init the handle config. */
283         EnvironmentConfig useConfig = baseConfig.clone();
284 
285         /* Apply the je.properties file. */
286         if (useConfig.getLoadPropertyFile()) {
287             DbConfigManager.applyFileConfig(envHome,
288                                             DbInternal.getProps(useConfig),
289                                             false);       // forReplication
290         }
291         copyToHandleConfig(useConfig, useConfig, repConfig);
292         return useConfig;
293     }
294 
295     /**
296      * @hidden
297      * Obtain a validated replication configuration. In a non-HA environment,
298      * return null.
299      */
300     protected RepConfigProxy
setupRepConfig(final File envHome, final RepConfigProxy repConfigProxy, final EnvironmentConfig envConfig)301         setupRepConfig(final File envHome,
302                        final RepConfigProxy repConfigProxy,
303                        final EnvironmentConfig envConfig) {
304 
305         return null;
306     }
307 
308     /**
309      * The Environment.close method closes the Berkeley DB environment.
310      *
311      * <p>When the last environment handle is closed, allocated resources are
312      * freed, and daemon threads are stopped, even if they are performing work.
313      * For example, if the cleaner is still cleaning the log, it will be
314      * stopped at the next reasonable opportunity and perform no more cleaning
315      * operations.</p>
316      *
317      * <p>The Environment handle should not be closed while any other handle
318      * that refers to it is not yet closed; for example, database environment
319      * handles must not be closed while database handles remain open, or
320      * transactions in the environment have not yet committed or aborted.
321      * Specifically, this includes {@link com.sleepycat.je.Database Database},
322      * {@link com.sleepycat.je.Cursor Cursor} and {@link
323      * com.sleepycat.je.Transaction Transaction} handles.</p>
324      *
325      * <p>If this handle has already been closed, this method does nothing and
326      * returns without throwing an exception.</p>
327      *
328      * <p>In multithreaded applications, only a single thread should call
329      * Environment.close.</p>
330      *
331      * <p>The environment handle may not be used again after this method has
332      * been called, regardless of the method's success or failure, with one
333      * exception:  the {@code close} method itself may be called any number of
334      * times.</p>
335      *
336      * <p>WARNING: To guard against memory leaks, the application should
337      * discard all references to the closed handle.  While BDB makes an effort
338      * to discard references from closed objects to the allocated memory for an
339      * environment, this behavior is not guaranteed.  The safe course of action
340      * for an application is to discard all references to closed BDB
341      * objects.</p>
342      *
343      * @throws EnvironmentFailureException if an unexpected, internal or
344      * environment-wide failure occurs.
345      *
346      * @throws IllegalStateException if any open databases or transactions
347      * refer to this handle.
348      */
close()349     public synchronized void close()
350         throws DatabaseException {
351 
352         if (envImpl == null) {
353             return;
354         }
355 
356         if (!envImpl.isValid()) {
357 
358             /*
359              * We're trying to close on an environment that has seen a fatal
360              * exception. Try to do the minimum, such as closing file
361              * descriptors, to support re-opening the environment in the same
362              * JVM.
363              */
364             try {
365                 envImpl.closeAfterInvalid();
366             } finally {
367                 envImpl = null;
368             }
369             return;
370         }
371 
372         final StringBuilder errors = new StringBuilder();
373         try {
374             checkForCloseErrors(errors);
375 
376             try {
377                 envImpl.close();
378             } catch (RuntimeException e) {
379                 if (!envImpl.isValid()) {
380                     /* Propagate if env is invalidated. */
381                     throw e;
382                 }
383                 errors.append
384                   ("\nWhile closing Environment encountered exception: ");
385                 errors.append(e).append("\n");
386             }
387 
388             if (errors.length() > 0) {
389                 throw new IllegalStateException(errors.toString());
390             }
391         } finally {
392             envImpl = null;
393         }
394     }
395 
396     /**
397      * Close an InternalEnvironment handle.  We do not call
398      * EnvironmentImpl.close here, since an InternalEnvironment is not
399      * registered like a non-internal handle.  However, we must call
400      * checkForCloseErrors to auto-close internal databases, as well as check
401      * for errors.
402      */
closeInternalHandle()403     synchronized void closeInternalHandle() {
404         final StringBuilder errors = new StringBuilder();
405         checkForCloseErrors(errors);
406         if (errors.length() > 0) {
407             throw new IllegalStateException(errors.toString());
408         }
409     }
410 
checkForCloseErrors(StringBuilder errors)411     private void checkForCloseErrors(StringBuilder errors) {
412 
413         checkOpenDbs(errors);
414 
415         checkOpenTxns(errors);
416 
417         if (!isInternalHandle()) {
418 
419             /*
420              * Only check for open XA transactions against user created
421              * environment handles.
422              */
423             checkOpenXATransactions(errors);
424         }
425     }
426 
427     /**
428      * Appends error messages to the errors argument if there are
429      * open XA transactions associated with the underlying EnvironmentImpl.
430      */
checkOpenXATransactions(final StringBuilder errors)431     private void checkOpenXATransactions(final StringBuilder errors) {
432         Xid[] openXids = envImpl.getTxnManager().XARecover();
433         if (openXids != null && openXids.length > 0) {
434             errors.append("There ");
435             int nXATxns = openXids.length;
436             if (nXATxns == 1) {
437                 errors.append("is 1 existing XA transaction opened");
438                 errors.append(" in the Environment.\n");
439                 errors.append("It");
440             } else {
441                 errors.append("are ");
442                 errors.append(nXATxns);
443                 errors.append(" existing transactions opened in");
444                 errors.append(" the Environment.\n");
445                 errors.append("They");
446             }
447             errors.append(" will be left open ...\n");
448         }
449     }
450 
451     /**
452      * Appends error messages to the errors argument if there are open
453      * transactions associated with the environment.
454      */
checkOpenTxns(final StringBuilder errors)455     private void checkOpenTxns(final StringBuilder errors) {
456         int nTxns = (referringDbTxns == null) ? 0 : referringDbTxns.size();
457         if (nTxns == 0) {
458             return;
459         }
460 
461         errors.append("There ");
462         if (nTxns == 1) {
463             errors.append("is 1 existing transaction opened");
464             errors.append(" against the Environment.\n");
465         } else {
466             errors.append("are ");
467             errors.append(nTxns);
468             errors.append(" existing transactions opened against");
469             errors.append(" the Environment.\n");
470         }
471         errors.append("Aborting open transactions ...\n");
472 
473        for (Transaction txn : referringDbTxns.keySet()) {
474             try {
475                 errors.append("aborting " + txn);
476                 txn.abort();
477             } catch (RuntimeException e) {
478                 if (!envImpl.isValid()) {
479                     /* Propagate if env is invalidated. */
480                     throw e;
481                 }
482                 errors.append("\nWhile aborting transaction ");
483                 errors.append(txn.getId());
484                 errors.append(" encountered exception: ");
485                 errors.append(e).append("\n");
486             }
487         }
488     }
489 
490     /**
491      * Appends error messages to the errors argument if there are open database
492      * handles associated with the environment.
493      */
checkOpenDbs(final StringBuilder errors)494     private void checkOpenDbs(final StringBuilder errors) {
495 
496         if (referringDbs.isEmpty()) {
497             return;
498         }
499 
500         int nOpenUserDbs = 0;
501 
502         for (Database db : referringDbs.keySet()) {
503             String dbName = "";
504             try {
505 
506                 /*
507                  * Save the db name before we attempt the close, it's
508                  * unavailable after the close.
509                  */
510                 dbName = db.getDebugName();
511 
512                 if (!db.getDatabaseImpl().isInternalDb()) {
513                     nOpenUserDbs += 1;
514                     errors.append("Unclosed Database: ");
515                     errors.append(dbName).append("\n");
516                 }
517                 db.close();
518             } catch (RuntimeException e) {
519                 if (!envImpl.isValid()) {
520                     /* Propagate if env is invalidated. */
521                     throw e;
522                 }
523                 errors.append("\nWhile closing Database ");
524                 errors.append(dbName);
525                 errors.append(" encountered exception: ");
526                 errors.append(LoggerUtils.getStackTrace(e)).append("\n");
527             }
528         }
529 
530         if (nOpenUserDbs > 0) {
531             errors.append("Databases left open: ");
532             errors.append(nOpenUserDbs).append("\n");
533         }
534     }
535 
536     /**
537      * Opens, and optionally creates, a <code>Database</code>.
538      *
539      * @param txn For a transactional database, an explicit transaction may be
540      * specified, or null may be specified to use auto-commit.  For a
541      * non-transactional database, null must be specified.
542      *
543      * @param databaseName The name of the database.
544      *
545      * @param dbConfig The database attributes.  If null, default attributes
546      * are used.
547      *
548      * @return Database handle.
549      *
550      * @throws DatabaseExistsException if the database already exists and the
551      * {@code DatabaseConfig ExclusiveCreate} parameter is true.
552      *
553      * @throws DatabaseNotFoundException if the database does not exist and the
554      * {@code DatabaseConfig AllowCreate} parameter is false.
555      *
556      * @throws OperationFailureException if one of the <a
557      * href="../je/OperationFailureException.html#readFailures">Read Operation
558      * Failures</a> occurs. If the database does not exist and the {@link
559      * DatabaseConfig#setAllowCreate AllowCreate} parameter is true, then one
560      * of the <a
561      * href="../je/OperationFailureException.html#writeFailures">Write
562      * Operation Failures</a> may also occur.
563      *
564      * @throws EnvironmentFailureException if an unexpected, internal or
565      * environment-wide failure occurs.
566      *
567      * @throws IllegalStateException if this handle or the underlying
568      * environment has been closed.
569      *
570      * @throws IllegalArgumentException if an invalid parameter is specified,
571      * for example, an invalid {@code DatabaseConfig} property.
572      *
573      * @throws IllegalStateException if DatabaseConfig properties are changed
574      * and there are other open handles for this database.
575      */
openDatabase(Transaction txn, String databaseName, DatabaseConfig dbConfig)576     public synchronized Database openDatabase(Transaction txn,
577                                               String databaseName,
578                                               DatabaseConfig dbConfig)
579         throws DatabaseNotFoundException,
580                DatabaseExistsException,
581                IllegalArgumentException,
582                IllegalStateException {
583 
584         checkHandleIsValid();
585         checkEnv();
586 
587         try {
588             if (dbConfig == null) {
589                 dbConfig = DatabaseConfig.DEFAULT;
590             }
591 
592             Database db = new Database(this);
593             setupDatabase(txn, db, databaseName, dbConfig,
594                           false /*isInternalDb*/);
595             return db;
596         } catch (Error E) {
597             envImpl.invalidate(E);
598             throw E;
599         }
600     }
601 
602     /**
603      * Opens an internal database for internal JE use.
604      *  - permits opening DBs with reserved (internal) names.
605      *  - permits opening a transactional DB in a non-transactional env.
606      *
607      * DbConfig should not be null. Note that the dbConfig argument determines
608      * whether the database that is created is to be replicated or is
609      * standalone. The current default is for dbConfig to be replicated, so use
610      * DatabaseConfig.setReplicated(false) to create a local internal database.
611      */
openInternalDatabase(Transaction txn, String databaseName, DatabaseConfig dbConfig)612     synchronized Database openInternalDatabase(Transaction txn,
613                                                String databaseName,
614                                                DatabaseConfig dbConfig)
615         throws DatabaseNotFoundException, DatabaseExistsException {
616 
617         assert DbTree.isReservedDbName(databaseName) : databaseName;
618         Database db = new Database(this);
619         setupDatabase(txn, db, databaseName, dbConfig, true /*isInternalDb*/);
620         return db;
621     }
622 
623     /**
624      * Opens and optionally creates a <code>SecondaryDatabase</code>.
625      *
626      * <p>Note that the associations between primary and secondary databases
627      * are not stored persistently.  Whenever a primary database is opened for
628      * write access by the application, the appropriate associated secondary
629      * databases should also be opened by the application.  This is necessary
630      * to ensure data integrity when changes are made to the primary
631      * database.</p>
632      *
633      * @param txn For a transactional database, an explicit transaction may be
634      * specified, or null may be specified to use auto-commit.  For a
635      * non-transactional database, null must be specified.
636      *
637      * @param databaseName The name of the database.
638      *
639      * @param primaryDatabase the primary database with which the secondary
640      * database will be associated.  The primary database must not be
641      * configured for duplicates.
642      *
643      * @param dbConfig The secondary database attributes.  If null, default
644      * attributes are used.
645      *
646      * @return Database handle.
647      *
648      * @throws DatabaseExistsException if the database already exists and the
649      * {@code DatabaseConfig ExclusiveCreate} parameter is true.
650      *
651      * @throws DatabaseNotFoundException if the database does not exist and the
652      * {@code DatabaseConfig AllowCreate} parameter is false.
653      *
654      * @throws OperationFailureException if one of the <a
655      * href="../je/OperationFailureException.html#readFailures">Read Operation
656      * Failures</a> occurs. If the database does not exist and the {@link
657      * DatabaseConfig#setAllowCreate AllowCreate} parameter is true, then one
658      * of the <a
659      * href="../je/OperationFailureException.html#writeFailures">Write
660      * Operation Failures</a> may also occur.
661      *
662      * @throws EnvironmentFailureException if an unexpected, internal or
663      * environment-wide failure occurs.
664      *
665      * @throws IllegalStateException if this handle or the underlying
666      * environment has been closed.
667      *
668      * @throws IllegalArgumentException if an invalid parameter is specified,
669      * for example, an invalid {@code SecondaryConfig} property.
670      *
671      * @throws IllegalStateException if DatabaseConfig properties are changed
672      * and there are other open handles for this database.
673      */
674     public synchronized
openSecondaryDatabase(Transaction txn, String databaseName, Database primaryDatabase, SecondaryConfig dbConfig)675         SecondaryDatabase openSecondaryDatabase(Transaction txn,
676                                                 String databaseName,
677                                                 Database primaryDatabase,
678                                                 SecondaryConfig dbConfig)
679         throws DatabaseNotFoundException,
680                DatabaseExistsException,
681                DatabaseException,
682                IllegalArgumentException,
683                IllegalStateException {
684 
685         checkHandleIsValid();
686         checkEnv();
687         try {
688             envImpl.getSecondaryAssociationLock().
689                 writeLock().lockInterruptibly();
690         } catch (InterruptedException e) {
691             throw new ThreadInterruptedException(envImpl, e);
692         }
693         try {
694             if (dbConfig == null) {
695                 dbConfig = SecondaryConfig.DEFAULT;
696             }
697             final SecondaryDatabase db =
698                 new SecondaryDatabase(this, dbConfig, primaryDatabase);
699 
700             setupDatabase(txn, db, databaseName, dbConfig,
701                           false /*isInternalDb*/);
702             return db;
703         } catch (Error E) {
704             envImpl.invalidate(E);
705             throw E;
706         } finally {
707             envImpl.getSecondaryAssociationLock().writeLock().unlock();
708         }
709     }
710 
711     /**
712      * The meat of open database processing.
713      *
714      * @param txn may be null
715      * @param newDb is the Database handle which houses this database
716      *
717      * @throws IllegalArgumentException via openDatabase and
718      * openSecondaryDatabase
719      *
720      * @see HandleLocker
721      */
setupDatabase(Transaction txn, Database newDb, String databaseName, DatabaseConfig dbConfig, boolean isInternalDb)722     private void setupDatabase(Transaction txn,
723                                Database newDb,
724                                String databaseName,
725                                DatabaseConfig dbConfig,
726                                boolean isInternalDb)
727         throws DatabaseNotFoundException, DatabaseExistsException {
728 
729         checkEnv();
730         DatabaseUtil.checkForNullParam(databaseName, "databaseName");
731 
732         LoggerUtils.envLogMsg(Level.FINEST, envImpl,
733                               "Environment.open: " + " name=" + databaseName +
734                               " dbConfig=" + dbConfig);
735 
736         final boolean autoTxnIsReplicated =
737             dbConfig.getReplicated() && envImpl.isReplicated();
738 
739         /*
740          * Check that the open configuration is valid and doesn't conflict with
741          * the envImpl configuration.
742          */
743         dbConfig.validateOnDbOpen(databaseName, autoTxnIsReplicated);
744         validateDbConfigAgainstEnv(dbConfig, databaseName, isInternalDb);
745 
746         /* Perform eviction before each operation that allocates memory. */
747         envImpl.criticalEviction(false /*backgroundIO*/);
748 
749         DatabaseImpl database = null;
750         boolean operationOk = false;
751         HandleLocker handleLocker = null;
752         final Locker locker = LockerFactory.getWritableLocker
753             (this, txn, isInternalDb, dbConfig.getTransactional(),
754              autoTxnIsReplicated, null);
755         try {
756 
757             /*
758              * Create the handle locker and lock the NameLN of an existing
759              * database.  A read lock on the NameLN is acquired for both locker
760              * and handleLocker.  Note: getDb may return a deleted database.
761              */
762             handleLocker = newDb.initHandleLocker(envImpl, locker);
763             database = envImpl.getDbTree().getDb(locker, databaseName,
764                                                  handleLocker);
765 
766             boolean dbCreated = false;
767             final boolean databaseExists =
768                 (database != null) && !database.isDeleted();
769 
770             if (databaseExists) {
771                 if (dbConfig.getAllowCreate() &&
772                     dbConfig.getExclusiveCreate()) {
773                     throw new DatabaseExistsException
774                         ("Database " + databaseName + " already exists");
775                 }
776 
777                 newDb.initExisting(this, locker, database, databaseName,
778                                    dbConfig);
779             } else {
780                 /* Release deleted DB. [#13415] */
781                 envImpl.getDbTree().releaseDb(database);
782                 database = null;
783 
784                 if (!isInternalDb &&
785                     DbTree.isReservedDbName(databaseName)) {
786                     throw new IllegalArgumentException
787                         (databaseName + " is a reserved database name.");
788                 }
789 
790                 if (!dbConfig.getAllowCreate()) {
791                     throw new DatabaseNotFoundException("Database " +
792                                                         databaseName +
793                                                         " not found.");
794                 }
795 
796                 /*
797                  * Init a new DB. This calls DbTree.createDb and the new
798                  * database is returned.  A write lock on the NameLN is
799                  * acquired by locker and a read lock by the handleLocker.
800                  */
801                 database = newDb.initNew(this, locker, databaseName, dbConfig);
802                 dbCreated = true;
803             }
804 
805             /*
806              * The open is successful.  We add the opened database handle to
807              * this environment to track open handles in general, and to the
808              * locker so that it can be invalidated by a user txn abort.
809              */
810             operationOk = true;
811             addReferringHandle(newDb);
812             locker.addOpenedDatabase(newDb);
813 
814             /* Run triggers before any subsequent auto commits. */
815             final boolean firstWriteHandle =
816                 newDb.isWritable() &&
817                 (newDb.getDatabaseImpl().noteWriteHandleOpen() == 1);
818 
819             if (dbCreated || firstWriteHandle) {
820                 TriggerManager.runOpenTriggers(locker, newDb, dbCreated);
821             }
822         } finally {
823 
824             /*
825              * If the open fails, decrement the DB usage count, release
826              * handle locks and remove references from other objects.  In other
827              * cases this is done by Database.close() or invalidate(), the
828              * latter in the case of a user txn abort.
829              */
830             if (!operationOk) {
831                 envImpl.getDbTree().releaseDb(database);
832                 if (handleLocker != null) {
833                     handleLocker.operationEnd(false);
834                 }
835                 newDb.removeReferringAssociations();
836             }
837 
838             /*
839              * Tell the locker that this operation is over. Some types of
840              * lockers (BasicLocker and auto Txn) will actually finish.
841              */
842             locker.operationEnd(operationOk);
843         }
844     }
845 
846     /**
847      * @throws IllegalArgumentException via openDatabase and
848      * openSecondaryDatabase
849      */
validateDbConfigAgainstEnv(DatabaseConfig dbConfig, String databaseName, boolean isInternalDb)850     private void validateDbConfigAgainstEnv(DatabaseConfig dbConfig,
851                                             String databaseName,
852                                             boolean isInternalDb)
853         throws IllegalArgumentException {
854 
855         /*
856          * R/W database handles on a replicated database must be transactional,
857          * for now. In the future we may support non-transactional database
858          * handles.
859          */
860         if (envImpl.isReplicated() &&
861             dbConfig.getReplicated() &&
862             !dbConfig.getReadOnly()) {
863             if (!dbConfig.getTransactional()) {
864                 throw new IllegalArgumentException
865                 ("Read/Write Database instances for replicated " +
866                  "database " + databaseName + " must be transactional.");
867             }
868         }
869 
870         /* Check operation's transactional status against the Environment */
871         if (!isInternalDb &&
872             dbConfig.getTransactional() &&
873             !(envImpl.isTransactional())) {
874             throw new IllegalArgumentException
875                 ("Attempted to open Database " + databaseName +
876                  " transactionally, but parent Environment is" +
877                  " not transactional");
878         }
879 
880         /* Check read/write status */
881         if (envImpl.isReadOnly() && (!dbConfig.getReadOnly())) {
882             throw new IllegalArgumentException
883                 ("Attempted to open Database " + databaseName +
884                  " as writable but parent Environment is read only ");
885         }
886     }
887 
888     /**
889      * Removes a database from the environment, discarding all records in the
890      * database and removing the database name itself.
891      *
892      * <p>Compared to deleting all the records in a database individually,
893      * {@code removeDatabase} is a very efficient operation.  Some internal
894      * housekeeping information is updated, but the database records are not
895      * read or written, and very little I/O is needed.</p>
896      *
897      * <p>When called on a database configured with secondary indices, the
898      * application is responsible for also removing all associated secondary
899      * indices.  To guarantee integrity, a primary database and all of its
900      * secondary databases should be removed atomically using a single
901      * transaction.</p>
902      *
903      * <p>Applications should not remove a database with open {@link Database
904      * Database} handles.  If the database is open with the same transaction as
905      * passed in the {@code txn} parameter, {@link IllegalStateException} is
906      * thrown by this method.  If the database is open using a different
907      * transaction, this method will block until all database handles are
908      * closed, or until the conflict is resolved by throwing {@link
909      * LockConflictException}.</p>
910      *
911      * @param txn For a transactional environment, an explicit transaction
912      * may be specified or null may be specified to use auto-commit.  For a
913      * non-transactional environment, null must be specified.
914      *
915      * @param databaseName The database to be removed.
916      *
917      * @throws DatabaseNotFoundException if the database does not exist.
918      *
919      * @throws OperationFailureException if one of the <a
920      * href="../je/OperationFailureException.html#writeFailures">Write
921      * Operation Failures</a> occurs.
922      *
923      * @throws EnvironmentFailureException if an unexpected, internal or
924      * environment-wide failure occurs.
925      *
926      * @throws UnsupportedOperationException if this is a read-only
927      * environment.
928      *
929      * @throws IllegalStateException if the database is currently open using
930      * the transaction passed in the {@code txn} parameter, or if this handle
931      * or the underlying environment has been closed.
932      *
933      * @throws IllegalArgumentException if an invalid parameter is specified.
934      */
removeDatabase(final Transaction txn, final String databaseName)935     public void removeDatabase(final Transaction txn,
936                                final String databaseName)
937         throws DatabaseNotFoundException {
938 
939         DatabaseUtil.checkForNullParam(databaseName, "databaseName");
940 
941         new DbNameOperation<Void>(txn, databaseName) {
942 
943             Pair<DatabaseImpl, Void> runWork(final Locker locker)
944                 throws DatabaseNotFoundException,
945                        DbTree.NeedRepLockerException {
946 
947                 final DatabaseImpl dbImpl =
948                     dbTree.dbRemove(locker, databaseName, null /*checkId*/);
949 
950                 return new Pair<>(dbImpl, null);
951             }
952 
953             void runTriggers(final Locker locker, final DatabaseImpl dbImpl) {
954                 TriggerManager.runRemoveTriggers(locker, dbImpl);
955             }
956         }.run();
957     }
958 
959     /**
960      * Renames a database, without removing the records it contains.
961      *
962      * <p>Applications should not rename a database with open {@link Database
963      * Database} handles.  If the database is open with the same transaction as
964      * passed in the {@code txn} parameter, {@link IllegalStateException} is
965      * thrown by this method.  If the database is open using a different
966      * transaction, this method will block until all database handles are
967      * closed, or until the conflict is resolved by throwing {@link
968      * LockConflictException}.</p>
969      *
970      * @param txn For a transactional environment, an explicit transaction
971      * may be specified or null may be specified to use auto-commit.  For a
972      * non-transactional environment, null must be specified.
973      *
974      * @param databaseName The new name of the database.
975      *
976      * @throws DatabaseNotFoundException if the database does not exist.
977      *
978      * @throws OperationFailureException if one of the <a
979      * href="../je/OperationFailureException.html#writeFailures">Write
980      * Operation Failures</a> occurs.
981      *
982      * @throws EnvironmentFailureException if an unexpected, internal or
983      * environment-wide failure occurs.
984      *
985      * @throws UnsupportedOperationException if this is a read-only
986      * environment.
987      *
988      * @throws IllegalStateException if the database is currently open using
989      * the transaction passed in the {@code txn} parameter, or if this handle
990      * or the underlying environment has been closed.
991      *
992      * @throws IllegalArgumentException if an invalid parameter is specified.
993      */
renameDatabase(final Transaction txn, final String databaseName, final String newName)994     public void renameDatabase(final Transaction txn,
995                                final String databaseName,
996                                final String newName)
997         throws DatabaseNotFoundException {
998 
999         DatabaseUtil.checkForNullParam(databaseName, "databaseName");
1000         DatabaseUtil.checkForNullParam(newName, "newName");
1001 
1002         new DbNameOperation<Void>(txn, databaseName) {
1003 
1004             Pair<DatabaseImpl, Void> runWork(final Locker locker)
1005                 throws DatabaseNotFoundException,
1006                        DbTree.NeedRepLockerException {
1007 
1008                 final DatabaseImpl dbImpl =
1009                     dbTree.dbRename(locker, databaseName, newName);
1010 
1011                 return new Pair<>(dbImpl, null);
1012             }
1013 
1014             void runTriggers(final Locker locker, final DatabaseImpl dbImpl) {
1015                 TriggerManager.runRenameTriggers(locker, dbImpl, newName);
1016             }
1017         }.run();
1018     }
1019 
1020     /**
1021      * Empties the database, discarding all the records it contains, without
1022      * removing the database name.
1023      *
1024      * <p>Compared to deleting all the records in a database individually,
1025      * {@code truncateDatabase} is a very efficient operation.  Some internal
1026      * housekeeping information is updated, but the database records are not
1027      * read or written, and very little I/O is needed.</p>
1028      *
1029      * <p>When called on a database configured with secondary indices, the
1030      * application is responsible for also truncating all associated secondary
1031      * indices.  To guarantee integrity, a primary database and all of its
1032      * secondary databases should be truncated atomically using a single
1033      * transaction.</p>
1034      *
1035      * <p>Applications should not truncate a database with open {@link Database
1036      * Database} handles.  If the database is open with the same transaction as
1037      * passed in the {@code txn} parameter, {@link IllegalStateException} is
1038      * thrown by this method.  If the database is open using a different
1039      * transaction, this method will block until all database handles are
1040      * closed, or until the conflict is resolved by throwing {@link
1041      * LockConflictException}.</p>
1042      *
1043      * @param txn For a transactional environment, an explicit transaction may
1044      * be specified or null may be specified to use auto-commit.  For a
1045      * non-transactional environment, null must be specified.
1046      *
1047      * @param databaseName The database to be truncated.
1048      *
1049      * @param returnCount If true, count and return the number of records
1050      * discarded.
1051      *
1052      * @return The number of records discarded, or -1 if returnCount is false.
1053      *
1054      * @throws DatabaseNotFoundException if the database does not exist.
1055      *
1056      * @throws OperationFailureException if one of the <a
1057      * href="../je/OperationFailureException.html#writeFailures">Write
1058      * Operation Failures</a> occurs.
1059      *
1060      * @throws EnvironmentFailureException if an unexpected, internal or
1061      * environment-wide failure occurs.
1062      *
1063      * @throws UnsupportedOperationException if this is a read-only
1064      * environment.
1065      *
1066      * @throws IllegalStateException if the database is currently open using
1067      * the transaction passed in the {@code txn} parameter, or if this handle
1068      * or the underlying environment has been closed.
1069      *
1070      * @throws IllegalArgumentException if an invalid parameter is specified.
1071      */
truncateDatabase(final Transaction txn, final String databaseName, final boolean returnCount)1072     public long truncateDatabase(final Transaction txn,
1073                                  final String databaseName,
1074                                  final boolean returnCount)
1075         throws DatabaseNotFoundException {
1076 
1077         DatabaseUtil.checkForNullParam(databaseName, "databaseName");
1078 
1079         return (new DbNameOperation<Long>(txn, databaseName) {
1080 
1081             Pair<DatabaseImpl, Long> runWork(final Locker locker)
1082                 throws DatabaseNotFoundException,
1083                        DbTree.NeedRepLockerException {
1084 
1085                 final TruncateDbResult result =
1086                     dbTree.truncate(locker, databaseName, returnCount);
1087 
1088                 return new Pair<>(result.newDb, result.recordCount);
1089             }
1090 
1091             void runTriggers(final Locker locker, final DatabaseImpl dbImpl) {
1092                 TriggerManager.runTruncateTriggers(locker, dbImpl);
1093             }
1094         }).run();
1095     }
1096 
1097     /**
1098      * Runs a DB naming operation: remove, truncate or rename.  The common code
1099      * is factored out here. In particular this class handles non-replicated
1100      * DBs in a replicated environment, when auto-commit is used.
1101      * <p>
1102      * For a non-replicated DB, an auto-commit txn must be created by calling
1103      * LockerFactory.getWritableLocker with the autoTxnIsReplicated param set
1104      * to false.  If autoTxnIsReplicated is set to true in a replicated
1105      * environment, HA consistency checks will be made when the txn is begun
1106      * and acks will be enforced at commit.  For example, for an HA node in an
1107      * unknown state, the consistency checks would fail and prevent performing
1108      * the operation on the local/non-replicated DB.
1109      * <p>
1110      * Unfortunately, we need to create a txn/locker in order to query the DB
1111      * metadata, to determine whether it is replicated.  Therefore, we always
1112      * attempt the operation initially with autoTxnIsReplicated set to false.
1113      * The DbTree name operation methods (see DbTree.lockNameLN) will throw an
1114      * internal exception (NeedRepLockerException) if a non-replicated
1115      * auto-commit txn is used on a replicated DB.  That signals this class to
1116      * retry the operation with autoTxnIsReplicated set to true.
1117      * <p>
1118      * Via an unlikely series of DB renaming it is possible that on the 2nd try
1119      * with a replicated txn, we find that the DB is non-replicated.  However,
1120      * there is little harm in proceeding, since the consistency check is
1121      * already done.
1122      */
1123     private abstract class DbNameOperation<R> {
1124 
1125         private final Transaction txn;
1126         private final String databaseName;
1127         final DbTree dbTree;
1128 
1129         DbNameOperation(final Transaction txn, final String databaseName) {
1130             this.txn = txn;
1131             this.databaseName = databaseName;
1132 
1133             checkHandleIsValid();
1134             checkEnv();
1135             checkWritable();
1136 
1137             dbTree = envImpl.getDbTree();
1138         }
1139 
1140         /** Run the DB name operation. */
1141         abstract Pair<DatabaseImpl, R> runWork(final Locker locker)
1142             throws DatabaseNotFoundException, DbTree.NeedRepLockerException;
1143 
1144         /** Run triggers after a successful DB name operation. */
1145         abstract void runTriggers(final Locker locker,
1146                                   final DatabaseImpl dbImpl);
1147 
1148         /**
1149          * Try the operation with autoTxnIsReplicated=false, and then again
1150          * with autoTxnIsReplicated=true if NeedRepLockerException is thrown.
1151          */
1152         R run() throws DatabaseNotFoundException {
1153             try {
1154                 return runOnce(getWritableLocker(false));
1155             } catch (DbTree.NeedRepLockerException e) {
1156                 try {
1157                     return runOnce(getWritableLocker(true));
1158                 } catch (DbTree.NeedRepLockerException e2) {
1159                     /* Should never happen. */
1160                     throw EnvironmentFailureException.unexpectedException(
1161                         envImpl, e);
1162                 }
1163             }
1164         }
1165 
1166         private R runOnce(final Locker locker)
1167             throws DatabaseNotFoundException, DbTree.NeedRepLockerException {
1168 
1169             boolean success = false;
1170             try {
1171                 final Pair<DatabaseImpl, R> results = runWork(locker);
1172                 final DatabaseImpl dbImpl = results.first();
1173                 if (dbImpl == null) {
1174                     /* Should never happen. */
1175                     throw EnvironmentFailureException.unexpectedState(envImpl);
1176                 }
1177                 success = true;
1178                 runTriggers(locker, dbImpl);
1179                 return results.second();
1180             } catch (Error E) {
1181                 envImpl.invalidate(E);
1182                 throw E;
1183             } finally {
1184                 locker.operationEnd(success);
1185             }
1186         }
1187 
1188         private Locker getWritableLocker(boolean autoTxnIsReplicated) {
1189             return LockerFactory.getWritableLocker(
1190                 Environment.this, txn, false /*isInternalDb*/,
1191                 envImpl.isTransactional(), autoTxnIsReplicated);
1192         }
1193     }
1194 
1195     /**
1196      * For unit testing.  Returns the current memory usage in bytes for all
1197      * btrees in the envImpl.
1198      */
1199     long getMemoryUsage()
1200         throws DatabaseException {
1201 
1202         checkHandleIsValid();
1203         checkEnv();
1204 
1205         return envImpl.getMemoryBudget().getCacheMemoryUsage();
1206     }
1207 
1208     /**
1209      * Returns the database environment's home directory.
1210      *
1211      * @return The database environment's home directory.
1212      *
1213      * @throws EnvironmentFailureException if an unexpected, internal or
1214      * environment-wide failure occurs.
1215      *
1216      * @throws IllegalStateException if this handle or the underlying
1217      * environment has been closed.
1218      */
1219     public File getHome()
1220         throws DatabaseException {
1221 
1222         checkHandleIsValid();
1223 
1224         return envImpl.getEnvironmentHome();
1225     }
1226 
1227     /*
1228      * Transaction management
1229      */
1230 
1231     /**
1232      * Returns the default txn config for this environment handle.
1233      */
1234     TransactionConfig getDefaultTxnConfig() {
1235         return defaultTxnConfig;
1236     }
1237 
1238     /**
1239      * Copies the handle properties out of the config properties, and
1240      * initializes the default transaction config.
1241      */
1242     private void copyToHandleConfig(EnvironmentMutableConfig useConfig,
1243                                     EnvironmentConfig initStaticConfig,
1244                                     RepConfigProxy initRepConfig) {
1245 
1246         /*
1247          * Create the new objects, initialize them, then change the instance
1248          * fields.  This avoids synchronization issues.
1249          */
1250         EnvironmentMutableConfig newHandleConfig =
1251             new EnvironmentMutableConfig();
1252         useConfig.copyHandlePropsTo(newHandleConfig);
1253         this.handleConfig = newHandleConfig;
1254 
1255         TransactionConfig newTxnConfig =
1256             TransactionConfig.DEFAULT.clone();
1257         newTxnConfig.setNoSync(handleConfig.getTxnNoSync());
1258         newTxnConfig.setWriteNoSync(handleConfig.getTxnWriteNoSync());
1259         newTxnConfig.setDurability(handleConfig.getDurability());
1260 
1261         if (initStaticConfig != null) {
1262             newTxnConfig.setSerializableIsolation
1263                 (initStaticConfig.getTxnSerializableIsolation());
1264             newTxnConfig.setReadCommitted
1265                 (initStaticConfig.getTxnReadCommitted());
1266         } else {
1267             newTxnConfig.setSerializableIsolation
1268                 (defaultTxnConfig.getSerializableIsolation());
1269             newTxnConfig.setReadCommitted
1270                 (defaultTxnConfig.getReadCommitted());
1271             newTxnConfig.setConsistencyPolicy
1272                 (defaultTxnConfig.getConsistencyPolicy());
1273         }
1274         if (initRepConfig != null) {
1275             newTxnConfig.setConsistencyPolicy
1276                 (initRepConfig.getConsistencyPolicy());
1277         }
1278         this.defaultTxnConfig = newTxnConfig;
1279     }
1280 
1281     /**
1282      * Creates a new transaction in the database environment.
1283      *
1284      * <p>Transaction handles are free-threaded; transactions handles may be
1285      * used concurrently by multiple threads.</p>
1286      *
1287      * <p>Cursors may not span transactions; that is, each cursor must be
1288      * opened and closed within a single transaction. The parent parameter is a
1289      * placeholder for nested transactions, and must currently be null.</p>
1290      *
1291      * @param txnConfig The transaction attributes.  If null, default
1292      * attributes are used.
1293      *
1294      * @return The newly created transaction's handle.
1295      *
1296      * @throws com.sleepycat.je.rep.InsufficientReplicasException if the Master
1297      * in a replicated environment could not contact a quorum of replicas as
1298      * determined by the {@link ReplicaAckPolicy}.
1299      *
1300      * @throws com.sleepycat.je.rep.ReplicaConsistencyException if a replica
1301      * in a replicated environment cannot become consistent within the timeout
1302      * period.
1303      *
1304      * @throws EnvironmentFailureException if an unexpected, internal or
1305      * environment-wide failure occurs.
1306      *
1307      * @throws UnsupportedOperationException if this is not a transactional
1308      * environment.
1309      *
1310      * @throws IllegalStateException if this handle or the underlying
1311      * environment has been closed.
1312      *
1313      * @throws IllegalArgumentException if an invalid parameter is specified,
1314      * for example, an invalid {@code TransactionConfig} parameter.
1315      */
1316     public Transaction beginTransaction(Transaction parent,
1317                                         TransactionConfig txnConfig)
1318         throws DatabaseException,
1319                IllegalArgumentException {
1320 
1321         try {
1322             return beginTransactionInternal(parent, txnConfig,
1323                                             false /*isInternalTxn*/);
1324         } catch (Error E) {
1325             if (envImpl != null) {
1326                 envImpl.invalidate(E);
1327             }
1328             throw E;
1329         }
1330     }
1331 
1332     /**
1333      * Like beginTransaction, but does not require that the Environment is
1334      * transactional.
1335      */
1336     Transaction beginInternalTransaction(TransactionConfig txnConfig) {
1337         return beginTransactionInternal(null /*parent*/, txnConfig,
1338                                         true /*isInternalTxn*/);
1339     }
1340 
1341     /**
1342      * @throws IllegalArgumentException via beginTransaction.
1343      * @throws UnsupportedOperationException via beginTransaction.
1344      */
1345     private Transaction beginTransactionInternal(Transaction parent,
1346                                                  TransactionConfig txnConfig,
1347                                                  boolean isInternalTxn )
1348         throws DatabaseException {
1349 
1350         checkHandleIsValid();
1351         checkEnv();
1352 
1353         if (parent != null) {
1354             throw new IllegalArgumentException
1355                 ("Parent txn is non-null. " +
1356                  "Nested transactions are not supported.");
1357         }
1358 
1359         if (!isInternalTxn && !envImpl.isTransactional()) {
1360             throw new UnsupportedOperationException
1361                 ("Transactions can not be used in a non-transactional " +
1362                  "environment");
1363         }
1364 
1365         checkTxnConfig(txnConfig);
1366 
1367         /*
1368          * Apply txn config defaults.  We don't need to clone unless we have to
1369          * apply the env default, since we don't hold onto a txn config
1370          * reference.
1371          */
1372         TransactionConfig useConfig = null;
1373         if (txnConfig == null) {
1374             useConfig = defaultTxnConfig;
1375         } else {
1376             if (defaultTxnConfig.getNoSync() ||
1377                 defaultTxnConfig.getWriteNoSync()) {
1378 
1379                 /*
1380                  * The environment sync settings have been set, check if any
1381                  * were set in the user's txn config. If none were set in the
1382                  * user's config, apply the environment defaults
1383                  */
1384                 if (!txnConfig.getNoSync() &&
1385                     !txnConfig.getSync() &&
1386                     !txnConfig.getWriteNoSync()) {
1387                     useConfig = txnConfig.clone();
1388                     if (defaultTxnConfig.getWriteNoSync()) {
1389                         useConfig.setWriteNoSync(true);
1390                     } else {
1391                         useConfig.setNoSync(true);
1392                     }
1393                 }
1394             }
1395 
1396             if ((defaultTxnConfig.getDurability() != null) &&
1397                  (txnConfig.getDurability() == null)) {
1398 
1399                 /*
1400                  * Inherit transaction durability from the environment in the
1401                  * absence of an explicit transaction config durability.
1402                  */
1403                 if (useConfig == null) {
1404                     useConfig = txnConfig.clone();
1405                 }
1406                 useConfig.setDurability(defaultTxnConfig.getDurability());
1407             }
1408 
1409             if ((defaultTxnConfig.getConsistencyPolicy() != null) &&
1410                 (txnConfig.getConsistencyPolicy() == null)) {
1411                    if (useConfig == null) {
1412                        useConfig = txnConfig.clone();
1413                    }
1414                    useConfig.setConsistencyPolicy
1415                        (defaultTxnConfig.getConsistencyPolicy());
1416             }
1417 
1418             /* Apply isolation level default. */
1419             if (!txnConfig.getSerializableIsolation() &&
1420                 !txnConfig.getReadCommitted() &&
1421                 !txnConfig.getReadUncommitted()) {
1422                 if (defaultTxnConfig.getSerializableIsolation()) {
1423                     if (useConfig == null) {
1424                         useConfig = txnConfig.clone();
1425                     }
1426                     useConfig.setSerializableIsolation(true);
1427                 } else if (defaultTxnConfig.getReadCommitted()) {
1428                     if (useConfig == null) {
1429                         useConfig = txnConfig.clone();
1430                     }
1431                     useConfig.setReadCommitted(true);
1432                 }
1433             }
1434 
1435             /* No environment level defaults applied. */
1436             if (useConfig == null) {
1437                 useConfig = txnConfig;
1438             }
1439         }
1440         Txn internalTxn = envImpl.txnBegin(parent, useConfig);
1441         Transaction txn = new Transaction(this, internalTxn);
1442         addReferringHandle(txn);
1443         return txn;
1444     }
1445 
1446     /**
1447      * Checks the txnConfig object to ensure that its correctly configured and
1448      * is compatible with the configuration of the Environment.
1449      *
1450      * @param txnConfig the configuration being checked.
1451      *
1452      * @throws IllegalArgumentException via beginTransaction
1453      */
1454     private void checkTxnConfig(TransactionConfig txnConfig)
1455         throws IllegalArgumentException {
1456 
1457         if (txnConfig == null) {
1458             return;
1459         }
1460         if ((txnConfig.getSerializableIsolation() &&
1461              txnConfig.getReadUncommitted()) ||
1462             (txnConfig.getSerializableIsolation() &&
1463              txnConfig.getReadCommitted()) ||
1464             (txnConfig.getReadUncommitted() &&
1465              txnConfig.getReadCommitted())) {
1466             throw new IllegalArgumentException
1467                 ("Only one may be specified: SerializableIsolation, " +
1468                 "ReadCommitted or ReadUncommitted");
1469         }
1470         if ((txnConfig.getDurability() != null) &&
1471             ((defaultTxnConfig.getSync() ||
1472               defaultTxnConfig.getNoSync() ||
1473               defaultTxnConfig.getWriteNoSync()))) {
1474            throw new IllegalArgumentException
1475                ("Mixed use of deprecated durability API for the " +
1476                 "Environment with the new durability API for " +
1477                 "TransactionConfig.setDurability()");
1478         }
1479         if ((defaultTxnConfig.getDurability() != null) &&
1480             ((txnConfig.getSync() ||
1481               txnConfig.getNoSync() ||
1482               txnConfig.getWriteNoSync()))) {
1483             throw new IllegalArgumentException
1484                    ("Mixed use of new durability API for the " +
1485                     "Environment with the deprecated durability API for " +
1486                     "TransactionConfig.");
1487         }
1488     }
1489 
1490     /**
1491      * Synchronously checkpoint the database environment.
1492      * <p>
1493      * This is an optional action for the application since this activity
1494      * is, by default, handled by a database environment owned background
1495      * thread.
1496      * <p>
1497      * A checkpoint has the side effect of flushing all preceding
1498      * non-transactional write operations, as well as any preceding
1499      * transactions that were committed with {@link
1500      * Durability.SyncPolicy#NO_SYNC no-sync durability}.  However, for best
1501      * performance, checkpoints should be used only to bound recovery time.
1502      * {@link #flushLog} can be used to write buffered data for durability
1503      * purposes.
1504      *
1505      * @param ckptConfig The checkpoint attributes.  If null, default
1506      * attributes are used.
1507      *
1508      * @throws EnvironmentFailureException if an unexpected, internal or
1509      * environment-wide failure occurs.
1510      *
1511      * @throws IllegalStateException if this handle or the underlying
1512      * environment has been closed.
1513      */
1514     public void checkpoint(CheckpointConfig ckptConfig)
1515         throws DatabaseException {
1516 
1517         try {
1518             checkHandleIsValid();
1519             checkEnv();
1520             CheckpointConfig useConfig =
1521                 (ckptConfig == null) ? CheckpointConfig.DEFAULT : ckptConfig;
1522             envImpl.invokeCheckpoint(useConfig, "api");
1523         } catch (Error E) {
1524             if (envImpl != null) {
1525                 envImpl.invalidate(E);
1526             }
1527             throw E;
1528         }
1529     }
1530 
1531     /**
1532      * Synchronously flushes database environment databases to stable storage.
1533      * Calling this method is equivalent to forcing a checkpoint and setting
1534      * {@link CheckpointConfig#setMinimizeRecoveryTime} to true.
1535      * <p>
1536      * A checkpoint has the side effect of flushing all preceding
1537      * non-transactional write operations, as well as any preceding
1538      * transactions that were committed with {@link
1539      * Durability.SyncPolicy#NO_SYNC no-sync durability}.  However, for best
1540      * performance, checkpoints should be used only to bound recovery time.
1541      * {@link #flushLog} can be used to write buffered data for durability
1542      * purposes.
1543      *
1544      * @throws EnvironmentFailureException if an unexpected, internal or
1545      * environment-wide failure occurs.
1546      *
1547      * @throws IllegalStateException if this handle or the underlying
1548      * environment has been closed.
1549      */
1550     public void sync()
1551         throws DatabaseException {
1552 
1553         try {
1554             checkHandleIsValid();
1555             checkEnv();
1556             CheckpointConfig config = new CheckpointConfig();
1557             config.setForce(true);
1558             config.setMinimizeRecoveryTime(true);
1559             envImpl.invokeCheckpoint(config, "sync");
1560         } catch (Error E) {
1561             if (envImpl != null) {
1562                 envImpl.invalidate(E);
1563             }
1564             throw E;
1565         }
1566     }
1567 
1568     /**
1569      * Writes buffered data to the log, and optionally performs an fsync to
1570      * guarantee that data is written to the physical device.
1571      * <p>
1572      * This method is used to make durable, by writing to the log, all
1573      * preceding non-transactional write operations, as well as any preceding
1574      * transactions that were committed with {@link
1575      * Durability.SyncPolicy#NO_SYNC no-sync durability}.  If the {@code fsync}
1576      * parameter is true, it can also be used to flush all logged data to the
1577      * physical storage device, by performing an fsync.
1578      * <p>
1579      * Note that this method <em>does not</em> flush previously unwritten data
1580      * in deferred-write databases; that is done by calling {@link
1581      * Database#sync} or performing a checkpoint.
1582      *
1583      * @param fsync is true to perform an fsync as well as a file write, or
1584      * false to perform only a file write.
1585      *
1586      * @throws EnvironmentFailureException if an unexpected, internal or
1587      * environment-wide failure occurs.
1588      *
1589      * @throws IllegalStateException if this handle or the underlying
1590      * environment has been closed.
1591      */
1592     public void flushLog(boolean fsync) {
1593         try {
1594             checkHandleIsValid();
1595             checkEnv();
1596             envImpl.flushLog(fsync);
1597         } catch (Error E) {
1598             if (envImpl != null) {
1599                 envImpl.invalidate(E);
1600             }
1601             throw E;
1602         }
1603     }
1604 
1605     /**
1606      * Synchronously invokes database environment log cleaning.  This method is
1607      * called periodically by the cleaner daemon thread.
1608      *
1609      * <p>Zero or more log files will be cleaned as necessary to bring the disk
1610      * space utilization of the environment above the configured minimum
1611      * utilization threshold.  The threshold is determined by the {@link
1612      * EnvironmentConfig#CLEANER_MIN_UTILIZATION} configuration setting.</p>
1613      *
1614      * <p>Note that <code>cleanLog</code> does not perform the complete task of
1615      * cleaning a log file.  Eviction and checkpointing migrate records that
1616      * are marked by the cleaner, and a full checkpoint is necessary following
1617      * cleaning before cleaned files will be deleted (or renamed).  Checkpoints
1618      * normally occur periodically and when the environment is closed.</p>
1619      *
1620      * <p>This is an optional action for the application since this activity
1621      * is, by default, handled by one or more database environment owned
1622      * background threads.</p>
1623      *
1624      * <p>There are two intended use cases for the <code>cleanLog</code>
1625      * method.  The first case is where the application wishes to disable the
1626      * built-in cleaner threads using the {@link
1627      * EnvironmentConfig#ENV_RUN_CLEANER} property.  To replace the
1628      * functionality of the cleaner threads, the application should call
1629      * <code>cleanLog</code> periodically.</p>
1630      *
1631      * <p>In the second use case, "batch cleaning", the application disables
1632      * the cleaner threads for maximum performance during active periods, and
1633      * calls <code>cleanLog</code> during periods when the application is
1634      * quiescent or less active than usual.  If the cleaner has a large number
1635      * of files to clean, <code>cleanLog</code> may stop without reaching the
1636      * target utilization; to ensure that the target utilization is reached,
1637      * <code>cleanLog</code> should be called in a loop until it returns
1638      * zero. And to complete the work of cleaning, a checkpoint is necessary.
1639      * An example of performing batch cleaning follows.</p>
1640      *
1641      * <pre>
1642      *       Environment env;
1643      *       boolean anyCleaned = false;
1644      *       while (env.cleanLog() &gt; 0) {
1645      *           anyCleaned = true;
1646      *       }
1647      *       if (anyCleaned) {
1648      *           CheckpointConfig force = new CheckpointConfig();
1649      *           force.setForce(true);
1650      *           env.checkpoint(force);
1651      *       }
1652      * </pre>
1653      *
1654      * <p><em>WARNING:</em>If batch cleaning (shown above) is performed
1655      * immediately before closing the environment, then the built-in cleaner
1656      * threads should normally be disabled using {@link
1657      * EnvironmentConfig#ENV_RUN_CLEANER} during the batch cleaning process.
1658      * If the built-in cleaner threads are actively working on one or more log
1659      * files, then those files will not be processed by the {@code cleanLog}
1660      * method.  Closing the environment will abort the work being done by the
1661      * built-in cleaner threads, and log cleaning may be incomplete.</p>
1662      *
1663      * @return The number of log files that were cleaned, and that will be
1664      * deleted (or renamed) when a qualifying checkpoint occurs.
1665      *
1666      * @throws EnvironmentFailureException if an unexpected, internal or
1667      * environment-wide failure occurs.
1668      *
1669      * @throws UnsupportedOperationException if this is a read-only or
1670      * memory-only environment.
1671      *
1672      * @throws IllegalStateException if this handle or the underlying
1673      * environment has been closed.
1674      */
1675     public int cleanLog()
1676         throws DatabaseException {
1677 
1678         try {
1679             checkHandleIsValid();
1680             checkEnv();
1681             return envImpl.invokeCleaner();
1682         } catch (Error E) {
1683             if (envImpl != null) {
1684                 envImpl.invalidate(E);
1685             }
1686             throw E;
1687         }
1688     }
1689 
1690     /**
1691      * Synchronously invokes the mechanism for keeping memory usage within the
1692      * cache size boundaries.
1693      *
1694      * <p>This is an optional action for the application since this activity
1695      * is, by default, handled by a database environment owned background
1696      * thread.</p>
1697      *
1698      * @throws EnvironmentFailureException if an unexpected, internal or
1699      * environment-wide failure occurs.
1700      *
1701      * @throws IllegalStateException if this handle or the underlying
1702      * environment has been closed.
1703      */
1704     public void evictMemory()
1705         throws DatabaseException {
1706 
1707         try {
1708             checkHandleIsValid();
1709             checkEnv();
1710             envImpl.invokeEvictor();
1711         } catch (Error E) {
1712             if (envImpl != null) {
1713                 envImpl.invalidate(E);
1714             }
1715             throw E;
1716         }
1717     }
1718 
1719     /**
1720      * Synchronously invokes the compressor mechanism which compacts in memory
1721      * data structures after delete operations.
1722      *
1723      * <p>This is an optional action for the application since this activity
1724      * is, by default, handled by a database environment owned background
1725      * thread.</p>
1726      *
1727      * @throws EnvironmentFailureException if an unexpected, internal or
1728      * environment-wide failure occurs.
1729      *
1730      * @throws IllegalStateException if this handle or the underlying
1731      * environment has been closed.
1732      */
1733     public void compress()
1734         throws DatabaseException {
1735 
1736         try {
1737             checkHandleIsValid();
1738             checkEnv();
1739             envImpl.invokeCompressor();
1740         } catch (Error E) {
1741             if (envImpl != null) {
1742                 envImpl.invalidate(E);
1743             }
1744             throw E;
1745         }
1746     }
1747 
1748     /**
1749      * Preloads the cache with multiple databases.  This method should only be
1750      * called when there are no operations being performed on the specified
1751      * databases in other threads.  Executing preload during concurrent updates
1752      * of the specified databases may result in some or all of the tree being
1753      * loaded into the JE cache.  Executing preload during any other types of
1754      * operations may result in JE exceeding its allocated cache
1755      * size. preload() effectively locks all of the specified database and
1756      * therefore will lock out the checkpointer, cleaner, and compressor, as
1757      * well as not allow eviction to occur.  If databases are replicated and
1758      * the environment is in the replica state, then the replica may become
1759      * temporarily disconnected from the master if the replica needs to replay
1760      * changes against the database and is locked out because the time taken by
1761      * the preload operation exceeds {@link
1762      * com.sleepycat.je.rep.ReplicationConfig#FEEDER_TIMEOUT}.
1763      *
1764      * @param config The PreloadConfig object that specifies the parameters
1765      * of the preload.
1766      *
1767      * @return A PreloadStats object with the result of the preload operation
1768      * and various statistics about the preload() operation.
1769      *
1770      * @throws OperationFailureException if one of the <a
1771      * href="OperationFailureException.html#readFailures">Read Operation
1772      * Failures</a> occurs.
1773      *
1774      * @throws EnvironmentFailureException if an unexpected, internal or
1775      * environment-wide failure occurs.
1776      *
1777      * @throws IllegalStateException if any of the databases has been closed.
1778      *
1779      * @see Database#preload(PreloadConfig)
1780      */
1781     public PreloadStats preload(final Database[] databases,
1782                                 final PreloadConfig config)
1783         throws DatabaseException {
1784 
1785         try {
1786             checkHandleIsValid();
1787             checkEnv();
1788             DatabaseUtil.checkForZeroLengthArrayParam(databases, "databases");
1789             PreloadConfig useConfig =
1790                 (config == null) ? new PreloadConfig() : config;
1791             int nDbs = databases.length;
1792             DatabaseImpl[] dbImpls = new DatabaseImpl[nDbs];
1793             for (int i = 0; i < nDbs; i += 1) {
1794                 dbImpls[i] = DbInternal.getDatabaseImpl(databases[i]);
1795             }
1796             return envImpl.preload(dbImpls, useConfig);
1797         } catch (Error E) {
1798             if (envImpl != null) {
1799                 envImpl.invalidate(E);
1800             }
1801             throw E;
1802         }
1803     }
1804 
1805     /**
1806      * Returns this object's configuration.
1807      *
1808      * @return This object's configuration.
1809      *
1810      * @throws EnvironmentFailureException if an unexpected, internal or
1811      * environment-wide failure occurs.
1812      *
1813      * @throws IllegalStateException if this handle has been closed.
1814      */
1815     public EnvironmentConfig getConfig()
1816         throws DatabaseException {
1817 
1818         try {
1819             checkHandleIsValid();
1820             EnvironmentConfig config = envImpl.cloneConfig();
1821             handleConfig.copyHandlePropsTo(config);
1822             config.fillInEnvironmentGeneratedProps(envImpl);
1823             return config;
1824         } catch (Error E) {
1825             if (envImpl != null) {
1826                 envImpl.invalidate(E);
1827             }
1828             throw E;
1829         }
1830     }
1831 
1832     /**
1833      * Sets database environment attributes.
1834      *
1835      * <p>Attributes only apply to a specific Environment object and are not
1836      * necessarily shared by other Environment objects accessing this
1837      * database environment.</p>
1838      *
1839      * @param mutableConfig The database environment attributes.  If null,
1840      * default attributes are used.
1841      *
1842      * @throws EnvironmentFailureException if an unexpected, internal or
1843      * environment-wide failure occurs.
1844      *
1845      * @throws IllegalStateException if this handle has been closed.
1846      */
1847     public synchronized
1848         void setMutableConfig(EnvironmentMutableConfig mutableConfig)
1849         throws DatabaseException {
1850 
1851         /*
1852          * This method is synchronized so that we atomically call both
1853          * EnvironmentImpl.setMutableConfig and copyToHandleConfig. This
1854          * ensures that the handle and the EnvironmentImpl properties match.
1855          */
1856         try {
1857             checkHandleIsValid();
1858             DatabaseUtil.checkForNullParam(mutableConfig, "mutableConfig");
1859 
1860             /*
1861              * Change the mutable properties specified in the given
1862              * configuration.
1863              */
1864             envImpl.setMutableConfig(mutableConfig);
1865 
1866             /* Reset the handle config properties. */
1867             copyToHandleConfig(mutableConfig, null, null);
1868         } catch (Error E) {
1869             if (envImpl != null) {
1870                 envImpl.invalidate(E);
1871             }
1872             throw E;
1873         }
1874     }
1875 
1876     /**
1877      * Returns database environment attributes.
1878      *
1879      * @return Environment attributes.
1880      *
1881      * @throws EnvironmentFailureException if an unexpected, internal or
1882      * environment-wide failure occurs.
1883      *
1884      * @throws IllegalStateException if this handle has been closed.
1885      */
1886     public EnvironmentMutableConfig getMutableConfig()
1887         throws DatabaseException {
1888 
1889         try {
1890             checkHandleIsValid();
1891             EnvironmentMutableConfig config =
1892                 envImpl.cloneMutableConfig();
1893             handleConfig.copyHandlePropsTo(config);
1894             config.fillInEnvironmentGeneratedProps(envImpl);
1895             return config;
1896         } catch (Error E) {
1897             if (envImpl != null) {
1898                 envImpl.invalidate(E);
1899             }
1900             throw E;
1901         }
1902     }
1903 
1904     /**
1905      * Returns the general database environment statistics.
1906      *
1907      * @param config The general statistics attributes.  If null, default
1908      * attributes are used.
1909      *
1910      * @return The general database environment statistics.
1911      *
1912      * @throws EnvironmentFailureException if an unexpected, internal or
1913      * environment-wide failure occurs.
1914      *
1915      * @throws IllegalStateException if this handle or the underlying
1916      * environment has been closed.
1917      */
1918     public EnvironmentStats getStats(StatsConfig config)
1919         throws DatabaseException {
1920 
1921         checkHandleIsValid();
1922         checkEnv();
1923         try {
1924             StatsConfig useConfig =
1925                 (config == null) ? StatsConfig.DEFAULT : config;
1926 
1927             if (envImpl != null) {
1928                 return envImpl.loadStats(useConfig);
1929             }
1930             return new EnvironmentStats();
1931         } catch (Error E) {
1932             if (envImpl != null) {
1933                 envImpl.invalidate(E);
1934             }
1935             throw E;
1936         }
1937     }
1938 
1939     /**
1940      * Returns the database environment's locking statistics.
1941      *
1942      * @param config The locking statistics attributes.  If null, default
1943      * attributes are used.
1944      *
1945      * @return The database environment's locking statistics.
1946      *
1947      * @throws EnvironmentFailureException if an unexpected, internal or
1948      * environment-wide failure occurs.
1949      *
1950      * @throws IllegalStateException if this handle or the underlying
1951      * environment has been closed.
1952      *
1953      * @deprecated as of 4.0.10, replaced by {@link
1954      * Environment#getStats(StatsConfig)}.</p>
1955      */
1956     public LockStats getLockStats(StatsConfig config)
1957         throws DatabaseException {
1958 
1959         try {
1960             checkHandleIsValid();
1961             checkEnv();
1962             StatsConfig useConfig =
1963                 (config == null) ? StatsConfig.DEFAULT : config;
1964 
1965             return envImpl.lockStat(useConfig);
1966         } catch (Error E) {
1967             if (envImpl != null) {
1968                 envImpl.invalidate(E);
1969             }
1970             throw E;
1971         }
1972     }
1973 
1974     /**
1975      * Returns the database environment's transactional statistics.
1976      *
1977      * @param config The transactional statistics attributes.  If null,
1978      * default attributes are used.
1979      *
1980      * @return The database environment's transactional statistics.
1981      *
1982      * @throws EnvironmentFailureException if an unexpected, internal or
1983      * environment-wide failure occurs.
1984      *
1985      * @throws IllegalStateException if this handle or the underlying
1986      * environment has been closed.
1987      */
1988     public TransactionStats getTransactionStats(StatsConfig config)
1989         throws DatabaseException {
1990 
1991         try {
1992             checkHandleIsValid();
1993             checkEnv();
1994             StatsConfig useConfig =
1995                 (config == null) ? StatsConfig.DEFAULT : config;
1996             return envImpl.txnStat(useConfig);
1997         } catch (Error E) {
1998             if (envImpl != null) {
1999                 envImpl.invalidate(E);
2000             }
2001             throw E;
2002         }
2003     }
2004 
2005     /**
2006      * Returns a List of database names for the database environment.
2007      *
2008      * <p>Each element in the list is a String.</p>
2009      *
2010      * @return A List of database names for the database environment.
2011      *
2012      * @throws OperationFailureException if one of the <a
2013      * href="OperationFailureException.html#readFailures">Read Operation
2014      * Failures</a> occurs.
2015      *
2016      * @throws EnvironmentFailureException if an unexpected, internal or
2017      * environment-wide failure occurs.
2018      *
2019      * @throws IllegalStateException if this handle or the underlying
2020      * environment has been closed.
2021      */
2022     public List<String> getDatabaseNames()
2023         throws DatabaseException {
2024 
2025         try {
2026             checkHandleIsValid();
2027             checkEnv();
2028             return envImpl.getDbTree().getDbNames();
2029         } catch (Error E) {
2030             if (envImpl != null) {
2031                 envImpl.invalidate(E);
2032             }
2033             throw E;
2034         }
2035     }
2036 
2037     /**
2038      * Returns if the database environment is consistent and correct.
2039      *
2040      * <p>Verification is an expensive operation that should normally only be
2041      * used for troubleshooting and debugging.</p>
2042      *
2043      * @param config The verification attributes.  If null, default
2044      * attributes are used.
2045      *
2046      * @param out The stream to which verification debugging information is
2047      * written.
2048      *
2049      * @return true if the database environment is consistent and correct.
2050      *
2051      * @throws EnvironmentFailureException if an unexpected, internal or
2052      * environment-wide failure occurs.
2053      *
2054      * @throws IllegalStateException if this handle or the underlying
2055      * environment has been closed.
2056      */
2057     public boolean verify(VerifyConfig config, PrintStream out)
2058         throws DatabaseException {
2059 
2060         try {
2061             checkHandleIsValid();
2062             checkEnv();
2063             VerifyConfig useConfig =
2064                 (config == null) ? VerifyConfig.DEFAULT : config;
2065             return envImpl.verify(useConfig, out);
2066         } catch (Error E) {
2067             if (envImpl != null) {
2068                 envImpl.invalidate(E);
2069             }
2070             throw E;
2071         }
2072     }
2073 
2074     /**
2075      * Returns the transaction associated with this thread if implied
2076      * transactions are being used.  Implied transactions are used in an XA or
2077      * JCA "Local Transaction" environment.  In an XA environment the
2078      * XAEnvironment.start() entrypoint causes a transaction to be created and
2079      * become associated with the calling thread.  Subsequent API calls
2080      * implicitly use that transaction.  XAEnvironment.end() causes the
2081      * transaction to be disassociated with the thread.  In a JCA Local
2082      * Transaction environment, the call to JEConnectionFactory.getConnection()
2083      * causes a new transaction to be created and associated with the calling
2084      * thread.
2085      *
2086      * @throws IllegalStateException if this handle or the underlying
2087      * environment has been closed.
2088      */
2089     public Transaction getThreadTransaction()
2090         throws DatabaseException {
2091 
2092         checkHandleIsValid();
2093         checkEnv();
2094         try {
2095             return envImpl.getTxnManager().getTxnForThread();
2096         } catch (Error E) {
2097             if (envImpl != null) {
2098                 envImpl.invalidate(E);
2099             }
2100             throw E;
2101         }
2102     }
2103 
2104     /**
2105      * Sets the transaction associated with this thread if implied transactions
2106      * are being used.  Implied transactions are used in an XA or JCA "Local
2107      * Transaction" environment.  In an XA environment the
2108      * XAEnvironment.start() entrypoint causes a transaction to be created and
2109      * become associated with the calling thread.  Subsequent API calls
2110      * implicitly use that transaction.  XAEnvironment.end() causes the
2111      * transaction to be disassociated with the thread.  In a JCA Local
2112      * Transaction environment, the call to JEConnectionFactory.getConnection()
2113      * causes a new transaction to be created and associated with the calling
2114      * thread.
2115      *
2116      * @throws IllegalStateException if this handle or the underlying
2117      * environment has been closed.
2118      */
2119     public void setThreadTransaction(Transaction txn) {
2120 
2121         checkHandleIsValid();
2122         checkEnv();
2123         try {
2124             envImpl.getTxnManager().setTxnForThread(txn);
2125         } catch (Error E) {
2126             envImpl.invalidate(E);
2127             throw E;
2128         }
2129     }
2130 
2131     /**
2132      * Returns whether this {@code Environment} is open, valid and can be used.
2133      * If this method returns false, {@link #close} should be called as soon as
2134      * possible.
2135      *
2136      * <p>When an {@link EnvironmentFailureException}, or one of its
2137      * subclasses, is caught, the {@code isValid} method should be called to
2138      * determine whether the {@code Environment} can continue to be used, or
2139      * should be closed.</p>
2140      */
2141     public boolean isValid() {
2142         return envImpl != null &&
2143             envImpl.isValid();
2144     }
2145 
2146     /**
2147      * Print a detailed report about the costs of different phases of
2148      * environment startup. This report is by default logged to the je.info
2149      * file if startup takes longer than je.env.startupThreshold.
2150      */
2151     public void printStartupInfo(PrintStream out) {
2152         envImpl.getStartupTracker().displayStats(out, Phase.TOTAL_ENV_OPEN);
2153     }
2154 
2155     /*
2156      * Non public api -- helpers
2157      */
2158 
2159     /**
2160      * Let the Environment remember what's opened against it.
2161      */
2162     private void addReferringHandle(Database db) {
2163         referringDbs.put(db, db);
2164     }
2165 
2166     /**
2167      * Lets the Environment remember what's opened against it.
2168      */
2169     private void addReferringHandle(Transaction txn) {
2170         referringDbTxns.put(txn, txn);
2171     }
2172 
2173     /**
2174      * The referring db has been closed.
2175      */
2176     void removeReferringHandle(Database db) {
2177         referringDbs.remove(db);
2178     }
2179 
2180     /**
2181      * The referring Transaction has been closed.
2182      */
2183     void removeReferringHandle(Transaction txn) {
2184         referringDbTxns.remove(txn);
2185     }
2186 
2187     /**
2188      * For internal use only.
2189      * @hidden
2190      * @throws IllegalStateException if the environment is not open.
2191      */
2192     public void checkHandleIsValid() {
2193         if (envImpl == null) {
2194             throw new IllegalStateException
2195                 ("Attempt to use non-open Environment object().");
2196         }
2197     }
2198 
2199     /*
2200      * Debugging aids.
2201      */
2202 
2203     /**
2204      * Internal entrypoint.
2205      */
2206     EnvironmentImpl getEnvironmentImpl() {
2207 
2208         /* Bandaid: Avoid NPE when handle is closed. [#22002] */
2209         checkHandleIsValid();
2210 
2211         return envImpl;
2212     }
2213 
2214     /* Returns true, if this is a handle allocated internally by JE. */
2215     protected boolean isInternalHandle() {
2216         return false;
2217     }
2218 
2219     /**
2220      * For internal use only.
2221      * @hidden
2222      * Throws if the envImpl is invalid.
2223      */
2224     protected void checkEnv()
2225         throws DatabaseException, EnvironmentFailureException {
2226 
2227         if (envImpl == null) {
2228             return;
2229         }
2230         envImpl.checkIfInvalid();
2231         envImpl.checkNotClosed();
2232     }
2233 
2234     /**
2235      * @throws UnsupportedOperationException via the database operation methods
2236      * (remove, truncate, rename) and potentially other methods that require a
2237      * writable environment.
2238      */
2239     private void checkWritable() {
2240         if (envImpl.isReadOnly()) {
2241             throw new UnsupportedOperationException
2242                 ("Environment is Read-Only.");
2243         }
2244     }
2245 }
2246