class ServiceProxy extends TOMSender {

	@Override
	public void replyReceived(TOMMessage reply) {
		Logger.println("Synchronously received reply from " + reply.getSender() + " with sequence number "
				+ reply.getSequence());

		try {
			canReceiveLock.lock();
			if (reqId == -1) {// no message being expected
				Logger.println("throwing out request: sender=" + reply.getSender() + " reqId=" + reply.getSequence());
				canReceiveLock.unlock();
				return;
			}

			int pos = getViewManager().getCurrentViewPos(reply.getSender());

			if (pos < 0) { // ignore messages that don't come from replicas
				canReceiveLock.unlock();
				return;
			}

			int sameContent = 1;
			if (reply.getSequence() == reqId && reply.getReqType() == requestType) {

				Logger.println("Receiving reply from " + reply.getSender() + " with reqId:" + reply.getSequence()
						+ ". Putting on pos=" + pos);

				if (requestType == TOMMessageType.UNORDERED_HASHED_REQUEST) {
					response = hashResponseController.getResponse(pos, reply);
					if (response != null) {
						reqId = -1;
						this.sm.release(); // resumes the thread that is executing the "invoke" method
						canReceiveLock.unlock();
						return;
					}

				} else {
					if (replies[pos] == null) {
						receivedReplies++;
					}
					replies[pos] = reply;

					// Compare the reply just received, to the others

					for (int i = 0; i < replies.length; i++) {

						if ((i != pos || getViewManager().getCurrentViewN() == 1) && replies[i] != null
								&& (comparator.compare(replies[i].getContent(), reply.getContent()) == 0)) {
							sameContent++;
							if (sameContent >= replyQuorum) {
								response = extractor.extractResponse(replies, sameContent, pos);
								reqId = -1;
								this.sm.release(); // resumes the thread that is executing the "invoke" method
								canReceiveLock.unlock();
								return;
							}
						}
					}
				}

				if (response == null) {
					if (requestType.equals(TOMMessageType.ORDERED_REQUEST)) {
						if (receivedReplies == getViewManager().getCurrentViewN()) {
							reqId = -1;
							this.sm.release(); // resumes the thread that is executing the "invoke" method
						}
					} else if (requestType.equals(TOMMessageType.UNORDERED_HASHED_REQUEST)) {
						if (hashResponseController.getNumberReplies() == getViewManager().getCurrentViewN()) {
							reqId = -1;
							this.sm.release(); // resumes the thread that is executing the "invoke" method
						}
					} else { // UNORDERED
						if (receivedReplies != sameContent) {
							reqId = -1;
							this.sm.release(); // resumes the thread that is executing the "invoke" method
						}
					}
				}
			} else {
				Logger.println("Ignoring reply from " + reply.getSender() + " with reqId:" + reply.getSequence()
						+ ". Currently wait reqId= " + reqId);

			}

			// Critical section ends here. The semaphore can be released
			canReceiveLock.unlock();
		} catch (Exception ex) {
			System.out.println("Problem at ServiceProxy.ReplyReceived()");
			ex.printStackTrace();
			canReceiveLock.unlock();
		}
	}

	public  ServiceProxy(int processId);
	public  ServiceProxy(int processId, String configHome);
	public  ServiceProxy(int processId, String configHome, Comparator<byte[]> replyComparator,
			Extractor replyExtractor);

}