package com.rubiconproject.oss.kv.distributed.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.rubiconproject.oss.kv.BaseManagedKeyValueStore;
import com.rubiconproject.oss.kv.DistributedKeyValueStoreClient;
import com.rubiconproject.oss.kv.KeyValueStore;
import com.rubiconproject.oss.kv.KeyValueStoreException;
import com.rubiconproject.oss.kv.backends.ConnectionFactory;
import com.rubiconproject.oss.kv.distributed.BulkContext;
import com.rubiconproject.oss.kv.distributed.Configuration;
import com.rubiconproject.oss.kv.distributed.ConfigurationException;
import com.rubiconproject.oss.kv.distributed.Configurator;
import com.rubiconproject.oss.kv.distributed.Context;
import com.rubiconproject.oss.kv.distributed.Node;
import com.rubiconproject.oss.kv.distributed.Operation;
import com.rubiconproject.oss.kv.distributed.OperationQueue;
import com.rubiconproject.oss.kv.distributed.OperationResult;
import com.rubiconproject.oss.kv.distributed.hashing.MD5HashAlgorithm;
import com.rubiconproject.oss.kv.transcoder.SerializingTranscoder;
import com.rubiconproject.oss.kv.transcoder.Transcoder;
public class DistributedKeyValueStoreClientImpl extends
BaseManagedKeyValueStore implements KeyValueStore,
DistributedKeyValueStoreClient {
public static final String IDENTIFIER = "yahbadc";
private static OperationLog log = OperationLog.getInstance();
private Configurator configurator;
private Configuration config;
private DefaultDistributedKeyValueStore store;
private Transcoder defaultTranscoder = new SerializingTranscoder();
public DistributedKeyValueStoreClientImpl() {
}
public DistributedKeyValueStoreClientImpl(Configurator configurator)
throws IOException {
this.configurator = configurator;
}
public String getIdentifier() {
return IDENTIFIER;
}
public void setConfigurator(Configurator configurator) {
this.configurator = configurator;
}
@Override
public void start() throws IOException {
try {
this.config = configurator.getConfiguration();
ConnectionFactory connectionFactory = config.getConnectionFactory();
OperationQueue syncOpQueue = config.getSyncOperationQueue();
syncOpQueue.setConnectionFactory(connectionFactory);
syncOpQueue.start();
OperationQueue asyncOpQueue = config.getAsyncOperationQueue();
asyncOpQueue.setConnectionFactory(connectionFactory);
asyncOpQueue.start();
DynamoNodeLocator nl = new DynamoNodeLocator();
config.getNodeStore().addChangeListener(nl);
config.getNodeStore().start();
store = new DefaultDistributedKeyValueStore();
store.setConfiguration(config);
store.setHashAlgorithm(new MD5HashAlgorithm());
store.setNodeLocator(nl);
store.setSyncOperationQueue(syncOpQueue);
store.setAsyncOperationQueue(asyncOpQueue);
store.setContextSerializer(new PassthroughContextSerializer());
store.setContextFilter(new NodeRankContextFilter<byte[]>(config));
store.start();
super.start();
} catch (ConfigurationException e) {
throw new IOException(e);
} finally {
}
}
@Override
public void stop() {
super.stop();
store.stop();
}
public boolean exists(String key) throws KeyValueStoreException,
IOException {
long start = System.currentTimeMillis();
boolean success = true;
try {
assertReadable();
Object obj = get(key);
return (obj != null);
} catch (KeyValueStoreException e1) {
success = false;
throw e1;
} catch (IOException e2) {
success = false;
throw e2;
} finally {
log(key, "exists", System.currentTimeMillis() - start, success);
}
}
public Object get(String key) throws KeyValueStoreException, IOException {
return get(key, defaultTranscoder);
}
public Object get(String key, Transcoder transcoder)
throws KeyValueStoreException, IOException {
long start = System.currentTimeMillis();
boolean success = true;
try {
assertReadable();
Context<byte[]> context = store.get(key);
byte[] bytes = context.getValue();
Object obj = null;
if (bytes != null) {
obj = transcoder.decode(bytes);
}
return obj;
} catch (KeyValueStoreException e1) {
success = false;
throw e1;
} catch (IOException e2) {
success = false;
throw e2;
} finally {
log(key, "get", System.currentTimeMillis() - start, success);
}
}
public List<Node> getPreferenceList(String key, int replicas) {
return store.getPreferenceList(key, replicas);
}
public <V> List<Context<V>> getContexts(String key,
boolean considerNullAsSuccess, boolean enableSlidingWindow,
long singleRequestTimeout, long operationTimeout)
throws KeyValueStoreException, IOException {
return getContexts(key, defaultTranscoder, considerNullAsSuccess,
enableSlidingWindow, singleRequestTimeout, operationTimeout);
}
public <V> List<Context<V>> getContexts(String key, Transcoder transcoder,
boolean considerNullAsSuccess, boolean enableSlidingWindow,
long singleRequestTimeout, long operationTimeout)
throws KeyValueStoreException, IOException {
long start = System.currentTimeMillis();
boolean success = true;
try {
assertReadable();
List<Context<byte[]>> contexts = store.getContexts(key,
considerNullAsSuccess, enableSlidingWindow,
singleRequestTimeout, operationTimeout);
List<Context<V>> results = new ArrayList<Context<V>>(contexts
.size());
for (Context<byte[]> context : contexts) {
// check for context value NOT being instanceof byte
Object value = context.getValue();
// some backends deliver a V directly rather than byte[]
V v = (value instanceof byte[]) ? (V) transcoder.decode((byte[]) value) : (V) value;
Operation<V> op = new GetOperation<V>(transcoder, key);
OperationResult<V> operationResult = new DefaultOperationResult<V>(
op, v, context.getResult().getStatus(), context
.getResult().getDuration(), context.getResult()
.getError());
Context<V> ctx = new DefaultContext<V>(operationResult, context
.getSourceNode(), context.getNodeRank(), context
.getVersion(), context.getKey(), v);
results.add(ctx);
}
return results;
} finally {
log(key, "getContexts", System.currentTimeMillis() - start, success);
}
}
public Map<String, Object> getBulk(String... keys)
throws KeyValueStoreException, IOException {
return getBulk(defaultTranscoder, keys);
}
public Map<String, Object> getBulk(Transcoder transcoder, String... keys)
throws KeyValueStoreException, IOException {
long start = System.currentTimeMillis();
boolean success = true;
try {
assertReadable();
Map<String, Object> map = new HashMap<String, Object>();
List<BulkContext<byte[]>> contexts;
contexts = store.getBulkContexts(keys);
for (BulkContext<byte[]> context : contexts) {
Map<String, byte[]> values = context.getValues();
for (Map.Entry<String, byte[]> entry : values.entrySet()) {
Object obj;
byte[] bytes = entry.getValue();
obj = transcoder.decode(bytes);
// TODO deal with multiple values for same key
if (map.containsKey(entry.getKey()))
map.put(entry.getKey(), obj);
else
map.put(entry.getKey(), obj);
}
}
return map;
} catch (KeyValueStoreException e1) {
success = false;
throw e1;
} catch (IOException e2) {
success = false;
throw e2;
} finally {
log("null", "getbulk", System.currentTimeMillis() - start, success);
}
}
public Map<String, Object> getBulk(List<String> keys)
throws KeyValueStoreException, IOException {
// doing a cast of keys.toArray() to (String[]) causes a
// ClassCastException
String[] strings = new String[keys.size()];
for (int i = 0; i < keys.size(); i++) {
strings[i] = keys.get(i);
}
return getBulk(defaultTranscoder, strings);
}
public Map<String, Object> getBulk(List<String> keys, Transcoder transcoder)
throws KeyValueStoreException, IOException {
return getBulk(transcoder, (String[]) keys.toArray());
}
public void set(String key, Object value) throws KeyValueStoreException,
IOException {
assertWriteable();
set(key, value, defaultTranscoder);
}
public void set(String key, Object value, Transcoder transcoder)
throws KeyValueStoreException, IOException {
long start = System.currentTimeMillis();
boolean success = true;
try {
assertWriteable();
byte[] bytes = transcoder.encode(value);
store.set(key, bytes);
} catch (KeyValueStoreException e1) {
success = false;
throw e1;
} catch (IOException e2) {
success = false;
throw e2;
} finally {
log(key, "set", System.currentTimeMillis() - start, success);
}
}
public void delete(String key) throws KeyValueStoreException, IOException {
long start = System.currentTimeMillis();
boolean success = true;
try {
assertWriteable();
store.delete(key);
} catch (KeyValueStoreException e1) {
success = false;
throw e1;
} finally {
log(key, "delete", System.currentTimeMillis() - start, success);
}
}
public Configuration getConfiguration() {
return config;
}
private void log(String key, String op, long duration, boolean success) {
log.log(key, op, duration, success);
}
}