/** Start collecting global monitoring information for the replication domain. */
  private void initializePendingMonitorData() {
    // Let's process our directly connected DS
    // - in the ServerHandler for a given DS1, the stored state contains :
    // -- the max CSN produced by DS1
    // -- the last CSN consumed by DS1 from DS2..n
    // - in the ReplicationDomainDB/ReplicaDB, the built-in state contains:
    // -- the max CSN produced by each server
    // So for a given DS connected we can take the state and the max from
    // the DS/state.

    for (ServerHandler ds : domain.getConnectedDSs().values()) {
      final int serverId = ds.getServerId();
      final ServerState dsState = ds.getServerState().duplicate();

      CSN maxCSN = dsState.getCSN(serverId);
      if (maxCSN == null) {
        // This directly connected LS has never produced any change
        maxCSN = new CSN(0, 0, serverId);
      }
      pendingMonitorData.setMaxCSN(maxCSN);
      pendingMonitorData.setLDAPServerState(serverId, dsState);
      pendingMonitorData.setFirstMissingDate(serverId, ds.getApproxFirstMissingDate());
    }

    // Then initialize the max CSN for the LS that produced something
    // - from our own local db state
    // - whatever they are directly or indirectly connected
    final ServerState dbServerState = domain.getLatestServerState();
    pendingMonitorData.setRSState(domain.getLocalRSServerId(), dbServerState);
    for (CSN storedCSN : dbServerState) {
      pendingMonitorData.setMaxCSN(storedCSN);
    }
  }
  /**
   * Recomputes the monitor data for this replication server domain.
   *
   * @return The recomputed monitor data for this replication server domain.
   * @throws InterruptedException If this thread is interrupted while waiting for a response.
   */
  public ReplicationDomainMonitorData recomputeMonitorData() throws InterruptedException {
    // Only allow monitor recalculation at a time.
    synchronized (pendingMonitorLock) {
      if (monitorDataLastBuildDate + monitorDataLifeTime < TimeThread.getTime()) {
        try {
          DN baseDN = domain.getBaseDN();

          // Prevent out of band monitor responses from updating our pending
          // table until we are ready.
          synchronized (pendingMonitorDataLock) {
            // Clear the pending monitor data.
            pendingMonitorDataServerIDs.clear();
            pendingMonitorData = new ReplicationDomainMonitorData();

            initializePendingMonitorData();

            // Send the monitor requests to the connected replication servers.
            for (ServerHandler rs : domain.getConnectedRSs().values()) {
              final int serverId = rs.getServerId();

              MonitorRequestMsg msg = new MonitorRequestMsg(domain.getLocalRSServerId(), serverId);
              try {
                rs.send(msg);

                // Only register this server ID to pending table if we were able
                // to send the message.
                pendingMonitorDataServerIDs.add(serverId);
              } catch (IOException e) {
                // Log a message and do a best effort from here.
                logger.error(
                    ERR_SENDING_REMOTE_MONITOR_DATA_REQUEST, baseDN, serverId, e.getMessage());
              }
            }

            // Create the pending response latch based on the number of expected
            // monitor responses.
            pendingMonitorDataLatch = new CountDownLatch(pendingMonitorDataServerIDs.size());
          }

          // Wait for the responses to come back.
          pendingMonitorDataLatch.await(5, TimeUnit.SECONDS);

          // Log messages for replication servers that have gone or come back.
          synchronized (pendingMonitorDataLock) {
            // Log servers that have come back.
            for (int serverId : monitorDataLateServers) {
              // Ensure that we only log once per server: don't fill the
              // error log with repeated messages.
              if (!pendingMonitorDataServerIDs.contains(serverId)) {
                logger.info(NOTE_MONITOR_DATA_RECEIVED, baseDN, serverId);
              }
            }

            // Log servers that have gone away.
            for (int serverId : pendingMonitorDataServerIDs) {
              // Ensure that we only log once per server: don't fill the
              // error log with repeated messages.
              if (!monitorDataLateServers.contains(serverId)) {
                logger.warn(WARN_MISSING_REMOTE_MONITOR_DATA, baseDN, serverId);
              }
            }

            // Remember which servers were late this time.
            monitorDataLateServers.clear();
            monitorDataLateServers.addAll(pendingMonitorDataServerIDs);
          }

          // Store the new computed data as the reference
          synchronized (pendingMonitorDataLock) {
            // Now we have the expected answers or an error occurred
            pendingMonitorData.completeComputing();
            monitorData = pendingMonitorData;
            monitorDataLastBuildDate = TimeThread.getTime();
          }
        } finally {
          synchronized (pendingMonitorDataLock) {
            // Clear pending state.
            pendingMonitorData = null;
            pendingMonitorDataLatch = null;
            pendingMonitorDataServerIDs.clear();
          }
        }
      }
    }

    return monitorData;
  }