private CompletableFuture<ImmutableMap<ServerName, ClientMetaService.Interface>>
    fetchClusterIdAndCreateStubs() {
    CompletableFuture<ImmutableMap<ServerName, ClientMetaService.Interface>> future =
      new CompletableFuture<>();
    addr2StubFuture = future;
    FutureUtils.addListener(
      new ClusterIdFetcher(noAuthConf, user, rpcControllerFactory, bootstrapNodes).fetchClusterId(),
      (clusterId, error) -> {
        synchronized (ConnectionRegistryRpcStubHolder.this) {
          if (error != null) {
            addr2StubFuture.completeExceptionally(error);
          } else {
            RpcClient c = RpcClientFactory.createClient(conf, clusterId);
            ImmutableMap<ServerName, ClientMetaService.Interface> m =
              createStubs(c, bootstrapNodes);
            rpcClient = c;
            addr2Stub = m;
            addr2StubFuture.complete(m);
          }
          addr2StubFuture = null;
        }
      });
    // here we must use the local variable future instead of addr2StubFuture, as the above listener
    // could be executed directly in the same thread(if the future completes quick enough), since
    // the synchronized lock is reentrant, it could set addr2StubFuture to null in the end, so when
    // arriving here the addr2StubFuture could be null.
    return future;
  }
A class for creating {@link RpcClient} and related stubs used by
{@link AbstractRpcBasedConnectionRegistry}. We need to connect to bootstrap nodes to get the
cluster id first, before creating the final {@link RpcClient} and related stubs.
<p>
See HBASE-25051 for more details.
hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java