package com.rubiconproject.oss.kv.backends;
import java.io.IOException;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.rubiconproject.oss.kv.BaseManagedKeyValueStore;
import com.rubiconproject.oss.kv.KeyValueStore;
import com.rubiconproject.oss.kv.KeyValueStoreException;
import com.rubiconproject.oss.kv.annotations.Configurable;
import com.rubiconproject.oss.kv.annotations.Configurable.Type;
import com.rubiconproject.oss.kv.transcoder.Transcoder;
/**
* A simple in-memory kv store based on {@link ConcurrentHashMap}. Useful for testing or debugging, particularly to simulate slow or non-responsive backends.
*
* Simulating a non-responsive backend:
* - use a uri like 'hash://hash?writeSleepTime=800&readSleepTime=100'
* - will write after sleeping for 800ms and return from reads after sleeping for 100ms
*
* @author stingleff
*
*/
public class ConcurrentHashMapKeyValueStore extends BaseManagedKeyValueStore
implements KeyValueStore {
public static final String IDENTIFIER = "hashtable";
private Map<String, Object> map = new ConcurrentHashMap<String, Object>();
private long writeSleepTime = -1;
private long readSleepTime = -1;
public String getIdentifier() {
return IDENTIFIER;
}
@Configurable(name = "writeSleepTime", accepts = Type.LongType)
public void setWriteSleepTime(long delay) {
this.writeSleepTime = delay;
}
@Configurable(name = "readSleepTime", accepts = Type.LongType)
public void setReadSleepTime(long delay) {
this.readSleepTime = delay;
}
public void stop() {
map.clear();
super.stop();
}
public boolean exists(String key) throws KeyValueStoreException,
IOException {
assertReadable();
return map.containsKey(key);
}
public Object get(String key) throws KeyValueStoreException, IOException {
assertReadable();
if (readSleepTime > 0)
sleep(readSleepTime);
return map.get(key);
}
public Object get(String key, Transcoder transcoder)
throws KeyValueStoreException, IOException {
assertReadable();
if (readSleepTime > 0)
sleep(readSleepTime);
return map.get(key);
}
public Map<String, Object> getBulk(String... keys)
throws KeyValueStoreException, IOException {
assertReadable();
if (readSleepTime > 0)
sleep(readSleepTime);
Map<String, Object> results = new HashMap<String, Object>();
for (String key : keys) {
Object obj = get(key);
if (obj != null)
results.put(key, obj);
}
return results;
}
public Map<String, Object> getBulk(final List<String> keys)
throws KeyValueStoreException, IOException {
assertReadable();
if (readSleepTime > 0)
sleep(readSleepTime);
Map<String, Object> results = new HashMap<String, Object>();
for (String key : keys) {
Object obj = get(key);
if (obj != null)
results.put(key, obj);
}
return results;
}
public Map<String, Object> getBulk(final List<String> keys,
Transcoder transcoder) throws KeyValueStoreException, IOException {
assertReadable();
if (readSleepTime > 0)
sleep(readSleepTime);
Map<String, Object> results = new HashMap<String, Object>();
for (String key : keys) {
Object obj = get(key, transcoder);
if (obj != null)
results.put(key, obj);
}
return results;
}
public void set(String key, Object value) throws KeyValueStoreException,
IOException {
if (writeSleepTime > 0)
sleep(writeSleepTime);
assertWriteable();
map.put(key, value);
}
public void set(String key, Object value, Transcoder transcoder)
throws KeyValueStoreException, IOException {
assertWriteable();
if (writeSleepTime > 0)
sleep(writeSleepTime);
map.put(key, value);
}
public void delete(String key) throws KeyValueStoreException, IOException {
assertWriteable();
if (writeSleepTime > 0)
sleep(writeSleepTime);
map.remove(key);
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
}
}