package bftsmart.tom;
import java.util.Hashtable;
import bftsmart.communication.client.ReplyListener;
import bftsmart.tom.core.messages.TOMMessage;
import bftsmart.tom.core.messages.TOMMessageType;
import bftsmart.tom.util.Extractor;
import bftsmart.tom.util.Logger;
import java.util.Arrays;
import java.util.Comparator;
/**
* This class is an extension of 'ServiceProxy' that can waits for replies
* asynchronously.
*
* @author Andre Nogueira
*
*/
public class AsynchServiceProxy extends ServiceProxy{
/**
*
*/
private Hashtable<Integer, RequestContext> requestsContext;
/**
*
* @param processId Replica id
*/
public AsynchServiceProxy(int processId) {
this(processId, null);
requestsContext = new Hashtable<Integer, RequestContext>();
}
/**
*
* @param processId Replica id
* @param configHome Configuration folder
*/
public AsynchServiceProxy(int processId, String configHome) {
super(processId,configHome);
requestsContext = new Hashtable<Integer, RequestContext>();
}
public AsynchServiceProxy(int processId, String configHome,
Comparator<byte[]> replyComparator, Extractor replyExtractor) {
super(processId, configHome, replyComparator, replyExtractor);
requestsContext = new Hashtable<Integer, RequestContext>();
}
/**
*
* @param request
* @param replyListener
* @param reqType Request type
* @return
*/
public int invokeAsynchRequest(byte[] request, ReplyListener replyListener, TOMMessageType reqType) {
return invokeAsynchRequest(request, super.getViewManager().getCurrentViewProcesses(), replyListener, reqType);
}
/**
*
* @param request
* @param targets
* @param replyListener
* @param reqType Request type
* @return
*/
public int invokeAsynchRequest(byte[] request, int[] targets, ReplyListener replyListener, TOMMessageType reqType) {
return invokeAsynch(request, targets, replyListener, reqType);
}
/**
*
* @param requestId Request
*/
public void cleanAsynchRequest(int requestId){
requestsContext.remove(requestId);
}
/**
*
*/
@Override
public void replyReceived(TOMMessage reply) {
Logger.println("Asynchronously received reply from " + reply.getSender() + " with sequence number " + reply.getSequence());
try {
canReceiveLock.lock();
RequestContext requestContext = requestsContext.get(reply.getSequence());
if(requestContext == null){ // it is not a asynchronous request
super.replyReceived(reply);
return;
}
if ( contains(requestContext.getTargets(), reply.getSender()) &&
(reply.getSequence() == requestContext.getReqId()) &&
(reply.getReqType().compareTo(requestContext.getRequestType())) == 0 ) {
Logger.println("Deliverying message from " + reply.getSender() + " with sequence number " + reply.getSequence() + " to the listener");
ReplyListener replyListener = requestContext.getReplyListener();
if (replyListener != null) {
requestContext.getReplyListener().replyReceived(requestContext, reply);
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally{
canReceiveLock.unlock();
}
}
/**
*
* @param request
* @param targets
* @param replyListener
* @param reqType
* @return
*/
private int invokeAsynch(byte[] request,int[] targets, ReplyListener replyListener, TOMMessageType reqType) {
Logger.println("Asynchronously sending request to " + Arrays.toString(targets));
RequestContext requestContext = null;
canSendLock.lock();
requestContext = new RequestContext(generateRequestId(reqType), generateOperationId(),
reqType, targets, System.currentTimeMillis(), replyListener);
try {
Logger.println("Storing request context for " + requestContext.getReqId());
requestsContext.put(requestContext.getReqId(), requestContext);
sendMessageToTargets(request, requestContext.getReqId(), requestContext.getOperationId(), targets, reqType);
} finally {
canSendLock.unlock();
}
return requestContext.getReqId();
}
/**
*
* @param targets
* @param senderId
* @return
*/
private boolean contains(int [] targets, int senderId){
for(int i=0;i<targets.length;i++)
if(targets[i] == senderId)
return true;
return false;
}
}