Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package com.fasterxml.storemate.backend.bdbje;
  
  import java.io.File;
  
  import com.sleepycat.je.*;
  
 
com.fasterxml.storemate.store.backend.StoreBackend implementation that builds on BDB-JE. Note that per-entry locking is assumed to be provided by caller; no attempt is made to synchronize individual operations at store level.
 
 public class BDBJEStoreBackend extends StoreBackend
 {
     private final BDBConverter BDB_CONV = new BDBConverter();
     
     /*
     /**********************************************************************
     /* Simple config, location
     /**********************************************************************
      */
 
     protected final File _dataRoot;
 
     /*
     /**********************************************************************
     /* BDB entities
     /**********************************************************************
      */

    
Underlying primary BDB-JE database
 
     protected final Database _entries;

    
Secondary database that tracks last-modified order of primary entries.
 
     protected final SecondaryDatabase _index;
 
     /*
     /**********************************************************************
     /* Life-cycle
     /**********************************************************************
      */
     
     public BDBJEStoreBackend(StorableConverter conv,
             File dbRootDatabase entryDBSecondaryDatabase lastModIndex)
     {
         super(conv);
          = dbRoot;
          = entryDB;
          = lastModIndex;
     }
 
     @Override
     public void start() {
         // nothing to do, yet
     }
 
     @Override
     public void prepareForStop() {
         // We don't have to do much; esp. since sync() can not be used when writes
         // are not deferred...
         // TODO: allow defer()ed writes?
 //        _entries.sync();
 //        _index.sync();
     }
 
     @Override
     public void stop()
     {
         Environment env = .getEnvironment();
         .close();
         .close();
         env.close();
     }
 
     /*
     /**********************************************************************
     /* Capability, statistics introspection
     /**********************************************************************
      */

    
Yes, BDB-JE can produce efficient entry count.
    public boolean hasEfficientEntryCount() { return true; }

    
Yes, BDB-JE can produce efficient index entry count.
    public boolean hasEfficientIndexCount() { return true; }
    @Override
        return _getStats(config);
    }
    @Override
        return _getStats(config);
    }
    protected DatabaseStats _getStats(Database dbBackendStatsConfig config) {
        StatsConfig statsConfig = new StatsConfig()
            .setFast(config.onlyCollectFast())
            .setClear(config.resetStatsAfterCollection())
            ;
        return db.getStats(statsConfig);
    }
    /*
    /**********************************************************************
    /* API Impl, metadata
    /**********************************************************************
     */
    @Override
    public long getEntryCount() {
        return .count();
    }
    @Override
    public long getIndexedCount() {
        return .count();
    }
    @Override
    public long countEntries() throws StoreException
    {
	long count = 0L;
        try {
            DiskOrderedCursorConfig config = new DiskOrderedCursorConfig();
            DiskOrderedCursor crsr = .openCursor(config);
    
            final DatabaseEntry keyEntry = new DatabaseEntry();
            final DatabaseEntry data = new DatabaseEntry();
            try {
                while (crsr.getNext(keyEntrydatanull) == .) {
                    ++count;
                }
                return count;
            } finally {
                crsr.close();
            }
        } catch (DatabaseException de) {
            _convertDBE(nullde);
	    return count;
        }
    }
    @Override
    public long countIndexed() throws StoreException
    {
	long count = 0L;
        try {
            SecondaryCursor crsr = .openCursor(nullnew CursorConfig());
            final DatabaseEntry keyEntry = new DatabaseEntry();
            final DatabaseEntry primaryKeyEntry = new DatabaseEntry();
            final DatabaseEntry data = new DatabaseEntry();
            try {
                OperationStatus status = crsr.getFirst(keyEntryprimaryKeyEntrydatanull);
                for (; status == .status = crsr.getNext(keyEntryprimaryKeyEntrydatanull)) {
                    ++count;
                }
                return count;
            } finally {
                crsr.close();
            }
        } catch (DatabaseException de) {
            _convertDBE(nullde);
	    return count;
        }
    }
    /*
    /**********************************************************************
    /* API Impl, read
    /**********************************************************************
     */
    @Override
    public boolean hasEntry(StorableKey keythrows StoreException
    {
        try {
            OperationStatus status = .get(nulldbKey(key), new DatabaseEntry(), null);
            switch (status) {
            case :
            case :
                return true;
            case // was deleted during operation.. shouldn't be getting
            case :
                // fall through
            }
            return false;
        } catch (DatabaseException de) {
            _convertDBE(keyde);
            return false// stupid javac; some versions can coerce, others not
        }
    }
        
    @Override
    public Storable findEntry(StorableKey keythrows StoreException
    {
        DatabaseEntry result = new DatabaseEntry();
        try {
            OperationStatus status = .get(nulldbKey(key), resultnull);
            if (status != .) {
                return null;
            }
            return .decode(keyresult.getData(), result.getOffset(), result.getSize());
        } catch (DatabaseException de) {
            return _convertDBE(keyde);
        }
    }
    /*
    /**********************************************************************
    /* API Impl, insert/update
    /**********************************************************************
     */
    @Override
    public Storable createEntry(StorableKey keyStorable storable)
        throws IOExceptionStoreException
    {
        DatabaseEntry dbKey = dbKey(key);
        try {
            // first, try creating:
            OperationStatus status = .putNoOverwrite(nulldbKeydbValue(storable));
            if (status == .) { // the usual case:
                return null;
            }
            if (status != .) { // what?
                throw new StoreException.Internal(key"Internal error, strange return value for 'putNoOverwrite()': "+status);
            }
            // otherwise, ought to find existing entry, return it
            DatabaseEntry result = new DatabaseEntry();
            status = .get(nulldbKeyresultnull);
            if (status != .) { // sanity check, should never occur:
                throw new StoreException.Internal(key"Internal error, failed to access old value, status: "+status);
            }
            return .decode(keyresult.getData(), result.getOffset(), result.getSize());
        } catch (DatabaseException de) {
            return _convertDBE(keyde);
        }
    }
    @Override
    public Storable putEntry(StorableKey keyStorable storable)
        throws IOExceptionStoreException
    {
        try {
            DatabaseEntry dbKey = dbKey(key);
            DatabaseEntry result = new DatabaseEntry();
            // First: do we have an entry? If so, read to be returned
            OperationStatus status = .get(nulldbKeyresultnull);
            if (status != .) {
                result = null;
            }
            // if not, create
            status = .put(nulldbKeydbValue(storable));
            if (status != .) {
                throw new StoreException.Internal(key"Failed to put entry, OperationStatus="+status);
            }
            if (result == null) {
                return null;
            }
            return .decode(keyresult.getData(), result.getOffset(), result.getSize());
        } catch (DatabaseException de) {
            return _convertDBE(keyde);
        }
    }
    @Override
    public void ovewriteEntry(StorableKey keyStorable storable)
        throws IOExceptionStoreException
    {
        try {
            OperationStatus status = .put(nulldbKey(key), dbValue(storable));
            if (status != .) {
                throw new StoreException.Internal(key"Failed to overwrite entry, OperationStatus="+status);
            }
        } catch (DatabaseException de) {
            _convertDBE(keyde);
        }
    }
    @Override
    public boolean upsertEntry(StorableKey keyStorable storable,
            OverwriteChecker checkerAtomicReference<StorableoldEntryRef)
        throws IOExceptionStoreException
    {
        try {
            DatabaseEntry dbKey = dbKey(key);
            DatabaseEntry result = new DatabaseEntry();
            // First: do we have an entry?
            OperationStatus status = .get(nulldbKeyresultnull);
            if (status == .) {
                // yes: is it ok to overwrite?
                Storable old = .decode(keyresult.getData(), result.getOffset(), result.getSize());
                if (oldEntryRef != null) {
                    oldEntryRef.set(old);
                }
                if (!checker.mayOverwrite(keyoldstorable)) {
                    // no, return
                    return false;
                }
            } else {
                if (oldEntryRef != null) {
                    oldEntryRef.set(null);
                }
            }
            // Ok we are good, go ahead:
            status = .put(nulldbKeydbValue(storable));
            if (status != .) {
                throw new StoreException.Internal(key"Failed to put entry, OperationStatus="+status);
            }
            return true;
        } catch (DatabaseException de) {
            _convertDBE(keyde);
            return false// stupid javac; some versions can coerce, others not
        }
    }
    
    /*
    /**********************************************************************
    /* API Impl, delete
    /**********************************************************************
     */
    @Override
    public boolean deleteEntry(StorableKey key)
        throws IOExceptionStoreException
    {
        try {
            OperationStatus status = .delete(nulldbKey(key));
            switch (status) {
            case :
                return true;
            case :
                return false;
            default:
                // should not be getting other choices so:
                throw new StoreException.Internal(key"Internal error, failed to delete entry, OperationStatus="+status);
            }
        } catch (DatabaseException de) {
            _convertDBE(keyde);
            return false// stupid javac; some versions can coerce, others not
        }
    }
    /*
    /**********************************************************************
    /* API Impl, iteration
    /**********************************************************************
     */
    @Override
        throws StoreException
    {
        try {
            DiskOrderedCursorConfig config = new DiskOrderedCursorConfig();
            DiskOrderedCursor crsr = .openCursor(config);
    
            final DatabaseEntry keyEntry = new DatabaseEntry();
            final DatabaseEntry data = new DatabaseEntry();
            
            try {
                main_loop:
                while (crsr.getNext(keyEntrydatanull) == .) {
                    StorableKey key = storableKey(keyEntry);
                    switch (cb.verifyKey(key)) {
                    case // nothing to do
                        continue main_loop;
                    case // bind, process
                        break;
                    case // all done?
                        return .;
                    }
                    Storable entry = .decode(keydata.getData(), data.getOffset(), data.getSize());
                    if (cb.processEntry(entry) == .) {
                        return .;
                    }
                }
                return .;
            } finally {
                crsr.close();
            }
        } catch (DatabaseException de) {
            return _convertDBE(nullde);
        }
    }
    @Override
            StorableKey firstKey)
        throws StoreException
    {
        try {
            CursorConfig config = new CursorConfig();
            Cursor crsr = .openCursor(nullconfig);
            final DatabaseEntry keyEntry;
            final DatabaseEntry data = new DatabaseEntry();
    
            OperationStatus status;
            if (firstKey == null) { // from beginning (i.e. no ranges)
                keyEntry = new DatabaseEntry();
                status = crsr.getFirst(keyEntrydatanull);
            } else {
                keyEntry = dbKey(firstKey);
                status = crsr.getSearchKeyRange(keyEntrydatanull);
            }
            try {
                main_loop:
                for (; status == .status = crsr.getNext(keyEntrydatanull)) {
                    StorableKey key = storableKey(keyEntry);
                    switch (cb.verifyKey(key)) {
                    case // nothing to do
                        continue main_loop;
                    case // bind, process
                        break;
                    case // all done?
                        return .;
                    }
                    Storable entry = .decode(keydata.getData(), data.getOffset(), data.getSize());
                    if (cb.processEntry(entry) == .) {
                        return .;
                    }
                    
                }
                return .;
            } finally {
                crsr.close();
            }
        } catch (DatabaseException de) {
            return _convertDBE(nullde);
        }
    }
    @Override
            StorableKey lastSeen)
        throws StoreException
    {
        try {
            Cursor crsr = .openCursor(nullnew CursorConfig());
    
            try {
                final DatabaseEntry data = new DatabaseEntry();
                final DatabaseEntry keyEntry = dbKey(lastSeen);
                OperationStatus status = crsr.getSearchKeyRange(keyEntrydatanull);
                do { // bogus loop so we can break
                    if (status != .) { // if it was the very last entry in store?
                        break;
                    }
                    // First, did we find the entry (should, but better safe than sorry)
                    byte[] b = keyEntry.getData();
                    if (lastSeen.equals(bkeyEntry.getOffset(), keyEntry.getSize())) { // yes, same thingy
                        status = crsr.getNext(keyEntrydatanull);
                        if (status != .) {
                            break;
                        }
                    }
                    main_loop:
                    for (; status == .status = crsr.getNext(keyEntrydatanull)) {
                        StorableKey key = storableKey(keyEntry);
                        switch (cb.verifyKey(key)) {
                        case // nothing to do
                            continue main_loop;
                        case // bind, process
                            break;
                        case // all done?
                            return .;
                        }
                        Storable entry = .decode(keydata.getData(), data.getOffset(), data.getSize());
                        if (cb.processEntry(entry) == .) {
                            return .;
                        }
                        
                    }
                } while (false);
                return .;
            } finally {
                crsr.close();
            }
        } catch (DatabaseException de) {
            return _convertDBE(nullde);
        }
    }
    
    @Override
            long firstTimestamp)
        throws StoreException
    {
        if (cb == null) {
            throw new IllegalArgumentException("Can not pass null 'cb' argument");
        }
        try {
            CursorConfig config = new CursorConfig();
            SecondaryCursor crsr = .openCursor(nullconfig);
            final DatabaseEntry keyEntry;
            final DatabaseEntry primaryKeyEntry = new DatabaseEntry();
            final DatabaseEntry data = new DatabaseEntry();
            
            OperationStatus status;
            if (firstTimestamp <= 0L) { // from beginning (i.e. no ranges)
                keyEntry = new DatabaseEntry();
                status = crsr.getFirst(keyEntryprimaryKeyEntrydatanull);
            } else {
                keyEntry = timestampKey(firstTimestamp);
                status = crsr.getSearchKeyRange(keyEntryprimaryKeyEntrydatanull);
            }
            
            try {
                main_loop:
                for (; status == .status = crsr.getNext(keyEntryprimaryKeyEntrydatanull)) {
                    // First things first: timestamp check
                    long timestamp = _getLongBE(keyEntry.getData(), keyEntry.getOffset());
                    switch (cb.verifyTimestamp(timestamp)) {
                    case :
                        continue main_loop;
                    case :
                        break;
                    case // all done?
                        return .;
                    }
                    StorableKey key = storableKey(primaryKeyEntry);
                    switch (cb.verifyKey(key)) {
                    case // nothing to do
                        continue main_loop;
                    case // bind, process
                        break;
                    case // all done?
                        return .;
                    }
                    Storable entry = .decode(keydata.getData(), data.getOffset(), data.getSize());
                    if (cb.processEntry(entry) == .) {
                        return .;
                    }
                    
                }
                return .;
            } finally {
                crsr.close();
            }
        } catch (DatabaseException de) {
            return _convertDBE(nullde);
        }
    }
   
    /*
    /**********************************************************************
    /* Internal methods
    /**********************************************************************
     */

    
Helper method used for creating more useful exceptions for given BDB exception
    protected <T> T _convertDBE(StorableKey keyDatabaseException bdbException)
        throws StoreException
    {
        if (bdbException instanceof LockTimeoutException) {
            throw new StoreException.ServerTimeout(keybdbException);
        }
        throw new StoreException.Internal(keybdbException);
    }
    
    protected DatabaseEntry dbKey(StorableKey key) {
        return key.with();
    }
    protected DatabaseEntry dbValue(Storable storable) {
        return storable.withRaw();
    }
    
    protected StorableKey storableKey(DatabaseEntry entry) {
        return new StorableKey(entry.getData(), entry.getOffset(), entry.getSize());
    }
    protected DatabaseEntry timestampKey(long timestamp)
    {
        byte[] raw = new byte[8];
        _putIntBE(raw, 0, (int) (timestamp >> 32));
        _putIntBE(raw, 4, (inttimestamp);
        return new DatabaseEntry(raw);
    }
    private final static void _putIntBE(byte[] bufferint offsetint value)
    {
        buffer[offset] = (byte) (value >> 24);
        buffer[++offset] = (byte) (value >> 16);
        buffer[++offset] = (byte) (value >> 8);
        buffer[++offset] = (bytevalue;
    }
    private final static long _getLongBE(byte[] bufferint offset)
    {
        long l1 = _getIntBE(bufferoffset);
        long l2 = _getIntBE(bufferoffset+4);
        return (l1 << 32) | ((l2 << 32) >>> 32);
    }
    
    private final static int _getIntBE(byte[] bufferint offset)
    {
        return (buffer[offset] << 24)
            | ((buffer[++offset] & 0xFF) << 16)
            | ((buffer[++offset] & 0xFF) << 8)
            | (buffer[++offset] & 0xFF)
            ;
    }
    
    /*
    /**********************************************************************
    /* Helper classes
    /**********************************************************************
     */
    
    private final static class BDBConverter implements WithBytesCallback<DatabaseEntry>
    {
        @Override
        public DatabaseEntry withBytes(byte[] bufferint offsetint length) {
            if (offset == 0 && length == buffer.length) {
                return new DatabaseEntry(buffer);
            }
            return new DatabaseEntry(bufferoffsetlength);
        }
    }
New to GrepCode? Check out our FAQ X