/**
  * Add listener to FailoverLoop if master connection is not active, so a reconnection will be
  * done. (the reconnection will be done by failover or if append before by the next query/method
  * that will use the failed connection) Remove listener from FailoverLoop is master connection is
  * active.
  */
 public void handleFailLoop() {
   if (isMasterHostFail()) {
     if (!isExplicitClosed()) {
       FailoverLoop.addListener(this);
     }
   } else {
     FailoverLoop.removeListener(this);
   }
 }
  /**
   * method called when a new Master connection is found after a fallback.
   *
   * @param protocol the new active connection
   */
  @Override
  public void foundActiveMaster(Protocol protocol) throws QueryException {
    if (isExplicitClosed()) {
      proxy.lock.lock();
      try {
        protocol.close();
      } finally {
        proxy.lock.unlock();
      }
      return;
    }
    syncConnection(this.currentProtocol, protocol);
    proxy.lock.lock();
    try {
      if (currentProtocol != null && !currentProtocol.isClosed()) {
        currentProtocol.close();
      }
      currentProtocol = protocol;
    } finally {
      proxy.lock.unlock();
    }

    resetMasterFailoverData();
    FailoverLoop.removeListener(this);
  }
  /**
   * Loop to connect failed hosts.
   *
   * @param searchFilter search parameters.
   * @throws QueryException if there is any error during reconnection
   */
  @Override
  public void reconnectFailedConnection(SearchFilter searchFilter) throws QueryException {
    proxy.lock.lock();
    try {
      if (!searchFilter.isInitialConnection() && (isExplicitClosed() || !isMasterHostFail())) {
        return;
      }

      currentConnectionAttempts.incrementAndGet();
      resetOldsBlackListHosts();

      List<HostAddress> loopAddress = new LinkedList<>(urlParser.getHostAddresses());
      if (HaMode.FAILOVER.equals(mode)) {
        // put the list in the following order
        // - random order not connected host
        // - random order blacklist host
        // - random order connected host
        loopAddress.removeAll(getBlacklistKeys());
        Collections.shuffle(loopAddress);
        List<HostAddress> blacklistShuffle = new LinkedList<>(getBlacklistKeys());
        Collections.shuffle(blacklistShuffle);
        loopAddress.addAll(blacklistShuffle);
      } else {
        // order in sequence
        loopAddress.removeAll(getBlacklistKeys());
        loopAddress.addAll(getBlacklistKeys());
      }

      // put connected at end
      if (currentProtocol != null && !isMasterHostFail()) {
        loopAddress.remove(currentProtocol.getHostAddress());
        // loopAddress.add(currentProtocol.getHostAddress());
      }

      MasterProtocol.loop(this, loopAddress, searchFilter);
      // close loop if all connection are retrieved
      if (!isMasterHostFail()) {
        FailoverLoop.removeListener(this);
      }

      // if no error, reset failover variables
      resetMasterFailoverData();
    } finally {
      proxy.lock.unlock();
    }
  }
  @Override
  public HandleErrorResult primaryFail(Method method, Object[] args) throws Throwable {
    boolean alreadyClosed = !currentProtocol.isConnected();
    boolean inTransaction = currentProtocol != null && currentProtocol.inTransaction();

    try {
      if (currentProtocol != null && currentProtocol.isConnected() && currentProtocol.ping()) {
        // connection re-established
        // if in transaction cannot be sure that the last query has been received by server of not,
        // so rollback.and throw exception
        if (currentProtocol.inTransaction()) {
          currentProtocol.rollback();
        }
        return new HandleErrorResult(true);
      }
    } catch (QueryException e) {
      proxy.lock.lock();
      try {
        currentProtocol.close();
      } finally {
        proxy.lock.unlock();
      }
      if (setMasterHostFail()) {
        addToBlacklist(currentProtocol.getHostAddress());
      }
    }

    try {
      reconnectFailedConnection(new SearchFilter(true, false));
      handleFailLoop();
      if (alreadyClosed
          || (!alreadyClosed && !inTransaction && isQueryRelaunchable(method, args))) {
        return relaunchOperation(method, args);
      }
      return new HandleErrorResult(true);
    } catch (Exception e) {
      // we will throw a Connection exception that will close connection
      FailoverLoop.removeListener(this);
      return new HandleErrorResult();
    }
  }