/**
   * Generate a dummy namenode proxy instance that utilizes our hacked {@link
   * LossyRetryInvocationHandler}. Proxy instance generated using this method will proactively drop
   * RPC responses. Currently this method only support HA setup. null will be returned if the given
   * configuration is not for HA.
   *
   * @param config the configuration containing the required IPC properties, client failover
   *     configurations, etc.
   * @param nameNodeUri the URI pointing either to a specific NameNode or to a logical nameservice.
   * @param xface the IPC interface which should be created
   * @param numResponseToDrop The number of responses to drop for each RPC call
   * @param fallbackToSimpleAuth set to true or false during calls to indicate if a secure client
   *     falls back to simple auth
   * @return an object containing both the proxy and the associated delegation token service it
   *     corresponds to. Will return null of the given configuration does not support HA.
   * @throws IOException if there is an error creating the proxy
   */
  @SuppressWarnings("unchecked")
  public static <T> ProxyAndInfo<T> createProxyWithLossyRetryHandler(
      Configuration config,
      URI nameNodeUri,
      Class<T> xface,
      int numResponseToDrop,
      AtomicBoolean fallbackToSimpleAuth)
      throws IOException {
    Preconditions.checkArgument(numResponseToDrop > 0);
    AbstractNNFailoverProxyProvider<T> failoverProxyProvider =
        createFailoverProxyProvider(config, nameNodeUri, xface, true, fallbackToSimpleAuth);

    if (failoverProxyProvider != null) { // HA case
      int delay =
          config.getInt(
              HdfsClientConfigKeys.Failover.SLEEPTIME_BASE_KEY,
              HdfsClientConfigKeys.Failover.SLEEPTIME_BASE_DEFAULT);
      int maxCap =
          config.getInt(
              HdfsClientConfigKeys.Failover.SLEEPTIME_MAX_KEY,
              HdfsClientConfigKeys.Failover.SLEEPTIME_MAX_DEFAULT);
      int maxFailoverAttempts =
          config.getInt(
              HdfsClientConfigKeys.Failover.MAX_ATTEMPTS_KEY,
              HdfsClientConfigKeys.Failover.MAX_ATTEMPTS_DEFAULT);
      int maxRetryAttempts =
          config.getInt(
              HdfsClientConfigKeys.Retry.MAX_ATTEMPTS_KEY,
              HdfsClientConfigKeys.Retry.MAX_ATTEMPTS_DEFAULT);
      InvocationHandler dummyHandler =
          new LossyRetryInvocationHandler<T>(
              numResponseToDrop,
              failoverProxyProvider,
              RetryPolicies.failoverOnNetworkException(
                  RetryPolicies.TRY_ONCE_THEN_FAIL,
                  maxFailoverAttempts,
                  Math.max(numResponseToDrop + 1, maxRetryAttempts),
                  delay,
                  maxCap));

      T proxy =
          (T)
              Proxy.newProxyInstance(
                  failoverProxyProvider.getInterface().getClassLoader(),
                  new Class[] {xface},
                  dummyHandler);
      Text dtService;
      if (failoverProxyProvider.useLogicalURI()) {
        dtService =
            HAUtilClient.buildTokenServiceForLogicalUri(nameNodeUri, HdfsConstants.HDFS_URI_SCHEME);
      } else {
        dtService = SecurityUtil.buildTokenService(NameNode.getAddress(nameNodeUri));
      }
      return new ProxyAndInfo<T>(proxy, dtService, NameNode.getAddress(nameNodeUri));
    } else {
      LOG.warn(
          "Currently creating proxy using " + "LossyRetryInvocationHandler requires NN HA setup");
      return null;
    }
  }
  /**
   * Creates the namenode proxy with the passed protocol. This will handle creation of either HA- or
   * non-HA-enabled proxy objects, depending upon if the provided URI is a configured logical URI.
   *
   * @param conf the configuration containing the required IPC properties, client failover
   *     configurations, etc.
   * @param nameNodeUri the URI pointing either to a specific NameNode or to a logical nameservice.
   * @param xface the IPC interface which should be created
   * @param fallbackToSimpleAuth set to true or false during calls to indicate if a secure client
   *     falls back to simple auth
   * @return an object containing both the proxy and the associated delegation token service it
   *     corresponds to
   * @throws IOException if there is an error creating the proxy
   */
  @SuppressWarnings("unchecked")
  public static <T> ProxyAndInfo<T> createProxy(
      Configuration conf, URI nameNodeUri, Class<T> xface, AtomicBoolean fallbackToSimpleAuth)
      throws IOException {
    AbstractNNFailoverProxyProvider<T> failoverProxyProvider =
        createFailoverProxyProvider(conf, nameNodeUri, xface, true, fallbackToSimpleAuth);

    if (failoverProxyProvider == null) {
      // Non-HA case
      return createNonHAProxy(
          conf,
          NameNode.getAddress(nameNodeUri),
          xface,
          UserGroupInformation.getCurrentUser(),
          true,
          fallbackToSimpleAuth);
    } else {
      // HA case
      DfsClientConf config = new DfsClientConf(conf);
      T proxy =
          (T)
              RetryProxy.create(
                  xface,
                  failoverProxyProvider,
                  RetryPolicies.failoverOnNetworkException(
                      RetryPolicies.TRY_ONCE_THEN_FAIL,
                      config.getMaxFailoverAttempts(),
                      config.getMaxRetryAttempts(),
                      config.getFailoverSleepBaseMillis(),
                      config.getFailoverSleepMaxMillis()));

      Text dtService;
      if (failoverProxyProvider.useLogicalURI()) {
        dtService =
            HAUtilClient.buildTokenServiceForLogicalUri(nameNodeUri, HdfsConstants.HDFS_URI_SCHEME);
      } else {
        dtService = SecurityUtil.buildTokenService(NameNode.getAddress(nameNodeUri));
      }
      return new ProxyAndInfo<T>(proxy, dtService, NameNode.getAddress(nameNodeUri));
    }
  }