package ch.usi.da.dmap.server;
/*
* Copyright (c) 2017 Università della Svizzera italiana (USI)
*
* This file is part of URingPaxos.
*
* URingPaxos is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* URingPaxos is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with URingPaxos. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.IOException;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import ch.usi.da.dmap.thrift.gen.Command;
import ch.usi.da.dmap.thrift.gen.Dmap.Iface;
import ch.usi.da.dmap.thrift.gen.MapError;
import ch.usi.da.dmap.thrift.gen.Partition;
import ch.usi.da.dmap.thrift.gen.RangeCommand;
import ch.usi.da.dmap.thrift.gen.RangeResponse;
import ch.usi.da.dmap.thrift.gen.RangeType;
import ch.usi.da.dmap.thrift.gen.ReplicaCommand;
import ch.usi.da.dmap.thrift.gen.Response;
import ch.usi.da.dmap.thrift.gen.WrongPartition;
import ch.usi.da.dmap.utils.Utils;
/**
* Name: ABSender<br>
* Description: <br>
*
* Creation date: Mar 03, 2017<br>
* $Id$
*
* @author Samuel Benz benz@geoid.ch
*/
public class ABSender<K extends Comparable<K>,V> implements Iface {
private final static Logger logger = Logger.getLogger(ABSender.class);
private final DMapReplica<K,V> replica;
public ABSender(DMapReplica<K,V> replica){
this.replica = replica;
}
@SuppressWarnings("unchecked")
@Override
public Response execute(Command cmd) throws MapError, WrongPartition, TException {
logger.debug("ABSender received " + cmd);
FutureResponse r = null;
int send_ring = replica.partition_ring;
switch(cmd.type){
case CLEAR:
case CONTAINSVALUE:
case FIRSTKEY:
case LASTKEY:
case SIZE:
r = new FutureResponse(replica.partitions.keySet());
send_ring = replica.default_ring;
break;
case GET:
case PUT:
case REMOVE:
r = new FutureResponse();
break;
}
replica.getResponses().put(cmd.id,r);
Response response = null;
try {
replica.getNode().getProposer(send_ring).propose(Utils.getBuffer(cmd).array());
//logger.debug("ABSender wait on response ..." + cmd);
List<Object> ol = r.getResponse();
//logger.debug("... got response");
for(Object o : ol){
if(o instanceof MapError){
throw (MapError)o;
}else if(o instanceof WrongPartition){
throw (WrongPartition)o;
}else if(o instanceof TException){
throw (TException)o;
}
}
switch(cmd.type){
case CONTAINSVALUE:
response = ((Response)ol.get(0));
for(Object o : ol){
Response ret = (Response)o;
if(ret.getCount() > 0){
response = ret;
}
}
break;
case FIRSTKEY:
response = ((Response)ol.get(0));
try {
for(Object o : ol){
Response ret = (Response)o;
if(response.getKey() != null && ret.getKey() != null){
K o1 = (K)Utils.getObject(response.getKey());
K o2 = (K)Utils.getObject(ret.getKey());
if(o1 != null && o2 != null && o1.compareTo(o2) > 0){
response = ret;
}
}
}
} catch (ClassNotFoundException e) {
logger.error(e);
}
break;
case LASTKEY:
response = ((Response)ol.get(0));
try {
for(Object o : ol){
Response ret = (Response)o;
if(response.getKey() != null && ret.getKey() != null){
K o1 = (K)Utils.getObject(response.getKey());
K o2 = (K)Utils.getObject(ret.getKey());
if(o1 != null && o2 != null && o1.compareTo(o2) < 0){
response = ret;
}
}
}
} catch (ClassNotFoundException e) {
logger.error(e);
}
break;
case SIZE:
long size = 0;
for(Object o : ol){
size += ((Response)o).getCount();
}
response = ((Response)ol.get(0)).setCount(size);
break;
case GET:
case PUT:
case REMOVE:
case CLEAR:
response = (Response)ol.get(0);
break;
}
} catch (InterruptedException | IOException e) {
throw new TException(e);
}
//logger.debug("ABSender return " + response);
return response;
}
@Override
public RangeResponse range(RangeCommand cmd) throws MapError, WrongPartition, TException {
logger.debug("ABSender received " + cmd);
if(cmd.getType() == RangeType.GETRANGE || cmd.getType() == RangeType.PARTITIONSIZE){ // direct response since based on consistent snapshot
return replica.range(cmd.snapshot, cmd);
}
FutureResponse r = new FutureResponse();
replica.getResponses().put(cmd.id,r);
RangeResponse response = null;
try {
replica.getNode().getProposer(replica.default_ring).propose(Utils.getBuffer(cmd).array());
Object o = r.getResponse().get(0);
if(o instanceof MapError){
throw (MapError)o;
}else if(o instanceof WrongPartition){
throw (WrongPartition)o;
}else if(o instanceof TException){
throw (TException)o;
}
response = (RangeResponse)o;
} catch (InterruptedException | IOException e) {
throw new TException(e);
}
return response;
}
@Override
public Partition partition(long id) throws TException {
Partition p = new Partition();
p.setVersion(replica.partition_version);
p.setPartitions(replica.partitions);
return p;
}
@Override
public void replica(ReplicaCommand cmd) throws TException {
try {
replica.getNode().getProposer(replica.default_ring).propose(Utils.getBuffer(cmd).array());
} catch (IOException e) {
throw new TException(e);
}
}
}