From 1d5f9c9484a47d91ab2c4cf3796e5b1effca89ec Mon Sep 17 00:00:00 2001
From: jvergara <jvergara@localhost>
Date: Wed, 18 Apr 2007 14:15:39 +0000
Subject: [PATCH] The following changes have two main goals:

---
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelLauncher.java       |    2 
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/StatusPanelDialog.java      |  223 +++++---
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/SortableTableModel.java       |   67 ++
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/UIFactory.java                |  125 ++++
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/Utils.java                  |   29 +
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromLDAP.java            |  202 +++++++
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromFile.java            |  113 ++++
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/DatabaseDescriptor.java        |   49 +
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelController.java     |    2 
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusCli.java                 |  136 ++++
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/DatabasesTableModel.java    |  405 +++++++++++++-
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/LoginDialog.java            |    2 
 opendj-sdk/opends/src/server/org/opends/server/util/SetupUtils.java                     |    8 
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/BaseDNDescriptor.java          |  177 ++++++
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/resources/Resources.properties |   10 
 opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/ListenersTableModel.java    |    1 
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties   |    6 
 17 files changed, 1,382 insertions(+), 175 deletions(-)

diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
index 2c473e8..dc168cb 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
@@ -203,6 +203,7 @@
 shutdown-button-label=Shutdown
 continue-button-label=Continue
 continue-button-install-tooltip=Continue with the QuickSetup Tool
+ok-button-label=OK
 
 #
 # Confirmation dialogs
@@ -749,11 +750,12 @@
 
 upgrade-review-panel-title=Review
 upgrade-review-panel-instructions=Review your settings and click Finish if they \
-  are correct.
+are correct.
 upgrade-review-panel-server-label=Server to Upgrade:
 upgrade-review-panel-server-tooltip=File system location of the build that will be upgraded
 upgrade-review-panel-old-version-label=Old Version:
 upgrade-review-panel-old-version-tooltip=The current version of the server
 upgrade-review-panel-new-version-label=New Version:
 upgrade-review-panel-new-version-tooltip=The target version of the server
-upgrade-review-panel-start-server=Start Server when the Upgrade has Completed
\ No newline at end of file
+upgrade-review-panel-start-server=Start Server when the Upgrade has Completed
+
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/SortableTableModel.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/SortableTableModel.java
new file mode 100644
index 0000000..fb45a66
--- /dev/null
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/SortableTableModel.java
@@ -0,0 +1,67 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.quicksetup.ui;
+
+import javax.swing.table.TableModel;
+
+/**
+ * A generic interface that must implement table models that are sortable.
+ */
+public interface SortableTableModel extends TableModel
+{
+  /**
+   * Returns whether the sort is ascending or descending.
+   * @return <CODE>true</CODE> if the sort is ascending and <CODE>false</CODE>
+   * otherwise.
+   */
+  public boolean isSortAscending();
+
+  /**
+   * Sets whether to sort ascending of descending.
+   * @param sortAscending whether to sort ascending or descending.
+   */
+  public void setSortAscending(boolean sortAscending);
+
+  /**
+   * Returns the column index used to sort.
+   * @return the column index used to sort.
+   */
+  public int getSortColumn();
+
+  /**
+   * Sets the column index used to sort.
+   * @param sortColumn column index used to sort..
+   */
+  public void setSortColumn(int sortColumn);
+
+  /**
+   * Updates the table model contents and sorts its contents depending on the
+   * sort options set by the user.
+   */
+  public void forceResort();
+}
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/UIFactory.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/UIFactory.java
index 4b9b33d..8766596 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/UIFactory.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/UIFactory.java
@@ -34,12 +34,18 @@
 import java.awt.Insets;
 import java.awt.Rectangle;
 import java.awt.Toolkit;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.util.HashMap;
 
+
 import javax.swing.*;
 import javax.swing.text.JTextComponent;
 import javax.swing.border.Border;
 import javax.swing.border.EmptyBorder;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
 
 import org.opends.quicksetup.i18n.ResourceProvider;
 
@@ -78,9 +84,13 @@
   public static final int LEFT_INSET_STEP = 5;
 
   /**
+   * Specifies the extra left inset for the sub-steps.
+   */
+  public static final int LEFT_INSET_SUBSTEP = 10;
+  /**
    * Specifies the top inset for the instructions sub panel.
    */
-  public static final int TOP_INSET_INSTRUCTIONS_SUBPANEL = 10;
+  public static final int TOP_INSET_INSTRUCTIONS_SUBPANEL = 5;
 
   /**
    * Specifies the top inset for input subpanel.
@@ -173,6 +183,11 @@
   public static final int LEFT_INSET_COPY_BUTTON = 10;
 
   /**
+   * Specifies the left inset for a subordinate subpanel.
+   */
+  public static final int LEFT_INSET_SUBPANEL_SUBORDINATE = 30;
+
+  /**
    * Specifies the left inset for the progress bar.
    */
   public static final int BOTTOM_INSET_PROGRESS_BAR = 10;
@@ -193,6 +208,16 @@
   public static final int RELATIVE_PATH_FIELD_SIZE = 10;
 
   /**
+   * Specifies the number of columns of a text field for a host name.
+   */
+  public static final int HOST_FIELD_SIZE = 20;
+
+  /**
+   * Specifies the number of columns of a text field for a UID.
+   */
+  public static final int UID_FIELD_SIZE = 15;
+
+  /**
    * Specifies the number of columns of a text field for a port.
    */
   public static final int PORT_FIELD_SIZE = 5;
@@ -335,7 +360,7 @@
    * Specifies the font for the instructions of the current panel.
    */
   public static final Font INSTRUCTIONS_FONT =
-    Font.decode("Arial-PLAIN-14");
+    Font.decode("Arial-PLAIN-12");
 
   /**
    * Specifies the font for the instructions of the current panel.
@@ -1087,7 +1112,7 @@
    */
   public static String applyFontToHtml(String html, Font font)
   {
-    StringBuffer buf = new StringBuffer();
+    StringBuilder buf = new StringBuilder();
 
     buf.append("<span style=\"").append(getFontStyle(font)).append("\">")
         .append(html).append(SPAN_CLOSE);
@@ -1096,6 +1121,56 @@
   }
 
   /**
+   * Returns a table created with the provided model and renderers.
+   * @param tableModel the table model.
+   * @param renderer the cell renderer.
+   * @param headerRenderer the header renderer.
+   * @return a table created with the provided model and renderers.
+   */
+  public static JTable makeSortableTable(final SortableTableModel tableModel,
+      TableCellRenderer renderer,
+      TableCellRenderer headerRenderer)
+  {
+    final JTable table = new JTable(tableModel);
+    table.setShowGrid(true);
+    table.setGridColor(UIFactory.PANEL_BORDER_COLOR);
+    table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
+    table.setBackground(UIFactory.CURRENT_STEP_PANEL_BACKGROUND);
+    table.getTableHeader().setBackground(UIFactory.DEFAULT_BACKGROUND);
+    table.setRowMargin(0);
+
+    for (int i=0; i<tableModel.getColumnCount(); i++)
+    {
+      TableColumn col = table.getColumn(table.getColumnName(i));
+      col.setCellRenderer(renderer);
+      col.setHeaderRenderer(headerRenderer);
+    }
+    MouseAdapter listMouseListener = new MouseAdapter() {
+      public void mouseClicked(MouseEvent e) {
+        TableColumnModel columnModel = table.getColumnModel();
+        int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+        int sortedBy = table.convertColumnIndexToModel(viewColumn);
+        if (e.getClickCount() == 1 && sortedBy != -1) {
+          tableModel.setSortAscending(!tableModel.isSortAscending());
+          tableModel.setSortColumn(sortedBy);
+          tableModel.forceResort();
+        }
+      }
+    };
+    table.getTableHeader().addMouseListener(listMouseListener);
+    return table;
+  }
+
+  /**
+   * Creates a header renderer for a JTable with our own look and feel.
+   * @return a header renderer for a JTable with our own look and feel.
+   */
+  public static TableCellRenderer makeHeaderRenderer()
+  {
+    return new HeaderRenderer();
+  }
+
+  /**
    * Returns a String that contains the html passed as parameter with a div
    * applied.  The div style corresponds to the Font specified as parameter.
    * The goal of this method is to be able to specify a font for an HTML string.
@@ -1107,7 +1182,7 @@
    */
   public static String applyFontToHtmlWithDiv(String html, Font font)
   {
-    StringBuffer buf = new StringBuffer();
+    StringBuilder buf = new StringBuilder();
 
     buf.append("<div style=\"").append(getFontStyle(font)).append("\">")
         .append(html).append(DIV_CLOSE);
@@ -1122,7 +1197,7 @@
    */
   private static String getFontStyle(Font font)
   {
-    StringBuffer buf = new StringBuffer();
+    StringBuilder buf = new StringBuilder();
 
     buf.append("font-family:" + font.getName()).append(
         ";font-size:" + font.getSize() + "pt");
@@ -1510,6 +1585,46 @@
 }
 
 /**
+ * Class used to render the table headers.
+ */
+class HeaderRenderer extends JLabel implements TableCellRenderer
+{
+  private static final long serialVersionUID = -8604332267021523835L;
+
+  /**
+   * Default constructor.
+   */
+  public HeaderRenderer()
+  {
+    super();
+    UIFactory.setTextStyle(this, UIFactory.TextStyle.PRIMARY_FIELD_VALID);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Component getTableCellRendererComponent(JTable table, Object value,
+      boolean isSelected, boolean hasFocus, int row, int column) {
+    setText((String)value);
+    if (column == 0)
+    {
+      setBorder(BorderFactory.createCompoundBorder(
+          BorderFactory.createMatteBorder(1, 1, 1, 1,
+              UIFactory.PANEL_BORDER_COLOR),
+              BorderFactory.createEmptyBorder(4, 4, 4, 4)));
+    }
+    else
+    {
+      setBorder(BorderFactory.createCompoundBorder(
+          BorderFactory.createMatteBorder(1, 0, 1, 1,
+              UIFactory.PANEL_BORDER_COLOR),
+              BorderFactory.createEmptyBorder(4, 4, 4, 4)));
+    }
+    return this;
+  }
+}
+
+/**
  * This class has been written to have a better behaviour with the scroll pane
  * than the one we have by default in the case of the progress panel.
  *
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/Utils.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/Utils.java
index 92583af..452ec9e 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/Utils.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/Utils.java
@@ -63,6 +63,8 @@
  */
 public class Utils
 {
+  private static final int DEFAULT_LDAP_CONNECT_TIMEOUT = 3000;
+
   private static final int BUFFER_SIZE = 1024;
 
   private static final int MAX_LINE_WIDTH = 80;
@@ -269,6 +271,15 @@
   }
 
   /**
+   * Returns a String representation of the OS we are running.
+   * @return a String representation of the OS we are running.
+   */
+  public static String getOSString()
+  {
+    return SetupUtils.getOSString();
+  }
+
+  /**
    * Returns <CODE>true</CODE> if the parent directory for the provided path
    * exists and <CODE>false</CODE> otherwise.
    * @param path the path that we are analyzing.
@@ -855,7 +866,8 @@
     try
     {
       InitialLdapContext ctx =
-        Utils.createLdapContext(ldapUrl, dn, pwd, 3000, null);
+        Utils.createLdapContext(ldapUrl, dn, pwd,
+            Utils.getDefaultLDAPTimeout(), null);
 
       /*
        * Search for the config to check that it is the directory manager.
@@ -950,13 +962,13 @@
    * Displays an error message dialog.
    *
    * @param parent
-   *          the parent frame of the error dialog.
+   *          the parent component of the error dialog.
    * @param msg
    *          the error message.
    * @param title
    *          the title for the dialog.
    */
-  public static void displayError(JFrame parent, String msg, String title)
+  public static void displayError(Component parent, String msg, String title)
   {
     JOptionPane.showMessageDialog(parent, msg, title,
         JOptionPane.ERROR_MESSAGE);
@@ -1107,6 +1119,17 @@
   }
 
   /**
+   * Returns the default LDAP timeout in milliseconds when we try to connect to
+   * a server.
+   * @return the default LDAP timeout in milliseconds when we try to connect to
+   * a server.
+   */
+  public static int getDefaultLDAPTimeout()
+  {
+    return DEFAULT_LDAP_CONNECT_TIMEOUT;
+  }
+
+  /**
    * Returns the max size in character of a line to be displayed in the command
    * line.
    * @return the max size in character of a line to be displayed in the command
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/SetupUtils.java b/opendj-sdk/opends/src/server/org/opends/server/util/SetupUtils.java
index 5dff2b5..22ead99 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/SetupUtils.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/SetupUtils.java
@@ -153,6 +153,14 @@
       return OperatingSystem.WINDOWS == getOperatingSystem();
   }
 
+  /**
+   * Returns a String representation of the OS we are running.
+   * @return a String representation of the OS we are running.
+   */
+  public static String getOSString()
+  {
+    return getOperatingSystem().toString();
+  }
 
   /**
    * Commodity method to help identifying the OS we are running on.
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/BaseDNDescriptor.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/BaseDNDescriptor.java
new file mode 100644
index 0000000..6b41336
--- /dev/null
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/BaseDNDescriptor.java
@@ -0,0 +1,177 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.statuspanel;
+
+import org.opends.quicksetup.util.Utils;
+
+/**
+ * This class is used to represent a Base DN / Replica and is aimed to be
+ * used by the classes in the DatabasesTableModel class.
+ *
+ */
+public class BaseDNDescriptor
+{
+  /**
+   * An enumeration describing the type of base DN for a given Database.
+   */
+  public enum Type
+  {
+    /**
+     * The base DN is not synchronized.
+     */
+    NOT_SYNCHRONIZED,
+    /**
+     * The base DN is synchronized.
+     */
+    SYNCHRONIZED
+  };
+
+  private int missingChanges;
+  private DatabaseDescriptor db;
+  private int ageOfOldestMissingChange;
+  private Type type;
+  private String baseDn;
+
+  /**
+   * Constructor for this class.
+   * @param type the type of synchronization.
+   * @param baseDn the base DN associated with the Synchronization.
+   * @param db the database containing this base DN.
+   * @param ageOfOldestMissingChange the number of missing changes.
+   * @param missingChanges the number of missing changes.
+   */
+  public BaseDNDescriptor(Type type, String baseDn, DatabaseDescriptor db,
+      int ageOfOldestMissingChange, int missingChanges)
+  {
+    this.baseDn = baseDn;
+    this.db = db;
+    this.type = type;
+    this.ageOfOldestMissingChange = ageOfOldestMissingChange;
+    this.missingChanges = missingChanges;
+  }
+
+  /**
+   * Return the String DN associated with the base DN..
+   * @return the String DN associated with the base DN.
+   */
+  public String getDn()
+  {
+    return baseDn;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean equals(Object v)
+  {
+    boolean equals = false;
+    if (this != v)
+    {
+      if (v instanceof BaseDNDescriptor)
+      {
+        BaseDNDescriptor desc = (BaseDNDescriptor)v;
+        equals = (getType() == desc.getType()) &&
+        Utils.areDnsEqual(getDn(), desc.getDn()) &&
+        (getAgeOfOldestMissingChange() == desc.getAgeOfOldestMissingChange()) &&
+        (getMissingChanges() == desc.getMissingChanges());
+      }
+    }
+    else
+    {
+      equals = true;
+    }
+    return equals;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public int hashCode()
+  {
+    return (getType().toString() + getAgeOfOldestMissingChange() + getDn() +
+        getMissingChanges()).hashCode();
+  }
+
+  /**
+   * Returns the number of missing changes in the synchronization topology for
+   * this base DN.
+   * @return the number of missing changes in the synchronization topology for
+   * this base DN.
+   */
+  public int getMissingChanges()
+  {
+    return missingChanges;
+  }
+
+  /**
+   * Returns the age of the oldest missing change in seconds in the
+   * synchronization topology for this base DN.
+   * @return the age of the oldest missing change in seconds in the
+   * synchronization topology for this base DN.
+   */
+  public int getAgeOfOldestMissingChange()
+  {
+    return ageOfOldestMissingChange;
+  }
+
+  /**
+   * Returns the type for this base DN.
+   * @return the type for this base DN.
+   */
+  public Type getType()
+  {
+    return type;
+  }
+
+  /**
+   * Returns the database where this base DN is defined.
+   * @return the database where this base DN is defined.
+   */
+  public DatabaseDescriptor getDatabase()
+  {
+    return db;
+  }
+
+  /**
+   * Sets the type of this base DN.
+   * @param type the new type for this base DN.
+   */
+  void setType(Type type)
+  {
+    this.type = type;
+  }
+
+  /**
+   * Sets the database containing this base DN.
+   * @param db the database containing this base DN.
+   */
+  void setDatabase(DatabaseDescriptor db)
+  {
+    this.db = db;
+  }
+}
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromFile.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromFile.java
index dbffbf1..5b22f9b 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromFile.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromFile.java
@@ -32,6 +32,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.TreeSet;
 
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.util.LDIFException;
@@ -62,6 +63,11 @@
     DirectoryServer.getObjectClass("ds-cfg-backend", true);
   private final ObjectClass administrativeUserOc =
     DirectoryServer.getObjectClass("ds-cfg-root-dn", true);
+  private final ObjectClass syncProviderOc =
+    DirectoryServer.getObjectClass("ds-cfg-synchronization-provider", true);
+  private final ObjectClass syncConfigOc =
+    DirectoryServer.getObjectClass("ds-cfg-synchronization-provider-config",
+    true);
 
   private HashSet<ListenerDescriptor> listeners =
     new HashSet<ListenerDescriptor>();
@@ -69,7 +75,8 @@
     new HashSet<DatabaseDescriptor>();
   private HashSet<String> administrativeUsers = new HashSet<String>();
   private String errorMessage;
-
+  private boolean synchronizationConfigured = false;
+  private HashSet<String> synchronizedSuffixes = new HashSet<String>();
 
   /**
    * Default constructor.
@@ -87,6 +94,11 @@
   public void readConfiguration()
   {
     errorMessage = null;
+    listeners.clear();
+    databases.clear();
+    administrativeUsers.clear();
+    synchronizationConfigured = false;
+    synchronizedSuffixes.clear();
     try
     {
       Installation installation =
@@ -99,6 +111,7 @@
       {
         updateConfig(entry);
       }
+      updateSynchronization();
     }
     catch (IOException ioe)
     {
@@ -246,6 +259,14 @@
    {
      updateConfigWithAdministrativeUser(entry);
    }
+   else if (entry.hasObjectClass(syncProviderOc))
+   {
+     updateConfigWithSyncProviderEntry(entry);
+   }
+   else if (entry.hasObjectClass(syncConfigOc))
+   {
+     updateConfigWithSyncConfig(entry);
+   }
   }
 
   /**
@@ -350,13 +371,26 @@
    */
   private void updateConfigWithBackend(Entry entry)
   {
-    String baseDn = getFirstValue(entry, "ds-cfg-backend-base-dn");
     String id = getFirstValue(entry, "ds-cfg-backend-id");
     int nEntries = -1; // Unknown
 
     if (!isConfigBackend(id))
     {
-      databases.add(new DatabaseDescriptor(id, baseDn, nEntries));
+      Set<String> baseDns = new TreeSet<String>();
+      baseDns.addAll(getValues(entry, "ds-cfg-backend-base-dn"));
+      Set<BaseDNDescriptor> replicas = new LinkedHashSet<BaseDNDescriptor>();
+
+      for (String baseDn : baseDns)
+      {
+        replicas.add(getBaseDNDescriptor(entry, baseDn));
+      }
+
+      DatabaseDescriptor db = new DatabaseDescriptor(id, replicas, nEntries);
+      databases.add(db);
+      for (BaseDNDescriptor rep: replicas)
+      {
+        rep.setDatabase(db);
+      }
     }
   }
 
@@ -371,6 +405,70 @@
   }
 
   /**
+   * Updates the synchronization configuration data we expose to the user with
+   * the provided entry object.
+   * @param entry the entry to analyze.
+   */
+  private void updateConfigWithSyncProviderEntry(Entry entry)
+  {
+    if ("true".equalsIgnoreCase(getFirstValue(entry,
+        "ds-cfg-synchronization-provider-enabled")))
+    {
+      synchronizationConfigured = true;
+    }
+    else
+    {
+      synchronizationConfigured = false;
+    }
+  }
+
+
+  /**
+   * Updates the databases suffixes with the list of synchronized suffixes
+   * found.
+   */
+  private void updateSynchronization()
+  {
+    if (synchronizationConfigured)
+    {
+      for (String suffixDn: synchronizedSuffixes)
+      {
+        BaseDNDescriptor replica = null;
+        for (DatabaseDescriptor db: databases)
+        {
+          Set<BaseDNDescriptor> replicas = db.getBaseDns();
+          for (BaseDNDescriptor rep: replicas)
+          {
+            if (Utils.areDnsEqual(rep.getDn(), suffixDn))
+            {
+              replica = rep;
+              break;
+            }
+          }
+          if (replica != null)
+          {
+            break;
+          }
+        }
+        if (replica != null)
+        {
+          replica.setType(BaseDNDescriptor.Type.SYNCHRONIZED);
+        }
+      }
+    }
+  }
+
+  /**
+   * Updates the synchronization configuration data we expose to the user with
+   * the provided entry object.
+   * @param entry the entry to analyze.
+   */
+  private void updateConfigWithSyncConfig(Entry entry)
+  {
+    synchronizedSuffixes.addAll(getValues(entry, "ds-cfg-synchronization-dn"));
+  }
+
+  /**
    * The following three methods are just commodity methods to get localized
    * messages.
    */
@@ -417,4 +515,13 @@
     }
     return v;
   }
+
+  /**
+   * Create a non synchronized base DN descriptor.
+   */
+  private BaseDNDescriptor getBaseDNDescriptor(Entry entry, String baseDn)
+  {
+    return new BaseDNDescriptor(BaseDNDescriptor.Type.NOT_SYNCHRONIZED,
+        baseDn, null, -1, -1);
+  }
 }
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromLDAP.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromLDAP.java
index 4ee12b3..5366004 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromLDAP.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ConfigFromLDAP.java
@@ -27,9 +27,13 @@
 
 package org.opends.statuspanel;
 
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.Set;
+import java.util.TreeSet;
 
+import javax.naming.NameNotFoundException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
@@ -55,6 +59,12 @@
     new HashSet<DatabaseDescriptor>();
   private HashSet<String> administrativeUsers = new HashSet<String>();
   private String errorMessage;
+  private boolean synchronizationConfigured = false;
+  private HashSet<String> synchronizedSuffixes = new HashSet<String>();
+  private HashMap<String, Integer> hmMissingChanges =
+    new HashMap<String, Integer>();
+  private HashMap<String, Integer> hmAgeOfOldestMissingChanges =
+    new HashMap<String, Integer>();
 
   private String dn;
   private String pwd;
@@ -130,6 +140,10 @@
     listeners.clear();
     databases.clear();
     administrativeUsers.clear();
+    synchronizationConfigured = false;
+    synchronizedSuffixes.clear();
+    hmMissingChanges.clear();
+    hmAgeOfOldestMissingChanges.clear();
     javaVersion = null;
     openConnections = -1;
 
@@ -138,6 +152,7 @@
       InitialLdapContext ctx = getDirContext();
       updateAdministrativeUsers(ctx);
       updateListeners(ctx);
+      updateSynchronization(ctx);
       updateDatabases(ctx);
       javaVersion = getJavaVersion(ctx);
       openConnections = getOpenConnections(ctx);
@@ -308,6 +323,124 @@
   }
 
   /**
+   * Updates the synchronization configuration data we expose to the user with
+   * the provided InitialLdapContext.
+   * @param ctx the InitialLdapContext to use to update the configuration.
+   * @throws NamingException if there was an error.
+   */
+  private void updateSynchronization(InitialLdapContext ctx)
+  throws NamingException
+  {
+    SearchControls ctls = new SearchControls();
+    ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
+    ctls.setReturningAttributes(
+        new String[] {
+            "ds-cfg-synchronization-provider-enabled"
+        });
+    String filter = "(objectclass=ds-cfg-synchronization-provider)";
+
+    LdapName jndiName = new LdapName(
+      "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config");
+
+    try
+    {
+      NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls);
+
+      while(syncProviders.hasMore())
+      {
+        SearchResult sr = (SearchResult)syncProviders.next();
+
+        if ("true".equalsIgnoreCase(getFirstValue(sr,
+          "ds-cfg-synchronization-provider-enabled")))
+        {
+          synchronizationConfigured = true;
+        }
+      }
+    }
+    catch (NameNotFoundException nse)
+    {
+    }
+
+    ctls = new SearchControls();
+    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+    ctls.setReturningAttributes(
+        new String[] {
+            "ds-cfg-synchronization-dn"
+        });
+    filter = "(objectclass=ds-cfg-synchronization-provider-config)";
+
+    jndiName = new LdapName(
+      "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config");
+
+    try
+    {
+      NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls);
+
+      while(syncProviders.hasMore())
+      {
+        SearchResult sr = (SearchResult)syncProviders.next();
+
+        synchronizedSuffixes.addAll(getValues(sr, "ds-cfg-synchronization-dn"));
+      }
+    }
+    catch (NameNotFoundException nse)
+    {
+    }
+
+    ctls = new SearchControls();
+    ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+    ctls.setReturningAttributes(
+    new String[] {
+      "approximate-delay", "waiting-changes", "base-dn"
+    });
+    filter = "(approximate-delay=*)";
+
+    jndiName = new LdapName("cn=monitor");
+
+    if (synchronizedSuffixes.size() > 0)
+    {
+      try
+      {
+        NamingEnumeration monitorEntries = ctx.search(jndiName, filter, ctls);
+
+        while(monitorEntries.hasMore())
+        {
+          SearchResult sr = (SearchResult)monitorEntries.next();
+
+          String dn = getFirstValue(sr, "base-dn");
+
+          for (String baseDn: synchronizedSuffixes)
+          {
+
+            if (Utils.areDnsEqual(dn, baseDn))
+            {
+              try
+              {
+                hmAgeOfOldestMissingChanges.put(baseDn,
+                  new Integer(getFirstValue(sr, "approximate-delay")));
+              }
+              catch (Throwable t)
+              {
+              }
+              try
+              {
+                hmMissingChanges.put(baseDn,
+                  new Integer(getFirstValue(sr, "waiting-changes")));
+              }
+              catch (Throwable t)
+              {
+              }
+            }
+          }
+        }
+      }
+      catch (NameNotFoundException nse)
+      {
+      }
+    }
+  }
+
+  /**
    * Updates the database configuration data we expose to the user with the
    * provided InitialLdapContext.
    * @param ctx the InitialLdapContext to use to update the configuration.
@@ -481,6 +614,58 @@
   }
 
   /**
+   * Create the base DN descriptor.  Assumes that the synchronizedSuffixes Set
+   * and synchronizationConfigured have already been initialized.
+   */
+  private BaseDNDescriptor getBaseDNDescriptor(InitialLdapContext ctx,
+      String baseDn)
+  throws NamingException
+  {
+    BaseDNDescriptor.Type type;
+    int missingChanges = -1;
+    int ageOfOldestMissingChange = -1;
+    String mapSuffixDn = null;
+
+    boolean replicated = false;
+    if (synchronizationConfigured)
+    {
+      for (String suffixDn: synchronizedSuffixes)
+      {
+        if (Utils.areDnsEqual(baseDn, suffixDn))
+        {
+          replicated = true;
+          mapSuffixDn = suffixDn;
+          break;
+        }
+      }
+    }
+    if (replicated)
+    {
+      type = BaseDNDescriptor.Type.SYNCHRONIZED;
+
+      Integer missing = hmMissingChanges.get(mapSuffixDn);
+      Integer age = hmAgeOfOldestMissingChanges.get(mapSuffixDn);
+
+      if (age != null)
+      {
+        ageOfOldestMissingChange = age.intValue();
+      }
+
+      if (missing != null)
+      {
+        missingChanges = missing.intValue();
+      }
+    }
+    else
+    {
+      type = BaseDNDescriptor.Type.NOT_SYNCHRONIZED;
+    }
+
+    return new BaseDNDescriptor(type, baseDn, null, ageOfOldestMissingChange,
+      missingChanges);
+  }
+
+  /**
    * Updates the listener configuration data we expose to the user with the
    * provided SearchResult object.
    * @param entry the entry to analyze.
@@ -587,13 +772,26 @@
       InitialLdapContext ctx)
   throws NamingException
   {
-    String baseDn = getFirstValue(entry, "ds-cfg-backend-base-dn");
     String id = getFirstValue(entry, "ds-cfg-backend-id");
 
     if (!isConfigBackend(id))
     {
+      Set<String> baseDns = new TreeSet<String>();
+      baseDns.addAll(getValues(entry, "ds-cfg-backend-base-dn"));
+      Set<BaseDNDescriptor> replicas = new LinkedHashSet<BaseDNDescriptor>();
       int nEntries = getEntryCount(ctx, id);
-      databases.add(new DatabaseDescriptor(id, baseDn, nEntries));
+
+      for (String baseDn : baseDns)
+      {
+        replicas.add(getBaseDNDescriptor(ctx, baseDn));
+      }
+
+      DatabaseDescriptor db = new DatabaseDescriptor(id, replicas, nEntries);
+      databases.add(db);
+      for (BaseDNDescriptor rep: replicas)
+      {
+        rep.setDatabase(db);
+      }
     }
   }
 
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/DatabaseDescriptor.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/DatabaseDescriptor.java
index 62881c2..e474b01 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/DatabaseDescriptor.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/DatabaseDescriptor.java
@@ -27,6 +27,8 @@
 
 package org.opends.statuspanel;
 
+import java.util.Set;
+
 import org.opends.quicksetup.util.Utils;
 
 /**
@@ -36,19 +38,20 @@
 public class DatabaseDescriptor
 {
   private String backendID;
-  private String baseDn;
+  private Set<BaseDNDescriptor> baseDns;
   private int entries;
 
   /**
    * Constructor for this class.
    * @param backendID the backend ID of the Database.
-   * @param baseDn the base DN associated with the Database.
+   * @param baseDns the base DNs associated with the Database.
    * @param entries the number of entries in the Database.
    */
-  public DatabaseDescriptor(String backendID, String baseDn, int entries)
+  public DatabaseDescriptor(String backendID, Set<BaseDNDescriptor> baseDns,
+      int entries)
   {
     this.backendID = backendID;
-    this.baseDn = baseDn;
+    this.baseDns = baseDns;
     this.entries = entries;
   }
 
@@ -62,12 +65,12 @@
   }
 
   /**
-   * Return the base DN associated with the database.
-   * @return the base DN associated with the database.
+   * Returns the Base DN objects associated with the database.
+   * @return the Base DN objects associated with the database.
    */
-  public String getBaseDn()
+  public Set<BaseDNDescriptor> getBaseDns()
   {
-    return baseDn;
+    return baseDns;
   }
 
   /**
@@ -92,8 +95,29 @@
       {
         DatabaseDescriptor desc = (DatabaseDescriptor)v;
         equals = getBackendID().equals(desc.getBackendID()) &&
-        Utils.areDnsEqual(getBaseDn(), desc.getBaseDn()) &&
         (getEntries() == desc.getEntries());
+
+        if (equals)
+        {
+          for (BaseDNDescriptor baseDn1 : baseDns)
+          {
+            boolean found = false;
+            for (BaseDNDescriptor baseDn2 : desc.getBaseDns())
+            {
+              found = Utils.areDnsEqual(baseDn1.getDn(),
+                  baseDn2.getDn());
+              if (found)
+              {
+                break;
+              }
+            }
+            if (!found)
+            {
+              equals = false;
+              break;
+            }
+          }
+        }
       }
     }
     else
@@ -108,6 +132,11 @@
    */
   public int hashCode()
   {
-    return (getBackendID() + getBaseDn() + getEntries()).hashCode();
+    StringBuilder buf = new StringBuilder();
+    for (BaseDNDescriptor rep: getBaseDns())
+    {
+      buf.append(rep.getDn());
+    }
+    return (getBackendID() + buf.toString() + getEntries()).hashCode();
   }
 }
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusCli.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusCli.java
index f7f73fa..0ee9279 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusCli.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusCli.java
@@ -31,6 +31,7 @@
 import java.io.File;
 import java.io.FileReader;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
 import java.util.TreeSet;
@@ -717,9 +718,15 @@
     else
     {
       DatabasesTableModel databasesTableModel = new DatabasesTableModel();
-      databasesTableModel.setData(desc.getDatabases());
+      Set<BaseDNDescriptor> replicas = new HashSet<BaseDNDescriptor>();
+      Set<DatabaseDescriptor> dbs = desc.getDatabases();
+      for (DatabaseDescriptor db: dbs)
+      {
+        replicas.addAll(db.getBaseDns());
+      }
+      databasesTableModel.setData(replicas);
 
-      writeTableModel(databasesTableModel, desc);
+      writeDatabasesTableModel(databasesTableModel, desc);
     }
   }
 
@@ -769,6 +776,12 @@
     return getMsg("not-available-label", false);
   }
 
+  /**
+   * Writes the contents of the provided table model simulating a table layout
+   * using text.
+   * @param tableModel the TableModel.
+   * @param desc the Server Status descriptor.
+   */
   private void writeTableModel(TableModel tableModel,
       ServerStatusDescriptor desc)
   {
@@ -828,7 +841,7 @@
       totalWidth += maxWidths[i];
     }
 
-    StringBuffer headerLine = new StringBuffer();
+    StringBuilder headerLine = new StringBuilder();
     for (int i=0; i<maxWidths.length; i++)
     {
       String header = tableModel.getColumnName(i);
@@ -840,7 +853,7 @@
       }
     }
     System.out.println(wrap(headerLine.toString()));
-    StringBuffer t = new StringBuffer();
+    StringBuilder t = new StringBuilder();
     for (int i=0; i<headerLine.length(); i++)
     {
       t.append("=");
@@ -849,7 +862,7 @@
 
     for (int i=0; i<tableModel.getRowCount(); i++)
     {
-      StringBuffer line = new StringBuffer();
+      StringBuilder line = new StringBuilder();
       for (int j=0; j<tableModel.getColumnCount(); j++)
       {
         int extra = maxWidths[j];
@@ -896,9 +909,118 @@
     }
   }
 
+  /**
+   * Writes the contents of the provided database table model.  Every base DN
+   * is written in a block containing pairs of labels and values.
+   * @param tableModel the TableModel.
+   * @param desc the Server Status descriptor.
+   */
+  private void writeDatabasesTableModel(DatabasesTableModel tableModel,
+  ServerStatusDescriptor desc)
+  {
+    boolean isRunning =
+      desc.getStatus() == ServerStatusDescriptor.ServerStatus.STARTED;
+
+    int labelWidth = 0;
+    String[] labels = new String[tableModel.getColumnCount()];
+    for (int i=0; i<tableModel.getColumnCount(); i++)
+    {
+      String header;
+      if (i == 5)
+      {
+        header = getMsg("age-of-oldest-missing-change-column-cli", false);
+      }
+      else
+      {
+        header = tableModel.getColumnName(i);
+      }
+      labels[i] = header+":";
+      labelWidth = Math.max(labelWidth, labels[i].length());
+    }
+
+    String synchronizedLabel = getMsg("suffix-synchronized-label", false);
+    for (int i=0; i<tableModel.getRowCount(); i++)
+    {
+      if (i > 0)
+      {
+        System.out.println();
+      }
+      for (int j=0; j<tableModel.getColumnCount(); j++)
+      {
+        String value;
+        Object v = tableModel.getValueAt(i, j);
+        if (v != null)
+        {
+          if (v instanceof String)
+          {
+            value = (String)v;
+          }
+          else if (v instanceof Integer)
+          {
+            int nEntries = ((Integer)v).intValue();
+            if (nEntries >= 0)
+            {
+              value = String.valueOf(nEntries);
+            }
+            else
+            {
+              if (!isRunning)
+              {
+                value = getNotAvailableBecauseServerIsDownText();
+              }
+              if (!desc.isAuthenticated())
+              {
+                value = getNotAvailableBecauseAuthenticationIsRequiredText();
+              }
+              else
+              {
+                value = getNotAvailableText();
+              }
+            }
+          }
+          else
+          {
+            throw new IllegalStateException("Unknown object type: "+v);
+          }
+        }
+        else
+        {
+          value = "";
+        }
+
+        if (value.equals(getNotAvailableText()))
+        {
+          if (!isRunning)
+          {
+            value = getNotAvailableBecauseServerIsDownText();
+          }
+          if (!desc.isAuthenticated())
+          {
+            value = getNotAvailableBecauseAuthenticationIsRequiredText();
+          }
+        }
+
+        boolean doWrite = true;
+        if ((j == 4) || (j == 5))
+        {
+          // If the suffix is not replicated we do not have to display these
+          // lines.
+          if (!synchronizedLabel.equals(tableModel.getValueAt(i, 3)))
+          {
+            doWrite = false;
+          }
+        }
+        if (doWrite)
+        {
+          writeLabelValue(labels[j], value, labelWidth);
+        }
+      }
+    }
+  }
+
   private void writeLabelValue(String label, String value, int maxLabelWidth)
   {
-    StringBuffer buf = new StringBuffer();
+    StringBuilder buf = new StringBuilder();
     buf.append(label);
 
     int extra = maxLabelWidth - label.length();
@@ -922,7 +1044,7 @@
     String centered;
     if (text.length() <= Utils.getCommandLineMaxLineWidth() - 8)
     {
-      StringBuffer buf = new StringBuffer();
+      StringBuilder buf = new StringBuilder();
       int extra = Math.min(10,
           (Utils.getCommandLineMaxLineWidth() - 8 - text.length()) / 2);
       for (int i=0; i<extra; i++)
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelController.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelController.java
index 8e0812b..6506664 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelController.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelController.java
@@ -1086,7 +1086,7 @@
             String line = reader.readLine();
             while (line != null)
             {
-              StringBuffer buf = new StringBuffer();
+              StringBuilder buf = new StringBuilder();
               if (!isFirstLine)
               {
                 buf.append(formatter.getLineBreak());
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelLauncher.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelLauncher.java
index aa29ce8..d94456c 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelLauncher.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/StatusPanelLauncher.java
@@ -76,7 +76,7 @@
    *
    * This code also assumes that if the call to SplashWindow.main worked (and
    * the splash screen was displayed) we will never get out of it (we will call
-   * a System.exit() when we close the graphical uninstall dialog).
+   * a System.exit() when we close the graphical status dialog).
    *
    * @params String[] args the arguments used to call the SplashWindow main
    *         method
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/resources/Resources.properties b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/resources/Resources.properties
index a4d5750..ef8ad05 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/resources/Resources.properties
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/resources/Resources.properties
@@ -86,9 +86,15 @@
 backendid-column=Backend ID
 basedn-column=Base DN
 number-entries-column=Entries
+synchronized-column=Synchronization
+missing-changes-column=Missing Changes
+age-of-oldest-missing-change-column=<html>Age of Oldest<br>Missing \
+Change (hh:mm:ss)
+age-of-oldest-missing-change-column-cli=Age of Oldest Missing Change (hh:mm:ss)
 enabled-label=Enabled
 disabled-label=Disabled
 unknown-label=--
+not-applicable-label=--
 ldap-protocol-label=LDAP
 ldaps-protocol-label=LDAPS
 jmx-protocol-label=JMX
@@ -99,10 +105,13 @@
 This could be caused because there is not an enabled LDAP port to retrieve \
 monitoring information or because you do not have read rights on the \
 configuration file.
+number-entries-multiple-suffixes-in-db={0} (for all base DNs in {1})
 error-reading-config-ldap=Error reading data from server.  Try \
 re-authenticating.\nDetails: {0}
 no-dbs-found=-No LDAP Databases Found-
 no-listeners-found=-No Listener Ports Found-
+suffix-synchronized-label=Enabled
+suffix-not-synchronized-label=Disabled
 
 #
 # Progress Dialog
@@ -138,7 +147,6 @@
 login-pwd-label=Administrative User Password:
 login-pwd-tooltip=Enter the password of the \
 Administrative User account that will used to retrieve monitoring information
-ok-button-label=OK
 login-dialog-server-not-running-msg=The Directory Server is not running.  \
 Click OK to continue to the Status Panel.
 login-dialog-server-not-running-title=Directory Server not Running
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/DatabasesTableModel.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/DatabasesTableModel.java
index ffcdd4e..d6ce8b5 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/DatabasesTableModel.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/DatabasesTableModel.java
@@ -35,7 +35,8 @@
 
 import javax.swing.table.AbstractTableModel;
 
-import org.opends.statuspanel.DatabaseDescriptor;
+import org.opends.quicksetup.ui.SortableTableModel;
+import org.opends.statuspanel.BaseDNDescriptor;
 import org.opends.statuspanel.i18n.ResourceProvider;
 
 /**
@@ -44,16 +45,19 @@
  *
  */
 public class DatabasesTableModel extends AbstractTableModel
-implements SortableTableModel, Comparator<DatabaseDescriptor>
+implements SortableTableModel, Comparator<BaseDNDescriptor>
 {
   private static final long serialVersionUID = -5650762484071136983L;
-  private HashSet<DatabaseDescriptor> data = new HashSet<DatabaseDescriptor>();
-  private ArrayList<DatabaseDescriptor> dataArray =
-    new ArrayList<DatabaseDescriptor>();
+  private HashSet<BaseDNDescriptor> data = new HashSet<BaseDNDescriptor>();
+  private ArrayList<BaseDNDescriptor> dataArray =
+    new ArrayList<BaseDNDescriptor>();
   private final String[] COLUMN_NAMES = {
-    getMsg("backendid-column"),
     getMsg("basedn-column"),
-    getMsg("number-entries-column")
+    getMsg("backendid-column"),
+    getMsg("number-entries-column"),
+    getMsg("synchronized-column"),
+    getMsg("missing-changes-column"),
+    getMsg("age-of-oldest-missing-change-column")
   };
   private int sortColumn = 0;
   private boolean sortAscending = true;
@@ -62,15 +66,15 @@
    * Sets the data for this table model.
    * @param newData the data for this table model.
    */
-  public void setData(Set<DatabaseDescriptor> newData)
+  public void setData(Set<BaseDNDescriptor> newData)
   {
     if (!newData.equals(data))
     {
       data.clear();
       data.addAll(newData);
       dataArray.clear();
-      TreeSet<DatabaseDescriptor> sortedSet =
-        new TreeSet<DatabaseDescriptor>(this);
+      TreeSet<BaseDNDescriptor> sortedSet =
+        new TreeSet<BaseDNDescriptor>(this);
       sortedSet.addAll(data);
       dataArray.addAll(sortedSet);
       fireTableDataChanged();
@@ -84,8 +88,8 @@
   public void forceResort()
   {
     dataArray.clear();
-    TreeSet<DatabaseDescriptor> sortedSet =
-      new TreeSet<DatabaseDescriptor>(this);
+    TreeSet<BaseDNDescriptor> sortedSet =
+      new TreeSet<BaseDNDescriptor>(this);
     sortedSet.addAll(data);
     dataArray.addAll(sortedSet);
     fireTableDataChanged();
@@ -93,77 +97,189 @@
 
   /**
    * Comparable implementation.
-   * @param desc1 the first database descriptor to compare.
-   * @param desc2 the second database descriptor to compare.
+   * @param desc1 the first replica descriptor to compare.
+   * @param desc2 the second replica descriptor to compare.
    * @return 1 if according to the sorting options set by the user the first
    * database descriptor must be put before the second descriptor, 0 if they
    * are equivalent in terms of sorting and -1 if the second descriptor must
    * be put before the first descriptor.
    */
-  public int compare(DatabaseDescriptor desc1, DatabaseDescriptor desc2)
+  public int compare(BaseDNDescriptor desc1, BaseDNDescriptor desc2)
   {
     int result = 0;
     if (sortColumn == 0)
     {
-      result = desc1.getBackendID().compareTo(desc2.getBackendID());
+      result = compareDns(desc1, desc2);
 
       if (result == 0)
       {
-        result = desc1.getBaseDn().compareTo(desc2.getBaseDn());
+        result = compareBackendIDs(desc1, desc2);
       }
 
       if (result == 0)
       {
-        if (desc1.getEntries() > desc2.getEntries())
-        {
-          result = 1;
-        }
-        else if (desc1.getEntries() < desc2.getEntries())
-        {
-          result = -1;
-        }
+        result = compareEntries(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareSync(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareMissingChanges(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareAgeOfOldestMissingChange(desc1, desc2);
       }
     }
-    else if (sortColumn == 1)
+
+    if (sortColumn == 1)
     {
-      result = desc1.getBaseDn().compareTo(desc2.getBaseDn());
+      result = compareBackendIDs(desc1, desc2);
 
       if (result == 0)
       {
-        result = desc1.getBackendID().compareTo(desc2.getBackendID());
+        result = compareDns(desc1, desc2);
       }
 
       if (result == 0)
       {
-        if (desc1.getEntries() > desc2.getEntries())
-        {
-          result = 1;
-        }
-        else if (desc1.getEntries() < desc2.getEntries())
-        {
-          result = -1;
-        }
+        result = compareEntries(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareSync(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareMissingChanges(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareAgeOfOldestMissingChange(desc1, desc2);
       }
     }
-    else
+    else if (sortColumn == 2)
     {
-      if (desc1.getEntries() > desc2.getEntries())
+      result = compareEntries(desc1, desc2);
+
+      if (result == 0)
       {
-        result = 1;
-      }
-      else if (desc1.getEntries() < desc2.getEntries())
-      {
-        result = -1;
+        result = compareBackendIDs(desc1, desc2);
       }
 
       if (result == 0)
       {
-        result = desc1.getBackendID().compareTo(desc2.getBackendID());
+        result = compareDns(desc1, desc2);
       }
 
       if (result == 0)
       {
-        result = desc1.getBaseDn().compareTo(desc2.getBaseDn());
+        result = compareSync(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareMissingChanges(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareAgeOfOldestMissingChange(desc1, desc2);
+      }
+    }
+    else if (sortColumn == 3)
+    {
+      result = compareSync(desc1, desc2);
+
+      if (result == 0)
+      {
+        result = compareBackendIDs(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareDns(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareEntries(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareMissingChanges(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareAgeOfOldestMissingChange(desc1, desc2);
+      }
+    }
+    else if (sortColumn == 4)
+    {
+      result = compareMissingChanges(desc1, desc2);
+
+      if (result == 0)
+      {
+        result = compareBackendIDs(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareDns(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareEntries(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareSync(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareAgeOfOldestMissingChange(desc1, desc2);
+      }
+    }
+    else if (sortColumn == 5)
+    {
+      result = compareAgeOfOldestMissingChange(desc1, desc2);
+
+      if (result == 0)
+      {
+        result = compareBackendIDs(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareDns(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareEntries(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareSync(desc1, desc2);
+      }
+
+      if (result == 0)
+      {
+        result = compareMissingChanges(desc1, desc2);
       }
     }
 
@@ -180,7 +296,7 @@
    */
   public int getColumnCount()
   {
-    return 3;
+    return 6;
   }
 
   /**
@@ -197,18 +313,34 @@
   public Object getValueAt(int row, int col)
   {
     Object v;
-    DatabaseDescriptor desc = dataArray.get(row);
+    BaseDNDescriptor desc = dataArray.get(row);
     if (col == 0)
     {
-      v = desc.getBackendID();
+      v = desc.getDn();
     }
     else if (col == 1)
     {
-      v = desc.getBaseDn();
+      v = desc.getDatabase().getBackendID();
+    }
+    else if (col == 2)
+    {
+      v = getValueForEntries(desc);
+    }
+    else if (col == 3)
+    {
+      v = getStringForSyncState(desc);
+    }
+    else if (col == 4)
+    {
+      v = getValueForMissingChanges(desc);
+    }
+    else if (col == 5)
+    {
+      v = getValueForOldestMissingChange(desc);
     }
     else
     {
-      v = new Integer(desc.getEntries());
+      throw new IllegalArgumentException("Invalid col number: "+col);
     }
     return v;
   }
@@ -257,6 +389,174 @@
     this.sortColumn = sortColumn;
   }
 
+  /*
+   * Several comparison methods to be able to sort the table model.
+   */
+  private int compareBackendIDs(BaseDNDescriptor desc1, BaseDNDescriptor desc2)
+  {
+    return desc1.getDatabase().getBackendID().compareTo(
+      desc2.getDatabase().getBackendID());
+  }
+
+  private int compareEntries(BaseDNDescriptor desc1, BaseDNDescriptor desc2)
+  {
+    int n1 = desc1.getDatabase().getEntries();
+    int n2 = desc2.getDatabase().getEntries();
+    return compareIntegers(n1, n2);
+  }
+
+  private int compareIntegers(int n1, int n2)
+  {
+    if (n1 == n2)
+    {
+      return 0;
+    }
+    if (n1 > n2)
+    {
+      return 1;
+    }
+    return -1;
+  }
+
+  private int compareDns(BaseDNDescriptor desc1, BaseDNDescriptor desc2)
+  {
+    return desc1.getDn().compareTo(desc2.getDn());
+  }
+
+  private int compareSync(BaseDNDescriptor desc1, BaseDNDescriptor desc2)
+  {
+    return (String.valueOf(desc1.getType()).compareTo(
+        String.valueOf(desc2.getType())));
+  }
+
+  private int compareMissingChanges(BaseDNDescriptor desc1,
+      BaseDNDescriptor desc2)
+  {
+    return compareIntegers(desc1.getMissingChanges(),
+        desc2.getMissingChanges());
+  }
+
+  private int compareAgeOfOldestMissingChange(BaseDNDescriptor desc1,
+      BaseDNDescriptor desc2)
+  {
+    return compareIntegers(desc1.getAgeOfOldestMissingChange(),
+        desc2.getAgeOfOldestMissingChange());
+  }
+
+  /**
+   * Returns the Object describing the number of entries of a given Base DN.
+   * The Object will be an Integer unless the database of the Base DN contains
+   * several Base DNs.  In this case we return a String.
+   * @param rep the Base DN object to handle.
+   * @return the Object describing the number of entries of a given Base DN.
+   */
+  private Object getValueForEntries(BaseDNDescriptor rep)
+  {
+    Object v;
+    int nEntries = rep.getDatabase().getEntries();
+    if ((rep.getDatabase().getBaseDns().size() > 1) &&
+      (nEntries >= 0))
+    {
+      String[] args = {
+        String.valueOf(nEntries),
+        rep.getDatabase().getBackendID()
+      };
+      v = getMsg("number-entries-multiple-suffixes-in-db", args);
+    }
+    else
+    {
+      v = new Integer(nEntries);
+    }
+    return v;
+  }
+
+  /**
+   * Returns the Object describing the number of missing changes of a given Base
+   * DN.  The Object will be a String unless the base DN is
+   * synchronized and we could not find a valid value (in this case we return
+   * an Integer with the invalid value).
+   * @param rep the Base DN object to handle.
+   * @return the Object describing the number of missing changes of
+   * a given Base DN.
+   */
+  private Object getValueForMissingChanges(BaseDNDescriptor rep)
+  {
+    Object v;
+    if (rep.getType() == BaseDNDescriptor.Type.SYNCHRONIZED)
+    {
+      v = new Integer(rep.getMissingChanges());
+    }
+    else
+    {
+      v = getMsg("not-applicable-label");
+    }
+    return v;
+  }
+
+  /**
+   * Returns the Object describing the age of oldest missing change of
+   * a given Base DN.  The Object will be a String unless the base DN is
+   * synchronized and we could not find a valid value (in this case we return
+   * an Integer with the invalid value).
+   * @param rep the Base DN object to handle.
+   * @return the Object describing the age of oldest missing change of
+   * a given Base DN.
+   */
+  private Object getValueForOldestMissingChange(BaseDNDescriptor rep)
+  {
+    Object v;
+    if (rep.getType() == BaseDNDescriptor.Type.SYNCHRONIZED)
+    {
+      int age = rep.getAgeOfOldestMissingChange();
+      if (age >= 0)
+      {
+        int remainingSeconds = age % 60;
+        int minutes = age / 60;
+        int remainingMinutes = minutes % 60;
+        int hours = minutes / 60;
+
+        String sMinutes = (remainingMinutes>=10)?
+        String.valueOf(remainingMinutes) : "0"+remainingMinutes;
+
+        String sSeconds = (remainingSeconds>=10)?
+        String.valueOf(remainingSeconds) : "0"+remainingSeconds;
+
+        String sHours = (hours>=10)?String.valueOf(hours):"0"+hours;
+
+        v = sHours+":"+sMinutes+":"+sSeconds;
+      }
+      else
+      {
+        v = new Integer(age);
+      }
+    }
+    else
+    {
+      v = getMsg("not-applicable-label");
+    }
+    return v;
+  }
+
+  /**
+   * Returns the localized String describing the synchronization state of
+   * a given Base DN.
+   * @param rep the Base DN object to handle.
+   * @return the localized String describing the synchronization state of
+   * a given Base DN.
+   */
+  private String getStringForSyncState(BaseDNDescriptor rep)
+  {
+    String s;
+    if (rep.getType() == BaseDNDescriptor.Type.SYNCHRONIZED)
+    {
+      s = getMsg("suffix-synchronized-label");
+    }
+    else
+    {
+      s = getMsg("suffix-not-synchronized-label");
+    }
+    return s;
+  }
 
   /**
    * The following three methods are just commodity methods to get localized
@@ -267,6 +567,11 @@
     return getI18n().getMsg(key);
   }
 
+  private String getMsg(String key, String[] args)
+  {
+    return getI18n().getMsg(key, args);
+  }
+
   private ResourceProvider getI18n()
   {
     return ResourceProvider.getInstance();
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/ListenersTableModel.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/ListenersTableModel.java
index 520a0cb..5b9b0b3 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/ListenersTableModel.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/ListenersTableModel.java
@@ -35,6 +35,7 @@
 
 import javax.swing.table.AbstractTableModel;
 
+import org.opends.quicksetup.ui.SortableTableModel;
 import org.opends.statuspanel.ListenerDescriptor;
 import org.opends.statuspanel.i18n.ResourceProvider;
 
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/LoginDialog.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/LoginDialog.java
index e700e7c..bcf8bc4 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/LoginDialog.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/LoginDialog.java
@@ -336,7 +336,7 @@
           if (ldapUrl != null)
           {
             ctx = Utils.createLdapContext(ldapUrl, tfDn.getText(),
-                  tfPwd.getText(), 3000, null);
+                  tfPwd.getText(), Utils.getDefaultLDAPTimeout(), null);
           }
           else
           {
diff --git a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/StatusPanelDialog.java b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/StatusPanelDialog.java
index e2238d1..edcf3eb 100644
--- a/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/StatusPanelDialog.java
+++ b/opendj-sdk/opends/src/statuspanel/org/opends/statuspanel/ui/StatusPanelDialog.java
@@ -42,6 +42,7 @@
 import java.awt.event.WindowEvent;
 import java.io.File;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -60,15 +61,17 @@
 import javax.swing.PopupFactory;
 import javax.swing.SwingConstants;
 import javax.swing.ToolTipManager;
+import javax.swing.table.JTableHeader;
 import javax.swing.table.TableCellRenderer;
 import javax.swing.table.TableColumn;
-import javax.swing.table.TableColumnModel;
 
 import org.opends.quicksetup.event.MinimumSizeComponentListener;
 import org.opends.quicksetup.ui.UIFactory;
 import org.opends.quicksetup.util.HtmlProgressMessageFormatter;
 import org.opends.quicksetup.util.Utils;
 
+import org.opends.statuspanel.DatabaseDescriptor;
+import org.opends.statuspanel.BaseDNDescriptor;
 import org.opends.statuspanel.ServerStatusDescriptor;
 import org.opends.statuspanel.event.StatusPanelButtonListener;
 import org.opends.statuspanel.i18n.ResourceProvider;
@@ -162,7 +165,6 @@
     int minWidth = Math.min(packedMinWidth, MAXIMAL_WIDTH);
     int minHeight = Math.min(packedMinHeight, MAXIMAL_HEIGHT);
 
-
     addComponentListener(new MinimumSizeComponentListener(this,
         minWidth, minHeight));
     if ((minWidth != packedMinWidth) || (minHeight != packedMinHeight))
@@ -681,9 +683,9 @@
     p.add(createSubsectionTitle(getMsg("listeners-title")), gbc);
 
     listenersTableModel = new ListenersTableModel();
-    listenersTable = createTable(listenersTableModel,
+    listenersTable = UIFactory.makeSortableTable(listenersTableModel,
         new ListenersCellRenderer(),
-        new HeaderRenderer());
+        UIFactory.makeHeaderRenderer());
 
     gbc.insets.top = UIFactory.TOP_INSET_PRIMARY_FIELD;
     p.add(listenersTable.getTableHeader(), gbc);
@@ -719,8 +721,9 @@
     p.add(createSubsectionTitle(getMsg("databases-title")), gbc);
 
     dbTableModel = new DatabasesTableModel();
-    dbTable = createTable(dbTableModel, new DatabasesCellRenderer(),
-        new HeaderRenderer());
+    dbTable = UIFactory.makeSortableTable(dbTableModel,
+        new DatabasesCellRenderer(),
+        UIFactory.makeHeaderRenderer());
     toolTipManager.registerComponent(dbTable);
 
     gbc.insets.top = UIFactory.TOP_INSET_PRIMARY_FIELD;
@@ -791,47 +794,6 @@
   }
 
   /**
-   * Returns a table created with the provided model and renderers.
-   * @param tableModel the table model.
-   * @param renderer the cell renderer.
-   * @param headerRenderer the header renderer.
-   * @return a table created with the provided model and renderers.
-   */
-  private JTable createTable(final SortableTableModel tableModel,
-      TableCellRenderer renderer,
-      TableCellRenderer headerRenderer)
-  {
-    final JTable table = new JTable(tableModel);
-    table.setShowGrid(true);
-    table.setGridColor(UIFactory.PANEL_BORDER_COLOR);
-    table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
-    table.setBackground(UIFactory.CURRENT_STEP_PANEL_BACKGROUND);
-    table.getTableHeader().setBackground(UIFactory.DEFAULT_BACKGROUND);
-    table.setRowMargin(0);
-
-    for (int i=0; i<tableModel.getColumnCount(); i++)
-    {
-      TableColumn col = table.getColumn(table.getColumnName(i));
-      col.setCellRenderer(renderer);
-      col.setHeaderRenderer(headerRenderer);
-    }
-    MouseAdapter listMouseListener = new MouseAdapter() {
-      public void mouseClicked(MouseEvent e) {
-        TableColumnModel columnModel = table.getColumnModel();
-        int viewColumn = columnModel.getColumnIndexAtX(e.getX());
-        int sortedBy = table.convertColumnIndexToModel(viewColumn);
-        if (e.getClickCount() == 1 && sortedBy != -1) {
-          tableModel.setSortAscending(!tableModel.isSortAscending());
-          tableModel.setSortColumn(sortedBy);
-          tableModel.forceResort();
-        }
-      }
-    };
-    table.getTableHeader().addMouseListener(listMouseListener);
-    return table;
-  }
-
-  /**
    * Updates the status contents displaying with what is specified in the
    * provided ServerStatusDescriptor object.
    * This method must be called from the event thread.
@@ -942,7 +904,9 @@
       }
 
       setTextValue(lAdministrativeUsers,"<html>"+
-          Utils.getStringFromCollection(ordered, "<br>"));
+          UIFactory.applyFontToHtml(
+              Utils.getStringFromCollection(ordered, "<br>"),
+              UIFactory.READ_ONLY_FONT));
     }
     else
     {
@@ -1069,7 +1033,13 @@
    */
   private void updateDatabaseContents(ServerStatusDescriptor desc)
   {
-    dbTableModel.setData(desc.getDatabases());
+    Set<BaseDNDescriptor> replicas = new HashSet<BaseDNDescriptor>();
+    Set<DatabaseDescriptor> dbs = desc.getDatabases();
+    for (DatabaseDescriptor db: dbs)
+    {
+      replicas.addAll(db.getBaseDns());
+    }
+    dbTableModel.setData(replicas);
 
     if (dbTableModel.getRowCount() == 0)
     {
@@ -1097,6 +1067,7 @@
       dbTable.setVisible(true);
       dbTable.getTableHeader().setVisible(true);
       lDbTableEmpty.setVisible(false);
+      updateTableSizes(dbTable);
     }
   }
 
@@ -1136,6 +1107,108 @@
   }
 
   /**
+   * Updates the size of the table rows according to the size of the
+   * rendered component.
+   * @param table the table to handle.
+   */
+  private void updateTableSizes(JTable table)
+  {
+    updateTableColumnWidth(table);
+    updateTableRowHeight(table);
+
+    /*
+    int totalWidth = 0;
+    int colMargin = table.getColumnModel().getColumnMargin();
+
+    int totalHeight = 0;
+
+    TableColumn tcol = table.getColumnModel().getColumn(0);
+    TableCellRenderer renderer = tcol.getHeaderRenderer();
+    Component comp = renderer.getTableCellRendererComponent(table,
+        table.getModel().getColumnName(0), false, false, 0, 0);
+    totalHeight = (int)comp.getPreferredSize().getHeight();
+    for (int row=0; row<table.getRowCount(); row++)
+    {
+      totalHeight += table.getRowHeight(row);
+    }
+
+    for (int col=0; col<table.getColumnCount(); col++)
+    {
+      tcol = table.getColumnModel().getColumn(col);
+      totalWidth += tcol.getPreferredWidth() + colMargin;
+    }
+
+    table.setPreferredScrollableViewportSize(
+        new Dimension(totalWidth, totalHeight));
+        */
+  }
+
+  /**
+   * Updates the height of the table rows according to the size of the
+   * rendered component.
+   * @param table the table to handle.
+   */
+  private void updateTableRowHeight(JTable table)
+  {
+    int headerMaxHeight = 0;
+
+    for (int col=0; col<table.getColumnCount(); col++)
+    {
+      TableColumn tcol = table.getColumnModel().getColumn(col);
+      TableCellRenderer renderer = tcol.getHeaderRenderer();
+      Component comp = renderer.getTableCellRendererComponent(table,
+          table.getModel().getColumnName(col), false, false, 0, col);
+      int colHeight = (int)comp.getPreferredSize().getHeight();
+      headerMaxHeight = Math.max(headerMaxHeight, colHeight);
+    }
+    JTableHeader header = table.getTableHeader();
+    header.setPreferredSize(new Dimension(
+        (int)header.getPreferredSize().getWidth(),
+        headerMaxHeight));
+
+    for (int row=0; row<table.getRowCount(); row++)
+    {
+      int rowMaxHeight = table.getRowHeight();
+      for (int col=0; col<table.getColumnCount(); col++)
+      {
+        TableCellRenderer renderer = table.getCellRenderer(row, col);
+        Component comp = table.prepareRenderer(renderer, row, col);
+        int colHeight = (int)comp.getPreferredSize().getHeight();
+        rowMaxHeight = Math.max(rowMaxHeight, colHeight);
+      }
+      table.setRowHeight(row, rowMaxHeight);
+    }
+  }
+
+  /**
+   * Updates the height of the table columns according to the size of the
+   * rendered component.
+   * @param table the table to handle.
+   */
+  private void updateTableColumnWidth(JTable table)
+  {
+
+    int margin = table.getIntercellSpacing().width;
+    for (int col=0; col<table.getColumnCount(); col++)
+    {
+      int colMaxWidth;
+      TableColumn tcol = table.getColumnModel().getColumn(col);
+      TableCellRenderer renderer = tcol.getHeaderRenderer();
+      Component comp = renderer.getTableCellRendererComponent(table,
+          table.getModel().getColumnName(col), false, false, 0, col);
+      colMaxWidth = (int)comp.getPreferredSize().getWidth();
+      for (int row=0; row<table.getRowCount(); row++)
+      {
+        renderer = table.getCellRenderer(row, col);
+        comp = table.prepareRenderer(renderer, row, col);
+        int colWidth = (int)comp.getPreferredSize().getWidth() + (2 * margin);
+        colMaxWidth = Math.max(colMaxWidth, colWidth);
+      }
+      tcol.setPreferredWidth(colMaxWidth);
+    }
+  }
+
+  /**
    * Method written for testing purposes.
    * @param args the arguments to be passed to the test program.
    */
@@ -1177,6 +1250,18 @@
       {
         setTextValue(this, (String)value);
       }
+      else if (value instanceof Set)
+      {
+        LinkedHashSet<String> baseDns = new LinkedHashSet<String>();
+        for (Object v : (Set)value)
+        {
+          baseDns.add((String)v);
+        }
+        setTextValue(this, "<html>" +
+            UIFactory.applyFontToHtml(Utils.getStringFromCollection(
+                baseDns, "<br>"),
+                UIFactory.SECONDARY_FIELD_VALID_FONT));
+      }
       else
       {
         /* Is the number of entries: check if it is available or not */
@@ -1257,46 +1342,6 @@
       return this;
     }
   }
-
-  /**
-   * Class used to render the table headers.
-   */
-  class HeaderRenderer extends JLabel implements TableCellRenderer
-  {
-    private static final long serialVersionUID = -8604332267021523835L;
-
-    /**
-     * Default constructor.
-     */
-    public HeaderRenderer()
-    {
-      super();
-      UIFactory.setTextStyle(this, UIFactory.TextStyle.PRIMARY_FIELD_VALID);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Component getTableCellRendererComponent(JTable table, Object value,
-        boolean isSelected, boolean hasFocus, int row, int column) {
-      setTextValue(this, (String)value);
-      if (column == 0)
-      {
-        setBorder(BorderFactory.createCompoundBorder(
-            BorderFactory.createMatteBorder(1, 1, 1, 1,
-                UIFactory.PANEL_BORDER_COLOR),
-                BorderFactory.createEmptyBorder(4, 4, 4, 4)));
-      }
-      else
-      {
-        setBorder(BorderFactory.createCompoundBorder(
-            BorderFactory.createMatteBorder(1, 0, 1, 1,
-                UIFactory.PANEL_BORDER_COLOR),
-                BorderFactory.createEmptyBorder(4, 4, 4, 4)));
-      }
-      return this;
-    }
-  }
 }
 
 /**

--
Gitblit v1.10.0