@Override
 public void run() {
   if (null != listeners.get(ledgerId)) {
     LOG.debug("Re-read ledger metadata for {}.", ledgerId);
     readLedgerMetadata(ledgerId, this, AbstractZkLedgerManager.this);
   } else {
     LOG.debug("Ledger metadata listener for ledger {} is already removed.", ledgerId);
   }
 }
    @Override
    public void operationComplete(int rc, final LedgerMetadata result) {
      if (BKException.Code.OK == rc) {
        // reset the back off after a successful operation
        currentZKEBackOff = ZK_CONNECT_BACKOFF_MS_MIN;
        final Set<LedgerMetadataListener> listenerSet = listeners.get(ledgerId);
        if (null != listenerSet) {
          LOG.debug("Ledger metadata is changed for {} : {}.", ledgerId, result);
          scheduler.submit(
              new Runnable() {
                @Override
                public void run() {
                  for (LedgerMetadataListener listener : listenerSet) {
                    listener.onChanged(ledgerId, result);
                  }
                }
              });
        }
      } else if (BKException.Code.NoSuchLedgerExistsException == rc) {
        // the ledger is removed, do nothing
        Set<LedgerMetadataListener> listenerSet = listeners.remove(ledgerId);
        if (null != listenerSet) {
          LOG.debug(
              "Removed ledger metadata listener set on ledger {} as its ledger is deleted : {}",
              ledgerId,
              listenerSet.size());
        }
      } else {
        // Use constant backoff for all bookkeeper specific exceptions;
        // for ZK exceptions, use exponential back off
        int backOff = ZK_CONNECT_BACKOFF_MS_MIN;
        if (BKException.Code.ZKException == rc) {
          backOff = currentZKEBackOff;
          // Double the backoff for the next retry up to the maximum allowed back off
          currentZKEBackOff = Math.min(2 * currentZKEBackOff, ZK_CONNECT_BACKOFF_MS_MAX);
        }

        LOG.warn("Failed on read ledger metadata of ledger {} : {}", ledgerId, rc);
        scheduler.schedule(this, backOff, TimeUnit.MILLISECONDS);
      }
    }