Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package com.fasterxml.storemate.backend.leveldb;
  
  import java.io.File;
  import java.util.*;
  
  import org.iq80.leveldb.*;
  
 
com.fasterxml.storemate.store.backend.StoreBackend implementation that uses Java LevelDB implementation. 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 LevelDBStoreBackend extends StoreBackend
 {
     private final static byte[] NO_BYTES = new byte[0];
 
     /*
     /**********************************************************************
     /* Simple config, location
     /**********************************************************************
      */
 
     protected final File _dataRoot;
     
     /*
     /**********************************************************************
     /* LevelDB entities
     /**********************************************************************
      */
 
     protected final DB _dataDB;
 
     protected final DB _indexDB;
 
     /*
     /**********************************************************************
     /* Life-cycle
     /**********************************************************************
      */
     
     public LevelDBStoreBackend(StorableConverter conv,
             File dbRootDB dataDBDB indexDB)
     {
         super(conv);
          = dbRoot;
          = dataDB;
          = indexDB;
     }
 
     @Override
     public void start() {
         // nothing to do, yet
     }
 
     @Override
     public void prepareForStop() {
         // anything we can do? Could stop compactions but...
     }
     
     @Override
     public void stop()
     {
         try {
             .close();
         } catch (IOException e) { }
         try {
             .close();
         } catch (IOException e) { }
     }
 
     /*
     /**********************************************************************
     /* Capability introspection
     /**********************************************************************
      */

    
No, LevelDB does not have means to produce efficient entry count.
 
     @Override
     public boolean hasEfficientEntryCount() { return false; }

    
No, LevelDB does not have means to produce efficient index entry count.
 
     @Override
     public boolean hasEfficientIndexCount() { return false; }
 
     @Override
        return _getStats(config);
    }
    @Override
        return _getStats(config);
    }
    protected LevelDBBackendStats _getStats(DB dbBackendStatsConfig config)
    {
        Map<String,Objectstats = new LinkedHashMap<String,Object>();
        // JNI-version apparently exposes this; not sure about Java version:
        final String JNI_STATS = "leveldb.stats";
        String value = db.getProperty(JNI_STATS);
        if (value != null) {
            stats.put(JNI_STATSvalue);
        }
        return new LevelDBBackendStats(config, System.currentTimeMillis(), stats);
    }
    
    /*
    /**********************************************************************
    /* API Impl, metadata
    /**********************************************************************
     */
    @Override
    public long getEntryCount() {
        return -1L;
    }
    @Override
    public long getIndexedCount() {
        return -1L;
    }
    @Override
    public long countEntries() throws StoreException {
        return _count();
    }
    @Override
    public long countIndexed() throws StoreException {
        return _count();
    }
    @Override
    public File getStorageDirectory() {
        return ;
    }
    
    private final long _count(DB dbthrows StoreException
    {
        long count = 0L;
        try {
            DBIterator iter = db.iterator();
            try {
                iter.seekToFirst();
                while (iter.hasNext()) {
                    ++count;
                    iter.next();
                }
                return count;
            } finally {
                try {
                    iter.close();
                } catch (IOException de) {
                    LevelDBUtil.convertIOE(nullde);
                }
            }
        } catch (DBException de) {
            LevelDBUtil.convertDBE(nullde);
        }
        return count// never gets here
    }
    
    /*
    /**********************************************************************
    /* API Impl, read
    /**********************************************************************
     */
    @Override
    public boolean hasEntry(StorableKey keythrows StoreException
    {
        // Bah. No efficient method for this...
        return findEntry(key) != null;
    }
        
    @Override
    public Storable findEntry(StorableKey keythrows StoreException
    {
        try {
            byte[] data = .get(dbKey(key)); // default options fine
            if (data == null) {
                return null;
            }
            return .decode(keydata);
        } catch (DBException de) {
            return LevelDBUtil.convertDBE(keyde);
        }
    }
    /*
    /**********************************************************************
    /* API Impl, insert/update
    /**********************************************************************
     */
    /* NOTE: all modification methods are protected by per-key partitioned
     * lock; so modification methods are transaction wrt other modifications,
     * although not wrt read methods. This has ramifications on ordering of
     * data vs index modifications.
     */
    
    @Override
    public Storable createEntry(StorableKey keyStorable newEntry)
        throws IOExceptionStoreException
    {
        byte[] dbKey = dbKey(key);
        try {
            // First things first: must check to see if an old entry exists; if so, return:
            byte[] oldData = .get(dbKey);
            if (oldData != null) {
                return .decode(keyoldData);
            }
            // If not, insert entry, add index
            .put(dbKeynewEntry.asBytes());
            .put(keyToLastModEntry(dbKeynewEntry), );
        } catch (DBException de) {
            return LevelDBUtil.convertDBE(keyde);
        }
        return null;
    }
    @Override
    public Storable putEntry(StorableKey keyStorable newEntry)
        throws IOExceptionStoreException
    {
        byte[] dbKey = dbKey(key);
        try {
            // First things first: must check to see if an old entry exists
            byte[] oldData = .get(dbKey);
            Storable oldEntry = (oldData == null) ? null : .decode(keyoldData);
            // and if so, there's also index entry to remove, first
            if (oldEntry != null) {
                .delete(keyToLastModEntry(dbKeyoldEntry));
            }
            // but then to actual business; insert new entry, index
            .put(dbKeynewEntry.asBytes());
            // and index:
            .put(keyToLastModEntry(dbKeynewEntry), );
            return oldEntry;
        } catch (DBException de) {
            return LevelDBUtil.convertDBE(keyde);
        }
    }
    @Override
    public void ovewriteEntry(StorableKey keyStorable newEntry)
        throws IOExceptionStoreException
    {
        byte[] dbKey = dbKey(key);
        try {
            // Must check if an entry exists even if we don't return it, to
            // manage secondary index
            byte[] oldData = .get(dbKey);
            if (oldData != null) {
                .delete(keyToLastModEntry(dbKeyoldData));
            }
            .put(dbKeynewEntry.asBytes());
            .put(keyToLastModEntry(dbKeynewEntry), );
        } catch (DBException de) {
            LevelDBUtil.convertDBE(keyde);
        }
    }
    @Override
    public boolean upsertEntry(StorableKey keyStorable newEntry,
            OverwriteChecker checkerAtomicReference<StorableoldEntryRef)
        throws IOExceptionStoreException
    {
        byte[] dbKey = dbKey(key);
        try {
            byte[] oldData = .get(dbKey);
            if (oldData != null) {
                Storable oldEntry = .decode(keyoldData);
                // yes: is it ok to overwrite?
                if (oldEntryRef != null) {
                    oldEntryRef.set(oldEntry);
                }
                if (!checker.mayOverwrite(keyoldEntrynewEntry)) {
                    // no, return
                    return false;
                }
                // but need to delete index
                .delete(keyToLastModEntry(dbKeyoldEntry));
            } else {
                if (oldEntryRef != null) {
                    oldEntryRef.set(null);
                }
            }
            // Ok we are good, go ahead:
            .put(dbKeynewEntry.asBytes());
            .put(keyToLastModEntry(dbKeynewEntry), );
            return true;
        } catch (DBException de) {
             LevelDBUtil.convertDBE(keyde);
             return false;
        }
    }
    
    /*
    /**********************************************************************
    /* API Impl, delete
    /**********************************************************************
     */
    @Override
    public boolean deleteEntry(StorableKey key)
        throws IOExceptionStoreException
    {
        try {
            /* Ok: we must actually fetch the entry, first, since we must have
             * lastmod timestamp to also delete index entry...
             */
            final byte[] dbKey = dbKey(key);
            byte[] data = .get(dbKey);
            
            // No entry?
            if (data == null) {
                return false;
            }
            Storable value = .decode(keydata);
            // First remove index entry so we won't have dangling entries
            .delete(keyToLastModEntry(dbKeyvalue));
            // can only return Snapshot, if we wanted that... 
            .delete(dbKey);
            return true;
        } catch (DBException de) {
            LevelDBUtil.convertDBE(keyde);
            return false;
        }
    }
    /*
    /**********************************************************************
    /* API Impl, iteration
    /**********************************************************************
     */
    @Override
        throws StoreException
    {
        // !!! TODO: make more efficient. Until then, just use in-order traversal
        //   Would Snapshot make sense here?
        return iterateEntriesByKey(cbnull);
    }
    @Override
            StorableKey firstKey)
        throws StoreException
    {
        StorableKey key = null;
        try {
            DBIterator iter = .iterator();
            try {
                if (firstKey == null) {
                    iter.seekToFirst();
                } else {
                    iter.seek(dbKey(firstKey));
                }
                main_loop:
                while (iter.hasNext()) {
                    Map.Entry<byte[], byte[]> entry = iter.next();
                    key = storableKey(entry.getKey());
                    switch (cb.verifyKey(key)) {
                    case // nothing to do
                        continue main_loop;
                    case // bind, process
                        break;
                    case // all done?
                        return .;
                    }
                    Storable dbValue = .decode(keyentry.getValue());
                    if (cb.processEntry(dbValue) == .) {
                        return .;
                    }
                }
                return .;
            } finally {
                try {
                    iter.close();
                } catch (IOException de) {
                    return LevelDBUtil.convertIOE(keyde);
                }
            }
        } catch (DBException de) {
            return LevelDBUtil.convertDBE(keyde);
        }
    }
    
    @Override
            StorableKey lastSeen)
        throws StoreException
    {
        StorableKey key = null;
        try {
            final byte[] lastSeenRaw = dbKey(lastSeen);
            DBIterator iter = .iterator();
            try {
                iter.seek(lastSeenRaw);
                // First: if we are at end, we are done
                if (!iter.hasNext()) { // last entry
                    return .;
                }
                Map.Entry<byte[], byte[]> entry = iter.next();
                // First, did we find the entry (should, but better safe than sorry)
                byte[] b = entry.getKey();
                if (_equals(lastSeenRawb)) { // yes, same thingy -- skip
                    if (!iter.hasNext()) {
                        return .;
                    }
                    entry = iter.next();
                    b = entry.getKey();
                }
                main_loop:
                while (true) {
                    key = storableKey(b);
                    switch (cb.verifyKey(key)) {
                    case // all done?
                        return .;
                    case // bind, process
                        break;
                    case // nothing to do
                    default// should we warn?
                        continue main_loop;
                    }
                    Storable dbEntry = .decode(keyentry.getValue());
                    if (cb.processEntry(dbEntry) == .) {
                        return .;
                    }
                    if (!iter.hasNext()) {
                        break;
                    }
                    entry = iter.next();
                    b = entry.getKey();
                }
                return .;
            } finally {
                try {
                    iter.close();
                } catch (IOException de) {
                    return LevelDBUtil.convertIOE(keyde);
                }
            }
        } catch (DBException de) {
            return LevelDBUtil.convertDBE(keyde);
        }
    }
    
    @Override
            long firstTimestamp)
        throws StoreException
    {
        if (cb == null) {
            throw new IllegalArgumentException("Can not pass null 'cb' argument");
        }
        StorableKey key = null;
        try {
            DBIterator iter = .iterator();
            if (firstTimestamp <= 0L) { // from beginning (i.e. no ranges)
                iter.seekToFirst();
            } else {
                iter.seek(timestampKey(firstTimestamp));
            }
            try {
                main_loop:
                while (iter.hasNext()) {
                    // First things first: timestamp check
                    byte[] rawKey = iter.next().getKey();
                    long timestamp = timestamp(rawKey);
                    switch (cb.verifyTimestamp(timestamp)) {
                    case :
                        continue main_loop;
                    case :
                        break;
                    case // all done?
                        return .;
                    }
                    key = _extractPrimaryKey(rawKey);
                    switch (cb.verifyKey(key)) {
                    case // nothing to do
                        continue main_loop;
                    case // bind, process
                        break;
                    case // all done?
                        return .;
                    }
                    // and then find it...
                    byte[] rawEntry = .get(dbKey(key));
                    // unusual but possible due to race condition:
                    IterationAction act;
                    if (rawEntry == null) {
                        act = cb.processMissingEntry(key);
                    } else {
                        // but more commonly:
                        act = cb.processEntry(.decode(keyrawEntry));
                    }
                    if (act == .) {
                        return .;
                    }
                    
                }
                return .;
            } finally {
                try {
                    iter.close();
                } catch (IOException de) {
                    return LevelDBUtil.convertIOE(keyde);
                }
            }
        } catch (DBException de) {
            return LevelDBUtil.convertDBE(keyde);
        }
    }
   
    /*
    /**********************************************************************
    /* Internal methods
    /**********************************************************************
     */
    protected byte[] dbKey(StorableKey key) {
        return key.asBytes();
    }
    protected StorableKey storableKey(byte[] raw) {
        return new StorableKey(raw);
    }
    protected long timestamp(byte[] value) {
        return _getLongBE(value, 0);
    }
    protected byte[] timestampKey(long timestamp)
    {
        byte[] raw = new byte[8];
        _putLongBE(raw, 0, timestamp);
        return raw;
    }
    
    protected byte[] keyToLastModEntry(byte[] keyStorable value)
    {
        byte[] result = new byte[8 + key.length];
        long timestamp = value.getLastModified();
        _putLongBE(result, 0, timestamp);
        System.arraycopy(key, 0, result, 8, key.length);
        return result;
    }
    protected byte[] keyToLastModEntry(byte[] keybyte[] rawStorable)
    {
        byte[] result = new byte[8 + key.length];
        // Storable starts with timestamp, so just copy
        appendTimestamp(rawStorableresult, 0);
        System.arraycopy(key, 0, result, 8, key.length);
        return result;
    }
    
    protected int appendTimestamp(byte[] frombyte[] toint toIndex)
    {
        to[toIndex] = from[0];
        to[++toIndex] = from[1];
        to[++toIndex] = from[2];
        to[++toIndex] = from[3];
        to[++toIndex] = from[4];
        to[++toIndex] = from[5];
        to[++toIndex] = from[6];
        to[++toIndex] = from[7];
        return toIndex+1;
    }
    protected StorableKey _extractPrimaryKey(byte[] indexKey) {
        // First 8 bytes are timestamp, rest is key
        return new StorableKey(indexKey, 8, indexKey.length - 8);
    }
    private final static void _putLongBE(byte[] bufferint offsetlong value)
    {
        _putIntBE(bufferoffset, (int) (value >> 32));
        _putIntBE(bufferoffset+4, (intvalue);
    }
    
    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)
            ;
    }
    private final boolean _equals(byte[] b1byte[] b2)
    {
        final int len = b1.length;
        if (b2.length != len) {
            return false;
        }
        for (int i = 0; i < len; ++i) {
            if (b1[i] != b2[i]) {
                return false;
            }
        }
        return true;
    }
New to GrepCode? Check out our FAQ X