@Override
  public void Reconfigure(
      NetworkAddress clientAddress,
      NetworkAddress serverAddress,
      int roundID,
      List<NetworkAddress> newServerAddresses)
      throws ClassNotFoundException, IOException {
    ArgumentValidator.IsNotNull("clientAddress", clientAddress);
    ArgumentValidator.IsNotNull("serverAddress", serverAddress);
    ArgumentValidator.IsGreaterThan("roundID", roundID, 0);
    ArgumentValidator.IsNotNull("newServerAddresses", newServerAddresses);

    RPCInvokePacket rpcInvokePacket =
        new RPCInvokePacket(
            clientAddress,
            serverAddress,
            UUID.randomUUID(),
            IRPCMethods.ReconfigureMethodName,
            new Serializable[] {roundID, (Serializable) newServerAddresses});

    RPCResultPacket rpcResultPacket = clientRPCFunctionality.Invoke(rpcInvokePacket);

    if (rpcResultPacket.ResultCode() != RPCResultCode.SUCCESS) {
      HandleRPCFailure(rpcInvokePacket, rpcResultPacket);
      return;
    }

    logger.LogInfo(
        "RPC for %s was successful! rpcInvokePacket: %s, rpcResultPacket: %s",
        IRPCMethods.ReconfigureMethodName, rpcInvokePacket.toString(), rpcResultPacket.toString());
  }
  public ClientRPCMethods(ILogger logger, IClientRPCFunctionality clientRPCFunctionality) {
    ArgumentValidator.IsNotNull("logger", logger);
    ArgumentValidator.IsNotNull("clientRPCFunctionality", clientRPCFunctionality);

    this.logger = logger;
    this.clientRPCFunctionality = clientRPCFunctionality;
  }
  @Override
  public AckMessage Sync(
      NetworkAddress clientAddress, NetworkAddress serverAddress, SyncMessage syncMessage)
      throws ClassNotFoundException, IOException, PaxosException {
    ArgumentValidator.IsNotNull("clientAddress", clientAddress);
    ArgumentValidator.IsNotNull("serverAddress", serverAddress);
    ArgumentValidator.IsNotNull("syncMessage", syncMessage);

    if (clientAddress.equals(serverAddress)) {
      throw new PaxosException(
          String.format(
              "ClientAddress and serverAddress are same for Decide RPC. "
                  + "This is not supported. ClientAddress=ServerAddress:%s",
              clientAddress.toString()));
    }

    RPCInvokePacket rpcInvokePacket =
        new RPCInvokePacket(
            clientAddress,
            serverAddress,
            UUID.randomUUID(),
            IRPCMethods.SyncMethodName,
            new Serializable[] {syncMessage});

    RPCResultPacket rpcResultPacket;
    rpcResultPacket = clientRPCFunctionality.Invoke(rpcInvokePacket);

    if (rpcResultPacket.ResultCode() != RPCResultCode.SUCCESS) {
      HandleRPCFailure(rpcInvokePacket, rpcResultPacket);
      return null;
    }

    logger.LogInfo(
        "RPC for %s was successful! rpcInvokePacket: %s, rpcResultPacket: %s",
        IRPCMethods.SyncMethodName, rpcInvokePacket.toString(), rpcResultPacket.toString());

    return (AckMessage) rpcResultPacket.Result();
  }
  /**
   * Client side implementation of GetLineFromFile RPC
   *
   * @param clientAddress
   * @param serverAddress
   * @param fileName
   * @param lineNumber
   * @return line read
   * @throws IOException
   * @throws ClassNotFoundException
   */
  @Override
  public String GetLineFromFile(
      NetworkAddress clientAddress,
      NetworkAddress serverAddress,
      int roundID,
      String fileName,
      int lineNumber)
      throws ClassNotFoundException, IOException {
    ArgumentValidator.IsNotNull("clientAddress", clientAddress);
    ArgumentValidator.IsNotNull("serverAddress", serverAddress);
    ArgumentValidator.IsGreaterThan("roundID", roundID, 0);
    ArgumentValidator.IsNotNullOrEmpty("fileName", fileName);
    ArgumentValidator.IsGreaterThan("lineNumber", lineNumber, 0);

    RPCInvokePacket rpcInvokePacket =
        new RPCInvokePacket(
            clientAddress,
            serverAddress,
            UUID.randomUUID(),
            IRPCMethods.GetLineFromFileMethodName,
            new Serializable[] {roundID, fileName, lineNumber});

    RPCResultPacket rpcResultPacket = clientRPCFunctionality.Invoke(rpcInvokePacket);

    if (rpcResultPacket.ResultCode() != RPCResultCode.SUCCESS) {
      HandleRPCFailure(rpcInvokePacket, rpcResultPacket);
      return null;
    }

    logger.LogInfo(
        "RPC for %s was successful! rpcInvokePacket: %s, rpcResultPacket: %s",
        IRPCMethods.AppendLineToFileMethodName,
        rpcInvokePacket.toString(),
        rpcResultPacket.toString());

    return (rpcResultPacket.Result() == null) ? null : rpcResultPacket.Result().toString();
  }
  /**
   * Client side implementation of AppendLineToFile RPC
   *
   * @param clientAddress
   * @param serverAddress
   * @param fileName
   * @param data
   * @throws IOException
   * @throws ClassNotFoundException
   */
  @Override
  public void AppendLineToFile(
      NetworkAddress clientAddress,
      NetworkAddress serverAddress,
      int roundID,
      String fileName,
      String data)
      throws ClassNotFoundException, IOException {
    ArgumentValidator.IsNotNull("clientAddress", clientAddress);
    ArgumentValidator.IsNotNull("serverAddress", serverAddress);
    ArgumentValidator.IsGreaterThan("roundID", roundID, 0);
    ArgumentValidator.IsNotNullOrEmpty("fileName", fileName);
    // data can be empty string, hence doing only null check
    ArgumentValidator.IsNotNull("data", data);

    RPCInvokePacket rpcInvokePacket =
        new RPCInvokePacket(
            clientAddress,
            serverAddress,
            UUID.randomUUID(),
            IRPCMethods.AppendLineToFileMethodName,
            new Serializable[] {roundID, fileName, data});

    RPCResultPacket rpcResultPacket = clientRPCFunctionality.Invoke(rpcInvokePacket);

    if (rpcResultPacket.ResultCode() != RPCResultCode.SUCCESS) {
      HandleRPCFailure(rpcInvokePacket, rpcResultPacket);
      return;
    }

    logger.LogInfo(
        "RPC for %s was successful! rpcInvokePacket: %s, rpcResultPacket: %s",
        IRPCMethods.AppendLineToFileMethodName,
        rpcInvokePacket.toString(),
        rpcResultPacket.toString());
  }
  @SuppressWarnings("unchecked")
  @Override
  public List<NetworkAddress> Join(
      NetworkAddress clientAddress,
      NetworkAddress serverAddress,
      int roundID,
      NetworkAddress newServerAddress)
      throws ClassNotFoundException, IOException {
    ArgumentValidator.IsNotNull("clientAddress", clientAddress);
    ArgumentValidator.IsNotNull("serverAddress", serverAddress);
    ArgumentValidator.IsGreaterThan("roundID", roundID, 0);
    ArgumentValidator.IsNotNull("newServerAddress", newServerAddress);

    RPCInvokePacket rpcInvokePacket =
        new RPCInvokePacket(
            clientAddress,
            serverAddress,
            UUID.randomUUID(),
            IRPCMethods.JoinMethodName,
            new Serializable[] {roundID, newServerAddress});

    RPCResultPacket rpcResultPacket = clientRPCFunctionality.Invoke(rpcInvokePacket);

    if (rpcResultPacket.ResultCode() != RPCResultCode.SUCCESS) {
      HandleRPCFailure(rpcInvokePacket, rpcResultPacket);
      return null;
    }

    logger.LogInfo(
        "RPC for %s was successful! rpcInvokePacket: %s, rpcResultPacket: %s",
        IRPCMethods.JoinMethodName, rpcInvokePacket.toString(), rpcResultPacket.toString());

    return (rpcResultPacket.Result() == null)
        ? null
        : (List<NetworkAddress>) rpcResultPacket.Result();
  }