All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.io.bdb.BerkeleyDBBinaryStore Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.io.bdb;


import com.tangosol.io.AbstractBinaryStore;

import com.tangosol.util.Base;
import com.tangosol.util.Binary;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.OperationStatus;

import java.util.Iterator;
import java.util.NoSuchElementException;


/**
* An implementation of the BinaryStore interface using Sleepycat Berkeley
* DB Java Edition.
*
* @see Berkeley DB
* JE JavaDoc
*
* @author mf 2005.09.29
*/
public class BerkeleyDBBinaryStore
       extends AbstractBinaryStore
    {
    // ----- constructors ---------------------------------------------------

    /**
    * Create a new Berkeley DB BinaryStore using the supplied DatabaseFactory.
    *
    * @param sDbName    the name of the table to store the cache's data in,
    *                    null indicates a temporary table name.
    * @param dbFactory  the factory to use to create the Database
    *
    * @throws DatabaseException if the Database creation failed
    */
    public BerkeleyDBBinaryStore(String sDbName, DatabaseFactory dbFactory)
            throws DatabaseException
        {
        azzert(dbFactory != null);

        init(sDbName, dbFactory);
        }

    /**
    * Initialize the BinaryStore.
    *
    * @param sDbName    the name of the table to store the cache's data in,
    *                    null indicates a temporary table name.
    * @param dbFactory  the factory to use to create the Database
    *
    * @throws DatabaseException if the Database creation failed
    */
    protected void init(String sDbName, DatabaseFactory dbFactory)
            throws DatabaseException
        {
        m_factory = dbFactory;
        m_db      = new DatabaseHolder(sDbName);
        }


    // ----- Object methods -------------------------------------------------

    /**
    * Return a human readable description of the BinaryStore.
    *
    * @return a human readable description of the BinaryStore
    */
    public String toString()
        {
        return "BerkeleyDBBinaryStore {" + " DB Holder: " + getDbHolder() +
                " Environment Directory: " +
                m_factory.getEnvHolder().getDirEnv() + '}';
        }

    // ----- BinaryStore interface ------------------------------------------

   /**
   * Return the value associated with the specified key, or null if the key
   * does not have an associated value in the underlying store.
   *
   * @param binKey key whose associated value is to be returned
   *
   * @return the value associated with the specified key, or null
   *          if no value is available for that key
   */
    public Binary load(Binary binKey)
        {
        DatabaseEntry  dbeKey   = new DatabaseEntry(binKey.toByteArray());
        DatabaseEntry  dbeValue = new DatabaseEntry();
        DatabaseHolder db       = getDbHolder();

        try
            {
            OperationStatus status = db.getDb().get(null /*tx*/,
                                                    dbeKey,
                                                    dbeValue,
                                                    null /* default lock mode */);
            if (status == OperationStatus.SUCCESS)
                {
                return new Binary(dbeValue.getData());
                }
            else if (status == OperationStatus.NOTFOUND)
                {
                // no value found
                return null;
                }
            else
                {
                // check if the DB was swapped from beneath us, if so
                // retry the operation, otherwise raise an exception
                throw new IllegalStateException(
                        "Unexpected status result from Berkeley DB load, status="
                                + status);
                }
            }
        catch (DatabaseException e)
            {
            // check if the DB was swapped from beneath us, if so
            // retry the operation, otherwise raise an exception
            throw Base.ensureRuntimeException(e, "Berkeley DB load operation failed");
            }
        }

    /**
    * Store the specified value under the specific key in the underlying store.
    * 

* This method is supports both key/value creation and value update for a * specific key. * * @param binKey key to store the value under * @param binValue value to be stored */ public void store(Binary binKey, Binary binValue) { DatabaseEntry dbeKey = new DatabaseEntry(binKey.toByteArray()); DatabaseEntry dbeValue = new DatabaseEntry(binValue.toByteArray()); DatabaseHolder db = getDbHolder(); try { OperationStatus status = db.getDb().put(null /*tx*/, dbeKey, dbeValue); if (status != OperationStatus.SUCCESS) { throw new IllegalStateException( "Unexpected Berkeley DB status result while storing an entry, status=" + status); } } catch (DatabaseException e) { throw Base.ensureRuntimeException(e, "Berkeley DB store operation failed."); } } /** * Remove the specified key from the underlying store if present. * * @param binKey key whose mapping is to be removed from the map */ public void erase(Binary binKey) { DatabaseEntry dbeKey = new DatabaseEntry(binKey.toByteArray()); DatabaseHolder db = getDbHolder(); try { OperationStatus status = db.getDb().delete(null /*tx*/, dbeKey); if (status != OperationStatus.SUCCESS && status != OperationStatus.NOTFOUND) { throw new IllegalStateException( "Unexpected Berkeley DB status result while deleting an entry, status=" + status); } } catch (DatabaseException e) { throw Base.ensureRuntimeException(e, "Berkeley DB delete operation failed."); } } /** * Remove all data from the underlying store. */ public void eraseAll() { if (m_db.isTemporary()) { // temporary databases can be quickly cleared by simply // switching to a new database reference // the old data will be deleted as part of the holder's // finalization try { m_db = new DatabaseHolder(null /*dbName*/); // let GC cleanup the old DB, see DatabaseHolder.finalize } catch (DatabaseException e) { // failed to create a new DB, thus our logical // eraseAll operation has failed, i.e. no data // will be cleared throw Base.ensureRuntimeException(e, "Failed to allocate a new Berkeley DB as part of the eraseAll operation."); } } else { // persistent databases cannot be deleted by simply swapping // the db reference, instead we must do a deletion of // all the keys, preserving the current DB reference try { DatabaseHolder db = getDbHolder(); Cursor cursor = null; try { cursor = db.getDb().openCursor(null /*tx*/, CursorConfig.READ_UNCOMMITTED); OperationStatus status; DatabaseEntry dbeTmp = new DatabaseEntry(); while (true) { status = cursor.getNext(dbeTmp, dbeTmp, null /*default lock mode*/); if (status == OperationStatus.NOTFOUND) { // end of cursor break; } else if (status == OperationStatus.SUCCESS) { status = cursor.delete(); if (status != OperationStatus.SUCCESS) { throw new IllegalStateException( "Unexpected Berkeley DB status result while performing eraseAll, status=" + status); } } else { throw new IllegalStateException( "Unexpected Berkeley DB status result while performing eraseAll, status=" + status); } } } finally { if (cursor != null) { cursor.close(); } } } catch (DatabaseException e) { throw Base.ensureRuntimeException(e, "Error while performing eraseAll on Berkeley DB."); } } } /** * Iterate all keys in the underlying store. * * @return a read-only iterator of the keys in the underlying store */ public Iterator keys() { try { final DatabaseHolder db = getDbHolder(); final Cursor cursor = db.getDb().openCursor(null /*tx*/, CursorConfig.READ_UNCOMMITTED); // return an Iterator which is based by a DB cursor return new Iterator() { public boolean hasNext() { return m_binNext != null; } public Object next() { Binary binKey = m_binNext; if (binKey == null) { throw new NoSuchElementException(); } m_binNext = readNext(); return binKey; } public void remove() { throw new UnsupportedOperationException(); } private Binary readNext() { byte[] abKey = null; try { DatabaseEntry dbeKey = m_dbeKey; DatabaseEntry dbeValue = m_dbeValue; // avoid reading the value from disk onto heap dbeValue.setPartial(0, 0, true); OperationStatus status = cursor.getNext(dbeKey, m_dbeValue, null /*default lock mode*/); if (status == OperationStatus.SUCCESS) { abKey = dbeKey.getData(); } } catch (DatabaseException e) {} // on error, or DB change trigger iteration end if (abKey == null || db != getDbHolder()) { invalidate(); return null; } return new Binary(abKey); } private void invalidate() { try { cursor.close(); } catch (Throwable e) {} } protected void finalize() throws Throwable { invalidate(); super.finalize(); } private DatabaseEntry m_dbeKey = new DatabaseEntry(); private DatabaseEntry m_dbeValue = new DatabaseEntry(); private Binary m_binNext = readNext(); }; } catch (DatabaseException e) { throw Base.ensureRuntimeException(e); } } // ----- methods -------------------------------------------------------- /** * Close the BinaryStore. */ public void close() { m_db = null; // let the Java GC do the cleanup, see DatabaseHolder.finalize } // ----- accessor methods ----------------------------------------------- /** * Get the DatabaseHolder. * To prevent the underlying Database from being deleted during usage, hold * onto this handle while using the Database object. * * @return the DatabaseHolder */ public DatabaseHolder getDbHolder() { DatabaseHolder db = m_db; if (db == null) { throw new IllegalStateException( "The Berkeley DB BinaryStore has been closed."); } return db; } /** * Get the DatabaseFactory used to create the underlying Database. * * @return the DatabaseFactory */ public DatabaseFactory getFactory() { return m_factory; } // ----- data members --------------------------------------------------- /** * The Database handle. */ protected volatile DatabaseHolder m_db; /** * Factory used to create this Database. */ protected DatabaseFactory m_factory; // ----- inner class: DatabaseHolder ------------------------------------ /** * The DatabaseHolder class is used as a wrapper around * a Berkeley DB Database object. *

* Database objects cannot be closed if they are in use by other threads. * The Java garbage collector and this holder are utilized to delay * closing the Database until it is guaranteed to not be in use. */ protected class DatabaseHolder { // ----- constructors ------------------------------------------- /** * Construct a DatabaseHolder, including a Database. * * @param sDbName if non null specifies the name of a persistent * database. */ public DatabaseHolder(String sDbName) throws DatabaseException { DatabaseFactory factory = BerkeleyDBBinaryStore.this.getFactory(); if (sDbName == null) { sDbName = factory.getUniqueDbName(); m_fTemporary = true; } else { m_fTemporary = false; } m_db = factory.createNamedDatabase(sDbName); m_sDbName = sDbName; // COH-1176 } // ----- Object methods ----------------------------------------- /** * Return the Holder's human readable description. * * @return the Holder's human readable description */ public String toString() { return "DatabaseHolder {" + "DB Name: " + getName() + " Temporary: " + isTemporary() + '}'; } /** * Finalize the holder, deleting the database if it is temporary. * * @throws Throwable if the removal fails */ protected void finalize() throws Throwable { closeDb(); super.finalize(); } // ----- accessor methods --------------------------------------- /** * Get the underlying Database handle. * * @return the Database handle */ public Database getDb() { return m_db; } /** * Get the name of the underlying Database. * * @return the Database name */ public String getName() { return m_sDbName; } /** * Return if the database is temporary. * * @return true if the database is temporary */ public boolean isTemporary() { return m_fTemporary; } // ----- helper methods ------------------------------------------ /** * Close the Database. *

* If this is a temporary, or empty persistent DB, then it will * be deleted. */ protected void closeDb() throws DatabaseException { Database db = m_db; // instruct the factory to stop tracking this DB handle BerkeleyDBBinaryStore.this.getFactory().forgetDatabase(db); boolean fRemove; if (isTemporary()) { fRemove = true; } else { // persistent store // if empty delete it, otherwise flush the data DatabaseEntry dbEntry = new DatabaseEntry(); Cursor csr = db.openCursor(null /*tx*/, CursorConfig.READ_UNCOMMITTED); OperationStatus status = csr.getNext(dbEntry, dbEntry, null /*lock mode*/); csr.close(); // if empty, then mark for removal fRemove = (status == OperationStatus.NOTFOUND); } if (fRemove) { // close DB & delete the from the environment Environment env = db.getEnvironment(); db.close(); env.removeDatabase(null/*tx*/, m_sDbName); } else { // flush the persistent database db.close(); } } // ----- data members ------------------------------------------- /** * The underlying Database Handle. */ protected Database m_db; /** * The name of the underlying Database. *

* The name is maintained externally from the Database as calls to * Database.getDatabaseName() are costly. */ protected String m_sDbName; /** * Flag indicating if the database is temporary. *

* Temporary databases are automatically deleted on shutdown or GC. */ protected boolean m_fTemporary; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy