Package com.sleepycat.je

Source Code of com.sleepycat.je.Environment

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2011 Oracle and/or its affiliates.  All rights reserved.
*
*/

package com.sleepycat.je;

import java.io.Closeable;
import java.io.File;
import java.io.PrintStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;

import javax.transaction.xa.Xid;

import com.sleepycat.je.Durability.ReplicaAckPolicy;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.DbConfigManager;
import com.sleepycat.je.dbi.DbEnvPool;
import com.sleepycat.je.dbi.DbTree;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.dbi.RepConfigProxy;
import com.sleepycat.je.dbi.StartupTracker.Phase;
import com.sleepycat.je.dbi.TriggerManager;
import com.sleepycat.je.dbi.DbTree.TruncateDbResult;
import com.sleepycat.je.txn.HandleLocker;
import com.sleepycat.je.txn.Locker;
import com.sleepycat.je.txn.LockerFactory;
import com.sleepycat.je.txn.Txn;
import com.sleepycat.je.utilint.DatabaseUtil;
import com.sleepycat.je.utilint.LoggerUtils;

/**
* A database environment.  Environments include support for some or all of
* caching, locking, logging and transactions.
*
* <p>To open an existing environment with default attributes the application
* may use a default environment configuration object or null:
* <blockquote>
*     <pre>
*      // Open an environment handle with default attributes.
*     Environment env = new Environment(home, new EnvironmentConfig());
*     </pre>
* </blockquote>
* or
* <blockquote><pre>
*     Environment env = new Environment(home, null);
* </pre></blockquote>
* <p>Note that many Environment objects may access a single environment.</p>
* <p>To create an environment or customize attributes, the application should
* customize the configuration class. For example:</p>
* <blockquote><pre>
*     EnvironmentConfig envConfig = new EnvironmentConfig();
*     envConfig.setTransactional(true);
*     envConfig.setAllowCreate(true);
*     envConfig.setCacheSize(1000000);
*     Environment newlyCreatedEnv = new Environment(home, envConfig);
* </pre></blockquote>
*
* <p>Note that environment configuration parameters can also be set through
* the &lt;environment home&gt;/je.properties file. This file takes precedence
* over any programmatically specified configuration parameters so that
* configuration changes can be made without recompiling. Environment
* configuration follows this order of precedence:</p>
*
* <ol>
* <li>Configuration parameters specified in
* &lt;environment home&gt;/je.properties take first precedence.
* <li> Configuration parameters set in the EnvironmentConfig object used at
* Environment construction are next.
* <li>Any configuration parameters not set by the application are set to
* system defaults, described along with the parameter name String constants
* in the EnvironmentConfig class.
* </ol>
*
* <p>An <em>environment handle</em> is an Environment instance.  More than one
* Environment instance may be created for the same physical directory, which
* is the same as saying that more than one Environment handle may be open at
* one time for a given environment.</p>
*
* The Environment handle should not be closed while any other handle remains
* open that is using it as a reference (for example, {@link
* com.sleepycat.je.Database Database} or {@link com.sleepycat.je.Transaction
* Transaction}.  Once {@link com.sleepycat.je.Environment#close
* Environment.close} is called, this object may not be accessed again.
*/
public class Environment implements Closeable {

    /**
     * @hidden
     * envImpl is a reference to the shared underlying environment.
     */
    protected EnvironmentImpl envImpl;
    private TransactionConfig defaultTxnConfig;
    private EnvironmentMutableConfig handleConfig;
    private final EnvironmentConfig appliedFinalConfig;

    private final Set<Database> referringDbs;
    private final Set<Transaction> referringDbTxns;

    /**
     * @hidden
     * The name of the cleaner daemon thread.  This constant is passed to an
     * ExceptionEvent's threadName argument when an exception is thrown in the
     * cleaner daemon thread.
     */
    public static final String CLEANER_NAME = "Cleaner";

    /**
     * @hidden
     * The name of the IN Compressor daemon thread.  This constant is passed to
     * an ExceptionEvent's threadName argument when an exception is thrown in
     * the IN Compressor daemon thread.
     */
    public static final String INCOMP_NAME = "INCompressor";

    /**
     * @hidden
     * The name of the Checkpointer daemon thread.  This constant is passed to
     * an ExceptionEvent's threadName argument when an exception is thrown in
     * the Checkpointer daemon thread.
     */
    public static final String CHECKPOINTER_NAME = "Checkpointer";

    /**
     * Creates a database environment handle.
     *
     * @param envHome The database environment's home directory.
     *
     * @param configuration The database environment attributes.  If null,
     * default attributes are used.
     *
     * @throws EnvironmentNotFoundException if the environment does not exist
     * (does not contain at least one log file) and the {@code
     * EnvironmentConfig AllowCreate} parameter is false.
     *
     * @throws EnvironmentLockedException when an environment cannot be opened
     * for write access because another process has the same environment open
     * for write access.  <strong>Warning:</strong> This exception should be
     * handled when an environment is opened by more than one process.
     *
     * @throws VersionMismatchException when the existing log is not compatible
     * with the version of JE that is running.  This occurs when a later
     * version of JE was used to create the log.  <strong>Warning:</strong>
     * This exception should be handled when more than one version of JE may be
     * used to access an environment.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws UnsupportedOperationException if this environment was previously
     * opened for replication and is not being opened read-only.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified,
     * for example, an invalid {@code EnvironmentConfig} parameter.
     */
    public Environment(File envHome, EnvironmentConfig configuration)
        throws EnvironmentNotFoundException,
               EnvironmentLockedException,
               VersionMismatchException,
               DatabaseException,
               IllegalArgumentException {

        this(envHome, configuration, true /*openIfNeeded*/,
             null /*repConfigProxy*/, null /*envImplParam*/);
    }

    /**
     * @hidden
     * Replication support.
     */
    protected Environment(File envHome,
                          EnvironmentConfig configuration,
                          RepConfigProxy repConfigProxy,
                          EnvironmentImpl envImplParam) {
        this(envHome, configuration, true /*openIfNeeded*/, repConfigProxy,
             envImplParam);
    }

    /**
     * Gets an Environment for an existing EnvironmentImpl. Used by utilities
     * such as the JMX MBean which don't want to open the environment or be
     * reference counted. The calling application must take care not to retain
     */
    Environment(File envHome) {
        this(envHome, null /*configuration*/, false /*openIfNeeded*/,
             null /*repConfigProxy*/, null /*envImplParam*/);
    }

    /**
     * @hidden
     * Internal common constructor.
     *
     * @param envImpl is non-null only when used by EnvironmentIml to create an
     * InternalEnvironment.
     */
    protected Environment(File envHome,
                          EnvironmentConfig envConfig,
                          boolean openIfNeeded,
                          RepConfigProxy repConfigProxy,
                          EnvironmentImpl envImplParam) {

        /* If openIfNeeded is false, then envConfig must be null. */
        assert openIfNeeded || envConfig == null || envImplParam != null;

        envImpl = null;
        referringDbs = Collections.synchronizedSet(new HashSet<Database>());
        referringDbTxns =
            Collections.synchronizedSet(new HashSet<Transaction>());

        DatabaseUtil.checkForNullParam(envHome, "envHome");

        appliedFinalConfig =
            setupHandleConfig(envHome, envConfig, repConfigProxy);

        if (envImplParam != null) {
            /* We're creating an InternalEnvironment in EnvironmentImpl. */
            envImpl = envImplParam;
        } else {
            /* Open a new or existing environment in the shared pool. */
            envImpl = makeEnvironmentImpl(envHome,
                                          envConfig,
                                          openIfNeeded,
                                          repConfigProxy);
        }
    }

    /**
     * @hidden
     * MakeEnvironmentImpl is called both by the Environment constructor and
     * by the ReplicatedEnvironment constructor when recreating the environment
     * for a hard recovery.
     */
    protected EnvironmentImpl makeEnvironmentImpl
        (File envHome,
         EnvironmentConfig envConfig,
         boolean openIfNeeded,
         RepConfigProxy repConfigProxy) {

        envImpl = DbEnvPool.getInstance().getEnvironment
            (envHome,
             appliedFinalConfig,
             envConfig != null /*checkImmutableParams*/,
             openIfNeeded,
             setupRepConfig(envHome, repConfigProxy, envConfig));

        if (envImpl != null) {
            envImpl.registerMBean(this);
        }

        return envImpl;
    }

    /**
     * Validate the parameters specified in the environment config.  Applies
     * the configurations specified in the je.properties file to override any
     * programmatically set configurations.  Create a copy to save in this
     * handle. The main reason to return a config instead of using the
     * handleConfig field is to return an EnvironmentConfig instead of a
     * EnvironmentMutableConfig.
     */
    private EnvironmentConfig setupHandleConfig(File envHome,
                                                EnvironmentConfig envConfig,
                                                RepConfigProxy repConfig)
        throws IllegalArgumentException {

        /* If the user specified a null object, use the default */
        EnvironmentConfig baseConfig = (envConfig == null) ?
            EnvironmentConfig.DEFAULT : envConfig;

        /* Make a copy, apply je.properties, and init the handle config. */
        EnvironmentConfig useConfig = baseConfig.clone();

        /* Apply the je.properties file. */
        if (useConfig.getLoadPropertyFile()) {
            DbConfigManager.applyFileConfig(envHome,
                                            DbInternal.getProps(useConfig),
                                            false);       // forReplication
        }
        copyToHandleConfig(useConfig, useConfig, repConfig);
        return useConfig;
    }

    /**
     * @hidden
     * Obtain a validated replication configuration. In a non-HA environment,
     * return null.
     */
    protected RepConfigProxy
        setupRepConfig(final File envHome,
                       final RepConfigProxy repConfigProxy,
                       final EnvironmentConfig envConfig) {

        return null;
    }

    /**
     * The Environment.close method closes the Berkeley DB environment.
     *
     * <p>When the last environment handle is closed, allocated resources are
     * freed, and daemon threads are stopped, even if they are performing work.
     * For example, if the cleaner is still cleaning the log, it will be
     * stopped at the next reasonable opportunity and perform no more cleaning
     * operations.</p>
     *
     * <p>The Environment handle should not be closed while any other handle
     * that refers to it is not yet closed; for example, database environment
     * handles must not be closed while database handles remain open, or
     * transactions in the environment have not yet committed or aborted.
     * Specifically, this includes {@link com.sleepycat.je.Database Database},
     * {@link com.sleepycat.je.Cursor Cursor} and {@link
     * com.sleepycat.je.Transaction Transaction} handles.</p>
     *
     * <p>If this handle has already been closed, this method does nothing and
     * returns without throwing an exception.</p>
     *
     * <p>In multithreaded applications, only a single thread should call
     * Environment.close.</p>
     *
     * <p>After this method has been called, regardless of its return, this
     * Berkeley DB environment handle may not be accessed again, with one
     * exception:  the {@code close} method itself may be called any number of
     *
     * <p>WARNING: To guard against memory leaks, the application should
     * discard all references to the closed handle.  While BDB makes an effort
     * to discard references from closed objects to the allocated memory for an
     * environment, this behavior is not guaranteed.  The safe course of action
     * for an application is to discard all references to closed BDB
     * objects.</p>
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if any open databases or transactions
     * refer to this handle.
     */
    public synchronized void close()
        throws DatabaseException {

        if (envImpl == null) {
            return;
        }

        if (!envImpl.isValid()) {

            /*
             * We're trying to close on an environment that has seen a fatal
             * exception. Try to do the minimum, such as closing file
             * descriptors, to support re-opening the environment in the same
             * JVM.
             */
            try {
                envImpl.closeAfterInvalid();
            } finally {
                envImpl = null;
            }
            return;
        }

        final StringBuilder errors = new StringBuilder();
        try {
            checkForCloseErrors(errors);

            try {
                envImpl.close();
            } catch (RuntimeException e) {
                if (!envImpl.isValid()) {
                    /* Propagate if env is invalidated. */
                    throw e;
                }
                errors.append
                  ("\nWhile closing Environment encountered exception: ");
                errors.append(e).append("\n");
            }

            if (errors.length() > 0) {
                throw new IllegalStateException(errors.toString());
            }
        } finally {
            envImpl = null;
        }
    }

    /**
     * Close an InternalEnvironment handle.  We do not call
     * EnvironmentImpl.close here, since an InternalEnvironment is not
     * registered like a non-internal handle.  However, we must call
     * checkForCloseErrors to auto-close internal databases, as well as check
     * for errors.
     */
    synchronized void closeInternalHandle() {
        final StringBuilder errors = new StringBuilder();
        checkForCloseErrors(errors);
        if (errors.length() > 0) {
            throw new IllegalStateException(errors.toString());
        }
    }

    private void checkForCloseErrors(StringBuilder errors) {

        checkOpenDbs(errors);

        checkOpenTxns(errors);

        if (!isInternalHandle()) {

            /*
             * Only check for open XA transactions against user created
             * environment handles.
             */
            checkOpenXATransactions(errors);
        }
    }

    /**
     * Appends error messages to the errors argument if there are
     * open XA transactions associated with the underlying EnvironmentImpl.
     */
    private void checkOpenXATransactions(final StringBuilder errors) {
        Xid[] openXids = envImpl.getTxnManager().XARecover();
        if (openXids != null && openXids.length > 0) {
            errors.append("There ");
            int nXATxns = openXids.length;
            if (nXATxns == 1) {
                errors.append("is 1 existing XA transaction opened");
                errors.append(" in the Environment.\n");
                errors.append("It");
            } else {
                errors.append("are ");
                errors.append(nXATxns);
                errors.append(" existing transactions opened in");
                errors.append(" the Environment.\n");
                errors.append("They");
            }
            errors.append(" will be left open ...\n");
        }
    }

    /**
     * Appends error messages to the errors argument if there are open
     * transactions associated with the environment.
     */
    private void checkOpenTxns(final StringBuilder errors) {
        int nTxns = (referringDbTxns == null) ? 0 : referringDbTxns.size();
        if (nTxns == 0) {
            return;
        }

        Iterator<Transaction> iter = referringDbTxns.iterator();
        errors.append("There ");
        if (nTxns == 1) {
            errors.append("is 1 existing transaction opened");
            errors.append(" against the Environment.\n");
        } else {
            errors.append("are ");
            errors.append(nTxns);
            errors.append(" existing transactions opened against");
            errors.append(" the Environment.\n");
        }
        errors.append("Aborting open transactions ...\n");

        while (iter.hasNext()) {
            Transaction txn = iter.next();
            try {
                errors.append("aborting " + txn);
                txn.abort();
            } catch (RuntimeException e) {
                if (!envImpl.isValid()) {
                    /* Propagate if env is invalidated. */
                    throw e;
                }
                errors.append("\nWhile aborting transaction ");
                errors.append(txn.getId());
                errors.append(" encountered exception: ");
                errors.append(e).append("\n");
            }
        }
    }

    /**
     * Appends error messages to the errors argument if there are open database
     * handles associated with the environment.
     */
    private void checkOpenDbs(final StringBuilder errors) {

        if (referringDbs.isEmpty()) {
            return;
        }

        /*
         * Copy the referringDbs Set because db.close() below modifies this
         * Set, potentially causing a ConcurrentModificationException.
         */
        final Iterator<Database> iter =
            new HashSet<Database>(referringDbs).iterator();
        int nOpenUserDbs = 0;
        while (iter.hasNext()) {
            String dbName = "";
            try {
                Database db = iter.next();

                /*
                 * Save the db name before we attempt the close, it's
                 * unavailable after the close.
                 */
                dbName = db.getDebugName();

                if (!db.getDatabaseImpl().isInternalDb()) {
                    nOpenUserDbs += 1;
                    errors.append("Unclosed Database: ");
                    errors.append(dbName).append("\n");
                }
                db.close();
            } catch (RuntimeException e) {
                if (!envImpl.isValid()) {
                    /* Propagate if env is invalidated. */
                    throw e;
                }
                errors.append("\nWhile closing Database ");
                errors.append(dbName);
                errors.append(" encountered exception: ");
                errors.append(e).append("\n");
            }
        }

        if (nOpenUserDbs > 0) {
            errors.append("Databases left open: ");
            errors.append(nOpenUserDbs).append("\n");
        }
    }

    /**
     * Opens, and optionally creates, a <code>Database</code>.
     *
     * @param txn For a transactional database, an explicit transaction may be
     * specified, or null may be specified to use auto-commit.  For a
     * non-transactional database, null must be specified.
     *
     * @param databaseName The name of the database.
     *
     * @param dbConfig The database attributes.  If null, default attributes
     * are used.
     *
     * @return Database handle.
     *
     * @throws DatabaseExistsException if the database already exists and the
     * {@code DatabaseConfig ExclusiveCreate} parameter is true.
     *
     * @throws DatabaseNotFoundException if the database does not exist and the
     * {@code DatabaseConfig AllowCreate} parameter is false.
     *
     * @throws OperationFailureException if one of the <a
     * href="../je/OperationFailureException.html#readFailures">Read Operation
     * Failures</a> occurs. If the database does not exist and the {@link
     * DatabaseConfig#setAllowCreate AllowCreate} parameter is true, then one
     * of the <a
     * href="../je/OperationFailureException.html#writeFailures">Write
     * Operation Failures</a> may also occur.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified,
     * for example, an invalid {@code DatabaseConfig} property.
     *
     * @throws IllegalStateException if DatabaseConfig properties are changed
     * and there are other open handles for this database.
     */
    public synchronized Database openDatabase(Transaction txn,
                                              String databaseName,
                                              DatabaseConfig dbConfig)
        throws DatabaseNotFoundException,
               DatabaseExistsException,
               IllegalArgumentException,
               IllegalStateException {

        checkHandleIsValid();
        checkEnv();

        try {
            if (dbConfig == null) {
                dbConfig = DatabaseConfig.DEFAULT;
            }

            Database db = new Database(this);
            setupDatabase(txn, db, databaseName, dbConfig,
                          false /*isInternalDb*/);
            return db;
        } catch (Error E) {
            envImpl.invalidate(E);
            throw E;
        }
    }

    /**
     * Opens an internal database for internal JE use.
     *  - permits opening DBs with reserved (internal) names.
     *  - permits opening a transactional DB in a non-transactional env.
     *
     * DbConfig should not be null. Note that the dbConfig argument determines
     * whether the database that is created is to be replicated or is
     * standalone. The current default is for dbConfig to be replicated, so use
     * DatabaseConfig.setReplicated(false) to create a local internal database.
     */
    synchronized Database openInternalDatabase(Transaction txn,
                                               String databaseName,
                                               DatabaseConfig dbConfig)
        throws DatabaseNotFoundException, DatabaseExistsException {

        assert DbTree.isReservedDbName(databaseName) : databaseName;
        Database db = new Database(this);
        setupDatabase(txn, db, databaseName, dbConfig, true /*isInternalDb*/);
        return db;
    }

    /**
     * Opens and optionally creates a <code>SecondaryDatabase</code>.
     *
     * <p>Note that the associations between primary and secondary databases
     * are not stored persistently.  Whenever a primary database is opened for
     * write access by the application, the appropriate associated secondary
     * databases should also be opened by the application.  This is necessary
     * to ensure data integrity when changes are made to the primary
     * database.</p>
     *
     * @param txn For a transactional database, an explicit transaction may be
     * specified, or null may be specified to use auto-commit.  For a
     * non-transactional database, null must be specified.
     *
     * @param databaseName The name of the database.
     *
     * @param primaryDatabase the primary database with which the secondary
     * database will be associated.  The primary database must not be
     * configured for duplicates.
     *
     * @param dbConfig The secondary database attributes.  If null, default
     * attributes are used.
     *
     * @return Database handle.
     *
     * @throws DatabaseExistsException if the database already exists and the
     * {@code DatabaseConfig ExclusiveCreate} parameter is true.
     *
     * @throws DatabaseNotFoundException if the database does not exist and the
     * {@code DatabaseConfig AllowCreate} parameter is false.
     *
     * @throws OperationFailureException if one of the <a
     * href="../je/OperationFailureException.html#readFailures">Read Operation
     * Failures</a> occurs. If the database does not exist and the {@link
     * DatabaseConfig#setAllowCreate AllowCreate} parameter is true, then one
     * of the <a
     * href="../je/OperationFailureException.html#writeFailures">Write
     * Operation Failures</a> may also occur.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified,
     * for example, an invalid {@code SecondaryConfig} property.
     *
     * @throws IllegalStateException if DatabaseConfig properties are changed
     * and there are other open handles for this database.
     */
    public synchronized
        SecondaryDatabase openSecondaryDatabase(Transaction txn,
                                                String databaseName,
                                                Database primaryDatabase,
                                                SecondaryConfig dbConfig)
        throws DatabaseNotFoundException,
               DatabaseExistsException,
               DatabaseException,
               IllegalArgumentException,
               IllegalStateException {

        checkHandleIsValid();
        checkEnv();
        try {
            if (dbConfig == null) {
                dbConfig = SecondaryConfig.DEFAULT;
            }
            SecondaryDatabase db =
                new SecondaryDatabase(this, dbConfig, primaryDatabase);

            setupDatabase(txn, db, databaseName, dbConfig,
                          false /*isInternalDb*/);
            return db;
        } catch (Error E) {
            envImpl.invalidate(E);
            throw E;
        }
    }

    /**
     * The meat of open database processing.
     *
     * @param txn may be null
     * @param newDb is the Database handle which houses this database
     *
     * @throws IllegalArgumentException via openDatabase and
     * openSecondaryDatabase
     *
     * @see HandleLocker
     */
    private void setupDatabase(Transaction txn,
                               Database newDb,
                               String databaseName,
                               DatabaseConfig dbConfig,
                               boolean isInternalDb)
        throws DatabaseNotFoundException, DatabaseExistsException {

        checkEnv();
        DatabaseUtil.checkForNullParam(databaseName, "databaseName");

        LoggerUtils.envLogMsg(Level.FINEST, envImpl,
                              "Environment.open: " + " name=" + databaseName +
                              " dbConfig=" + dbConfig);

        final boolean autoTxnIsReplicated =
            DbInternal.getReplicated(dbConfig) && envImpl.isReplicated();

        /*
         * Check that the open configuration is valid and doesn't conflict with
         * the envImpl configuration.
         */
        dbConfig.validateOnDbOpen(databaseName, autoTxnIsReplicated);
        validateDbConfigAgainstEnv(dbConfig, databaseName, isInternalDb);

        /* Perform eviction before each operation that allocates memory. */
        envImpl.criticalEviction(false /*backgroundIO*/);

        DatabaseImpl database = null;
        boolean operationOk = false;
        HandleLocker handleLocker = null;
        final Locker locker = LockerFactory.getWritableLocker
            (this, txn, isInternalDb, dbConfig.getTransactional(),
             autoTxnIsReplicated, null);
        try {

            /*
             * Create the handle locker and lock the NameLN of an existing
             * database.  A read lock on the NameLN is acquired for both locker
             * and handleLocker.  Note: getDb may return a deleted database.
             */
            handleLocker = newDb.initHandleLocker(envImpl, locker);
            database = envImpl.getDbTree().getDb(locker, databaseName,
                                                 handleLocker);

            boolean dbCreated = false;
            final boolean databaseExists =
                (database != null) && !database.isDeleted();

            if (databaseExists) {
                if (dbConfig.getAllowCreate() &&
                    dbConfig.getExclusiveCreate()) {
                    throw new DatabaseExistsException
                        ("Database " + databaseName + " already exists");
                }

                newDb.initExisting(this, locker, database, dbConfig);
            } else {
                /* Release deleted DB. [#13415] */
                envImpl.getDbTree().releaseDb(database);
                database = null;

                if (!isInternalDb &&
                    DbTree.isReservedDbName(databaseName)) {
                    throw new IllegalArgumentException
                        (databaseName + " is a reserved database name.");
                }

                if (!dbConfig.getAllowCreate()) {
                    throw new DatabaseNotFoundException("Database " +
                                                        databaseName +
                                                        " not found.");
                }

                /*
                 * Init a new DB. This calls DbTree.createDb and the new
                 * database is returned.  A write lock on the NameLN is
                 * acquired by locker and a read lock by the handleLocker.
                 */
                database = newDb.initNew(this, locker, databaseName, dbConfig);
                dbCreated = true;
            }

            /*
             * The open is successful.  We add the opened database handle to
             * this environment to track open handles in general, and to the
             * locker so that it can be invalidated by a user txn abort.
             */
            operationOk = true;
            addReferringHandle(newDb);
            locker.addOpenedDatabase(newDb);

            /* Run triggers before any subsequent auto commits. */
            final boolean firstWriteHandle =
                newDb.isWritable() &&
                (newDb.getDatabaseImpl().noteWriteHandleOpen() == 1);

            if (dbCreated || firstWriteHandle) {
                TriggerManager.runOpenTriggers(locker, newDb, dbCreated);
            }
        } finally {

            /*
             * If the open fails, decrement the DB usage count and release
             * handle locks.  In other cases this is done by Database.close()
             * or invalidate(), the latter in the case of a user txn abort.
             */
            if (!operationOk) {
                envImpl.getDbTree().releaseDb(database);
                if (handleLocker != null) {
                    handleLocker.operationEnd(false);
                }
            }

            /*
             * Tell the locker that this operation is over. Some types of
             * lockers (BasicLocker and auto Txn) will actually finish.
             */
            locker.operationEnd(operationOk);
        }
    }

    /**
     * @throws IllegalArgumentException via openDatabase and
     * openSecondaryDatabase
     */
    private void validateDbConfigAgainstEnv(DatabaseConfig dbConfig,
                                            String databaseName,
                                            boolean isInternalDb)
        throws IllegalArgumentException {

        /*
         * R/W database handles on a replicated database must be transactional,
         * for now. In the future we may support non-transactional database
         * handles.
         */
        if (envImpl.isReplicated() &&
            dbConfig.getReplicated() &&
            !dbConfig.getReadOnly()) {
            if (!dbConfig.getTransactional()) {
                throw new IllegalArgumentException
                ("Read/Write Database instances for replicated " +
                 "database " + databaseName + " must be transactional.");
            }
        }

        /* Check operation's transactional status against the Environment */
        if (!isInternalDb &&
            dbConfig.getTransactional() &&
            !(envImpl.isTransactional())) {
            throw new IllegalArgumentException
                ("Attempted to open Database " + databaseName +
                 " transactionally, but parent Environment is" +
                 " not transactional");
        }

        /* Check read/write status */
        if (envImpl.isReadOnly() && (!dbConfig.getReadOnly())) {
            throw new IllegalArgumentException
                ("Attempted to open Database " + databaseName +
                 " as writable but parent Environment is read only ");
        }
    }

    /**
     * Removes a database from the environment, discarding all records in the
     * database and removing the database name itself.
     *
     * <p>Compared to deleting all the records in a database individually,
     * {@code removeDatabase} is a very efficient operation.  Some internal
     * housekeeping information is updated, but the database records are not
     * read or written, and very little I/O is needed.</p>
     *
     * <p>When called on a database configured with secondary indices, the
     * application is responsible for also removing all associated secondary
     * indices.  To guarantee integrity, a primary database and all of its
     * secondary databases should be removed atomically using a single
     * transaction.</p>
     *
     * <p>Applications should not remove a database with open {@link Database
     * Database} handles.  If the database is open with the same transaction as
     * passed in the {@code txn} parameter, {@link IllegalStateException} is
     * thrown by this method.  If the database is open using a different
     * transaction, this method will block until all database handles are
     * closed, or until the conflict is resolved by throwing {@link
     * LockConflictException}.</p>
     *
     * @param txn For a transactional environment, an explicit transaction
     * may be specified or null may be specified to use auto-commit.  For a
     * non-transactional environment, null must be specified.
     *
     * @param databaseName The database to be removed.
     *
     * @throws DatabaseNotFoundException if the database does not exist.
     *
     * @throws OperationFailureException if one of the <a
     * href="../je/OperationFailureException.html#writeFailures">Write
     * Operation Failures</a> occurs.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws UnsupportedOperationException if this is a read-only
     * environment.
     *
     * @throws IllegalStateException if the database is currently open using
     * the transaction passed in the {@code txn} parameter, or if this handle
     * or the underlying environment has been closed.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified.
     */
    public void removeDatabase(Transaction txn, String databaseName)
        throws DatabaseNotFoundException {

        checkHandleIsValid();
        DatabaseUtil.checkForNullParam(databaseName, "databaseName");
        checkEnv();
        checkWritable();

        final boolean autoTxnIsReplicated = envImpl.isReplicated();
        Locker locker = null;
        DatabaseImpl dbImpl = null;
        try {

            /*
             * Note: use env level isTransactional as proxy for the db
             * isTransactional.
             */
            locker = LockerFactory.getWritableLocker
                (this,
                 txn,
                 false /*isInternalDb*/,
                 envImpl.isTransactional(),
                 autoTxnIsReplicated,
                 null);
            dbImpl = envImpl.getDbTree().dbRemove(locker,
                                         databaseName,
                                         null /*checkId*/);
        } catch (Error E) {
            envImpl.invalidate(E);
            throw E;
        } finally {
            if (dbImpl != null) {
                TriggerManager.runRemoveTriggers(locker, dbImpl);
            }
            if (locker != null) {
                locker.operationEnd(dbImpl != null);
            }
        }
    }

    /**
     * Renames a database, without removing the records it contains.
     *
     * <p>Applications should not rename a database with open {@link Database
     * Database} handles.  If the database is open with the same transaction as
     * passed in the {@code txn} parameter, {@link IllegalStateException} is
     * thrown by this method.  If the database is open using a different
     * transaction, this method will block until all database handles are
     * closed, or until the conflict is resolved by throwing {@link
     * LockConflictException}.</p>
     *
     * @param txn For a transactional environment, an explicit transaction
     * may be specified or null may be specified to use auto-commit.  For a
     * non-transactional environment, null must be specified.
     *
     * @param databaseName The new name of the database.
     *
     * @throws DatabaseNotFoundException if the database does not exist.
     *
     * @throws OperationFailureException if one of the <a
     * href="../je/OperationFailureException.html#writeFailures">Write
     * Operation Failures</a> occurs.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws UnsupportedOperationException if this is a read-only
     * environment.
     *
     * @throws IllegalStateException if the database is currently open using
     * the transaction passed in the {@code txn} parameter, or if this handle
     * or the underlying environment has been closed.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified.
     */
    public void renameDatabase(Transaction txn,
                               String databaseName,
                               String newName)
        throws DatabaseNotFoundException {

        DatabaseUtil.checkForNullParam(databaseName, "databaseName");
        DatabaseUtil.checkForNullParam(newName, "newName");

        checkHandleIsValid();
        checkEnv();
        checkWritable();

        Locker locker = null;
        DatabaseImpl dbImpl = null;
        try {

            /*
             * Note: use env level isTransactional as proxy for the db
             * isTransactional.
             */
            locker = LockerFactory.getWritableLocker
                (this, txn,
                 false /*isInternalDb*/,
                 envImpl.isTransactional(),
                 envImpl.isReplicated()// autoTxnIsReplicated
                 null);
            dbImpl = envImpl.getDbTree().
                dbRename(locker, databaseName, newName);
        } catch (Error E) {
            envImpl.invalidate(E);
            throw E;
        } finally {
            if (dbImpl != null) {
                TriggerManager.runRenameTriggers(locker, dbImpl, newName);
            }
            if (locker != null) {
                locker.operationEnd(dbImpl != null);
            }
        }
    }

    /**
     * Empties the database, discarding all the records it contains, without
     * removing the database name.
     *
     * <p>Compared to deleting all the records in a database individually,
     * {@code truncateDatabase} is a very efficient operation.  Some internal
     * housekeeping information is updated, but the database records are not
     * read or written, and very little I/O is needed.</p>
     *
     * <p>When called on a database configured with secondary indices, the
     * application is responsible for also truncating all associated secondary
     * indices.  To guarantee integrity, a primary database and all of its
     * secondary databases should be truncated atomically using a single
     * transaction.</p>
     *
     * <p>Applications should not truncate a database with open {@link Database
     * Database} handles.  If the database is open with the same transaction as
     * passed in the {@code txn} parameter, {@link IllegalStateException} is
     * thrown by this method.  If the database is open using a different
     * transaction, this method will block until all database handles are
     * closed, or until the conflict is resolved by throwing {@link
     * LockConflictException}.</p>
     *
     * @param txn For a transactional environment, an explicit transaction may
     * be specified or null may be specified to use auto-commit.  For a
     * non-transactional environment, null must be specified.
     *
     * @param databaseName The database to be truncated.
     *
     * @param returnCount If true, count and return the number of records
     * discarded.
     *
     * @return The number of records discarded, or -1 if returnCount is false.
     *
     * @throws DatabaseNotFoundException if the database does not exist.
     *
     * @throws OperationFailureException if one of the <a
     * href="../je/OperationFailureException.html#writeFailures">Write
     * Operation Failures</a> occurs.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws UnsupportedOperationException if this is a read-only
     * environment.
     *
     * @throws IllegalStateException if the database is currently open using
     * the transaction passed in the {@code txn} parameter, or if this handle
     * or the underlying environment has been closed.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified.
     */
    public long truncateDatabase(Transaction txn,
                                 String databaseName,
                                 boolean returnCount)
        throws DatabaseNotFoundException {

        checkHandleIsValid();
        DatabaseUtil.checkForNullParam(databaseName, "databaseName");
        checkEnv();
        checkWritable();

        final boolean autoTxnIsReplicated = envImpl.isReplicated();
        Locker locker = null;
        TruncateDbResult result = null;
        try {

            /*
             * Note: use env level isTransactional as proxy for the db
             * isTransactional.
             */
            locker = LockerFactory.getWritableLocker
                (this, txn,
                 false /*isInternalDb*/,
                 envImpl.isTransactional(),
                 autoTxnIsReplicated,
                 null);

            result = envImpl.getDbTree().truncate(locker,
                                                 databaseName,
                                                 returnCount);
        } catch (Error E) {
            envImpl.invalidate(E);
            throw E;
        } finally {
            if (result != null) {
                TriggerManager.runTruncateTriggers(locker, result.newDb);
            }
            if (locker != null) {
                locker.operationEnd(result != null);
            }
        }

        if (result == null) {
            /* Added to suppress compiler warnings. */
            throw EnvironmentFailureException.unexpectedState(envImpl,
                                                              "Cannot happen");
        }

        return result.recordCount;
    }

    /**
     * For unit testing.  Returns the current memory usage in bytes for all
     * btrees in the envImpl.
     */
    long getMemoryUsage()
        throws DatabaseException {

        checkHandleIsValid();
        checkEnv();

        return envImpl.getMemoryBudget().getCacheMemoryUsage();
    }

    /**
     * Returns the database environment's home directory.
     *
     * @return The database environment's home directory.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public File getHome()
        throws DatabaseException {

        checkHandleIsValid();

        return envImpl.getEnvironmentHome();
    }

    /*
     * Transaction management
     */

    /**
     * Returns the default txn config for this environment handle.
     */
    TransactionConfig getDefaultTxnConfig() {
        return defaultTxnConfig;
    }

    /**
     * Copies the handle properties out of the config properties, and
     * initializes the default transaction config.
     */
    private void copyToHandleConfig(EnvironmentMutableConfig useConfig,
                                    EnvironmentConfig initStaticConfig,
                                    RepConfigProxy    initRepConfig) {

        /*
         * Create the new objects, initialize them, then change the instance
         * fields.  This avoids synchronization issues.
         */
        EnvironmentMutableConfig newHandleConfig =
            new EnvironmentMutableConfig();
        useConfig.copyHandlePropsTo(newHandleConfig);
        this.handleConfig = newHandleConfig;

        TransactionConfig newTxnConfig =
            TransactionConfig.DEFAULT.clone();
        newTxnConfig.setNoSync(handleConfig.getTxnNoSync());
        newTxnConfig.setWriteNoSync(handleConfig.getTxnWriteNoSync());
        newTxnConfig.setDurability(handleConfig.getDurability());

        if (initStaticConfig != null) {
            newTxnConfig.setSerializableIsolation
                (initStaticConfig.getTxnSerializableIsolation());
            newTxnConfig.setReadCommitted
                (initStaticConfig.getTxnReadCommitted());
        } else {
            newTxnConfig.setSerializableIsolation
                (defaultTxnConfig.getSerializableIsolation());
            newTxnConfig.setReadCommitted
                (defaultTxnConfig.getReadCommitted());
            newTxnConfig.setConsistencyPolicy
                (defaultTxnConfig.getConsistencyPolicy());
        }
        if (initRepConfig != null) {
            newTxnConfig.setConsistencyPolicy
                (initRepConfig.getConsistencyPolicy());
        }
        this.defaultTxnConfig = newTxnConfig;
    }

    /**
     * Creates a new transaction in the database environment.
     *
     * <p>Transaction handles are free-threaded; transactions handles may be
     * used concurrently by multiple threads.</p>
     *
     * <p>Cursors may not span transactions; that is, each cursor must be
     * opened and closed within a single transaction. The parent parameter is a
     * placeholder for nested transactions, and must currently be null.</p>
     *
     * @param txnConfig The transaction attributes.  If null, default
     * attributes are used.
     *
     * @return The newly created transaction's handle.
     *
     * @throws com.sleepycat.je.rep.InsufficientReplicasException if the Master
     * in a replicated environment could not contact a quorum of replicas as
     * determined by the {@link ReplicaAckPolicy}.
     *
     * @throws com.sleepycat.je.rep.ReplicaConsistencyException if a replica
     * in a replicated environment cannot become consistent within the timeout
     * period.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws UnsupportedOperationException if this is not a transactional
     * environment.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified,
     * for example, an invalid {@code TransactionConfig} parameter.
     */
    public Transaction beginTransaction(Transaction parent,
                                        TransactionConfig txnConfig)
        throws DatabaseException,
               IllegalArgumentException {

        try {
            return beginTransactionInternal(parent, txnConfig,
                                            false /*isInternalTxn*/);
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Like beginTransaction, but does not require that the Environment is
     * transactional.
     */
    Transaction beginInternalTransaction(TransactionConfig txnConfig) {
        return beginTransactionInternal(null /*parent*/, txnConfig,
                                        true /*isInternalTxn*/);
    }

    /**
     * @throws IllegalArgumentException via beginTransaction.
     * @throws UnsupportedOperationException via beginTransaction.
     */
    private Transaction beginTransactionInternal(Transaction parent,
                                                 TransactionConfig txnConfig,
                                                 boolean isInternalTxn )
        throws DatabaseException {

        checkHandleIsValid();
        checkEnv();

        if (parent != null) {
            throw new IllegalArgumentException
                ("Parent txn is non-null. " +
                 "Nested transactions are not supported.");
        }

        if (!isInternalTxn && !envImpl.isTransactional()) {
            throw new UnsupportedOperationException
                ("Transactions can not be used in a non-transactional " +
                 "environment");
        }

        checkTxnConfig(txnConfig);

        /*
         * Apply txn config defaults.  We don't need to clone unless we have to
         * apply the env default, since we don't hold onto a txn config
         * reference.
         */
        TransactionConfig useConfig = null;
        if (txnConfig == null) {
            useConfig = defaultTxnConfig;
        } else {
            if (defaultTxnConfig.getNoSync() ||
                defaultTxnConfig.getWriteNoSync()) {

                /*
                 * The environment sync settings have been set, check if any
                 * were set in the user's txn config. If none were set in the
                 * user's config, apply the environment defaults
                 */
                if (!txnConfig.getNoSync() &&
                    !txnConfig.getSync() &&
                    !txnConfig.getWriteNoSync()) {
                    useConfig = txnConfig.clone();
                    if (defaultTxnConfig.getWriteNoSync()) {
                        useConfig.setWriteNoSync(true);
                    } else {
                        useConfig.setNoSync(true);
                    }
                }
            }

            if ((defaultTxnConfig.getDurability() != null) &&
                 (txnConfig.getDurability() == null)) {

                /*
                 * Inherit transaction durability from the environment in the
                 * absence of an explicit transaction config durability.
                 */
                if (useConfig == null) {
                    useConfig = txnConfig.clone();
                }
                useConfig.setDurability(defaultTxnConfig.getDurability());
            }

            if ((defaultTxnConfig.getConsistencyPolicy() != null) &&
                (txnConfig.getConsistencyPolicy() == null)) {
                   if (useConfig == null) {
                       useConfig = txnConfig.clone();
                   }
                   useConfig.setConsistencyPolicy
                       (defaultTxnConfig.getConsistencyPolicy());
            }

            /* Apply isolation level default. */
            if (!txnConfig.getSerializableIsolation() &&
                !txnConfig.getReadCommitted() &&
                !txnConfig.getReadUncommitted()) {
                if (defaultTxnConfig.getSerializableIsolation()) {
                    if (useConfig == null) {
                        useConfig = txnConfig.clone();
                    }
                    useConfig.setSerializableIsolation(true);
                } else if (defaultTxnConfig.getReadCommitted()) {
                    if (useConfig == null) {
                        useConfig = txnConfig.clone();
                    }
                    useConfig.setReadCommitted(true);
                }
            }

            /* No environment level defaults applied. */
            if (useConfig == null) {
                useConfig = txnConfig;
            }
        }
        Txn internalTxn = envImpl.txnBegin(parent, useConfig);
        Transaction txn = new Transaction(this, internalTxn);
        addReferringHandle(txn);
        return txn;
    }

    /**
     * Checks the txnConfig object to ensure that its correctly configured and
     * is compatible with the configuration of the Environment.
     *
     * @param txnConfig the configuration being checked.
     *
     * @throws IllegalArgumentException via beginTransaction
     */
    private void checkTxnConfig(TransactionConfig txnConfig)
        throws IllegalArgumentException {

        if (txnConfig == null) {
            return;
        }
        if ((txnConfig.getSerializableIsolation() &&
             txnConfig.getReadUncommitted()) ||
            (txnConfig.getSerializableIsolation() &&
             txnConfig.getReadCommitted()) ||
            (txnConfig.getReadUncommitted() &&
             txnConfig.getReadCommitted())) {
            throw new IllegalArgumentException
                ("Only one may be specified: SerializableIsolation, " +
                "ReadCommitted or ReadUncommitted");
        }
        if ((txnConfig.getDurability() != null) &&
            ((defaultTxnConfig.getSync() ||
              defaultTxnConfig.getNoSync() ||
              defaultTxnConfig.getWriteNoSync()))) {
           throw new IllegalArgumentException
               ("Mixed use of deprecated durability API for the " +
                "Environment with the new durability API for " +
                "TransactionConfig.setDurability()");
        }
        if ((defaultTxnConfig.getDurability() != null) &&
            ((txnConfig.getSync() ||
              txnConfig.getNoSync() ||
              txnConfig.getWriteNoSync()))) {
            throw new IllegalArgumentException
                   ("Mixed use of new durability API for the " +
                    "Environment with the deprecated durability API for " +
                    "TransactionConfig.");
        }
    }

    /**
     * Synchronously checkpoint the database environment.
     * <p>
     * This is an optional action for the application since this activity
     * is, by default, handled by a database environment owned background
     * thread.
     * <p>
     * A checkpoint has the side effect of flushing all preceding
     * non-transactional write operations, as well as any preceding
     * transactions that were committed with {@link
     * Durability.SyncPolicy#NO_SYNC no-sync durability}.  However, for best
     * performance, checkpoints should be used only to bound recovery time.
     * {@link #flushLog} can be used to write buffered data for durability
     * purposes.
     *
     * @param ckptConfig The checkpoint attributes.  If null, default
     * attributes are used.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public void checkpoint(CheckpointConfig ckptConfig)
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            CheckpointConfig useConfig =
                (ckptConfig == null) ? CheckpointConfig.DEFAULT : ckptConfig;
            envImpl.invokeCheckpoint(useConfig, "api");
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Synchronously flushes database environment databases to stable storage.
     * Calling this method is equivalent to forcing a checkpoint and setting
     * {@link CheckpointConfig#setMinimizeRecoveryTime} to true.
     * <p>
     * A checkpoint has the side effect of flushing all preceding
     * non-transactional write operations, as well as any preceding
     * transactions that were committed with {@link
     * Durability.SyncPolicy#NO_SYNC no-sync durability}.  However, for best
     * performance, checkpoints should be used only to bound recovery time.
     * {@link #flushLog} can be used to write buffered data for durability
     * purposes.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public void sync()
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            CheckpointConfig config = new CheckpointConfig();
            config.setForce(true);
            config.setMinimizeRecoveryTime(true);
            envImpl.invokeCheckpoint(config, "sync");
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Writes buffered data to the log, and optionally performs an fsync to
     * guarantee that data is written to the physical device.
     * <p>
     * This method is used to make durable, by writing to the log, all
     * preceding non-transactional write operations, as well as any preceding
     * transactions that were committed with {@link
     * Durability.SyncPolicy#NO_SYNC no-sync durability}.  If the {@code fsync}
     * parameter is true, it can also be used to flush all logged data to the
     * physical storage device, by performing an fsync.
     * <p>
     * Note that this method <em>does not</em> flush previously unwritten data
     * in deferred-write databases; that is done by calling {@link
     * Database#sync} or performing a checkpoint.
     *
     * @param fsync is true to perform an fsync as well as a file write, or
     * false to perform only a file write.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public void flushLog(boolean fsync) {
        try {
            checkHandleIsValid();
            checkEnv();
            envImpl.flushLog(fsync);
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Synchronously invokes database environment log cleaning.  This method is
     * called periodically by the cleaner daemon thread.
     *
     * <p>Zero or more log files will be cleaned as necessary to bring the disk
     * space utilization of the environment above the configured minimum
     * utilization threshold.  The threshold is determined by the {@link
     * EnvironmentConfig#CLEANER_MIN_UTILIZATION} configuration setting.</p>
     *
     * <p>Note that <code>cleanLog</code> does not perform the complete task of
     * cleaning a log file.  Eviction and checkpointing migrate records that
     * are marked by the cleaner, and a full checkpoint is necessary following
     * cleaning before cleaned files will be deleted (or renamed).  Checkpoints
     * normally occur periodically and when the environment is closed.</p>
     *
     * <p>This is an optional action for the application since this activity
     * is, by default, handled by one or more database environment owned
     * background threads.</p>
     *
     * <p>There are two intended use cases for the <code>cleanLog</code>
     * method.  The first case is where the application wishes to disable the
     * built-in cleaner threads using the {@link
     * EnvironmentConfig#ENV_RUN_CLEANER} property.  To replace the
     * functionality of the cleaner threads, the application should call
     * <code>cleanLog</code> periodically.</p>
     *
     * <p>In the second use case, "batch cleaning", the application disables
     * the cleaner threads for maximum performance during active periods, and
     * calls <code>cleanLog</code> during periods when the application is
     * quiescent or less active than usual.  If the cleaner has a large number
     * of files to clean, <code>cleanLog</code> may stop without reaching the
     * target utilization; to ensure that the target utilization is reached,
     * <code>cleanLog</code> should be called in a loop until it returns
     * zero. And to complete the work of cleaning, a checkpoint is necessary.
     * An example of performing batch cleaning follows.</p>
     *
     * <pre>
     *       Environment env;
     *       boolean anyCleaned = false;
     *       while (env.cleanLog() &gt; 0) {
     *           anyCleaned = true;
     *       }
     *       if (anyCleaned) {
     *           CheckpointConfig force = new CheckpointConfig();
     *           force.setForce(true);
     *           env.checkpoint(force);
     *       }
     * </pre>
     *
     * <p><em>WARNING:</em>If batch cleaning (shown above) is performed
     * immediately before closing the environment, then the built-in cleaner
     * threads should normally be disabled using {@link
     * EnvironmentConfig#ENV_RUN_CLEANER} during the batch cleaning process.
     * If the built-in cleaner threads are actively working on one or more log
     * files, then those files will not be processed by the {@code cleanLog}
     * method.  Closing the environment will abort the work being done by the
     * built-in cleaner threads, and log cleaning may be incomplete.</p>
     *
     * @return The number of log files that were cleaned, and that will be
     * deleted (or renamed) when a qualifying checkpoint occurs.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws UnsupportedOperationException if this is a read-only or
     * memory-only environment.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public int cleanLog()
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            return envImpl.invokeCleaner();
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Synchronously invokes the mechanism for keeping memory usage within the
     * cache size boundaries.
     *
     * <p>This is an optional action for the application since this activity
     * is, by default, handled by a database environment owned background
     * thread.</p>
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public void evictMemory()
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            envImpl.invokeEvictor();
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Synchronously invokes the compressor mechanism which compacts in memory
     * data structures after delete operations.
     *
     * <p>This is an optional action for the application since this activity
     * is, by default, handled by a database environment owned background
     * thread.</p>
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public void compress()
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            envImpl.invokeCompressor();
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Preloads the cache with multiple databases.  This method should only be
     * called when there are no operations being performed on the specified
     * databases in other threads.  Executing preload during concurrent updates
     * of the specified databases may result in some or all of the tree being
     * loaded into the JE cache.  Executing preload during any other types of
     * operations may result in JE exceeding its allocated cache
     * size. preload() effectively locks all of the specified database and
     * therefore will lock out the checkpointer, cleaner, and compressor, as
     * well as not allow eviction to occur.
     *
     * @param config The PreloadConfig object that specifies the parameters
     * of the preload.
     *
     * @return A PreloadStats object with the result of the preload operation
     * and various statistics about the preload() operation.
     *
     * @throws OperationFailureException if one of the <a
     * href="OperationFailureException.html#readFailures">Read Operation
     * Failures</a> occurs.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if any of the databases has been closed.
     *
     * @see Database#preload(PreloadConfig)
     */
    public PreloadStats preload(final Database[] databases,
                                final PreloadConfig config)
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            DatabaseUtil.checkForZeroLengthArrayParam(databases, "databases");
            PreloadConfig useConfig =
                (config == null) ? new PreloadConfig() : config;
            int nDbs = databases.length;
            DatabaseImpl[] dbImpls = new DatabaseImpl[nDbs];
            for (int i = 0; i < nDbs; i += 1) {
                dbImpls[i] = DbInternal.getDatabaseImpl(databases[i]);
            }
            return envImpl.preload(dbImpls, useConfig);
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns this object's configuration.
     *
     * @return This object's configuration.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle has been closed.
     */
    public EnvironmentConfig getConfig()
        throws DatabaseException {

        try {
            checkHandleIsValid();
            EnvironmentConfig config = envImpl.cloneConfig();
            handleConfig.copyHandlePropsTo(config);
            config.fillInEnvironmentGeneratedProps(envImpl);
            return config;
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Sets database environment attributes.
     *
     * <p>Attributes only apply to a specific Environment object and are not
     * necessarily shared by other Environment objects accessing this
     * database environment.</p>
     *
     * @param mutableConfig The database environment attributes.  If null,
     * default attributes are used.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle has been closed.
     */
    public synchronized
        void setMutableConfig(EnvironmentMutableConfig mutableConfig)
        throws DatabaseException {

        /*
         * This method is synchronized so that we atomically call both
         * EnvironmentImpl.setMutableConfig and copyToHandleConfig. This
         * ensures that the handle and the EnvironmentImpl properties match.
         */
        try {
            checkHandleIsValid();
            DatabaseUtil.checkForNullParam(mutableConfig, "mutableConfig");

            /*
             * Change the mutable properties specified in the given
             * configuration.
             */
            envImpl.setMutableConfig(mutableConfig);

            /* Reset the handle config properties. */
            copyToHandleConfig(mutableConfig, null, null);
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns database environment attributes.
     *
     * @return Environment attributes.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle has been closed.
     */
    public EnvironmentMutableConfig getMutableConfig()
        throws DatabaseException {

        try {
            checkHandleIsValid();
            EnvironmentMutableConfig config =
                envImpl.cloneMutableConfig();
            handleConfig.copyHandlePropsTo(config);
            config.fillInEnvironmentGeneratedProps(envImpl);
            return config;
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns the general database environment statistics.
     *
     * @param config The general statistics attributes.  If null, default
     * attributes are used.
     *
     * @return The general database environment statistics.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public EnvironmentStats getStats(StatsConfig config)
        throws DatabaseException {

        checkHandleIsValid();
        checkEnv();
        try {
            StatsConfig useConfig =
                (config == null) ? StatsConfig.DEFAULT : config;

            if (envImpl != null) {
                return envImpl.loadStats(useConfig);
            }
            return new EnvironmentStats();
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns the database environment's locking statistics.
     *
     * @param config The locking statistics attributes.  If null, default
     * attributes are used.
     *
     * @return The database environment's locking statistics.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     *
     * @deprecated as of 4.0.10, replaced by {@link
     * Environment#getStats(StatsConfig)}.</p>
     */
    public LockStats getLockStats(StatsConfig config)
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            StatsConfig useConfig =
                (config == null) ? StatsConfig.DEFAULT : config;

            return envImpl.lockStat(useConfig);
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns the database environment's transactional statistics.
     *
     * @param config The transactional statistics attributes.  If null,
     * default attributes are used.
     *
     * @return The database environment's transactional statistics.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public TransactionStats getTransactionStats(StatsConfig config)
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            StatsConfig useConfig =
                (config == null) ? StatsConfig.DEFAULT : config;
            return envImpl.txnStat(useConfig);
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns a List of database names for the database environment.
     *
     * <p>Each element in the list is a String.</p>
     *
     * @return A List of database names for the database environment.
     *
     * @throws OperationFailureException if one of the <a
     * href="OperationFailureException.html#readFailures">Read Operation
     * Failures</a> occurs.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public List<String> getDatabaseNames()
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            return envImpl.getDbTree().getDbNames();
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns if the database environment is consistent and correct.
     *
     * <p>Verification is an expensive operation that should normally only be
     * used for troubleshooting and debugging.</p>
     *
     * @param config The verification attributes.  If null, default
     * attributes are used.
     *
     * @param out The stream to which verification debugging information is
     * written.
     *
     * @return true if the database environment is consistent and correct.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public boolean verify(VerifyConfig config, PrintStream out)
        throws DatabaseException {

        try {
            checkHandleIsValid();
            checkEnv();
            VerifyConfig useConfig =
                (config == null) ? VerifyConfig.DEFAULT : config;
            return envImpl.verify(useConfig, out);
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Returns the transaction associated with this thread if implied
     * transactions are being used.  Implied transactions are used in an XA or
     * JCA "Local Transaction" environment.  In an XA environment the
     * XAEnvironment.start() entrypoint causes a transaction to be created and
     * become associated with the calling thread.  Subsequent API calls
     * implicitly use that transaction.  XAEnvironment.end() causes the
     * transaction to be disassociated with the thread.  In a JCA Local
     * Transaction environment, the call to JEConnectionFactory.getConnection()
     * causes a new transaction to be created and associated with the calling
     * thread.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public Transaction getThreadTransaction()
        throws DatabaseException {

        checkHandleIsValid();
        checkEnv();
        try {
            return envImpl.getTxnManager().getTxnForThread();
        } catch (Error E) {
            if (envImpl != null) {
                envImpl.invalidate(E);
            }
            throw E;
        }
    }

    /**
     * Sets the transaction associated with this thread if implied transactions
     * are being used.  Implied transactions are used in an XA or JCA "Local
     * Transaction" environment.  In an XA environment the
     * XAEnvironment.start() entrypoint causes a transaction to be created and
     * become associated with the calling thread.  Subsequent API calls
     * implicitly use that transaction.  XAEnvironment.end() causes the
     * transaction to be disassociated with the thread.  In a JCA Local
     * Transaction environment, the call to JEConnectionFactory.getConnection()
     * causes a new transaction to be created and associated with the calling
     * thread.
     *
     * @throws IllegalStateException if this handle or the underlying
     * environment has been closed.
     */
    public void setThreadTransaction(Transaction txn) {

        checkHandleIsValid();
        checkEnv();
        try {
            envImpl.getTxnManager().setTxnForThread(txn);
        } catch (Error E) {
            envImpl.invalidate(E);
            throw E;
        }
    }

    /**
     * Returns whether this {@code Environment} is open, valid and can be used.
     * If this method returns false, {@link #close} should be called as soon as
     * possible.
     *
     * <p>When an {@link EnvironmentFailureException}, or one of its
     * subclasses, is caught, the {@code isValid} method should be called to
     * determine whether the {@code Environment} can continue to be used, or
     * should be closed.</p>
     */
    public boolean isValid() {
        return envImpl != null &&
            envImpl.isValid();
    }

    /**
     * Print a detailed report about the costs of different phases of
     * environment startup. This report is by default logged to the je.info
     * file if startup takes longer than je.env.startupThreshold.
     */
    public void printStartupInfo(PrintStream out) {
        envImpl.getStartupTracker().displayStats(out, Phase.TOTAL_ENV_OPEN);
    }

    /*
     * Non public api -- helpers
     */

    /**
     * Let the Environment remember what's opened against it.
     */
    private void addReferringHandle(Database db) {
        referringDbs.add(db);
    }

    /**
     * Lets the Environment remember what's opened against it.
     */
    private void addReferringHandle(Transaction txn) {
        referringDbTxns.add(txn);
    }

    /**
     * The referring db has been closed.
     */
    void removeReferringHandle(Database db) {
        referringDbs.remove(db);
    }

    /**
     * The referring Transaction has been closed.
     */
    void removeReferringHandle(Transaction txn) {
        referringDbTxns.remove(txn);
    }

    /**
     * For internal use only.
     * @hidden
     * @throws IllegalStateException if the environment is not open.
     */
    public void checkHandleIsValid()
        throws DatabaseException {

        if (envImpl == null) {
            throw new IllegalStateException
                ("Attempt to use non-open Environment object().");
        }
    }

    /*
     * Debugging aids.
     */

    /**
     * Internal entrypoint.
     */
    EnvironmentImpl getEnvironmentImpl() {
        return envImpl;
    }

    /* Returns true, if this is a handle allocated internally by JE. */
    protected boolean isInternalHandle() {
        return false;
    }

    /**
     * For internal use only.
     * @hidden
     * Throws if the envImpl is invalid.
     */
    protected void checkEnv()
        throws DatabaseException, EnvironmentFailureException {

        if (envImpl == null) {
            return;
        }
        envImpl.checkIfInvalid();
        envImpl.checkNotClosed();
    }

    /**
     * @throws UnsupportedOperationException via the database operation methods
     * (remove, truncate, rename) and potentially other methods that require a
     * writable environment.
     */
    private void checkWritable() {
        if (envImpl.isReadOnly()) {
            throw new UnsupportedOperationException
                ("Environment is Read-Only.");
        }
    }
}
TOP

Related Classes of com.sleepycat.je.Environment

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.