package mapserver;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import mapserver.util.LRULinkedHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bftsmart.tom.MessageContext;
import bftsmart.tom.ReplicaContext;
import bftsmart.tom.ServiceReplica;
import bftsmart.tom.server.defaultservices.DefaultSingleRecoverable;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams;
import com.google.common.primitives.UnsignedBytes;
class ByteArrayWrapper implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
public final byte[] value;
//TODO - change this for something that can be serializable?
public static final HashFunction hf = Hashing.murmur3_32();
public ByteArrayWrapper(byte[] v){
value = v;
}
@Override
public final boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ByteArrayWrapper other = (ByteArrayWrapper) obj;
if (!Arrays.equals(value, other.value))
return false;
return true;
}
@Override
public final
int hashCode(){
return hf.hashBytes(value).asInt();
}
}
public class MapSmart extends DefaultSingleRecoverable{
//TODO extract these methods to a standalone package.
public static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(b);
o.writeObject(obj);
o.flush();
o.close();
return b.toByteArray();
}
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream b = new ByteArrayInputStream(bytes);
ObjectInputStream o = new ObjectInputStream(b);
return o.readObject();
}
public static void main(String[] args){
new MapSmart(0);
new MapSmart(1);
new MapSmart(2);
new MapSmart(3);
File f = new File("./config/currentView");
if (f.exists()){
f.delete();
}
}
ServiceReplica replica = null;
@SuppressWarnings("unused")
private ReplicaContext replicaContext;
private Map<String, Map<ByteArrayWrapper, byte[]>> datastore;
private Logger log = LoggerFactory.getLogger(MapSmart.class);
public MapSmart(int id) {
replica = new ServiceReplica(id, this, this);
datastore = new HashMap<String, Map<ByteArrayWrapper,byte[]>>();
}
@Override
public void setReplicaContext(ReplicaContext replicaContext) {
this.replicaContext = replicaContext;
}
@Override
public byte[] executeUnordered(byte[] command, MessageContext msgCtx) {
return null;
}
@Override
public byte[] appExecuteOrdered(byte[] command, MessageContext msgCtx) {
ByteArrayInputStream in = new ByteArrayInputStream(command);
try {
DataInputStream dis = new DataInputStream(in);
DataStoreVersion
RequestType reqType = RequestType.values()[dis.readInt()];
switch(reqType){
case GET_AND_INCREMENT:
return get_and_increment(dis);
case CREATE_TABLE:
return create_table(dis);
case CREATE_TABLE_MAX_SIZE:
return create_table_max_size(dis);
case REMOVE_TABLE:
return remove_table(dis);
case CONTAINS_TABLE:
return contains_table(dis);
case CLEAR_DATASTORE:
datastore.clear();
return null;
case CLEAR_TABLE:
return clear_table(dis);
case CONTAINS_KEY_IN_TABLE:
return contains_key_in_table(in, dis);
case GET_TABLE:
return get_table(dis);
case GET_VALUE_IN_TABLE:
return get_value_in_table(in, dis);
case IS_DATASTORE_EMPTY:
return is_datastore_empty();
case IS_TABLE_EMPTY:
return is_table_empty(dis);
case PUT_VALUE_IN_TABLE:
return put_value_in_table(in, dis);
case PUT_VALUES_IN_TABLE:
return put_Values_in_table(dis,in);
case REMOVE_VALUE_FROM_TABLE:
return remove_value_from_table(in, dis);
case SIZE_OF_TABLE:
return size_of_table(dis);
//TODO default.
case ATOMIC_REPLACE_VALUE_IN_TABLE:
return atomic_replace_value_in_table(in, dis);
case ATOMIC_REMOVE_IF_VALUE:
return atomic_remove_if_value(in, dis);
case ATOMIC_PUT_IF_ABSENT:
return atomic_put_if_absent(in, dis);
}
} catch (IOException e) {
System.err.println("Exception reading data in the replica: " + e.getMessage());
e.printStackTrace();
return null;
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//FIXME
private byte[] get_and_increment(DataInputStream dis) throws IOException, ClassNotFoundException{
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
byte[] key =readNextByteArray(dis);
byte[] val = datastore.get(tableName).get(new ByteArrayWrapper(key));
if (val != null){
Long l =(Long) MapSmart.deserialize(val);
System.out.println(l);
datastore.get(tableName).put(new ByteArrayWrapper(key), MapSmart.serialize(l +1));
return MapSmart.serialize(l);
}
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private byte[] create_table(DataInputStream dis) throws IOException {
String tableName = dis.readUTF();
if (!datastore.containsKey(tableName)){
datastore.put(tableName, new HashMap<ByteArrayWrapper,byte[]>());
return new byte[1]; //TODO - remove hack.
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private 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 new byte[1];
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private byte[] remove_table(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
datastore.remove(tableName); //TODO - remove hack
return new byte[1];
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private byte[] contains_table(DataInputStream dis) throws IOException {
String needle = dis.readUTF();
if (datastore.containsKey(needle)){
return new byte[1];
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private 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 new byte[1];
}
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
private byte[] contains_key_in_table(ByteArrayInputStream in,
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 new byte[1];
}
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private byte[] get_table(DataInputStream dis) throws IOException {
String tableName;
tableName = dis.readUTF();
boolean d= false;
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;
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
private byte[] get_value_in_table(ByteArrayInputStream in,
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));
}
return null;
}
/**
* @return
*/
private byte[] is_datastore_empty() {
if (datastore.isEmpty()){
return new byte[1];
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private 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 new byte[1];
}
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
private byte[] put_value_in_table(ByteArrayInputStream in,
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);
}
return null;
}
private byte[] insert_value_in_table(ByteArrayInputStream in,
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);
table.put(new ByteArrayWrapper(key), value);
return new byte[1];
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private byte[] put_Values_in_table(DataInputStream dis, ByteArrayInputStream in) 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 new byte[1];
} catch (ClassNotFoundException e) {
log.error("Could not deserialize the map of key-value pairs entries to add to table: " + tableName + e.getStackTrace());
}
}
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
private byte[] remove_value_from_table(ByteArrayInputStream in,
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);
}
}
return null;
}
/**
* @param dis
* @return
* @throws IOException
*/
private byte[] size_of_table(DataInputStream dis) throws IOException {
String tableName = dis.readUTF();
if (datastore.containsKey(tableName)){
return toBytes(datastore.get(tableName).size());
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
private byte[] atomic_replace_value_in_table(ByteArrayInputStream in,
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 new byte[1];
}
}
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
private byte[] atomic_remove_if_value(ByteArrayInputStream in,
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 new byte[1];
}
}
}
return null;
}
/**
* @param in
* @param dis
* @return
* @throws IOException
*/
private byte[] atomic_put_if_absent(ByteArrayInputStream in,
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;
}
@SuppressWarnings("unchecked")
@Override
public void installSnapshot(byte[] state) {
ByteArrayInputStream bis = new ByteArrayInputStream(state);
try {
ObjectInputStream in = new ObjectInputStream(bis);
datastore = (Map<String,Map<ByteArrayWrapper, byte[]>>) in.readObject();
in.close();
bis.close();
} catch (ClassNotFoundException e) {
System.err.print("Coudn't find Map: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.print("Exception installing the application state: " + e.getMessage());
e.printStackTrace();
}
}
@Override
public byte[] getSnapshot() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(datastore);
out.flush();
out.close();
bos.close();
return bos.toByteArray();
} catch (IOException e) {
System.out.println("Exception when trying to take a + " +
"snapshot of the application state" + e.getMessage());
e.printStackTrace();
return new byte[0];
} // TODO Auto-generated method stub
}
private byte[] toBytes(int i) {
byte[] result = new byte[4];
result[0] = (byte) (i >> 24);
result[1] = (byte) (i >> 16);
result[2] = (byte) (i >> 8);
result[3] = (byte) (i);
return result;
}
}