code: private CompletableFuture<Void> establishConnection(PravegaNodeUri location, FlowHandler handler) {  
          final Bootstrap b = getNettyBootstrap().handler(getChannelInitializer(location, handler));
          // Initiate Connection.
          final CompletableFuture<Void> connectionComplete = new CompletableFuture<>();
          try {
              b.connect(location.getEndpoint(), location.getPort()).addListener((ChannelFutureListener) future -> {
                  if (future.isSuccess()) {
                      //since ChannelFuture is complete future.channel() is not a blocking call.
                      Channel ch = future.channel();
                      log.debug("Connect operation completed for channel:{}, local address:{}, remote address:{}",
                                ch.id(), ch.localAddress(), ch.remoteAddress());
                      channelGroup.add(ch); // Once a channel is closed the channel group implementation removes it.
                      connectionComplete.complete(null);
                  } else {
                      connectionComplete.completeExceptionally(new ConnectionFailedException(future.cause()));
                  }
              });
          } catch (Exception e) {
              connectionComplete.completeExceptionally(new ConnectionFailedException(e));
          }
  
          final CompletableFuture<Void> channelRegisteredFuture = new CompletableFuture<>(); //to track channel registration.
          handler.completeWhenRegistered(channelRegisteredFuture);
  
          return CompletableFuture.allOf(connectionComplete, channelRegisteredFuture);
      }
docs: Establish a new connection to the Pravega Node.
  @param location The Pravega Node Uri
  @param handler The flow handler for the connection
  @return A future, which completes once the connection has been established, returning a FlowHandler that can be used to create
  flows on the connection.