Package com.sleepycat.je

Source Code of com.sleepycat.je.Database$DbState

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2006
*      Sleepycat Software.  All rights reserved.
*
* $Id: Database.java,v 1.205 2006/01/03 21:55:35 bostic Exp $
*/

package com.sleepycat.je;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.dbi.GetMode;
import com.sleepycat.je.dbi.PutMode;
import com.sleepycat.je.dbi.TruncateResult;
import com.sleepycat.je.dbi.CursorImpl.SearchMode;
import com.sleepycat.je.txn.Locker;
import com.sleepycat.je.txn.LockerFactory;
import com.sleepycat.je.utilint.TinyHashSet;
import com.sleepycat.je.utilint.Tracer;

public class Database {

    /*
     * DbState embodies the Database handle state.
     */
    static class DbState {
        private String stateName;

        DbState(String stateName) {
            this.stateName = stateName;
        }

        public String toString() {
            return "DbState." + stateName;
        }
    }

    static DbState OPEN = new DbState("OPEN");
    static DbState CLOSED = new DbState("CLOSED");
    static DbState INVALID = new DbState("INVALID");

    /* The current state of the handle. */
    private DbState state;

    /* Handles onto the owning environment and the databaseImpl object. */
    Environment envHandle;            // used by subclasses
    private DatabaseImpl databaseImpl;

    DatabaseConfig configuration;     // properties used at execution

    /* True if this handle permits write operations; */
    private boolean isWritable;

    /* Transaction that owns the db lock held while the Database is open. */
    Locker handleLocker;

    /* Set of cursors open against this db handle. */
    private TinyHashSet cursors = new TinyHashSet();

    /*
     * DatabaseTrigger list.  The list is null if empty, and is checked for
     * null to avoiding read locking overhead when no triggers are present.
     * Access to this list is protected by the shared trigger latch in
     * EnvironmentImpl.
     */
    private List triggerList;

    private Logger logger;

    /**
     * Creates a database but does not open or fully initialize it.
     * Is protected for use in compat package.
     */
    protected Database(Environment env) {
        this.envHandle = env;
        handleLocker = null;
  logger = envHandle.getEnvironmentImpl().getLogger();
    }

    /**
     * Create a database, called by Environment.
     */
    void initNew(Environment env,
                 Locker locker,
                 String databaseName,
                 DatabaseConfig dbConfig)
        throws DatabaseException {

        if (dbConfig.getReadOnly()) {
            throw new DatabaseException
    ("DatabaseConfig.setReadOnly() must be set to false " +
     "when creating a Database");
        }

        init(env, dbConfig);

        /* Make the databaseImpl. */
        EnvironmentImpl environmentImpl =
            DbInternal.envGetEnvironmentImpl(envHandle);
        databaseImpl = environmentImpl.createDb(locker,
                                                databaseName,
                                                dbConfig,
                                                this);
        databaseImpl.addReferringHandle(this);
    }

    /**
     * Open a database, called by Environment.
     */
    void initExisting(Environment env,
                      Locker locker,
                      DatabaseImpl databaseImpl,
                      DatabaseConfig dbConfig)
        throws DatabaseException {

        /*
         * Make sure the configuration used for the open is compatible with the
         * existing databaseImpl.
         */
        validateConfigAgainstExistingDb(dbConfig, databaseImpl);

        init(env, dbConfig);
        this.databaseImpl = databaseImpl;
        databaseImpl.addReferringHandle(this);

        /*
         * Copy the duplicates and transactional properties of the underlying
         * database, in case the useExistingConfig property is set.
         */
        configuration.setSortedDuplicates(databaseImpl.getSortedDuplicates());
        configuration.setTransactional(databaseImpl.isTransactional());
    }

    private void init(Environment env,
                      DatabaseConfig config)
        throws DatabaseException {

        handleLocker = null;

        envHandle = env;
        configuration = config.cloneConfig();
        isWritable = !configuration.getReadOnly();
        state = OPEN;       
    }

    /**
     * See if this new handle's configuration is compatible with the
     * pre-existing database.
     */
    private void validateConfigAgainstExistingDb(DatabaseConfig config,
                                                 DatabaseImpl databaseImpl)
        throws DatabaseException {

        /*
         * The allowDuplicates property is persistent and immutable.  It does
         * not need to be specified if the useExistingConfig property is set.
         */
        if (!config.getUseExistingConfig()) {
            if (databaseImpl.getSortedDuplicates() !=
                config.getSortedDuplicates()) {
                throw new DatabaseException
        ("You can't open a Database with a duplicatesAllowed " +
         "configuration of " +
         config.getSortedDuplicates() +
         " if the underlying database was created with a " +
         "duplicatesAllowedSetting of " +
         databaseImpl.getSortedDuplicates() + ".");
            }
        }

        /*
         * The transactional property is kept constant while any handles are
         * open, and set when the first handle is opened.  It does not need to
         * be specified if the useExistingConfig property is set.
         */
        if (databaseImpl.hasOpenHandles()) {
            if (!config.getUseExistingConfig()) {
                if (config.getTransactional() !=
                    databaseImpl.isTransactional()) {
                    throw new DatabaseException
                        ("You can't open a Database with a transactional " +
                         "configuration of " + config.getTransactional() +
                         " if the underlying database was created with a " +
                         "transactional configuration of " +
                         databaseImpl.isTransactional() + ".");
                }
            }
        } else {
            databaseImpl.setTransactional(config.getTransactional());
        }

        /*
         * Only re-set the comparators if the override is allowed.
         */
        if (config.getOverrideBtreeComparator()) {
            databaseImpl.setBtreeComparator(config.getBtreeComparator());
        }

        if (config.getOverrideDuplicateComparator()) {
            databaseImpl.setDuplicateComparator
    (config.getDuplicateComparator());
        }
    }

    public synchronized void close()
        throws DatabaseException {

        StringBuffer errors = null;

        checkEnv();
        checkProhibitedDbState(CLOSED, "Can't close Database:");

        trace(Level.FINEST, "Database.close: ", null, null);

        /* Disassociate triggers before closing. */
        removeAllTriggers();

        envHandle.removeReferringHandle(this);
        if (cursors.size() > 0) {
            errors = new StringBuffer
                ("There are open cursors against the database.\n");
            errors.append("They will be closed.\n");

            /*
             * Copy the cursors set before iterating since the dbc.close()
             * mutates the set.
             */
            Iterator iter = cursors.copy().iterator();
            while (iter.hasNext()) {
                Cursor dbc = (Cursor) iter.next();

                try {
                    dbc.close();
                } catch (DatabaseException DBE) {
                    errors.append("Exception while closing cursors:\n");
                    errors.append(DBE.toString());
                }
            }
        }

        if (databaseImpl != null) {
            databaseImpl.removeReferringHandle(this);
            databaseImpl = null;

            /*
             * Tell our protecting txn that we're closing. If this type
             * of transaction doesn't live beyond the life of the handle,
             * it will release the db handle lock.
             */
            handleLocker.setHandleLockOwner(true, this, true);
            handleLocker.operationEnd(true);
            state = CLOSED;
        }

        if (errors != null) {
            throw new DatabaseException(errors.toString());
        }
    }

    /**
     * Javadoc for this public method is generated via
     * the doc templates in the doc_src directory.
     */
    public Sequence openSequence(Transaction txn,
                                 DatabaseEntry key,
                                 SequenceConfig config)
        throws DatabaseException {

        checkEnv();
        DatabaseUtil.checkForNullDbt(key, "key", true);
        checkRequiredDbState(OPEN, "Can't call Database.openSequence:");
        checkWritable("openSequence");
        trace(Level.FINEST, "Database.openSequence", txn, key, null, null);

        return new Sequence(this, txn, key, config);
    }

    /**
     * Javadoc for this public method is generated via
     * the doc templates in the doc_src directory.
     */
    public void removeSequence(Transaction txn, DatabaseEntry key)
        throws DatabaseException {

        delete(txn, key);
    }

    public synchronized Cursor openCursor(Transaction txn,
                                          CursorConfig cursorConfig)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't open a cursor");
        CursorConfig useConfig =
            (cursorConfig == null) ? CursorConfig.DEFAULT : cursorConfig;

        if (useConfig.getReadUncommitted() && useConfig.getReadCommitted()) {
            throw new IllegalArgumentException
                ("Only one may be specified: ReadCommitted or ReadUncommitted");
        }

        trace(Level.FINEST, "Database.openCursor", txn, cursorConfig);
        Cursor ret = newDbcInstance(txn, useConfig);

        return ret;
    }
    /**
     * Is overridden by SecondaryDatabase.
     */
    Cursor newDbcInstance(Transaction txn,
                          CursorConfig cursorConfig)
        throws DatabaseException {

        return new Cursor(this, txn, cursorConfig);
    }

    public OperationStatus delete(Transaction txn, DatabaseEntry key)
        throws DatabaseException {

        checkEnv();
        DatabaseUtil.checkForNullDbt(key, "key", true);
        checkRequiredDbState(OPEN, "Can't call Database.delete:");
        checkWritable("delete");
        trace(Level.FINEST, "Database.delete", txn, key, null, null);

        OperationStatus commitStatus = OperationStatus.NOTFOUND;
        Locker locker = null;
        try {
            locker = LockerFactory.getWritableLocker
                (envHandle, txn, isTransactional());
            commitStatus = deleteInternal(locker, key);
            return commitStatus;
        } finally {
            if (locker != null) {
                locker.operationEnd(commitStatus);
            }
        }
    }

    /**
     * Internal version of delete() that does no parameter checking.  Notify
     * triggers.  Deletes all duplicates.
     */
    OperationStatus deleteInternal(Locker locker, DatabaseEntry key)
        throws DatabaseException {

        Cursor cursor = null;
        try {
      cursor = new Cursor(this, locker, null);
      cursor.setNonCloning(true);
            OperationStatus commitStatus = OperationStatus.NOTFOUND;

            /* Position a cursor at the specified data record. */
            DatabaseEntry oldData = new DatabaseEntry();
            OperationStatus searchStatus =
                cursor.search(key, oldData, LockMode.RMW, SearchMode.SET);

            /* Delete all records with that key. */
            if (searchStatus == OperationStatus.SUCCESS) {
                do {

                    /*
                     * Notify triggers before the actual deletion so that a
                     * primary record never exists while secondary keys refer
                     * to it.  This is relied on by secondary read-uncommitted.
                     */
                    if (hasTriggers()) {
                        notifyTriggers(locker, key, oldData, null);
                    }
                    /* The actual deletion. */
                    commitStatus = cursor.deleteNoNotify();
                    if (commitStatus != OperationStatus.SUCCESS) {
                        return commitStatus;
                    }
                    /* Get another duplicate. */
                    if (databaseImpl.getSortedDuplicates()) {
                        searchStatus =
                            cursor.retrieveNext(key, oldData,
                                                LockMode.RMW,
                                                GetMode.NEXT_DUP);
                    } else {
                        searchStatus = OperationStatus.NOTFOUND;
                    }
                } while (searchStatus == OperationStatus.SUCCESS);
                commitStatus = OperationStatus.SUCCESS;
            }
            return commitStatus;
        } finally {
      if (cursor != null) {
    cursor.close();
      }
        }
    }

    public OperationStatus get(Transaction txn,
                               DatabaseEntry key,
                               DatabaseEntry data,
                               LockMode lockMode)
        throws DatabaseException {

        checkEnv();
        DatabaseUtil.checkForNullDbt(key, "key", true);
        DatabaseUtil.checkForNullDbt(data, "data", false);
        checkRequiredDbState(OPEN, "Can't call Database.get:");
        trace(Level.FINEST, "Database.get", txn, key, null, lockMode);

        CursorConfig cursorConfig = CursorConfig.DEFAULT;
        if (lockMode == LockMode.READ_COMMITTED) {
            cursorConfig = CursorConfig.READ_COMMITTED;
            lockMode = null;
        }

        Cursor cursor = null;
        try {
      cursor = new Cursor(this, txn, cursorConfig);
      cursor.setNonCloning(true);
            return cursor.search(key, data, lockMode, SearchMode.SET);
        } finally {
      if (cursor != null) {
    cursor.close();
      }
        }
    }

    public OperationStatus getSearchBoth(Transaction txn,
                                         DatabaseEntry key,
                                         DatabaseEntry data,
                                         LockMode lockMode)
        throws DatabaseException {

        checkEnv();
        DatabaseUtil.checkForNullDbt(key, "key", true);
        DatabaseUtil.checkForNullDbt(data, "data", true);
        checkRequiredDbState(OPEN, "Can't call Database.getSearchBoth:");
        trace(Level.FINEST, "Database.getSearchBoth", txn, key, data,
              lockMode);

        CursorConfig cursorConfig = CursorConfig.DEFAULT;
        if (lockMode == LockMode.READ_COMMITTED) {
            cursorConfig = CursorConfig.READ_COMMITTED;
            lockMode = null;
        }

        Cursor cursor = null;
        try {
      cursor = new Cursor(this, txn, cursorConfig);
      cursor.setNonCloning(true);
            return cursor.search(key, data, lockMode, SearchMode.BOTH);
        } finally {
      if (cursor != null) {
    cursor.close();
      }
        }
    }

    public OperationStatus put(Transaction txn,
                               DatabaseEntry key,
                               DatabaseEntry data)
        throws DatabaseException {

        checkEnv();
        DatabaseUtil.checkForNullDbt(key, "key", true);
        DatabaseUtil.checkForNullDbt(data, "data", true);
        DatabaseUtil.checkForPartialKey(key);
        checkRequiredDbState(OPEN, "Can't call Database.put");
        checkWritable("put");
        trace(Level.FINEST, "Database.put", txn, key, data, null);

        return putInternal(txn, key, data, PutMode.OVERWRITE);
    }

    public OperationStatus putNoOverwrite(Transaction txn,
                                          DatabaseEntry key,
                                          DatabaseEntry data)
        throws DatabaseException {

        checkEnv();
        DatabaseUtil.checkForNullDbt(key, "key", true);
        DatabaseUtil.checkForNullDbt(data, "data", true);
        DatabaseUtil.checkForPartialKey(key);
        checkRequiredDbState(OPEN, "Can't call Database.putNoOverWrite");
        checkWritable("putNoOverwrite");
        trace(Level.FINEST, "Database.putNoOverwrite", txn, key, data, null);

        return putInternal(txn, key, data, PutMode.NOOVERWRITE);
    }

    public OperationStatus putNoDupData(Transaction txn,
                                        DatabaseEntry key,
                                        DatabaseEntry data)
        throws DatabaseException {

        checkEnv();
        DatabaseUtil.checkForNullDbt(key, "key", true);
        DatabaseUtil.checkForNullDbt(data, "data", true);
        DatabaseUtil.checkForPartialKey(key);
        checkRequiredDbState(OPEN, "Can't call Database.putNoDupData");
        checkWritable("putNoDupData");
        trace(Level.FINEST, "Database.putNoDupData", txn, key, data, null);

        return putInternal(txn, key, data, PutMode.NODUP);
    }

    /**
     * Internal version of put() that does no parameter checking.
     */
    OperationStatus putInternal(Transaction txn,
                                DatabaseEntry key,
        DatabaseEntry data,
        PutMode putMode)
        throws DatabaseException {

        Locker locker = null;
        Cursor cursor = null;
        OperationStatus commitStatus = OperationStatus.KEYEXIST;
        try {
            locker = LockerFactory.getWritableLocker
    (envHandle, txn, isTransactional());

            cursor = new Cursor(this, locker, null);
      cursor.setNonCloning(true);
            commitStatus = cursor.putInternal(key, data, putMode);
            return commitStatus;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            if (locker != null) {
                locker.operationEnd(commitStatus);
            }
        }
    }

    /**
     */
    public JoinCursor join(Cursor[] cursors, JoinConfig config)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't call Database.join");
        DatabaseUtil.checkForNullParam(cursors, "cursors");
        if (cursors.length == 0) {
            throw new IllegalArgumentException
                    ("At least one cursor is required.");
        }

        /*
         * Check that all cursors use the same locker, if any cursor is
         * transactional.  And if non-transactional, that all databases are in
         * the same environment.
         */
        Locker locker = cursors[0].getCursorImpl().getLocker();
        if (!locker.isTransactional()) {
            EnvironmentImpl env = envHandle.getEnvironmentImpl();
            for (int i = 1; i < cursors.length; i += 1) {
                Locker locker2 = cursors[i].getCursorImpl().getLocker();
                if (locker2.isTransactional()) {
                    throw new IllegalArgumentException
                            ("All cursors must use the same transaction.");
                }
                EnvironmentImpl env2 = cursors[i].getDatabaseImpl()
                                                 .getDbEnvironment();
                if (env != env2) {
                    throw new IllegalArgumentException
                            ("All cursors must use the same environment.");
                }
            }
            locker = null; /* Don't reuse a non-transactional locker. */
        } else {
            for (int i = 1; i < cursors.length; i += 1) {
                Locker locker2 = cursors[i].getCursorImpl().getLocker();
                if (locker.getTxnLocker() != locker2.getTxnLocker()) {
                    throw new IllegalArgumentException
                            ("All cursors must use the same transaction.");
                }
            }
        }

        /* Create the join cursor. */
        return new JoinCursor(locker, this, cursors, config);
    }

    /**
     * @deprecated It has not been possible to implement this method with
     * correct transactional semantics without incurring a performance penalty
     * on all Database operations. Truncate functionality has been moved to
     * Environment.truncateDatabase(), which requires that all Database handles
     * on the database are closed before the truncate operation can execute.
     */
    public int truncate(Transaction txn, boolean countRecords)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't call Database.truncate");
        checkWritable("truncate");
        Tracer.trace(Level.FINEST,
                     envHandle.getEnvironmentImpl(),
                     "Database.truncate: txnId=" +
                     ((txn == null) ? "null" : Long.toString(txn.getId())));

        Locker locker = null;
        boolean triggerLock = false;
        boolean operationOk = false;

        try {
            locker = LockerFactory.getWritableLocker
                (envHandle, txn, isTransactional(), true /*retainLocks*/,
                 null);

            /*
       * Pass true to always get a read lock on the triggers, so we are
       * sure that no secondaries are added during truncation.
       */
            acquireTriggerListReadLock();
            triggerLock = true;

            /* Truncate primary. */
            int count = truncateInternal(locker, countRecords);

            /* Truncate secondaries. */
            for (int i = 0; i < triggerList.size(); i += 1) {
                Object obj = triggerList.get(i);
                if (obj instanceof SecondaryTrigger) {
                    SecondaryDatabase secDb = ((SecondaryTrigger) obj).getDb();
                    secDb.truncateInternal(locker, false);
                }
            }

            operationOk = true;
            return count;
        } finally {
            if (locker != null) {
                locker.operationEnd(operationOk);
            }
            if (triggerLock) {
                releaseTriggerListReadLock();
            }
        }
    }
           
    /**
     * Internal unchecked truncate that optionally counts records.
     * @deprecated
     */
    int truncateInternal(Locker locker, boolean countRecords)
        throws DatabaseException {

        if (databaseImpl == null) {
            throw new DatabaseException
                ("couldn't find database - truncate");
        }
        databaseImpl.checkIsDeleted("truncate");

        /*
         * Truncate must obtain a write lock. In order to do so, it assumes
         * ownership for the handle lock and transfers it from this Database
         * object to the txn.
         */
        if (handleLocker.isHandleLockTransferrable()) {
            handleLocker.transferHandleLock(this, locker, false);
        }

        boolean operationOk = false;
        try {

            /*
             * truncate clones the existing database and returns a new one to
             * replace it with.  The old databaseImpl object is marked
             * 'deleted'.
             */
            TruncateResult result =
                envHandle.getEnvironmentImpl().truncate(locker, databaseImpl);
            databaseImpl = result.getDatabase();

            operationOk = true;
            return countRecords ? result.getRecordCount() : -1;
        } finally {

            /*
             * The txn will know if it's living past the end of this operation,
             * and if it needs to transfer the handle lock.  operationEnd()
             * will be called one level up by the public truncate() method.
             */
            locker.setHandleLockOwner(operationOk, this, false);
        }
    }

    /*
     * @deprecated As of JE 2.0.55, replaced by
     * {@link Database#preload(PreloadConfig)}.
     */
    public void preload(long maxBytes)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't call Database.preload");
        databaseImpl.checkIsDeleted("preload");

  PreloadConfig config = new PreloadConfig();
  config.setMaxBytes(maxBytes);
        databaseImpl.preload(config);
    }

    /*
     * @deprecated As of JE 2.1.1, replaced by
     * {@link Database#preload(PreloadConfig)}.
     */
    public void preload(long maxBytes, long maxMillisecs)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't call Database.preload");
        databaseImpl.checkIsDeleted("preload");

  PreloadConfig config = new PreloadConfig();
  config.setMaxBytes(maxBytes);
  config.setMaxMillisecs(maxMillisecs);
        databaseImpl.preload(config);
    }

    public PreloadStats preload(PreloadConfig config)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't call Database.preload");
        databaseImpl.checkIsDeleted("preload");

        return databaseImpl.preload(config);
    }

    public DatabaseStats getStats(StatsConfig config)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't call Database.stat");
        StatsConfig useConfig =
            (config == null) ? StatsConfig.DEFAULT : config;

        if (databaseImpl != null) {
            databaseImpl.checkIsDeleted("stat");
            return databaseImpl.stat(useConfig);
        }
        return null;
    }

    public DatabaseStats verify(VerifyConfig config)
        throws DatabaseException {

        checkEnv();
        checkRequiredDbState(OPEN, "Can't call Database.verify");
        databaseImpl.checkIsDeleted("verify");
        VerifyConfig useConfig =
            (config == null) ? VerifyConfig.DEFAULT : config;

        DatabaseStats stats = databaseImpl.getEmptyStats();
        databaseImpl.verify(useConfig, stats);
        return stats;
    }

    public String getDatabaseName()
        throws DatabaseException {

        checkEnv();
        if (databaseImpl != null) {
            return databaseImpl.getName();
        } else {
            return null;
        }
    }

    /*
     * Non-transactional database name, safe to access when creating error
     * messages.
     */
    String getDebugName() {
        if (databaseImpl != null) {
            return databaseImpl.getDebugName();
        } else {
            return null;
        }
    }

    public DatabaseConfig getConfig()
        throws DatabaseException {

        DatabaseConfig showConfig = configuration.cloneConfig();

        /*
         * Set the comparators from the database impl, they might have
         * changed from another handle.
         */
  Comparator btComp =
      (databaseImpl == null ?
       null :
       databaseImpl.getBtreeComparator());
  Comparator dupComp =
      (databaseImpl == null ?
       null :
       databaseImpl.getDuplicateComparator());
        showConfig.setBtreeComparator
      (btComp == null ? null : btComp.getClass());
        showConfig.setDuplicateComparator
      (dupComp == null ? null: dupComp.getClass());
        return showConfig;
    }

    /**
     * Equivalent to getConfig().getTransactional() but cheaper.
     */
    boolean isTransactional()
        throws DatabaseException {

        return databaseImpl.isTransactional();
    }

    public Environment getEnvironment()
        throws DatabaseException {

        return envHandle;
    }

    public List getSecondaryDatabases()
        throws DatabaseException {

        List list = new ArrayList();
        if (hasTriggers()) {
            acquireTriggerListReadLock();
            try {
                for (int i = 0; i < triggerList.size(); i += 1) {
                    Object obj = triggerList.get(i);
                    if (obj instanceof SecondaryTrigger) {
                        list.add(((SecondaryTrigger) obj).getDb());
                    }
                }
            } finally {
                releaseTriggerListReadLock();
            }
        } else {
        }
        return list;
    }

    /*
     * Helpers, not part of the public API
     */

    /**
     * @return true if the Database was opened read/write.
     */
    boolean isWritable() {
        return isWritable;
    }

    /**
     * Return the databaseImpl object instance.
     */
    DatabaseImpl getDatabaseImpl() {
        return databaseImpl;
    }

    /**
     * The handleLocker is the one that holds the db handle lock.
     */
    void setHandleLocker(Locker locker) {
        handleLocker = locker;
    }

    synchronized void removeCursor(Cursor dbc) {
        cursors.remove(dbc);
    }

    synchronized void addCursor(Cursor dbc) {
        cursors.add(dbc);
    }

    /**
     * @throws DatabaseException if the Database state is not this value.
     */
    void checkRequiredDbState(DbState required, String msg)
        throws DatabaseException {

        if (state != required) {
            throw new DatabaseException
                (msg + " Database state can't be " + state +
                 " must be " + required);
        }
    }

    /**
     * @throws DatabaseException if the Database state is this value.
     */
    void checkProhibitedDbState(DbState prohibited, String msg)
        throws DatabaseException {

        if (state == prohibited) {
            throw new DatabaseException
                (msg + " Database state must not be " + prohibited);
        }
    }

    /**
     * @throws RunRecoveryException if the underlying environment is
     * invalid
     */
    void checkEnv()
        throws RunRecoveryException {

        EnvironmentImpl env = envHandle.getEnvironmentImpl();
        if (env != null) {
            env.checkIfInvalid();
        }
    }

    /**
     * Invalidate the handle, called by txn.abort by way of DbInternal.
     */
    synchronized void invalidate() {
        state = INVALID;
        envHandle.removeReferringHandle(this);
        if (databaseImpl != null) {
            databaseImpl.removeReferringHandle(this);
        }
    }

    /**
     * Check that write operations aren't used on a readonly Database.
     */
    private void checkWritable(String operation)
        throws DatabaseException {

        if (!isWritable) {
            throw new DatabaseException
                ("Database is Read Only: " + operation);
        }
    }

    /**
     * Send trace messages to the java.util.logger. Don't rely on the logger
     * alone to conditionalize whether we send this message, we don't even want
     * to construct the message if the level is not enabled.
     */
    void trace(Level level,
               String methodName,
               Transaction txn,
               DatabaseEntry key,
               DatabaseEntry data,
               LockMode lockMode)
        throws DatabaseException {

        if (logger.isLoggable(level)) {
            StringBuffer sb = new StringBuffer();
            sb.append(methodName);
            if (txn != null) {
                sb.append(" txnId=").append(txn.getId());
            }
            sb.append(" key=").append(key.dumpData());
            if (data != null) {
                sb.append(" data=").append(data.dumpData());
            }
            if (lockMode != null) {
                sb.append(" lockMode=").append(lockMode);
            }
            logger.log(level, sb.toString());
        }
    }

    /**
     * Send trace messages to the java.util.logger. Don't rely on the logger
     * alone to conditionalize whether we send this message, we don't even want
     * to construct the message if the level is not enabled.
     */
    void trace(Level level,
               String methodName,
               Transaction txn,
               CursorConfig config)
        throws DatabaseException {

        if (logger.isLoggable(level)) {
            StringBuffer sb = new StringBuffer();
            sb.append(methodName);
            sb.append(" name=" + getDebugName());
            if (txn != null) {
                sb.append(" txnId=").append(txn.getId());
            }
            if (config != null) {
                sb.append(" config=").append(config);
            }
            logger.log(level, sb.toString());
        }
    }

    /*
     * Manage triggers.
     */

    /**
     * Returns whether any triggers are currently associated with this primary.
     * Note that an update of the trigger list may be in progress and this
     * method does not wait for that update to be completed.
     */
    boolean hasTriggers() {

        return triggerList != null;
    }

    /**
     * Gets a read-lock on the list of triggers.  releaseTriggerListReadLock()
     * must be called to release the lock.  Called by all primary put and
     * delete operations.
     */
    private void acquireTriggerListReadLock()
        throws DatabaseException {

        EnvironmentImpl env = envHandle.getEnvironmentImpl();
        env.getTriggerLatch().acquireShared();
        if (triggerList == null) {
            triggerList = new ArrayList();
        }
    }

    /**
     * Releases a lock acquired by calling acquireTriggerListReadLock().
     */
    private void releaseTriggerListReadLock()
        throws DatabaseException {

        EnvironmentImpl env = envHandle.getEnvironmentImpl();
        env.getTriggerLatch().release();
    }

    /**
     * Gets a write lock on the list of triggers.  An empty list is created if
     * necessary, so null is never returned.  releaseTriggerListWriteLock()
     * must always be called to release the lock.
     */
    private void acquireTriggerListWriteLock()
        throws DatabaseException {

        EnvironmentImpl env = envHandle.getEnvironmentImpl();
        env.getTriggerLatch().acquireExclusive();
        if (triggerList == null) {
            triggerList = new ArrayList();
        }
    }

    /**
     * Releases a lock acquired by calling acquireTriggerListWriteLock().  If
     * the list is now empty then it is set to null, that is, hasTriggers()
     * will subsequently return false.
     */
    private void releaseTriggerListWriteLock()
        throws DatabaseException {

        if (triggerList.size() == 0) {
            triggerList = null;
        }
        EnvironmentImpl env = envHandle.getEnvironmentImpl();
        env.getTriggerLatch().release();
    }

    /**
     * Adds a given trigger to the list of triggers.  Called while opening
     * a SecondaryDatabase.
     *
     * @param insertAtFront true to insert at the front, or false to append.
     */
    void addTrigger(DatabaseTrigger trigger, boolean insertAtFront)
        throws DatabaseException {

        acquireTriggerListWriteLock();
        try {
            if (insertAtFront) {
                triggerList.add(0, trigger);
            } else {
                triggerList.add(trigger);
            }
            trigger.triggerAdded(this);
        } finally {
            releaseTriggerListWriteLock();
        }
    }

    /**
     * Removes a given trigger from the list of triggers.  Called by
     * SecondaryDatabase.close().
     */
    void removeTrigger(DatabaseTrigger trigger)
        throws DatabaseException {

        acquireTriggerListWriteLock();
        try {
            triggerList.remove(trigger);
            trigger.triggerRemoved(this);
        } finally {
            releaseTriggerListWriteLock();
        }
    }

    /**
     * Clears the list of triggers.  Called by close(), this allows closing the
     * primary before its secondaries, although we document that secondaries
     * should be closed first.
     */
    private void removeAllTriggers()
        throws DatabaseException {

        acquireTriggerListWriteLock();
        try {
            for (int i = 0; i < triggerList.size(); i += 1) {
                DatabaseTrigger trigger = (DatabaseTrigger) triggerList.get(i);
                trigger.triggerRemoved(this);
            }
            triggerList.clear();
        } finally {
            releaseTriggerListWriteLock();
        }
    }

    /**
     * Notifies associated triggers when a put() or delete() is performed on
     * the primary.  This method is normally called only if hasTriggers() has
     * returned true earlier.  This avoids acquiring a shared latch for
     * primaries with no triggers.  If a trigger is added during the update
     * process, there is no requirement to immediately start updating it.
     *
     * @param locker the internal locker.
     *
     * @param priKey the primary key.
     *
     * @param oldData the primary data before the change, or null if the record
     * did not previously exist.
     *
     * @param newData the primary data after the change, or null if the record
     * has been deleted.
     */
    void notifyTriggers(Locker locker,
                        DatabaseEntry priKey,
                        DatabaseEntry oldData,
                        DatabaseEntry newData)
        throws DatabaseException {

        acquireTriggerListReadLock();
        try {
            for (int i = 0; i < triggerList.size(); i += 1) {
                DatabaseTrigger trigger = (DatabaseTrigger) triggerList.get(i);

                /* Notify trigger. */
                trigger.databaseUpdated
                    (this, locker, priKey, oldData, newData);
            }
        } finally {
            releaseTriggerListReadLock();
        }
    }
}
TOP

Related Classes of com.sleepycat.je.Database$DbState

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.