/**
*
*/
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.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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;
//TODO: logger prints only in one replica.
/**
* @author fabiim
*
*/
//FIXME : this screams memory leak all over the place. The key, map cannot be garbage collected after it has been eliminated.
public class KeyValueColumnStore implements ColumnDatastore, Serializable{
private Map<String, Map<ByteArrayWrapper, Map<String,byte[]>>> datastore;
private Map<String, Map<String,Integer>> counters;
//FIXME :Addd timestamps map.
public KeyValueColumnStore(){
datastore = new HashMap<String, Map<ByteArrayWrapper, Map<String,byte[]>>>();
counters = new HashMap<String,Map<String,Integer>>();
pointers = Maps.newHashMap();
}
/* (non-Javadoc)
* @see mapserver.Datastore#get_and_increment(java.io.DataInputStream)
*/
@Override
public byte[] get_and_increment(DataInputStream dis) throws IOException, ClassNotFoundException{
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
if (!counters.containsKey(tableName)){
counters.put(tableName, new HashMap<String,Integer>());
//FIXME : memory leak.. values are never deleted...
}
//FIXME
String key =dis.readUTF();
Integer val = counters.get(tableName).get(key);
if (val != null){
//FIXME: move me to another key value store... With ints...
counters.get(tableName).put(key, val +1);
return Ints.toByteArray(val);
}
else{
counters.get(tableName).put(key, 1);
return Ints.toByteArray(0);
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#create_table(java.io.DataInputStream)
*/
@Override
public byte[] create_table(DataInputStream dis) throws Exception {
String tableName = dis.readUTF();
if (!datastore.containsKey(tableName)){
datastore.put(tableName, new HashMap<ByteArrayWrapper,Map<String,byte[]>>());
return new byte[1];
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#create_table_max_size(java.io.DataInputStream)
*/
@Override
public byte[] create_table_max_size(DataInputStream dis) throws Exception {
String tableName;
tableName = dis.readUTF();
if (!datastore.containsKey(tableName)){
int size = dis.read();
datastore.put(tableName, new LRULinkedHashMap<ByteArrayWrapper,Map<String,byte[]>>(size));
return new byte[1];
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#remove_table(java.io.DataInputStream)
*/
@Override
public byte[] remove_table(DataInputStream dis) throws Exception {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
datastore.remove(tableName); //TODO - remove hack
return new byte[1];
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#contains_table(java.io.DataInputStream)
*/
@Override
public byte[] contains_table(DataInputStream dis) throws Exception {
String needle = dis.readUTF();
if (datastore.containsKey(needle)){
return new byte[1];
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#clear()
*/
@Override
public byte[] clear() throws Exception {
datastore.clear();
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#clear_table(java.io.DataInputStream)
*/
@Override
public byte[] clear_table(DataInputStream dis) throws Exception {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
Map<?,?> m = datastore.get(tableName);
if (m != null){
m.clear();
return new byte[1];
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#contains_key_in_table(java.io.DataInputStream)
*/
@Override
public byte[] contains_key_in_table(DataInputStream dis) throws Exception {
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 new byte[1];
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#get_table(java.io.DataInputStream)
*/
@Override
public byte[] get_table(DataInputStream dis) throws Exception {
String tableName;
tableName = dis.readUTF();
boolean d= false;
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,Map<String,byte[]>> allTable = datastore.get(tableName);
Map<byte[],Map<String,byte[]>> c = Maps.newTreeMap(UnsignedBytes.lexicographicalComparator());
for (Entry<ByteArrayWrapper, Map<String,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;
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#get_value_in_table(java.io.DataInputStream)
*/
@Override
public byte[] get_value_in_table(DataInputStream dis) throws Exception {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
byte[] key =readNextByteArray(dis);
byte[] val= MapSmart.serialize(datastore.get(tableName).get(new ByteArrayWrapper(key)));
return val;
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#is_datastore_empty()
*/
@Override
public byte[] is_datastore_empty() throws Exception {
return datastore.isEmpty() ? new byte[1] : null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#is_table_empty(java.io.DataInputStream)
*/
@Override
public byte[] is_table_empty(DataInputStream dis) throws Exception {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
Map<?,?> table = datastore.get(tableName);
if (table.isEmpty()){
return new byte[1];
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#put_value_in_table(java.io.DataInputStream)
*/
@Override
public byte[] put_value_in_table(DataInputStream dis) throws Exception {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
final byte[] key = readNextByteArray(dis);
@SuppressWarnings("unchecked")
Map<String,byte[]> value = (Map<String, byte[]>) MapSmart.deserialize(ByteStreams.toByteArray(dis));
Map<ByteArrayWrapper, Map<String,byte[]>> table = datastore.get(tableName);
return MapSmart.serialize(table.put(new ByteArrayWrapper(key), value));
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#put_Values_in_table(java.io.DataInputStream)
*/
@Override
public byte[] put_Values_in_table(DataInputStream dis) throws Exception {
String tableName;
tableName =dis.readUTF();
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,Map<String,byte[]>> table = datastore.get(tableName);
byte[] valuesInBytes = ByteStreams.toByteArray(dis);
if (valuesInBytes != null){
try {
@SuppressWarnings("unchecked")
Map<byte[],Map<String,byte[]>> values = (Map<byte[], Map<String,byte[]>>) MapSmart.deserialize(valuesInBytes);
Map<ByteArrayWrapper,Map<String, byte[]>> realValues = Maps.newHashMap();
for (Entry<byte[],Map<String,byte[]>> en : values.entrySet()){
realValues.put(new ByteArrayWrapper(en.getKey()), en.getValue());
}
table.putAll(realValues);
return new byte[1];
} catch (ClassNotFoundException e) {
;
}
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#remove_value_from_table(java.io.DataInputStream)
*/
@Override
public byte[] remove_value_from_table(DataInputStream dis) throws Exception {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,Map<String,byte[]>> table = datastore.get(tableName);
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
if (table.containsKey(key)){
return MapSmart.serialize(table.remove(key));
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#size_of_table(java.io.DataInputStream)
*/
@Override
public byte[] size_of_table(DataInputStream dis) throws Exception {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
return Ints.toByteArray((datastore.get(tableName).size()));
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#atomic_replace_value_in_table(java.io.DataInputStream)
*/
@Override
public byte[] atomic_replace_value_in_table(DataInputStream dis)
throws Exception {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
//TODO - efficient/safe
ByteArrayWrapper k = new ByteArrayWrapper(readNextByteArray(dis));
Map<ByteArrayWrapper,Map<String,byte[]>> table = datastore.get(tableName);
if (table.containsKey(k)){
Map<String,byte[]> oldValue = (Map<String, byte[]>) MapSmart.deserialize(readNextByteArray(dis));
//XXX- maybe too heavyweight foo. Check this.
if (areByteArrayValueMapEqual(table.get(k), oldValue)){
final Map<String,byte[]> newValue =(Map<String, byte[]>) MapSmart.deserialize( ByteStreams.toByteArray(dis));
table.put(k, newValue);
return new byte[1];
}
}
}
return null;
}
/**
* @param map
* @param oldValue
* @return
*/
//TODO: move to util...
private boolean areByteArrayValueMapEqual(Map<String, byte[]> a,
Map<String, byte[]> b) {
if (a.size() == b.size() ){
for (Entry<String, byte[]> en : a.entrySet()){
if (b.containsKey(en.getKey()) && Arrays.equals(en.getValue(), b.get(en.getKey()))){
continue;
}
return false;
}
return true;
}
return false;
}
/* (non-Javadoc)
* @see mapserver.Datastore#atomic_remove_if_value(java.io.DataInputStream)
*/
@Override
public byte[] atomic_remove_if_value(DataInputStream dis) throws Exception {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
//TODO - efficient/safe - maybe use
ByteArrayWrapper k = new ByteArrayWrapper(readNextByteArray(dis));
Map<ByteArrayWrapper, Map<String, byte[]>> table = datastore.get(tableName);
if (table.containsKey(k)){
@SuppressWarnings("unchecked")
Map<String, byte[]> oldValue = (Map<String, byte[]>) MapSmart.deserialize(ByteStreams.toByteArray(dis));
if (areByteArrayValueMapEqual(table.get(k), oldValue)){
table.remove(k);
return new byte[1];
}
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.Datastore#atomic_put_if_absent(java.io.DataInputStream)
*/
@SuppressWarnings("unchecked")
@Override
public byte[] atomic_put_if_absent(DataInputStream dis) throws Exception {
String tableName;
ByteArrayWrapper key;
tableName = dis.readUTF();
key= new ByteArrayWrapper(readNextByteArray(dis));
if (datastore.containsKey(tableName)){
Map<ByteArrayWrapper,Map<String,byte[]>> table = datastore.get(tableName);
if (!table.containsKey(key)){
return MapSmart.serialize(table.put(key, (Map<String, byte[]>) MapSmart.deserialize(ByteStreams.toByteArray(dis))));
}
else{
return MapSmart.serialize(table.get(key));
}
}
return null;
}
/* (non-Javadoc)
* @see mapserver.ColumnDatastore#get_column(java.io.DataInputStream)
*/
@Override
public byte[] get_column(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
byte[] key =readNextByteArray(dis);
Map<String,byte[]> value = datastore.get(tableName).get(new ByteArrayWrapper(key));
String columnName = dis.readUTF();
return value.get(columnName);
}
return null;
}
/* (non-Javadoc)
* @see mapserver.ColumnDatastore#put_column(java.io.DataInputStream)
*/
@Override
public byte[] put_column(DataInputStream dis) throws Exception{
String tableName =dis.readUTF();
//System.out.println("Setting column@" + tableName);
if (datastore.containsKey(tableName)){
byte[] key = readNextByteArray(dis);
Map<String, byte[]> value = datastore.get(tableName).get(new ByteArrayWrapper(key));
//System.out.println("Setting column : " + Arrays.toString(key) + "V =" + value);
if (value != null) {
String column= dis.readUTF();
if (value.containsKey(column)){
value.put(column, ByteStreams.toByteArray(dis));
return new byte[1];
}
}
}
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#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);
@SuppressWarnings("unchecked")
Map<String,byte[]> value = (Map<String, byte[]>) MapSmart.deserialize(ByteStreams.toByteArray(dis));
Map<ByteArrayWrapper, Map<String,byte[]>> table = datastore.get(tableName);
MapSmart.serialize(table.put(new ByteArrayWrapper(key), value));
return new byte[1];
}
return null;
}
//XXX - create an hashmap wich takes byte wrappers.
/* (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 , Map<String,byte[]>> table = datastore.get(tableName);
List<ByteArrayWrapper> sortedKeys= Lists.newArrayList(table.keySet());
Collections.sort(sortedKeys, ByteArrayWrapper.COMPARE);
List<Map<String,byte[]>> values = new ArrayList<Map<String,byte[]>>(sortedKeys.size());
for (ByteArrayWrapper k : sortedKeys){
values.add(new TreeMap<String,byte[]>(table.get(k)));
}
return MapSmart.serialize(values);
}
return null;
}
private Map<String,String> pointers;
public static final byte[] TRUE = new byte[0];
@Override
public byte[] create_pointer_table(DataInputStream dis) throws IOException{
String tableName = dis.readUTF();
String destinyTable = dis.readUTF();
System.out.println("Creating pointers from :" + tableName + " to : " + destinyTable);
if (!datastore.containsKey(tableName) && datastore.containsKey(destinyTable)){
pointers.put(tableName, destinyTable);
datastore.put(tableName, new HashMap<ByteArrayWrapper,Map<String,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("Get references:" + tableName);
if (pointers.containsKey(tableName)){
System.out.println("In pointers" );
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
Map<ByteArrayWrapper, Map<String,byte[]>> keysTable = datastore.get(tableName);
if (keysTable.containsKey(key)){
System.out.println("I have the key" );
Map<ByteArrayWrapper, Map<String,byte[]>> endTable = datastore.get(pointers.get(tableName));
return MapSmart.serialize(endTable.get(keysTable.get(key)));
}
}
return null;
}
@Override
public byte[] get_column_by_reference(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (pointers.containsKey(tableName)){
ByteArrayWrapper key = new ByteArrayWrapper(readNextByteArray(dis));
Map<ByteArrayWrapper, Map<String,byte[]>> keysTable = datastore.get(tableName);
if (keysTable.containsKey(key)){
Map<ByteArrayWrapper, Map<String,byte[]>> endTable = datastore.get(pointers.get(tableName));
String columnName = dis.readUTF();
return endTable.get(keysTable.get(key)).get(columnName);
}
}
return null;
}
//FIXME delete referenced values.
}