package com.guokr.hebo.engine; import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.mapdb.DB; import org.mapdb.DBMaker; import org.mapdb.HTreeMap; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import com.guokr.hebo.HeboEngine; import com.guokr.hebo.HeboCallback; @SuppressWarnings("unchecked") public class HeboEngineImpl implements HeboEngine { public abstract class SafeRunner implements Runnable { HeboCallback callback; String scope; public SafeRunner(String scope, HeboCallback callback) { this.scope = scope; this.callback = callback; } public abstract void invoke(); @Override public void run() { try { invoke(); } catch (Throwable ex) { String errMsg = ex.getMessage(); System.out.println("errorCMD = " + scope); callback.error(errMsg); } finally { callback.response(); } } } private DB dbmaker = DBMaker.newFileDB(new File("data/hebodb")).closeOnJvmShutdown() .transactionDisable().make(); private DB dbmakerForHashmap = DBMaker.newFileDB(new File("data/hebomapdb")) .closeOnJvmShutdown().transactionDisable().make(); private final ExecutorService exec = Executors.newSingleThreadExecutor(); private String[] keysMatching(Set<String> set, String pattern) { pattern = pattern.replaceAll("\\*", ".*"); List<String> result = new ArrayList<>(); for (String k : set) { if (k.matches(pattern)) { result.add(k); } } String allMatchedKeys[] = new String[result.size()]; result.toArray(allMatchedKeys); return allMatchedKeys; } @Override public void keys(HeboCallback callback, final String pattern) { exec.execute(new SafeRunner("keys", callback) { @Override public void invoke() { Set<String> allKeySet = new HashSet<String>(); allKeySet.addAll(dbmaker.getAll().keySet()); allKeySet.addAll(dbmakerForHashmap.getAll().keySet()); callback.stringList(keysMatching(allKeySet, pattern)); } }); } @Override public void del(HeboCallback callback, final String key) { exec.execute(new SafeRunner("del", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { dbmaker.delete(key); callback.integerValue(1); } else { if (dbmakerForHashmap.exists(key)) { dbmakerForHashmap.delete(key); callback.integerValue(1); } else { callback.integerValue(0); } } } }); } @Override public void get(HeboCallback callback, final String key) { exec.execute(new SafeRunner("get", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { callback.stringValue(dbmaker.getAtomicString(key).get()); } else { callback.stringValue(null); } } }); } @Override public void set(HeboCallback callback, final String key, final String value) { exec.execute(new SafeRunner("set", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { dbmaker.getAtomicString(key).set(value); } else { dbmaker.createAtomicString(key, value); } callback.ok(); } }); } @Override public void smembers(HeboCallback callback, final String key) { exec.execute(new SafeRunner("smembers", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { Set<String> set = (Set<String>) dbmaker.get(key); String[] allElements = new String[set.size()]; set.toArray(allElements); callback.stringList(allElements); } else { callback.stringList(new String[0]); } } }); } @Override public void sismember(HeboCallback callback, final String key, final String value) { exec.execute(new SafeRunner("sismember", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { Set<String> set = (Set<String>) dbmaker.get(key); callback.integerValue(set.contains(value) ? 1 : 0); } else { callback.integerValue(0); } } }); } @Override public void sadd(HeboCallback callback, final String key, final String value) { exec.execute(new SafeRunner("sadd", callback) { @Override public void invoke() { Set<String> set = null; if (dbmaker.exists(key)) { set = (Set<String>) dbmaker.get(key); } else { set = dbmaker.createHashSet(key).make(); } callback.integerValue(set.add(value) ? 1 : 0); } }); } @Override public void srem(HeboCallback callback, final String key, final String value) { exec.execute(new SafeRunner("srem", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { Set<String> set = (Set<String>) dbmaker.get(key); callback.integerValue(set.remove(value) ? 1 : 0); if (set.isEmpty()) { dbmaker.delete(key); } } else { callback.integerValue(0); } } }); } private String[] setsCompare(final Set<String> set1, final Set<String> set2) { if (set2 != null) { SetView<String> setdiff = Sets.difference(set1, set2); String diffarr[] = new String[setdiff.size()]; setdiff.toArray(diffarr); return diffarr; } else { String set1arr[] = new String[set1.size()]; set1.toArray(set1arr); return set1arr; } } @Override public void sdiff(HeboCallback callback, final String key1, final String key2) { exec.execute(new SafeRunner("sdiff", callback) { @Override public void invoke() { if (dbmaker.exists(key1)) { Set<String> set1 = (Set<String>) dbmaker.get(key1); Set<String> set2 = (Set<String>) dbmaker.get(key2); callback.stringList(setsCompare(set1, set2)); } else { callback.stringList(new String[0]); } } }); } @Override public void sdiffstore(HeboCallback callback, final String key1, final String key2, final String key3) { exec.execute(new SafeRunner("sdiffstore", callback) { @Override public void invoke() { String[] diffarr = null; if (dbmaker.exists(key2)) { Set<String> set1 = (Set<String>) dbmaker.get(key2); Set<String> set2 = (Set<String>) dbmaker.get(key3); diffarr = setsCompare(set1, set2); } else { diffarr = new String[0]; } if (dbmaker.exists(key1)) { dbmaker.delete(key1); } if (diffarr.length > 0) { Set<String> set = dbmaker.createHashSet(key1).make(); for (String element : diffarr) { set.add(element); } } callback.integerValue(diffarr.length); } }); } @Override public void spop(HeboCallback callback, final String key) { exec.execute(new SafeRunner("spop", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { Set<String> set = (Set<String>) dbmaker.get(key); Iterator<String> iterator = set.iterator(); String value = null; if (iterator.hasNext()) { value = iterator.next(); set.remove(value); } callback.stringValue(value); if (set.isEmpty()) { dbmaker.delete(key); } } else { callback.stringValue(null); } } }); } @Override public void hdel(HeboCallback callback, final String key, final String hkey) { exec.execute(new SafeRunner("hdel", callback) { @Override public void invoke() { if (dbmakerForHashmap.exists(key)) { HTreeMap<String, String> map = (HTreeMap<String, String>) dbmakerForHashmap.get(key); callback.integerValue(map.remove(hkey) == null ? 0 : 1); if (map.isEmpty()) { dbmakerForHashmap.delete(key); } } else { callback.integerValue(0); } } }); } @Override public void hset(HeboCallback callback, final String key, final String hkey, final String hvalue) { exec.execute(new SafeRunner("hset", callback) { @Override public void invoke() { HTreeMap<String, String> map = null; if (dbmakerForHashmap.exists(key)) { map = (HTreeMap<String, String>) dbmakerForHashmap.get(key); } else { map = dbmakerForHashmap.createHashMap(key).make(); } callback.integerValue(map.put(hkey, hvalue) == null ? 1 : 0); } }); } @Override public void hkeys(HeboCallback callback, final String pattern) { exec.execute(new SafeRunner("hkeys", callback) { @Override public void invoke() { if (dbmakerForHashmap.exists(pattern)) { HTreeMap<String, String> hkeys = (HTreeMap<String, String>) dbmakerForHashmap.get(pattern); String res[] = new String[hkeys.size()]; hkeys.keySet().toArray(res); callback.stringList(res); } else { callback.stringList(new String[0]); } } }); } @Override public void hgetall(HeboCallback callback, final String key) { exec.execute(new SafeRunner("hgetall", callback) { @Override public void invoke() { if (dbmakerForHashmap.exists(key)) { HTreeMap<String, String> map = (HTreeMap<String, String>) dbmakerForHashmap.get(key); Set<String> keyset = map.keySet(); String result[] = new String[map.size() * 2]; int idx = 0; for (String k : keyset) { result[idx++] = k; result[idx++] = map.get(k); } callback.stringList(result); } else { callback.stringList(new String[0]); } } }); } @Override public void hget(HeboCallback callback, final String key, final String hkey) { exec.execute(new SafeRunner("hget", callback) { @Override public void invoke() { if (dbmakerForHashmap.exists(key)) { HTreeMap<String, String> map = (HTreeMap<String, String>) dbmakerForHashmap.get(key); callback.stringValue(map.get(hkey)); } else { callback.stringValue(null); } } }); } @Override public void rpush(HeboCallback callback, final String key, final String value) { exec.execute(new SafeRunner("rpush", callback) { @Override public void invoke() { HTreeMap<Integer, String> map = null; if (dbmaker.exists(key)) { map = (HTreeMap<Integer, String>) dbmaker.get(key); } else { map = dbmaker.createHashMap(key).make(); } map.put(map.size(), value); callback.integerValue(map.size()); } }); } @Override public void llen(HeboCallback callback, final String key) { exec.execute(new SafeRunner("llen", callback) { @Override public void invoke() { if (dbmaker.exists(key)) { HTreeMap<Integer, String> map = (HTreeMap<Integer, String>) dbmaker.get(key); callback.integerValue(map.size()); } else { callback.integerValue(0); } } }); } }