From 2e7a20a59e968a6b73e3fb9e11f4fffc7e73d121 Mon Sep 17 00:00:00 2001
From: jvergara <jvergara@localhost>
Date: Tue, 25 Sep 2007 14:45:59 +0000
Subject: [PATCH] Fix for issue  2261 (dsreplication should provide a view of the replicated baseDNs in a topology).

---
 opendj-sdk/opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliMain.java |  710 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 697 insertions(+), 13 deletions(-)

diff --git a/opendj-sdk/opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliMain.java b/opendj-sdk/opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliMain.java
index b35863a..8246061 100644
--- a/opendj-sdk/opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliMain.java
+++ b/opendj-sdk/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

--
Gitblit v1.10.0