/**
* CopyRight by Chinamobile
*
* BDBMap.java
*/
package com.chinamobile.bcbsp.comm;
import java.io.File;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.chinamobile.bcbsp.util.BSPJob;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.collections.StoredSortedMap;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DatabaseExistsException;
import com.sleepycat.je.DatabaseNotFoundException;
import com.sleepycat.je.EnvironmentConfig;
/**
* Persistent Map builded by BerkeleyDB Java edition uing je-4.10
*
* @param <K>
* @param <V>
*/
public class BDBMap<K extends Serializable, V extends Serializable>
implements Cloneable, Serializable{
private static final Log LOG = LogFactory.getLog(BDBMap.class);
private static final long serialVersionUID = 3427799316155220967L;
private transient BdbEnvironment dbEnv; // one database environment can contain several databases
private transient Database mapDb; // database for storing key-value
private transient StoredSortedMap<K,V> storedMap; // Stored Collections provided by dbd
@SuppressWarnings("unused")
private transient File dbDir; // directory the database
@SuppressWarnings("unused")
private transient String dbName; // database name
private AtomicLong bdbMapSize = new AtomicLong(0);
private Map<K,Integer> keyFrequnce = new HashMap<K,Integer>();
/**
* Open an existing database
* @param db
* @param keyClass
* @param valueClass
* @param classCatalog
*/
public BDBMap(BSPJob job, Database db,Class<K> keyClass, Class<V> valueClass, StoredClassCatalog classCatalog){
mapDb = db;
dbName = db.getDatabaseName();
bdbMapSize = new AtomicLong(0);
bindDatabase(mapDb, keyClass, valueClass, classCatalog);
}
/**
* Create a database
* @param dbDir
* @param dbName
* @param keyClass
* @param valueClass
*/
public BDBMap(BSPJob job, File dbDir, String dbName, Class<K> keyClass, Class<V> valueClass){
this.dbDir = dbDir;
this.dbName = dbName;
bdbMapSize = new AtomicLong(0);
createAndBindDatabase(dbDir,dbName,keyClass,valueClass);
}
/**
* Bind a database db with BDB's Stored Collection
* @param db
* @param keyClass
* @param valueClass
* @param classCatalog
*/
public void bindDatabase(Database db, Class<K> keyClass, Class<V> valueClass, StoredClassCatalog classCatalog){
EntryBinding<K> keyBinding = TupleBinding.getPrimitiveBinding(keyClass);
EntryBinding<V> valueBinding = TupleBinding.getPrimitiveBinding(valueClass);
if(keyBinding == null) {
keyBinding = new SerialBinding<K>(classCatalog, keyClass);
}
if(valueBinding == null) {
valueBinding = new SerialBinding<V>(classCatalog, valueClass);
}
mapDb = db;
storedMap = new StoredSortedMap<K,V>(
db, // db
keyBinding, //Key
valueBinding, // Value
true);
}
/**
* Create and Bind a database db using bindDatabase()
* @param dbDir
* @param dbName
* @param keyClass
* @param valueClass
*/
public void createAndBindDatabase(File dbDir, String dbName, Class<K> keyClass, Class<V> valueClass) throws DatabaseNotFoundException,
DatabaseExistsException,DatabaseException,IllegalArgumentException{
File envFile = null;
EnvironmentConfig envConfig = null;
DatabaseConfig dbConfig = null;
Database db=null;
try {
envFile = dbDir;
envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
envConfig.setTransactional(false);
envConfig.setCachePercent(30);
envConfig.setConfigParam("je.evictor.nodesPerScan","20");
dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
dbConfig.setTransactional(false);
dbConfig.setTemporary(true);
dbConfig.setSortedDuplicates(true);
dbEnv = new BdbEnvironment(envFile, envConfig);
db = dbEnv.openDatabase(null, dbName, dbConfig);
bindDatabase(db,keyClass,valueClass,dbEnv.getClassCatalog());
} catch (DatabaseNotFoundException e) {
throw e;
} catch (DatabaseExistsException e) {
throw e;
} catch (DatabaseException e) {
throw e;
} catch (IllegalArgumentException e) {
throw e;
}
}
/**
* return current BDBMap size
*/
public int size() {
synchronized(bdbMapSize){
return (int)(bdbMapSize.get());
}
}
/**
*
*/
public void keyFrequnceIncrement(K key){
Integer freq = keyFrequnce.get(key);
keyFrequnce.put(key, freq == null ? 1 : freq + 1);
}
public void keyFrequnceDecrement(K key, int count){
Integer freq = keyFrequnce.get(key);
keyFrequnce.put(key, freq - count);
}
public int getkeyFrequnce(K key){
int freq = 0;
try {
freq = keyFrequnce.get(key);
} catch (Exception e) {
LOG.error("BDB getkeyFrequnce Exception", e);
}
return freq;
}
public K getMostFrequetKey(){
int maxSize = 0;
Integer tmp;
Entry<K, Integer> entry = null;
Iterator<Entry<K, Integer>> it =
keyFrequnce.entrySet().iterator();
K key = null;
while (it.hasNext()) {
entry = it.next();
tmp = entry.getValue();
if (tmp> maxSize) {
maxSize = tmp;
key = entry.getKey();
}
}
return key;
}
/**
* put a key-value pair into database
*/
public V put(K key, V value) {
synchronized(bdbMapSize){
storedMap.put(key, value);
bdbMapSize.incrementAndGet();
}
return value;
}
/**
* delete a key-value pair from database
*/
public V delete(K key) {
V value ;
try {
synchronized(bdbMapSize){
if(storedMap.containsKey(key)){
int count = getkeyFrequnce(key);
value = storedMap.remove(key);
bdbMapSize.addAndGet(-count);
}
else value = null;
}
return value;
} catch (Exception e) {
LOG.error("BDB delete() Exception", e);
}
return null;
}
public V get(K key) {
V value = null;
try {
value = storedMap.get(key);
} catch (Exception e) {
LOG.error("BDB get() Exception", e);
}
return value;
}
/**
* When duplicates are allowed, this method return all values associated with the key
*/
public ConcurrentLinkedQueue<BSPMessage> getDupilcates(K key){
String tmp;
int indexOf$;
Collection<V> valueSet = storedMap.duplicates(key);
ConcurrentLinkedQueue<BSPMessage> list = new ConcurrentLinkedQueue<BSPMessage>();
Iterator<V> it = valueSet.iterator();
while(it.hasNext()){
BSPMessage tmpmsg = new BSPMessage();
tmp = it.next().toString();
indexOf$ = tmp.indexOf('$');
tmpmsg.fromString(tmp.substring(indexOf$+1));
list.add(tmpmsg);
}
storedMap.remove(key);
bdbMapSize.addAndGet(-list.size());
return list;
}
/**
* When duplicates are allowed, this method return values' count associated with the key
*/
public int getDupilcatesCount(K key){
return storedMap.duplicates(key).size();
}
/**
* Return true if the database contains the key.Otherwise return false
*/
public Boolean containsKey(K key){
return storedMap.containsKey(key);
}
/**
* Delete all key-value pairs in the database
*/
public void clear(){
keyFrequnce.clear();
storedMap.clear();
bdbMapSize.set(0);
}
public void shutdown(){
mapDb.close();
dbEnv.close();
}
}