/**
*
*/
package smartkv.server;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import smartkv.server.util.LRULinkedHashMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.google.common.primitives.Ints;
import com.google.common.primitives.UnsignedBytes;
/**
* @author fabiim
*
*/
class VersionedDataBucket{
private byte[] val=null;
private int version =0;
public VersionedDataBucket (byte[] val){
this.val = val;
}
public VersionedDataBucket(VersionedDataBucket v){
this.val = v.getVal();
this.version = v.getVersion();
}
public byte[] getVal() {
return val;
}
public void setVal(byte[] val) {
if (!Arrays.equals(val, this.val)){
incrementVersion();
}
this.val = val;
}
private void incrementVersion() {
version = (version +1) % (Integer.MAX_VALUE-1);
}
public int getVersion() {
return version;
}
}
class VersionMap{
Map<ByteArrayWrapper, VersionedDataBucket> values;
public void clear() {
values.clear();
}
public boolean containsKey(Object key) {
return values.containsKey(key);
}
public boolean containsValue(Object value) {
return values.containsValue(value);
}
public Set<Entry<ByteArrayWrapper, VersionedDataBucket>> entrySet() {
return values.entrySet();
}
@Override
public boolean equals(Object o) {
return values.equals(o);
}
public VersionedDataBucket get(Object key) {
return values.get(key);
}
public int hashCode() {
return values.hashCode();
}
@Override
public String toString() {
// TODO Auto-generated method stub
return values.toString();
}
public boolean isEmpty() {
return values.isEmpty();
}
public Set<ByteArrayWrapper> keySet() {
return values.keySet();
}
public VersionedDataBucket put(ByteArrayWrapper key, byte[] data) {
VersionedDataBucket tsBucket = values.get(key);
if (tsBucket != null){
VersionedDataBucket oldBucket = new VersionedDataBucket(tsBucket);
tsBucket.setVal(data);
return oldBucket;
}
values.put(key, new VersionedDataBucket(data));
return null;
}
public void putWithNoReturn(ByteArrayWrapper key, byte[] data){
VersionedDataBucket tsBucket = values.get(key);
if (tsBucket != null){
tsBucket.setVal(data);
return;
}
values.put(key, new VersionedDataBucket(data));
}
public VersionedDataBucket remove(Object key) {
return values.remove(key);
}
public int size() {
return values.size();
}
public Collection<VersionedDataBucket> values() {
return values.values();
}
}
public class KeyValueStore implements Datastore, Serializable{
public static final byte[] TRUE = new byte[0];
/**
*
*/
private static final long serialVersionUID = 1L;
private Map<String, Map<ByteArrayWrapper, byte[]>> datastore;
private Map<String, String> pointers;
public KeyValueStore(){
datastore = new HashMap<String, Map<ByteArrayWrapper,byte[]>>();
pointers = Maps.newHashMap();
}
@Override
public byte[] get_and_increment(DataInputStream dis) throws IOException, ClassNotFoundException{
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
//FIXME
byte[] key = dis.readUTF().getBytes();
byte[] val = datastore.get(tableName).get(new ByteArrayWrapper(key));
if (val != null){
//FIXME: move me to another key value store... With ints...
int l =(Integer) Ints.fromByteArray(val);
datastore.get(tableName).put(new ByteArrayWrapper(key), Ints.toByteArray(l +1));
return Ints.toByteArray(l);
}
else{
datastore.get(tableName).put(new ByteArrayWrapper(key), Ints.toByteArray(1));
return Ints.toByteArray(0);
}
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] create_table(DataInputStream dis) throws IOException {
String tableName = dis.readUTF();
if (!datastore.containsKey(tableName)){
datastore.put(tableName, new HashMap<ByteArrayWrapper,byte[]>());
return TRUE;
}
return null;
}
@Override
public byte[] create_pointer_table(DataInputStream dis) throws IOException{
String tableName = dis.readUTF();
String destinyTable = dis.readUTF();
// System.out.println("K Creating pointers from :" + tableName + " to : " + destinyTable);
if (!datastore.containsKey(tableName) && datastore.containsKey(destinyTable)){
//System.out.println("created");
pointers.put(tableName, destinyTable);
datastore.put(tableName, new HashMap<ByteArrayWrapper,byte[]>());
return TRUE;
}
return null;
//FIXME deleted tables and such.
}
@Override
public byte[] get_referenced_value(DataInputStream dis) throws IOException{
String tableName = dis.readUTF();
//System.out.println("K Get references:" + tableName);
if (pointers.containsKey(tableName)){
//System.out.println("K In pointers" );
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
Map<ByteArrayWrapper, byte[]> keysTable = datastore.get(tableName);
if (keysTable.containsKey(key)){
//System.out.println("K I have the key" );
Map<ByteArrayWrapper, byte[]> endTable = datastore.get(pointers.get(tableName));
//System.out.println(endTable.containsKey(new ByteArrayWrapper(keysTable.get(key))));
byte[] b = endTable.get(new ByteArrayWrapper(keysTable.get(key)));
//System.out.println(b);
return b;
}
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] create_table_max_size(DataInputStream dis)
throws IOException {
String tableName;
tableName = dis.readUTF();
if (!datastore.containsKey(tableName)){
int size = dis.read();
datastore.put(tableName, new LRULinkedHashMap<ByteArrayWrapper,byte[]>(size));
return TRUE;
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] remove_table(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
datastore.remove(tableName); //TODO - remove hack
if (pointers.containsKey(tableName)){
pointers.remove(tableName);
}
//FIXME we should not traverse every pointer table and delete references to deleted tables.
return TRUE;
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] contains_table(DataInputStream dis) throws IOException {
String needle = dis.readUTF();
if (datastore.containsKey(needle)){
return TRUE;
}
/* else if (pointers.containsKey(needle)){
return TRUE;
}*/
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] clear_table(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
Map<?,?> m = datastore.get(tableName);
if (m != null){
m.clear();
return TRUE;
}
}
/* else if (pointers.containsKey(tableName)){
pointers.get(tableName).key2key.clear();
return TRUE;
}*/
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] contains_key_in_table(
DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
Map<?,?> tableHayStack = datastore.get(tableName);
byte[] key = readNextByteArray(dis);
if (tableHayStack.containsKey(new ByteArrayWrapper(key))){
return TRUE;
}
}
/* else if (pointers.containsKey(tableName)){
Map<?,?> tableHayStack = pointers.get(tableName).key2key;
byte[] key = readNextByteArray(dis);
if (tableHayStack.containsKey(new ByteArrayWrapper(key))){
return TRUE;
}
}
*/
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] get_table(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,byte[]> allTable = datastore.get(tableName);
Map<byte[],byte[]> c = Maps.newTreeMap(UnsignedBytes.lexicographicalComparator());
for (Entry<ByteArrayWrapper, byte[]> en: allTable.entrySet()){
c.put(en.getKey().value, en.getValue());
}
byte[] ret = MapSmart.serialize(c); //TODO: allTable is always != than null if table contains the key. We are the only ones to use it.
return ret;
}
/* else if (pointers.containsKey(tableName)){
throw new UnsupportedOperationException("Not yet implemented!");
}*/
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] get_value_in_table(
DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
byte[] key =readNextByteArray(dis);
return datastore.get(tableName).get(new ByteArrayWrapper(key));
}
/* else if (pointers.containsKey(tableName)){
ByteArrayWrapper key =new ByteArrayWrapper(readNextByteArray(dis));
Pointers2Table pointer = pointers.get(tableName);
if (pointer.key2key.containsKey(key)){
//Will return the pointed value from the intermediate map.
Map<ByteArrayWrapper,byte[]> endTable = datastore.get(pointer.table);
return endTable.get(pointer.key2key.get(key));
}
}*/
return null;
}
/**
* @return
*/
@Override
public byte[] is_datastore_empty() {
if (datastore.isEmpty()){
return TRUE;
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] is_table_empty(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName) ){
Map<?,?> table = datastore.get(tableName);
if (table.isEmpty()){
return TRUE;
}
}
/* else if (pointers.containsKey(tableName)){
if (pointers.get(tableName).key2key.isEmpty()){
return TRUE;
}
}*/
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] put_value_in_table(
DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
final byte[] key = readNextByteArray(dis);
final byte[] value = ByteStreams.toByteArray(dis);
Map<ByteArrayWrapper, byte[]> table = datastore.get(tableName);
return table.put(new ByteArrayWrapper(key), value);
}
/* else if(pointers.containsKey(tableName)){
final byte[] key = readNextByteArray(dis);
final byte[] value = ByteStreams.toByteArray(dis);
ByteArrayWrapper val = pointers.get(tableName).key2key.put(new ByteArrayWrapper(key), new ByteArrayWrapper(value));
return val != null ? val.value : null;
}*/
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] put_Values_in_table(DataInputStream dis) throws IOException {
String tableName;
tableName =dis.readUTF();
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,byte[]> table = datastore.get(tableName);
byte[] valuesInBytes = ByteStreams.toByteArray(dis);
if (valuesInBytes != null){
try {
@SuppressWarnings("unchecked")
Map<byte[],byte[]> values = (Map<byte[], byte[]>) MapSmart.deserialize(valuesInBytes);
Map<ByteArrayWrapper, byte[]> realValues = Maps.newHashMap();
for (Entry<byte[],byte[]> en : values.entrySet()){
realValues.put(new ByteArrayWrapper(en.getKey()), en.getValue());
}
table.putAll(realValues);
return TRUE;
} catch (ClassNotFoundException e) {
;
}
}
}
/* else if (pointers.containsKey(tableName)){
throw new UnsupportedOperationException("Not yet Implemented!");
}*/
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] remove_value_from_table(
DataInputStream dis) throws IOException {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,byte[]> table = datastore.get(tableName);
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
if (table.containsKey(key)){
return table.remove(key);
}
}
/* else if (pointers.containsKey(tableName)){
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
ByteArrayWrapper val = pointers.get(tableName).key2key.remove(key);
return val != null? val.value : null;
}*/
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] size_of_table(DataInputStream dis) throws IOException {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
return Ints.toByteArray((datastore.get(tableName).size()));
}
/* else if (pointers.containsKey(tableName)){
return Ints.toByteArray((pointers.get(tableName).key2key.size()));
}*/
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] atomic_replace_value_in_table(
DataInputStream dis) throws IOException {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
//TODO - efficient/safe - maybe use
ByteArrayWrapper k = new ByteArrayWrapper(readNextByteArray(dis));
Map<ByteArrayWrapper,byte[]> table = datastore.get(tableName);
if (table.containsKey(k)){
byte[] oldValue = readNextByteArray(dis);
if (Arrays.equals(table.get(k), oldValue)){
final byte[] newValue = ByteStreams.toByteArray(dis);
table.put(k, newValue);
return TRUE;
}
}
}
/* else if (pointers.containsKey(tableName)){
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
if (pointers.get(tableName).key2key.containsKey(key)){
Pointers2Table pointer = pointers.get(tableName);
ByteArrayWrapper oldValue = new ByteArrayWrapper(readNextByteArray(dis));
if (pointer.key2key.get(key).equals(oldValue)){
ByteArrayWrapper newValue = new ByteArrayWrapper(readNextByteArray(dis));
pointers.put(key, value);
}
return TRUE;
}
}*/
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] atomic_remove_if_value(
DataInputStream dis) throws IOException {
String tableName = dis.readUTF();
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,byte[]> table = datastore.get(tableName);
if (table.containsKey(key)){
byte[] currValue = ByteStreams.toByteArray(dis);
if (Arrays.equals(table.get(key), currValue)){
table.remove(key);
return TRUE;
}
}
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
@Override
public byte[] atomic_put_if_absent(
DataInputStream dis) throws IOException {
String tableName;
ByteArrayWrapper key;
tableName = dis.readUTF();
key= new ByteArrayWrapper(readNextByteArray(dis));
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,byte[]> table = datastore.get(tableName);
if (!table.containsKey(key)){
return table.put(key, ByteStreams.toByteArray(dis));
}
else{
return table.get(key);
}
}
return null;
}
private byte[] readNextByteArray(DataInputStream in) throws IOException {
int size = in.readInt();
byte[] result = new byte[size];
in.readFully(result);
return result;
}
/* (non-Javadoc)
* @see mapserver.Datastore#clear()
*/
@Override
public byte[] clear() throws Exception {
datastore.clear();
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#insert_value_in_table(java.io.DataInputStream)
*/
@Override
public byte[] insert_value_in_table(DataInputStream dis) throws Exception{
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
final byte[] key = readNextByteArray(dis);
final byte[] value = ByteStreams.toByteArray(dis);
Map<ByteArrayWrapper, byte[]> table = datastore.get(tableName);
table.put(new ByteArrayWrapper(key), value);
return TRUE;
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#values(java.io.DataInputStream)
*/
@Override
public byte[] values(DataInputStream msg) throws Exception {
String tableName = msg.readUTF();
if (datastore.containsKey(tableName)){
//FIXME : get smart to check this return value with unordered maps.
Map<ByteArrayWrapper, byte[]> table = datastore.get(tableName);
Collection<byte[]> values = table.values();
List<byte[]> finalValues= Lists.newArrayList(values);
Collections.sort(finalValues, UnsignedBytes.lexicographicalComparator());
return MapSmart.serialize(finalValues);
}
return null;
}
}