mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

jvergara
25.45.2007 67f22097f2e23feed40d54ffad407672a73b7ce8
opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliMain.java
@@ -97,7 +97,8 @@
/**
 * This class provides a tool that can be used to enable and disable replication
 * and also to initialize the contents of a replicated suffix with the contents
 * of another suffix.
 * of another suffix.  It also allows to display the replicated status of the
 * different base DNs of the servers that are registered in the ADS.
 */
public class ReplicationCliMain extends CliApplicationHelper
{
@@ -312,6 +313,10 @@
        {
          returnValue = initializeReplication();
        }
        else if (argParser.isStatusReplicationSubcommand())
        {
          returnValue = statusReplication();
        }
        else
        {
          err.println(wrapText(ERR_REPLICATION_VALID_SUBCOMMAND_NOT_FOUND.get(),
@@ -390,6 +395,35 @@
  }
  /**
   * Based on the data provided in the command-line it displays replication
   * status.
   * @return the error code if the operation failed and SUCCESSFUL if it was
   * successful.
   */
  private ReplicationCliReturnCode statusReplication()
  {
    ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
    StatusReplicationUserData uData = new StatusReplicationUserData();
    if (argParser.isInteractive())
    {
      if (promptIfRequired(uData))
      {
        returnValue = statusReplication(uData);
      }
      else
      {
        returnValue = USER_CANCELLED;
      }
    }
    else
    {
      initializeWithArgParser(uData);
      returnValue = statusReplication(uData);
    }
    return returnValue;
  }
  /**
   * Based on the data provided in the command-line it initializes replication
   * between two servers.
   * @return the error code if the operation failed and SUCCESSFUL if it was
@@ -1126,6 +1160,148 @@
  }
  /**
   * Updates the contents of the provided StatusReplicationUserData object
   * with the information provided in the command-line.  If some information
   * is missing, ask the user to provide valid data.
   * We assume that if this method is called we are in interactive mode.
   * @param uData the object to be updated.
   * @return <CODE>true</CODE> if the object was successfully updated and
   * <CODE>false</CODE> if the user cancelled the operation.
   */
  private boolean promptIfRequired(StatusReplicationUserData uData)
  {
    boolean cancelled = false;
    String adminPwd = argParser.getBindPasswordAdmin();
    String adminUid = argParser.getAdministratorUID();
    String host = argParser.getHostNameToStatus();
    if (host == null)
    {
      host = promptForString(
          INFO_REPLICATION_STATUS_HOSTNAME_PROMPT.get(),
          argParser.getDefaultHostNameToStatus(), false);
    }
    int port = argParser.getPortToStatus();
    if (port == -1)
    {
      port = promptForPort(
          INFO_REPLICATION_STATUS_PORT_PROMPT.get(),
          argParser.getDefaultPortToStatus(), false);
    }
    boolean useSSL = argParser.useSSLToStatus();
    boolean useStartTLS = argParser.useStartTLSToStatus();
    if (!useSSL && !useStartTLS)
    {
      useSSL = confirm(INFO_CLI_USESSL_PROMPT.get(), false);
      if (!useSSL)
      {
        useStartTLS =
          confirm(INFO_CLI_USESTARTTLS_PROMPT.get(), false);
      }
    }
    if (adminUid == null)
    {
      adminUid = askForAdministratorUID(argParser.getDefaultAdministratorUID());
    }
    if (adminPwd == null)
    {
      adminPwd = askForAdministratorPwd();
    }
    /*
     * Try to connect to the server.
     */
    InitialLdapContext ctx = null;
    while ((ctx == null) && !cancelled)
    {
      try
      {
        ctx = createContext(host, port, useSSL, useStartTLS,
            ADSContext.getAdministratorDN(adminUid), adminPwd,
            getTrustManager());
      }
      catch (NamingException ne)
      {
        LOG.log(Level.WARNING, "Error connecting to "+host+":"+port, ne);
        if (Utils.isCertificateException(ne))
        {
          String usedUrl = ConnectionUtils.getLDAPUrl(host, port, useSSL);
          if (!promptForCertificateConfirmation(ne, getTrustManager(), usedUrl,
              getTrustManager()))
          {
            cancelled = true;
          }
        }
        else
        {
          printLineBreak();
          printErrorMessage(ERR_ERROR_CONNECTING_TO_SERVER_PROMPT_AGAIN.get(
              host+":"+port, ne.toString()));
          printLineBreak();
          host = promptForString(
                INFO_REPLICATION_STATUS_HOSTNAME_PROMPT.get(),
                getValue(host, argParser.getDefaultHostNameToStatus()), false);
          port = promptForPort(
                INFO_REPLICATION_STATUS_PORT_PROMPT.get(),
              getValue(port, argParser.getDefaultPortToStatus()), false);
          useSSL = confirm(INFO_CLI_USESSL_PROMPT.get(), useSSL);
          if (!useSSL)
          {
            useStartTLS =
              confirm(INFO_CLI_USESTARTTLS_PROMPT.get(), useStartTLS);
          }
          adminUid = askForAdministratorUID(adminUid);
          adminPwd = askForAdministratorPwd();
        }
      }
    }
    if (!cancelled)
    {
      uData.setHostName(host);
      uData.setPort(port);
      uData.setUseSSL(useSSL);
      uData.setUseStartTLS(useStartTLS);
      uData.setAdminUid(adminUid);
      uData.setAdminPwd(adminPwd);
      uData.setScriptFriendly(argParser.isScriptFriendly());
    }
    if (ctx != null)
    {
      // If the server contains an ADS, try to load it and only load it: if
      // there are issues with the ADS they will be encountered in the
      // statusReplication(StatusReplicationUserData) method.  Here we have
      // to load the ADS to ask the user to accept the certificates and
      // eventually admin authentication data.
      InitialLdapContext[] aux = new InitialLdapContext[] {ctx};
      cancelled = !loadADSAndAcceptCertificates(aux, uData, false);
      ctx = aux[0];
    }
    if (!cancelled)
    {
      LinkedList<String> suffixes = argParser.getBaseDNs();
      uData.setBaseDNs(suffixes);
    }
    if (ctx != null)
    {
      try
      {
        ctx.close();
      }
      catch (Throwable t)
      {
      }
    }
    return !cancelled;
  }
  /**
   * Updates the contents of the provided InitializeReplicationUserData object
   * with the information provided in the command-line.  If some information
   * is missing, ask the user to provide valid data.
@@ -1652,6 +1828,34 @@
    uData.setUseStartTLS(argParser.useStartTLSToDisable());
  }
  /**
   * Initializes the contents of the provided status replication user data
   * object with what was provided in the command-line without prompting to the
   * user.
   * @param uData the disable replication user data object to be initialized.
   */
  private void initializeWithArgParser(StatusReplicationUserData uData)
  {
    uData.setBaseDNs(new LinkedList<String>(argParser.getBaseDNs()));
    String adminUid = getValue(argParser.getAdministratorUID(),
        argParser.getDefaultAdministratorUID());
    uData.setAdminUid(adminUid);
    String adminPwd = argParser.getBindPasswordAdmin();
    uData.setAdminPwd(adminPwd);
    String hostName = getValue(argParser.getHostNameToStatus(),
        argParser.getDefaultHostNameToStatus());
    uData.setHostName(hostName);
    int port = getValue(argParser.getPortToStatus(),
        argParser.getDefaultPortToStatus());
    uData.setPort(port);
    uData.setUseSSL(argParser.useSSLToStatus());
    uData.setUseStartTLS(argParser.useStartTLSToStatus());
    uData.setScriptFriendly(argParser.isScriptFriendly());
  }
  /**
   * Tells whether the server to which the LdapContext is connected has a
   * replication port or not.
@@ -1866,10 +2070,21 @@
        }
        if ((exceptionMsgs.size() > 0) && !cancelled)
        {
          cancelled = !confirm(
              ERR_REPLICATION_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE.
              get(Utils.getMessageFromCollection(exceptionMsgs,
          if (uData instanceof StatusReplicationUserData)
          {
            printWarningMessage(
                ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get(
                    Utils.getMessageFromCollection(exceptionMsgs,
                        Constants.LINE_SEPARATOR).toString()));
            printLineBreak();
          }
          else
          {
            cancelled = !confirm(
               ERR_REPLICATION_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE.
                get(Utils.getMessageFromCollection(exceptionMsgs,
                    Constants.LINE_SEPARATOR).toString()));
          }
        }
      }
    }
@@ -2335,6 +2550,69 @@
  }
  /**
   * Displays the replication status of the baseDNs specified in the
   * StatusReplicationUserData object.  This method does not prompt
   * to the user for information if something is missing.
   * @param uData the StatusReplicationUserData object.
   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
   * successful.
   * and the replication could be enabled and an error code otherwise.
   */
  private ReplicationCliReturnCode statusReplication(
      StatusReplicationUserData uData)
  {
    ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
    InitialLdapContext ctx = null;
    try
    {
      ctx = createContext(uData.getHostName(), uData.getPort(),
          uData.useSSL(), uData.useStartTLS(),
          ADSContext.getAdministratorDN(uData.getAdminUid()),
          uData.getAdminPwd(), getTrustManager());
    }
    catch (NamingException ne)
    {
      String hostPort = uData.getHostName()+":"+uData.getPort();
      printLineBreak();
      printErrorMessage(getMessageForException(ne, hostPort));
      LOG.log(Level.SEVERE, "Complete error stack:", ne);
    }
    if (ctx != null)
    {
      uData.setBaseDNs(uData.getBaseDNs());
      try
      {
        displayStatus(ctx, uData);
        returnValue = SUCCESSFUL;
      }
      catch (ReplicationCliException rce)
      {
        returnValue = rce.getErrorCode();
        printLineBreak();
        printErrorMessage(rce.getMessageObject());
        LOG.log(Level.SEVERE, "Complete error stack:", rce);
      }
    }
    else
    {
      returnValue = ERROR_CONNECTING;
    }
    if (ctx != null)
    {
      try
      {
        ctx.close();
      }
      catch (Throwable t)
      {
      }
    }
    return returnValue;
  }
  /**
   * Initializes the contents of one server with the contents of the other
   * using the parameters in the provided InitializeReplicationUserData.
   * This method does not prompt to the user for information if something is
@@ -2577,8 +2855,10 @@
            printErrorMessage(ERR_NO_SUFFIXES_SELECTED_TO_REPLICATE.get());
            for (String dn : availableSuffixes)
            {
              if (!Utils.areDnsEqual(ADSContext.getAdministrationSuffixDN(),
                  dn) && !Utils.areDnsEqual(Constants.SCHEMA_DN, dn))
              if (!Utils.areDnsEqual(dn,
                  ADSContext.getAdministrationSuffixDN()) &&
                  !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) &&
                  !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN))
              {
                if (confirm(INFO_REPLICATION_ENABLE_SUFFIX_PROMPT.get(dn)))
                {
@@ -2732,8 +3012,10 @@
            printErrorMessage(ERR_NO_SUFFIXES_SELECTED_TO_DISABLE.get());
            for (String dn : availableSuffixes)
            {
              if (!Utils.areDnsEqual(ADSContext.getAdministrationSuffixDN(),
                  dn) && !Utils.areDnsEqual(Constants.SCHEMA_DN, dn))
              if (!Utils.areDnsEqual(dn,
                  ADSContext.getAdministrationSuffixDN()) &&
                  !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) &&
                  !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN))
              {
                if (confirm(INFO_REPLICATION_DISABLE_SUFFIX_PROMPT.get(dn)))
                {
@@ -2829,8 +3111,10 @@
            for (String dn : availableSuffixes)
            {
              if (!Utils.areDnsEqual(ADSContext.getAdministrationSuffixDN(),
                  dn) && !Utils.areDnsEqual(Constants.SCHEMA_DN, dn))
              if (!Utils.areDnsEqual(dn,
                  ADSContext.getAdministrationSuffixDN()) &&
                  !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) &&
                  !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN))
              {
                if (confirm(INFO_REPLICATION_INITIALIZE_SUFFIX_PROMPT.get(dn)))
                {
@@ -2850,7 +3134,7 @@
   * @param ctx1 the connection to the first server.
   * @param ctx2 the connection to the second server.
   * @param uData the EnableReplicationUserData object containing the required
   * parameters to update the confgiuration.
   * parameters to update the configuration.
   * @throws ReplicationCliException if there is an error.
   */
  private void updateConfiguration(InitialLdapContext ctx1,
@@ -3099,7 +3383,7 @@
    if (uData.replicateSchema())
    {
      baseDNs = uData.getBaseDNs();
      baseDNs.add("cn=schema");
      baseDNs.add(Constants.SCHEMA_DN);
      uData.setBaseDNs(baseDNs);
    }
    TopologyCache cache1 = null;
@@ -3318,7 +3602,7 @@
   * they are referenced) to disable replication.
   * @param ctx the connection to the server.
   * @param uData the DisableReplicationUserData object containing the required
   * parameters to update the confgiuration.
   * parameters to update the configuration.
   * @throws ReplicationCliException if there is an error.
   */
  private void updateConfiguration(InitialLdapContext ctx,
@@ -3421,6 +3705,406 @@
  }
  /**
   * Displays the replication status of the different base DNs in the servers
   * registered in the ADS.
   * @param ctx the connection to the server.
   * @param uData the StatusReplicationUserData object containing the required
   * parameters to update the configuration.
   * @throws ReplicationCliException if there is an error.
   */
  private void displayStatus(InitialLdapContext ctx,
      StatusReplicationUserData uData) throws ReplicationCliException
  {
    ADSContext adsCtx = new ADSContext(ctx);
    TopologyCache cache = null;
    try
    {
      if (adsCtx.hasAdminData())
      {
        cache = new TopologyCache(adsCtx, getTrustManager());
        cache.reloadTopology();
      }
    }
    catch (ADSContextException adce)
    {
      throw new ReplicationCliException(
          ERR_REPLICATION_READING_ADS.get(adce.getMessage()),
          ERROR_READING_ADS, adce);
    }
    catch (TopologyCacheException tce)
    {
      throw new ReplicationCliException(
          ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
          ERROR_READING_TOPOLOGY_CACHE, tce);
    }
    if (!argParser.isInteractive())
    {
      // Inform the user of the potential errors that we found.
      LinkedHashSet<Message> messages = new LinkedHashSet<Message>();
      if (cache != null)
      {
        messages.addAll(getErrorMessages(cache));
      }
      if (!messages.isEmpty())
      {
        Message msg =
            ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get(
                Utils.getMessageFromCollection(messages,
                    Constants.LINE_SEPARATOR).toString());
        printWarningMessage(msg);
      }
    }
    LinkedList<String> userBaseDNs = uData.getBaseDNs();
    LinkedList<Set<ReplicaDescriptor>> replicaLists =
      new LinkedList<Set<ReplicaDescriptor>>();
    boolean oneReplicated = false;
    for (SuffixDescriptor suffix : cache.getSuffixes())
    {
      String dn = suffix.getDN();
      // If no base DNs where specified display all the base DNs but the schema
      // and cn=admin data.
      boolean found = userBaseDNs.isEmpty() &&
      !Utils.areDnsEqual(dn, ADSContext.getAdministrationSuffixDN()) &&
      !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) &&
      !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN);
      for (String baseDN : userBaseDNs)
      {
        found = Utils.areDnsEqual(baseDN, dn);
        if (found)
        {
          break;
        }
      }
      if (found)
      {
        boolean replicated = false;
        for (ReplicaDescriptor replica : suffix.getReplicas())
        {
          if (replica.isReplicated())
          {
            replicated = true;
            break;
          }
        }
        if (replicated)
        {
          oneReplicated = true;
          replicaLists.add(suffix.getReplicas());
        }
        else
        {
          // Check if there are already some non replicated base DNs.
          found = false;
          for (Set<ReplicaDescriptor> replicas : replicaLists)
          {
            ReplicaDescriptor replica = replicas.iterator().next();
            if (!replica.isReplicated() &&
                Utils.areDnsEqual(dn, replica.getSuffix().getDN()))
            {
              replicas.addAll(suffix.getReplicas());
              found = true;
              break;
            }
          }
          if (!found)
          {
            replicaLists.add(suffix.getReplicas());
          }
        }
      }
    }
    if (replicaLists.isEmpty())
    {
      printProgressMessage(INFO_REPLICATION_STATUS_NO_BASEDNS.get());
      printProgressLineBreak();
    }
    else
    {
      LinkedList<Set<ReplicaDescriptor>> orderedReplicaLists =
        new LinkedList<Set<ReplicaDescriptor>>();
      for (Set<ReplicaDescriptor> replicas1 : replicaLists)
      {
        String dn1 = replicas1.iterator().next().getSuffix().getDN();
        boolean inserted = false;
        for (int i=0; i<orderedReplicaLists.size() && !inserted; i++)
        {
          String dn2 =
            orderedReplicaLists.get(i).iterator().next().getSuffix().getDN();
          if (dn1.compareTo(dn2) < 0)
          {
            orderedReplicaLists.add(i, replicas1);
            inserted = true;
          }
        }
        if (!inserted)
        {
          orderedReplicaLists.add(replicas1);
        }
      }
      for (Set<ReplicaDescriptor> replicas : orderedReplicaLists)
      {
        printProgressLineBreak();
        displayStatus(replicas, uData.isScriptFriendly());
      }
      if (oneReplicated && !uData.isScriptFriendly())
      {
        printProgressLineBreak();
        printProgressMessage(INFO_REPLICATION_STATUS_REPLICATED_LEGEND.get());
        printProgressLineBreak();
      }
    }
  }
  /**
   * Displays the replication status of the replicas provided.  The code assumes
   * that all the replicas have the same baseDN and that if they are replicated
   * all the replicas are replicated with each other.
   * @param replicas the list of replicas that we are trying to display.
   * @param scriptFriendly wheter to display it on script-friendly mode or not.
   */
  private void displayStatus(Set<ReplicaDescriptor> replicas,
      boolean scriptFriendly)
  {
    boolean isReplicated = false;
    Set<ReplicaDescriptor> orderedReplicas =
      new LinkedHashSet<ReplicaDescriptor>();
    Set<String> hostPorts = new TreeSet<String>();
    for (ReplicaDescriptor replica : replicas)
    {
      if (replica.isReplicated())
      {
        isReplicated = true;
      }
      hostPorts.add(replica.getServer().getHostPort(true));
    }
    for (String hostPort : hostPorts)
    {
      for (ReplicaDescriptor replica : replicas)
      {
        if (replica.getServer().getHostPort(true).equals(hostPort))
        {
          orderedReplicas.add(replica);
        }
      }
    }
    int nCols;
    final int SERVERPORT = 0;
    final int NUMBER_ENTRIES = 1;
    final int MISSING_CHANGES = 2;
    final int AGE_OF_OLDEST_MISSING_CHANGE = 3;
    Message[] headers;
    if (scriptFriendly)
    {
      if (isReplicated)
      {
        nCols = 4;
        headers = new Message[] {
            INFO_REPLICATION_STATUS_LABEL_SERVERPORT.get(),
            INFO_REPLICATION_STATUS_LABEL_NUMBER_ENTRIES.get(),
            INFO_REPLICATION_STATUS_LABEL_MISSING_CHANGES.get(),
            INFO_REPLICATION_STATUS_LABEL_AGE_OF_OLDEST_MISSING_CHANGE.get()
        };
      }
      else
      {
        nCols = 2;
        headers = new Message[] {
            INFO_REPLICATION_STATUS_LABEL_SERVERPORT.get(),
            INFO_REPLICATION_STATUS_LABEL_NUMBER_ENTRIES.get()
        };
      }
    }
    else
    {
      if (isReplicated)
      {
        nCols = 4;
        headers = new Message[] {
            INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get(),
            INFO_REPLICATION_STATUS_HEADER_NUMBER_ENTRIES.get(),
            INFO_REPLICATION_STATUS_HEADER_MISSING_CHANGES.get(),
            INFO_REPLICATION_STATUS_HEADER_AGE_OF_OLDEST_MISSING_CHANGE.get()
        };
      }
      else
      {
        nCols = 2;
        headers = new Message[] {
            INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get(),
            INFO_REPLICATION_STATUS_HEADER_NUMBER_ENTRIES.get()
        };
      }
    }
    Message[][] values = new Message[orderedReplicas.size()][nCols];
    int[] maxWidths = new int[nCols];
    int i;
    for (i=0; i<maxWidths.length; i++)
    {
      maxWidths[i] = Message.toString(headers[i]).length();
    }
    i = 0;
    for (ReplicaDescriptor replica : orderedReplicas)
    {
      Message v;
      for (int j=0; j<nCols; j++)
      {
        switch (j)
        {
        case SERVERPORT:
          v = Message.raw(replica.getServer().getHostPort(true));
          break;
        case NUMBER_ENTRIES:
          int nEntries = replica.getEntries();
          if (nEntries >= 0)
          {
            v = Message.raw(String.valueOf(nEntries));
          }
          else
          {
            v = INFO_NOT_AVAILABLE_LABEL.get();
          }
          break;
        case MISSING_CHANGES:
          int missingChanges = replica.getMissingChanges();
          if (missingChanges >= 0)
          {
            v = Message.raw(String.valueOf(missingChanges));
          }
          else
          {
            v = INFO_NOT_AVAILABLE_LABEL.get();
          }
          break;
        case AGE_OF_OLDEST_MISSING_CHANGE:
          int ageOfOldestMissingChange = replica.getAgeOfOldestMissingChange();
          if (ageOfOldestMissingChange >= 0)
          {
            v = Message.raw(String.valueOf(ageOfOldestMissingChange));
          }
          else
          {
            v = INFO_NOT_AVAILABLE_LABEL.get();
          }
          break;
        default:
          throw new IllegalStateException("Unknown index: "+j);
        }
        values[i][j] = v;
        maxWidths[j] = Math.max(maxWidths[j], v.toString().length());
      }
      i++;
    }
    int totalWidth = 0;
    for (i=0; i<maxWidths.length; i++)
    {
      if (i < maxWidths.length - 1)
      {
        maxWidths[i] += 5;
      }
      totalWidth += maxWidths[i];
    }
    String dn = replicas.iterator().next().getSuffix().getDN();
    if (scriptFriendly)
    {
      Message[] labels = {
          INFO_REPLICATION_STATUS_BASEDN.get(),
          INFO_REPLICATION_STATUS_IS_REPLICATED.get()
      };
      Message[] vs = {
          Message.raw(dn),
          isReplicated ? INFO_BASEDN_REPLICATED_LABEL.get() :
            INFO_BASEDN_NOT_REPLICATED_LABEL.get()
      };
      for (i=0; i<labels.length; i++)
      {
        printProgressMessage(labels[i]+": "+vs[i]);
        printProgressLineBreak();
      }
      for (i=0; i<values.length; i++)
      {
        printProgressMessage("-");
        printProgressLineBreak();
        for (int j=0; j<values[i].length; j++)
        {
          printProgressMessage(headers[j]+": "+values[i][j]);
          printProgressLineBreak();
        }
      }
    }
    else
    {
      if (isReplicated)
      {
        printProgressMessage(
            INFO_REPLICATION_STATUS_REPLICATED.get(dn));
        printProgressLineBreak();
      }
      else
      {
        printProgressMessage(
            INFO_REPLICATION_STATUS_NOT_REPLICATED.get(dn));
        printProgressLineBreak();
      }
      MessageBuilder headerLine = new MessageBuilder();
      for (i=0; i<maxWidths.length; i++)
      {
        String header = headers[i].toString();
        headerLine.append(header);
        int extra = maxWidths[i] - header.length();
        for (int j=0; j<extra; j++)
        {
          headerLine.append(" ");
        }
      }
      StringBuilder builder = new StringBuilder();
      for (i=0; i<headerLine.length(); i++)
      {
        builder.append("=");
      }
      printProgressMessage(builder.toString());
      printProgressLineBreak();
      printProgressMessage(headerLine.toMessage());
      printProgressLineBreak();
      builder = new StringBuilder();
      for (i=0; i<headerLine.length(); i++)
      {
        builder.append("-");
      }
      printProgressMessage(builder.toString());
      printProgressLineBreak();
      for (i=0; i<values.length; i++)
      {
        MessageBuilder line = new MessageBuilder();
        for (int j=0; j<values[i].length; j++)
        {
          int extra = maxWidths[j];
          line.append(values[i][j]);
          extra -= values[i][j].length();
          for (int k=0; k<extra; k++)
          {
            line.append(" ");
          }
        }
        printProgressMessage(line.toMessage());
        printProgressLineBreak();
      }
    }
  }
  /**
   * Retrieves all the replication servers for a given baseDN.  The
   * ServerDescriptor is used to identify the server where the suffix is
   * defined and it cannot be null.  The TopologyCache is used to retrieve