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

jvergara
24.00.2007 ea55147a2ea1994c28702db6f0225ffd1f790cbb
Add some classes to the ads package so that the code is complete for it being us
ed by the QuickSetup to both read data from registered servers and to configure
ADS.

Some utilities have been also added in order to be able to implement command lin
es that handle the data in the ADS (such as the server groups).

9 files added
6 files modified
3246 ■■■■■ changed files
opends/src/ads/org/opends/admin/ads/ADSContext.java 148 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ADSContextHelper.java 142 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ReplicaDescriptor.java 63 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ServerDescriptor.java 683 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/SuffixDescriptor.java 99 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/TopologyCache.java 256 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/TopologyCacheException.java 148 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java 402 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/util/BlindHostnameVerifier.java 45 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/util/BlindTrustManager.java 63 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/util/ConnectionUtils.java 548 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/util/ServerLoader.java 349 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/util/TrustedSocketFactory.java 237 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java 14 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/installer/ui/SuffixesToReplicatePanel.java 49 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ADSContext.java
@@ -64,7 +64,6 @@
 */
public class ADSContext
{
  /**
   * Enumeration containing the different server properties that are stored in
   * the ADS.
@@ -78,19 +77,23 @@
    /**
     * The host name of the server.
     */
    HOSTNAME("hostname"),
    HOST_NAME("hostname"),
    /**
     * The LDAP port of the server.
     */
    PORT("ldapport"),
    LDAP_PORT("ldapport"),
    /**
     * The JMX port of the server.
     */
    JMX_PORT("jmxport"),
    /**
     * The JMX secure port of the server.
     */
    JMXS_PORT("jmxsport"),
    /**
     * The LDAPS port of the server.
     */
    SECURE_PORT("ldapsport"),
    LDAPS_PORT("ldapsport"),
    /**
     * The certificate used by the server.
     */
@@ -124,6 +127,10 @@
     */
    JMX_ENABLED("jmxEnabled"),
    /**
     * Whether JMX is enabled or not.
     */
    JMXS_ENABLED("jmxsEnabled"),
    /**
     * The location of the server.
     */
    LOCATION("location"),
@@ -500,10 +507,10 @@
    // Build the filter according the properties passed in serverProperties
    int operandCount = 0;
    if (serverProperties.containsKey(ServerProperty.HOSTNAME))
    if (serverProperties.containsKey(ServerProperty.HOST_NAME))
    {
      filter.append("(cn=");
      filter.append(serverProperties.get(ServerProperty.HOSTNAME));
      filter.append(serverProperties.get(ServerProperty.HOST_NAME));
      filter.append("*");
      if (serverProperties.containsKey(ServerProperty.INSTANCE_PATH))
      {
@@ -513,12 +520,12 @@
      filter.append(")");
      operandCount++;
    }
    if (serverProperties.containsKey(ServerProperty.PORT))
    if (serverProperties.containsKey(ServerProperty.LDAP_PORT))
    {
      filter.append("(");
      filter.append(ServerProperty.PORT);
      filter.append(ServerProperty.LDAP_PORT);
      filter.append("=");
      filter.append(serverProperties.get(ServerProperty.PORT));
      filter.append(serverProperties.get(ServerProperty.LDAP_PORT));
      filter.append(")");
      operandCount++;
    }
@@ -738,9 +745,6 @@
  /**
   * Creates the Administration Data in the server.
   * NOTE: until we use the Administration Framework this does not work.
   * TODO: remove the installPath parameter once we are integrated in the
   * Administration Framework.
   * The call to this method assumes that OpenDS.jar has already been loaded.
   * So this should not be called by the Java Web Start before being sure that
   * this jar is loaded.
@@ -759,18 +763,25 @@
  }
  /**
   * TODO: remove this method if we can assume that the server during setup
   * can be started to do a number of things.
   * NOTE: this can only be called locally.
   * The call to this method assumes that OpenDS.jar has already been loaded.
   * So this should not be called by the Java Web Start before being sure that
   * this jar is loaded.
   * @param serverProperties the properties of the server to register.
   * @param serverProperties the properties of the servers to register.
   * @param serverGroupProperties the properties of the server groups to
   * register.
   * @param administratorProperties the properties of the administrators to
   * register.
   * @param installPath the installation path of the server.
   * @param backendName the backend where we create the administration data.
   * @throws ADSContextException if something goes wrong.
   */
  public static void createOfflineAdminData(
      Map<ServerProperty, Object> serverProperties, String installPath,
      String backendName)
      Set<Map<ServerProperty, Object>> serverProperties,
      Set<Map<ServerGroupProperty, Object>> serverGroupProperties,
      Set<Map<AdministratorProperty, Object>> administratorProperties,
      String installPath)
  throws ADSContextException
  {
    // Add the administration suffix
@@ -802,31 +813,29 @@
      lines.add("dn: "+getServerGroupContainerDN());
      lines.add("objectclass: extensibleobject");
      lines.add("");
      LdapName dn = makeDNFromServerProperties(serverProperties);
      BasicAttributes attrs = makeAttrsFromServerProperties(serverProperties);
      lines.add("dn: "+dn.toString());
      NamingEnumeration<String> ids = attrs.getIDs();
      while (ids.hasMoreElements())
      for (Map<ServerProperty, Object> props : serverProperties)
      {
        String attrID = ids.nextElement();
        Attribute attr = attrs.get(attrID);
        try
        {
          NamingEnumeration values = attr.getAll();
          while (values.hasMoreElements())
          {
            lines.add(attrID+": "+values.nextElement());
          }
        }
        catch (NamingException ne)
        {
          // This should not happen
          throw new ADSContextException(
              ADSContextException.ErrorType.ERROR_UNEXPECTED, ne);
        }
        lines.add("");
        LdapName dn = makeDNFromServerProperties(props);
        BasicAttributes attrs = makeAttrsFromServerProperties(props);
        addToLines(dn, attrs, lines);
      }
      for (Map<ServerGroupProperty, Object> props : serverGroupProperties)
      {
        lines.add("");
        LdapName dn = makeDNFromServerGroupProperties(props);
        BasicAttributes attrs = makeAttrsFromServerGroupProperties(props);
        addToLines(dn, attrs, lines);
      }
      for (Map<AdministratorProperty, Object> props : administratorProperties)
      {
        lines.add("");
        LdapName dn = makeDNFromAdministratorProperties(props);
        BasicAttributes attrs = makeAttrsFromAdministratorProperties(props);
        addToLines(dn, attrs, lines);
      }
      BufferedWriter writer = new BufferedWriter(new FileWriter(ldifFile));
      for (String line : lines)
@@ -848,7 +857,7 @@
          "config.ldif");
      argList.add("-n");
      argList.add(backendName);
      argList.add(getBackendName());
      argList.add("-t");
      argList.add(ldifFile.getAbsolutePath());
      argList.add("-S");
@@ -1141,7 +1150,7 @@
   * properties.
   * @throws ADSContextException if something goes wrong.
   */
  private LdapName makeDNFromServerGroupProperties(
  private static LdapName makeDNFromServerGroupProperties(
      Map<ServerGroupProperty, Object> serverGroupProperties)
  throws ADSContextException
  {
@@ -1179,7 +1188,7 @@
   * properties.
   * @throws ADSContextException if something goes wrong.
   */
  private LdapName makeDNFromAdministratorProperties(
  private static LdapName makeDNFromAdministratorProperties(
      Map<AdministratorProperty, Object> adminProperties)
  throws ADSContextException
  {
@@ -1196,7 +1205,7 @@
   * @return the attributes for the given administrator properties.
   * @throws ADSContextException if something goes wrong.
   */
  private BasicAttributes makeAttrsFromAdministratorProperties(
  private static BasicAttributes makeAttrsFromAdministratorProperties(
      Map<AdministratorProperty, Object> adminProperties)
  throws ADSContextException
  {
@@ -1247,7 +1256,7 @@
    switch(property)
    {
    case HOSTNAME:
    case HOST_NAME:
      result = null;
      break;
    case INSTANCE_PATH:
@@ -1273,7 +1282,7 @@
   * @return the attributes for the given server group properties.
   * @throws ADSContextException if something goes wrong.
   */
  private BasicAttributes makeAttrsFromServerGroupProperties(
  private static BasicAttributes makeAttrsFromServerGroupProperties(
      Map<ServerGroupProperty, Object> serverGroupProperties)
  {
    BasicAttributes result = new BasicAttributes();
@@ -1299,7 +1308,7 @@
   * @return the attribute for a given server group property.
   * @throws ADSContextException if something goes wrong.
   */
  private Attribute makeAttrFromServerGroupProperty(
  private static Attribute makeAttrFromServerGroupProperty(
      ServerGroupProperty property, Object value)
  {
    Attribute result;
@@ -1511,7 +1520,7 @@
    //
    // Put hostname and ipath
    //
    result.put(ServerProperty.HOSTNAME, hostName);
    result.put(ServerProperty.HOST_NAME, hostName);
    result.put(ServerProperty.INSTANCE_PATH, ipath);
    //
@@ -1588,7 +1597,7 @@
   * Returns the parent entry of the administrator entries.
   * @return the parent entry of the administrator entries.
   */
  private static String getAdministratorContainerDN()
  public static String getAdministratorContainerDN()
  {
    return "cn=Administrators," + getAdministrationSuffixDN();
  }
@@ -1612,7 +1621,7 @@
  private static String getHostname(
      Map<ServerProperty, Object> serverProperties) throws ADSContextException
  {
    String result = (String)serverProperties.get(ServerProperty.HOSTNAME);
    String result = (String)serverProperties.get(ServerProperty.HOST_NAME);
    if (result == null)
    {
      throw new ADSContextException(
@@ -1657,7 +1666,7 @@
   * @return the Administrator UID for the given properties.
   * @throws ADSContextException if the administrator UID could not be found.
   */
  private String getAdministratorUID(
  private static String getAdministratorUID(
      Map<AdministratorProperty, Object> adminProperties)
  throws ADSContextException {
    String result = (String)adminProperties.get(
@@ -1677,7 +1686,7 @@
   * @throws ADSContextException if the administrator password could not be
   * found.
   */
  private String getAdministratorPassword(
  private static String getAdministratorPassword(
      Map<AdministratorProperty, Object> adminProperties)
  throws ADSContextException {
    String result = (String)adminProperties.get(
@@ -1837,7 +1846,8 @@
  private void createAdministrationSuffix()
  throws ADSContextException
  {
    // TODO: use new administration framework.
    ADSContextHelper helper = new ADSContextHelper();
    helper.createAdministrationSuffix(getDirContext(), getBackendName());
  }
  /**
@@ -1849,7 +1859,7 @@
  private static void createOfflineAdministrationSuffix(String installPath)
  throws ADSContextException
  {
    // TODO: the call to this method assumes
    // NOTE: the call to this method assumes
    // that OpenDS.jar has already been loaded.  So this should not be called by
    // the Java Web Start before being sure that this jar is loaded.
    ArrayList<String> argList = new ArrayList<String>();
@@ -1881,7 +1891,13 @@
   */
  private void removeAdministrationSuffix() throws ADSContextException
  {
    // TODO: use new administration framework
    ADSContextHelper helper = new ADSContextHelper();
    helper.removeAdministrationSuffix(getDirContext(), getBackendName());
  }
  private static String getBackendName()
  {
    return "userRoot";
  }
  /**
@@ -1914,4 +1930,30 @@
    getAdministratorDN("*") +
    "\");)";
  }
  private static void addToLines(LdapName dn, BasicAttributes attrs,
      LinkedList<String> lines) throws ADSContextException
  {
    lines.add("dn: "+dn.toString());
    NamingEnumeration<String> ids = attrs.getIDs();
    while (ids.hasMoreElements())
    {
      String attrID = ids.nextElement();
      Attribute attr = attrs.get(attrID);
      try
      {
        NamingEnumeration values = attr.getAll();
        while (values.hasMoreElements())
        {
          lines.add(attrID+": "+values.nextElement());
        }
      }
      catch (NamingException ne)
      {
        // This should not happen
        throw new ADSContextException(
            ADSContextException.ErrorType.ERROR_UNEXPECTED, ne);
      }
    }
  }
}
opends/src/ads/org/opends/admin/ads/ADSContextHelper.java
New file
@@ -0,0 +1,142 @@
/*
 * 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.admin.ads;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.naming.ldap.InitialLdapContext;
import org.opends.server.admin.client.InitialManagedObject;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.client.ldap.LDAPManagementContext;
import org.opends.server.admin.std.client.*;
import org.opends.server.admin.std.meta.*;
import org.opends.server.types.DN;
/**
 * This is the only class in the org.opends.admin.ads package that uses the
 * classes from OpenDS.jar (in particular the administration client framework
 * API).  Before calling this class OpenDS.jar must be
 * loaded.  The goal is basically to centralize in one single place the
 * dependencies of this package on OpenDS.jar.  This is done in order the
 * QuickSetup code to be able to use some of the functionalities provided
 * by the ADSContext classes before OpenDS.jar is downloaded.
 */
public class ADSContextHelper
{
  /**
   * Default constructor.
   */
  public ADSContextHelper()
  {
  }
  /**
   * Removes the administration suffix.
   * @param ctx the DirContext to be used.
   * @param backendName the name of the backend where the administration
   * suffix is stored.
   * @throws ADSContextException if the administration suffix could not be
   * removed.
   */
  public void removeAdministrationSuffix(InitialLdapContext ctx,
      String backendName) throws ADSContextException
  {
    try
    {
      ManagementContext mCtx = LDAPManagementContext.createFromContext(
          JNDIDirContextAdaptor.adapt(ctx));
      RootCfgClient root = mCtx.getRootConfiguration();
      BackendCfgClient backend = root.getBackend(backendName);
      if (backend != null)
      {
        SortedSet<DN> suffixes = backend.getBackendBaseDN();
        if (suffixes != null)
        {
          if (suffixes.remove(
              DN.decode(ADSContext.getAdministrationSuffixDN())))
          {
            if (suffixes.size() > 0)
            {
              backend.commit();
            }
            else
            {
              root.removeBackend(backendName);
            }
          }
        }
      }
    }
    catch (Throwable t)
    {
      throw new ADSContextException(
          ADSContextException.ErrorType.ERROR_UNEXPECTED, t);
    }
  }
  /**
   * Creates the Administration Suffix.
   * @param ctx the DirContext to be used.
   * @param backendName the name of the backend where the administration
   * suffix is stored.
   * @throws ADSContextException if the administration suffix could not be
   * created.
   */
  public void createAdministrationSuffix(InitialLdapContext ctx,
      String backendName) throws ADSContextException
  {
    try
    {
      ManagementContext mCtx = LDAPManagementContext.createFromContext(
          JNDIDirContextAdaptor.adapt(ctx));
      RootCfgClient root = mCtx.getRootConfiguration();
      BackendCfgClient backend = root.getBackend(backendName);
      if (backend == null)
      {
        BackendCfgDefn provider = BackendCfgDefn.getInstance();
        backend = root.createBackend(provider, backendName,
            new InitialManagedObject<BackendCfgClient>(
                BackendCfgDefn.getInstance()));
      }
      SortedSet<DN> suffixes = backend.getBackendBaseDN();
      if (suffixes == null)
      {
        suffixes = new TreeSet<DN>();
      }
      suffixes.add(DN.decode(ADSContext.getAdministrationSuffixDN()));
      backend.commit();
    }
    catch (Throwable t)
    {
      throw new ADSContextException(
          ADSContextException.ErrorType.ERROR_UNEXPECTED, t);
    }
  }
}
opends/src/ads/org/opends/admin/ads/ReplicaDescriptor.java
@@ -27,6 +27,9 @@
package org.opends.admin.ads;
import java.util.HashSet;
import java.util.Set;
/**
 * The object of this class represent a Replica (i.e. a suffix in a given
 * server).
@@ -36,6 +39,8 @@
  private SuffixDescriptor suffix;
  private int entries = -1;
  private ServerDescriptor server;
  private Set<String> replicationServers = new HashSet<String>();
  private int replicationId = -1;
  /**
   * Returns the number of entries contained in the replica.
@@ -47,6 +52,16 @@
  }
  /**
   * Returns whether this replica is replicated or not.
   * @return <CODE>true</CODE> if the replica is replicated and
   * <CODE>false</CODE> otherwise.
   */
  public boolean isReplicated()
  {
    return replicationId != -1;
  }
  /**
   * Sets the number of entries contained in the replica.
   * @param entries the number of entries contained in the replica.
   */
@@ -97,4 +112,52 @@
  {
    this.suffix = suffix;
  }
  /**
   * Returns a set containing the String representation of the replication
   * servers that are defined in the replication domain for this replica.
   * @return a set containing the String representation of the replication
   * servers that are defined in the replication domain for this replica.
   */
  public Set<String> getReplicationServers()
  {
    HashSet<String> copy = new HashSet<String>();
    copy.addAll(replicationServers);
    return copy;
  }
  /**
   * Sets the list of replication servers (in their String representation) that
   * are defined in the replication domain for this replica.
   * @param replicationServers the list of replication servers (in their String
   * representation) that are defined in the replication domain for this
   * replica.
   */
  public void setReplicationServers(Set<String> replicationServers)
  {
    this.replicationServers.clear();
    this.replicationServers.addAll(replicationServers);
  }
  /**
   * Returns the replication server id for the replication domain associated
   * with this replica.
   * @return the replication server id for the replication domain associated
   * with this replica.
   */
  public int getReplicationId()
  {
    return replicationId;
  }
  /**
   * Sets the replication server id for the replication domain associated
   * with this replica.
   * @param replicationId the replication server id for the replication domain
   * associated with this replica.
   */
  public void setReplicationId(int replicationId)
  {
    this.replicationId = replicationId;
  }
}
opends/src/ads/org/opends/admin/ads/ServerDescriptor.java
@@ -27,10 +27,26 @@
package org.opends.admin.ads;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapName;
import org.opends.admin.ads.util.ConnectionUtils;
/**
 * The object of this class represent an OpenDS server.
@@ -40,6 +56,80 @@
  private Map<ADSContext.ServerProperty, Object> adsProperties =
    new HashMap<ADSContext.ServerProperty, Object>();
  private Set<ReplicaDescriptor> replicas = new HashSet<ReplicaDescriptor>();
  private Map<ServerProperty, Object> serverProperties =
    new HashMap<ServerProperty, Object>();
  private TopologyCacheException lastException;
  /**
   * Enumeration containing the different server properties that we can keep in
   * the ServerProperty object.
   */
  public enum ServerProperty
  {
    /**
     * The associated value is a String.
     */
    HOST_NAME,
    /**
     * The associated value is an ArrayList of Integer.
     */
    LDAP_PORT,
    /**
     * The associated value is an ArrayList of Integer.
     */
    LDAPS_PORT,
    /**
     * The associated value is an ArrayList of Boolean.
     */
    LDAP_ENABLED,
    /**
     * The associated value is an ArrayList of Boolean.
     */
    LDAPS_ENABLED,
    /**
     * The associated value is an ArrayList of Integer.
     */
    JMX_PORT,
    /**
     * The associated value is an ArrayList of Integer.
     */
    JMXS_PORT,
    /**
     * The associated value is an ArrayList of Boolean.
     */
    JMX_ENABLED,
    /**
     * The associated value is an ArrayList of Boolean.
     */
    JMXS_ENABLED,
    /**
     * The associated value is an Integer.
     */
    REPLICATION_SERVER_PORT,
    /**
     * The associated value is a Boolean.
     */
    IS_REPLICATION_SERVER,
    /**
     * The associated value is a Boolean.
     */
    IS_REPLICATION_ENABLED,
    /**
     * List of servers specified in the Replication Server configuration.
     * This is a Set of String.
     */
    EXTERNAL_REPLICATION_SERVERS,
    /**
     * The associated value is an Integer.
     */
    REPLICATION_SERVER_ID
  }
  private static final Logger LOG =
    Logger.getLogger(ServerDescriptor.class.getName());
  private ServerDescriptor()
  {
  }
  /**
   * Returns the replicas contained on the server.
@@ -47,7 +137,9 @@
   */
  public Set<ReplicaDescriptor> getReplicas()
  {
    return replicas;
    Set<ReplicaDescriptor> copy = new HashSet<ReplicaDescriptor>();
    copy.addAll(replicas);
    return copy;
  }
  /**
@@ -56,16 +148,26 @@
   */
  public void setReplicas(Set<ReplicaDescriptor> replicas)
  {
    this.replicas = replicas;
    this.replicas.clear();
    this.replicas.addAll(replicas);
  }
  /**
   * Returns a Map containing the ADS properties of the server.
   * @return a Map containing the ADS properties of the server.
   */
  public Map<ADSContext.ServerProperty, Object> getAdsProperties()
  {
    return adsProperties;
  }
  /**
   * Returns a Map containing the properties of the server.
   * @return a Map containing the properties of the server.
   */
  public Map<ADSContext.ServerProperty, Object> getAdsProperties()
  public Map<ServerProperty, Object> getServerProperties()
  {
    return adsProperties;
    return serverProperties;
  }
  /**
@@ -79,12 +181,581 @@
  }
  /**
   * Sets the properties of the server.
   * @param adsProperties a Map containing the properties of the server.
   * Sets the ADS properties of the server.
   * @param adsProperties a Map containing the ADS properties of the server.
   */
  public void setAdsProperties(
      Map<ADSContext.ServerProperty, Object> adsProperties)
  {
    this.adsProperties = adsProperties;
  }
  /**
   * Returns the host name of the server.
   * @return the host name of the server.
   */
  public String getHostName()
  {
    String host = (String)serverProperties.get(ServerProperty.HOST_NAME);
    if (host == null)
    {
      host = (String)adsProperties.get(ADSContext.ServerProperty.HOST_NAME);
    }
    return host;
  }
  /**
   * Returns a String of type host-name:port-number for the server.  If
   * the provided securePreferred is set to true the port that will be used
   * (if LDAPS is enabled) will be the LDAPS port.
   * @param securePreferred whether to try to use the secure port as part
   * of the returning String or not.
   * @return a String of type host-name:port-number for the server.
   */
  public String getHostPort(boolean securePreferred)
  {
    String host = getHostName();
    int port = -1;
    if (!serverProperties.isEmpty())
    {
      ArrayList s = (ArrayList)serverProperties.get(
          ServerProperty.LDAP_ENABLED);
      ArrayList p = (ArrayList)serverProperties.get(
          ServerProperty.LDAP_PORT);
      for (int i=0; i<s.size(); i++)
      {
        if (Boolean.TRUE.equals(s.get(i)))
        {
          port = (Integer)p.get(i);
          break;
        }
      }
      if (securePreferred)
      {
        s = (ArrayList)serverProperties.get(
            ServerProperty.LDAPS_ENABLED);
        p = (ArrayList)serverProperties.get(ServerProperty.LDAPS_PORT);
        for (int i=0; i<s.size(); i++)
        {
          if (Boolean.TRUE.equals(s.get(i)))
          {
            port = (Integer)p.get(i);
            break;
          }
        }
      }
    }
    else
    {
      boolean secure;
      Object v = adsProperties.get(ADSContext.ServerProperty.LDAPS_ENABLED);
      secure = securePreferred && "true".equalsIgnoreCase(String.valueOf(v));
      try
      {
        if (secure)
        {
          port = Integer.parseInt((String)adsProperties.get(
              ADSContext.ServerProperty.LDAPS_PORT));
        }
        else
        {
          port = Integer.parseInt((String)adsProperties.get(
              ADSContext.ServerProperty.LDAP_PORT));
        }
      }
      catch (Throwable t)
      {
      }
    }
    return host + "." + port;
  }
  /**
   * Returns an Id that is unique for this server.
   * @return an Id that is unique for this server.
   */
  public String getId()
  {
    StringBuilder buf = new StringBuilder();
    if (serverProperties.size() > 0)
    {
      buf.append(serverProperties.get(ServerProperty.HOST_NAME));
      ServerProperty [] props =
      {
          ServerProperty.LDAP_PORT, ServerProperty.LDAPS_PORT,
          ServerProperty.LDAP_ENABLED, ServerProperty.LDAPS_ENABLED
      };
      for (int i=0; i<props.length; i++)
      {
        ArrayList s = (ArrayList)serverProperties.get(props[i]);
        for (Object o : s)
        {
          buf.append(":"+o);
        }
      }
    }
    else
    {
      ADSContext.ServerProperty[] props =
      {
          ADSContext.ServerProperty.HOST_NAME,
          ADSContext.ServerProperty.LDAP_PORT,
          ADSContext.ServerProperty.LDAPS_PORT,
          ADSContext.ServerProperty.LDAP_ENABLED,
          ADSContext.ServerProperty.LDAPS_ENABLED
      };
      for (int i=0; i<props.length; i++)
      {
        if (i != 0)
        {
          buf.append(":");
        }
        buf.append(adsProperties.get(props[i]));
      }
    }
    return buf.toString();
  }
  /**
   * Returns the last exception that was encountered reading the configuration
   * of the server.  Returns null if there was no problem loading the
   * configuration of the server.
   * @return the last exception that was encountered reading the configuration
   * of the server.  Returns null if there was no problem loading the
   * configuration of the server.
   */
  public TopologyCacheException getLastException()
  {
    return lastException;
  }
  /**
   * Sets the last exception that occurred while reading the configuration of
   * the server.
   * @param lastException the last exception that occurred while reading the
   * configuration of the server.
   */
  public void setLastException(TopologyCacheException lastException)
  {
    this.lastException = lastException;
  }
  /**
   * Creates a ServerDescriptor object based on some ADS properties provided.
   * @param adsProperties the ADS properties of the server.
   * @return a ServerDescriptor object that corresponds to the provided ADS
   * properties.
   */
  public static ServerDescriptor createStandalone(
      Map<ADSContext.ServerProperty, Object> adsProperties)
  {
    ServerDescriptor desc = new ServerDescriptor();
    desc.setAdsProperties(adsProperties);
    return desc;
  }
  /**
   * Creates a ServerDescriptor object based on the configuration that we read
   * using the provided InitialLdapContext.
   * @param ctx the InitialLdapContext that will be used to read the
   * configuration of the server.
   * @return a ServerDescriptor object that corresponds to the read
   * configuration.
   * @throws NamingException if a problem occurred reading the server
   * configuration.
   */
  public static ServerDescriptor createStandalone(InitialLdapContext ctx)
  throws NamingException
  {
    ServerDescriptor desc = new ServerDescriptor();
    updateLdapConfiguration(desc, ctx);
    updateJmxConfiguration(desc, ctx);
    updateReplicas(desc, ctx);
    updateReplication(desc, ctx);
    String s = (String)ctx.getEnvironment().get(Context.PROVIDER_URL);
    try
    {
      URI ldapURL = new URI(s);
      desc.serverProperties.put(ServerProperty.HOST_NAME, ldapURL.getHost());
    }
    catch (URISyntaxException use)
    {
      // This is really strange.  Seems like a bug somewhere.
      LOG.log(Level.WARNING, "Error parsing ldap URL "+s, use);
    }
    return desc;
  }
  private static void updateLdapConfiguration(ServerDescriptor desc,
      InitialLdapContext ctx) throws NamingException
  {
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    ctls.setReturningAttributes(
        new String[] {
            "ds-cfg-connection-handler-enabled",
            "ds-cfg-listen-address",
            "ds-cfg-listen-port",
            "ds-cfg-use-ssl",
            "objectclass"
        });
    String filter = "(objectclass=ds-cfg-ldap-connection-handler)";
    LdapName jndiName = new LdapName("cn=config");
    NamingEnumeration listeners = ctx.search(jndiName, filter, ctls);
    ArrayList<Integer> ldapPorts = new ArrayList<Integer>();
    ArrayList<Integer> ldapsPorts = new ArrayList<Integer>();
    ArrayList<Boolean> ldapEnabled = new ArrayList<Boolean>();
    ArrayList<Boolean> ldapsEnabled = new ArrayList<Boolean>();
    desc.serverProperties.put(ServerProperty.LDAP_PORT, ldapPorts);
    desc.serverProperties.put(ServerProperty.LDAPS_PORT, ldapsPorts);
    desc.serverProperties.put(ServerProperty.LDAP_ENABLED, ldapEnabled);
    desc.serverProperties.put(ServerProperty.LDAPS_ENABLED, ldapsEnabled);
    while(listeners.hasMore())
    {
      SearchResult sr = (SearchResult)listeners.next();
      String port = getFirstValue(sr, "ds-cfg-listen-port");
      boolean isSecure = "true".equalsIgnoreCase(
          getFirstValue(sr, "ds-cfg-use-ssl"));
      boolean enabled = "true".equalsIgnoreCase(
            getFirstValue(sr, "ds-cfg-connection-handler-enabled"));
      if (isSecure)
      {
        ldapsPorts.add(new Integer(port));
        ldapsEnabled.add(enabled);
      }
      else
      {
        ldapPorts.add(new Integer(port));
        ldapEnabled.add(enabled);
      }
    }
  }
  private static void updateJmxConfiguration(ServerDescriptor desc,
      InitialLdapContext ctx) throws NamingException
  {
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    ctls.setReturningAttributes(
        new String[] {
            "ds-cfg-connection-handler-enabled",
            "ds-cfg-listen-address",
            "ds-cfg-listen-port",
            "ds-cfg-use-ssl",
            "objectclass"
        });
    String filter = "(objectclass=ds-cfg-jmx-connection-handler)";
    LdapName jndiName = new LdapName("cn=config");
    NamingEnumeration listeners = ctx.search(jndiName, filter, ctls);
    ArrayList<Integer> jmxPorts = new ArrayList<Integer>();
    ArrayList<Integer> jmxsPorts = new ArrayList<Integer>();
    ArrayList<Boolean> jmxEnabled = new ArrayList<Boolean>();
    ArrayList<Boolean> jmxsEnabled = new ArrayList<Boolean>();
    desc.serverProperties.put(ServerProperty.JMX_PORT, jmxPorts);
    desc.serverProperties.put(ServerProperty.JMXS_PORT, jmxsPorts);
    desc.serverProperties.put(ServerProperty.JMX_ENABLED, jmxEnabled);
    desc.serverProperties.put(ServerProperty.JMXS_ENABLED, jmxsEnabled);
    while(listeners.hasMore())
    {
      SearchResult sr = (SearchResult)listeners.next();
      String port = getFirstValue(sr, "ds-cfg-listen-port");
      boolean isSecure = "true".equalsIgnoreCase(
          getFirstValue(sr, "ds-cfg-use-ssl"));
      boolean enabled = "true".equalsIgnoreCase(
            getFirstValue(sr, "ds-cfg-connection-handler-enabled"));
      if (isSecure)
      {
        jmxsPorts.add(new Integer(port));
        jmxsEnabled.add(enabled);
      }
      else
      {
        jmxPorts.add(new Integer(port));
        jmxEnabled.add(enabled);
      }
    }
  }
  private static void updateReplicas(ServerDescriptor desc,
      InitialLdapContext ctx) throws NamingException
  {
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    ctls.setReturningAttributes(
        new String[] {
            "ds-cfg-backend-base-dn",
            "ds-cfg-backend-id"
        });
    String filter = "(objectclass=ds-cfg-backend)";
    LdapName jndiName = new LdapName("cn=config");
    NamingEnumeration databases = ctx.search(jndiName, filter, ctls);
    while(databases.hasMore())
    {
      SearchResult sr = (SearchResult)databases.next();
      String id = getFirstValue(sr, "ds-cfg-backend-id");
      if (!isConfigBackend(id))
      {
        Set<String> baseDns = getValues(sr, "ds-cfg-backend-base-dn");
        int nEntries = getEntryCount(ctx, id);
        for (String baseDn : baseDns)
        {
          SuffixDescriptor suffix = new SuffixDescriptor();
          suffix.setDN(baseDn);
          ReplicaDescriptor replica = new ReplicaDescriptor();
          replica.setServer(desc);
          Set<ReplicaDescriptor> replicas = new HashSet<ReplicaDescriptor>();
          replicas.add(replica);
          suffix.setReplicas(replicas);
          replica.setSuffix(suffix);
          desc.setReplicas(replicas);
          if (baseDns.size() == 1)
          {
            replica.setEntries(nEntries);
          }
          else
          {
            /* Cannot know how many entries correspond to this replica */
            replica.setEntries(-1);
          }
        }
      }
    }
  }
  private static void updateReplication(ServerDescriptor desc,
      InitialLdapContext ctx) throws NamingException
  {
    boolean replicationEnabled = false;
    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")))
        {
          replicationEnabled = true;
        }
      }
    }
    catch (NameNotFoundException nse)
    {
    }
    desc.serverProperties.put(ServerProperty.IS_REPLICATION_ENABLED,
        replicationEnabled ? Boolean.TRUE : Boolean.FALSE);
    ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    ctls.setReturningAttributes(
        new String[] {
            "ds-cfg-replication-dn",
            "ds-cfg-replication-server",
            "ds-cfg-directory-server-id"
        });
    filter = "(objectclass=ds-cfg-replication-domain-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();
        int id = Integer.parseInt(
            getFirstValue(sr, "ds-cfg-directory-server-id"));
        Set<String> replicationServers = getValues(sr,
            "ds-cfg-replication-server");
        Set<String> dns = getValues(sr, "ds-cfg-replication-dn");
        for (String dn : dns)
        {
          for (ReplicaDescriptor replica : desc.getReplicas())
          {
            if (areDnsEqual(replica.getSuffix().getDN(), dn))
            {
              replica.setReplicationId(id);
              replica.setReplicationServers(replicationServers);
            }
          }
        }
      }
    }
    catch (NameNotFoundException nse)
    {
    }
    ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
    ctls.setReturningAttributes(
    new String[] {
      "ds-cfg-replication-server-port", "ds-cfg-replication-server",
      "ds-cfg-replication-server-id"
    });
    filter = "(objectclass=ds-cfg-replication-server-config)";
    jndiName = new LdapName("cn=Replication Server,cn=Multimaster "+
        "Synchronization,cn=Synchronization Providers,cn=config");
    desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER,
        Boolean.FALSE);
    try
    {
      NamingEnumeration entries = ctx.search(jndiName, filter, ctls);
      while(entries.hasMore())
      {
        SearchResult sr = (SearchResult)entries.next();
        desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER,
            Boolean.TRUE);
        String v = getFirstValue(sr, "ds-cfg-replication-server-port");
        desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_PORT,
            Integer.parseInt(v));
        v = getFirstValue(sr, "ds-cfg-replication-server-id");
        desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_ID,
            Integer.parseInt(v));
      }
    }
    catch (NameNotFoundException nse)
    {
    }
  }
  /**
   * Returns the number of entries in a given backend using the provided
   * InitialLdapContext.
   * @param ctx the InitialLdapContext to use to update the configuration.
   * @param backenID the id of the backend.
   * @return the number of entries in the backend.
   * @throws NamingException if there was an error.
   */
  private static int getEntryCount(InitialLdapContext ctx, String backendID)
  throws NamingException
  {
    int nEntries = -1;
    String v = null;
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
    ctls.setReturningAttributes(
        new String[] {
            "ds-backend-entry-count"
        });
    String filter = "(ds-backend-id="+backendID+")";
    LdapName jndiName = new LdapName("cn=monitor");
    NamingEnumeration listeners = ctx.search(jndiName, filter, ctls);
    while(listeners.hasMore())
    {
      SearchResult sr = (SearchResult)listeners.next();
      v = getFirstValue(sr, "ds-backend-entry-count");
    }
    try
    {
      nEntries = Integer.parseInt(v);
    }
    catch (Exception ex)
    {
    }
    return nEntries;
  }
  /*
   * The following 2 methods are convenience methods to retrieve String values
   * from an entry.
   */
  private static String getFirstValue(SearchResult entry, String attrName)
  throws NamingException
  {
    return ConnectionUtils.getFirstValue(entry, attrName);
  }
  private static Set<String> getValues(SearchResult entry, String attrName)
  throws NamingException
  {
    return ConnectionUtils.getValues(entry, attrName);
  }
  /**
   * An convenience method to know if the provided ID corresponds to a
   * configuration backend or not.
   * @param id the backend ID to analyze
   * @return <CODE>true</CODE> if the the id corresponds to a configuration
   * backend and <CODE>false</CODE> otherwise.
   */
  private static boolean isConfigBackend(String id)
  {
    return "tasks".equalsIgnoreCase(id) ||
    "schema".equalsIgnoreCase(id) ||
    "config".equalsIgnoreCase(id) ||
    "monitor".equalsIgnoreCase(id) ||
    "backup".equalsIgnoreCase(id);
  }
  /**
   * Returns <CODE>true</CODE> if the the provided strings represent the same
   * DN and <CODE>false</CODE> otherwise.
   * @param dn1 the first dn to compare.
   * @param dn2 the second dn to compare.
   * @return <CODE>true</CODE> if the the provided strings represent the same
   * DN and <CODE>false</CODE> otherwise.
   */
  private static boolean areDnsEqual(String dn1, String dn2)
  {
    boolean areDnsEqual = false;
    try
    {
      LdapName name1 = new LdapName(dn1);
      LdapName name2 = new LdapName(dn2);
      areDnsEqual = name1.equals(name2);
    } catch (Exception ex)
    {
    }
    return areDnsEqual;
  }
}
opends/src/ads/org/opends/admin/ads/SuffixDescriptor.java
@@ -32,7 +32,8 @@
/**
 * The object of this class represent a topology of replicas across servers
 * that have the same suffix DN.  They may or might not be replicated.
 * that have the same suffix DN.  If there is more than one replica on the
 * suffix, the contents of the replicas are replicated.
 */
public class SuffixDescriptor
{
@@ -64,7 +65,9 @@
   */
  public Set<ReplicaDescriptor> getReplicas()
  {
    return replicas;
    Set<ReplicaDescriptor> copy = new HashSet<ReplicaDescriptor>();
    copy.addAll(replicas);
    return copy;
  }
  /**
@@ -74,78 +77,46 @@
   */
  public void setReplicas(Set<ReplicaDescriptor> replicas)
  {
    this.replicas = replicas;
  }
  /**
   * {@inheritDoc}
   */
  public boolean equals(Object v)
  {
    // TODO: this is buggy code
    boolean equals = false;
    if (this != v)
    {
      if (v instanceof SuffixDescriptor)
      {
        SuffixDescriptor desc = (SuffixDescriptor)v;
        equals = getDN().equals(desc.getDN());
        if (equals)
        {
          equals = getReplicas().size() == desc.getReplicas().size();
        }
        if (equals)
        {
          for (ReplicaDescriptor repl : getReplicas())
          {
            boolean serverFound = false;
            ServerDescriptor server = repl.getServer();
            for (ReplicaDescriptor repl1 : desc.getReplicas())
            {
              serverFound = serversEqual(server, repl1.getServer());
              if (serverFound)
              {
                break;
              }
            }
            if (!serverFound)
            {
              equals = false;
              break;
            }
          }
        }
      }
    }
    else
    {
      equals = true;
    }
    return equals;
    this.replicas.clear();
    this.replicas.addAll(replicas);
  }
  /**
   * Tells whether the two provided objects represent the same server or not.
   * @param serv1 the first ServerDescriptor to compare.
   * @param serv2 the second ServerDescriptor to compare.
   * @return <CODE>true</CODE> if the two objects represent the same server
   * and <CODE>false</CODE> otherwise.
   * Returns the Set of Replication servers for the whole suffix topology.  The
   * servers are provided in their String representation.
   * @return the Set of Replication servers for the whole suffix topology.
   */
  private boolean serversEqual(ServerDescriptor serv1, ServerDescriptor serv2)
  public Set<String> getReplicationServers()
  {
    return serv1.getAdsProperties().equals(serv2.getAdsProperties());
    Set<String> replicationServers = new HashSet<String>();
    for (ReplicaDescriptor replica : getReplicas())
    {
      replicationServers.addAll(replica.getReplicationServers());
    }
    return replicationServers;
  }
  /**
   * {@inheritDoc}
   */
  public int hashCode()
  {
//  FIXME: this is buggy code
    return getDN().hashCode();
    return getId().hashCode();
  }
  /**
   * Returns an Id that is unique for this suffix.
   * @return an Id that is unique for this suffix.
   */
  public String getId()
  {
    StringBuilder buf = new StringBuilder();
    buf.append(getDN());
    for (ReplicaDescriptor replica : getReplicas())
    {
      buf.append("-"+replica.getServer().getId());
    }
    return buf.toString();
  }
}
opends/src/ads/org/opends/admin/ads/TopologyCache.java
New file
@@ -0,0 +1,256 @@
/*
 * 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.admin.ads;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.LdapName;
import org.opends.admin.ads.ADSContext.ServerProperty;
import org.opends.admin.ads.util.ApplicationTrustManager;
import org.opends.admin.ads.util.ServerLoader;
/**
 * This class allows to read the configuration of the different servers that
 * are registered in a given ADS server.  It provides a read only view of the
 * configuration of the servers and of the replication topologies that might
 * be configured between them.
 */
public class TopologyCache
{
  private ADSContext adsContext;
  private ApplicationTrustManager trustManager;
  private String dn;
  private String pwd;
  private Set<ServerDescriptor> servers = new HashSet<ServerDescriptor>();
  private Set<SuffixDescriptor> suffixes = new HashSet<SuffixDescriptor>();
  private final boolean isMultiThreaded = true;
  private final static int MULTITHREAD_TIMEOUT = 10000;
  private static final Logger LOG =
    Logger.getLogger(TopologyCache.class.getName());
  /**
   * Constructor of the TopologyCache.
   * @param adsContext the adsContext to the ADS registry.
   * @param trustManager the ApplicationTrustManager that must be used to trust
   * certificates when we create connections to the registered servers to read
   * their configuration.
   * @throws TopologyCacheException if an error occurred reading the
   * authentication data in the DirContext associated with the provided
   * ADSContext.
   */
  public TopologyCache(ADSContext adsContext,
      ApplicationTrustManager trustManager)
  throws TopologyCacheException
  {
    this.adsContext = adsContext;
    this.trustManager = trustManager;
    try
    {
      dn = (String)adsContext.getDirContext().getEnvironment().get(
          Context.SECURITY_PRINCIPAL);
      pwd = (String)adsContext.getDirContext().getEnvironment().get(
          Context.SECURITY_CREDENTIALS);
    }
    catch (NamingException ne)
    {
      throw new TopologyCacheException(TopologyCacheException.Type.BUG, ne);
    }
  }
  /**
   * Reads the configuration of the registered servers.
   * @throws TopologyCacheException if there is an issue reading the
   * configuration of the registered servers.
   */
  public void reloadTopology() throws TopologyCacheException
  {
    suffixes.clear();
    servers.clear();
    try
    {
      Set<Map<ServerProperty,Object>> adsServers =
        adsContext.readServerRegistry();
      Set<ServerLoader> threadSet = new HashSet<ServerLoader>();
      for (Map<ServerProperty,Object> serverProperties : adsServers)
      {
        ServerLoader t = getServerLoader(serverProperties);
        if (isMultiThreaded)
        {
            t.start();
            threadSet.add(t);
        }
        else
        {
            t.run();
        }
      }
      if (isMultiThreaded)
      {
        joinThreadSet(threadSet);
      }
      /* Try to consolidate things (even if the data is not complete). */
      HashMap<LdapName, Set<SuffixDescriptor>> hmSuffixes =
        new HashMap<LdapName, Set<SuffixDescriptor>>();
      for (ServerLoader loader : threadSet)
      {
        ServerDescriptor descriptor = loader.getServerDescriptor();
        for (ReplicaDescriptor replica : descriptor.getReplicas())
        {
          boolean suffixFound = false;
          LdapName dn = new LdapName(replica.getSuffix().getDN());
          Set<SuffixDescriptor> sufs = hmSuffixes.get(dn);
          if (sufs != null)
          {
            Iterator<SuffixDescriptor> it = sufs.iterator();
            while (it.hasNext() && !suffixFound)
            {
              SuffixDescriptor suffix = it.next();
              Iterator<String> it2 = suffix.getReplicationServers().iterator();
              while (it2.hasNext() && !suffixFound)
              {
                if (replica.getReplicationServers().contains(it2.next()))
                {
                  suffixFound = true;
                  Set<ReplicaDescriptor> replicas = suffix.getReplicas();
                  replicas.add(replica);
                  suffix.setReplicas(replicas);
                  replica.setSuffix(suffix);
                }
              }
            }
          }
          if (!suffixFound)
          {
            if (sufs == null)
            {
              sufs = new HashSet<SuffixDescriptor>();
              hmSuffixes.put(dn, sufs);
            }
            sufs.add(replica.getSuffix());
            suffixes.add(replica.getSuffix());
          }
        }
        servers.add(descriptor);
      }
    }
    catch (ADSContextException ade)
    {
      throw new TopologyCacheException(ade);
    }
    catch (Throwable t)
    {
      throw new TopologyCacheException(TopologyCacheException.Type.BUG, t);
    }
  }
  /**
   * Returns a Set containing all the servers that are registered in the ADS.
   * @return a Set containing all the servers that are registered in the ADS.
   */
  public Set<ServerDescriptor> getServers()
  {
    HashSet<ServerDescriptor> copy = new HashSet<ServerDescriptor>();
    copy.addAll(servers);
    return copy;
  }
  /**
   * Returns a Set containing the suffixes (replication topologies) that could
   * be retrieved after the last call to reloadTopology.
   * @return a Set containing the suffixes (replication topologies) that could
   * be retrieved after the last call to reloadTopology.
   */
  public Set<SuffixDescriptor> getSuffixes()
  {
    HashSet<SuffixDescriptor> copy = new HashSet<SuffixDescriptor>();
    copy.addAll(suffixes);
    return copy;
  }
  /**
   * Method used to wait at most a certain time (MULTITHREAD_TIMEOUT) for the
   * different threads to finish.
   * @param threadSet the list of threads (we assume that they are started)
   * that we must wait for.
   */
  private void joinThreadSet(Set<ServerLoader> threadSet)
  {
    Date startDate = new Date();
    for (ServerLoader t : threadSet)
    {
      long timeToJoin = MULTITHREAD_TIMEOUT - System.currentTimeMillis() +
      startDate.getTime();
      try
      {
        if (timeToJoin > 0)
        {
          t.join(MULTITHREAD_TIMEOUT);
        }
      }
      catch (InterruptedException ie)
      {
        LOG.log(Level.INFO, ie + " caught and ignored", ie);
      }
      if (t.isAlive())
      {
        t.interrupt();
      }
    }
    Date endDate = new Date();
    long workingTime = endDate.getTime() - startDate.getTime();
    LOG.log(Level.INFO, "Loading ended at "+ workingTime + " ms");
  }
  /**
   * Creates a ServerLoader object based on the provided server properties.
   * @param serverProperties the server properties to be used to generate
   * the ServerLoader.
   * @return a ServerLoader object based on the provided server properties.
   */
  private ServerLoader getServerLoader(
      Map<ServerProperty,Object> serverProperties)
  {
    return new ServerLoader(serverProperties, dn, pwd,
        trustManager.createCopy());
  }
}
opends/src/ads/org/opends/admin/ads/TopologyCacheException.java
New file
@@ -0,0 +1,148 @@
/*
 * 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.admin.ads;
import javax.naming.NamingException;
import org.opends.admin.ads.util.ApplicationTrustManager;
/**
 * This class represents the Exception that can occur while reading server
 * configuration through the TopologyCache class.
 */
public class TopologyCacheException extends Exception
{
  private static final long serialVersionUID = 1709535837273360382L;
  private Type type;
  private String ldapUrl;
  private ApplicationTrustManager trustManager;
  /**
   * Error type.
   */
  public enum Type
  {
    /**
     * Error reading the ADS.
     */
    GENERIC_READING_ADS,
    /**
     * Creating connection to a particular server.
     */
    GENERIC_CREATING_CONNECTION,
    /**
     * Error reading the configuration of a particular server.
     */
    GENERIC_READING_SERVER,
    /**
     * The DN provided in the DirContext of ADS is not of a global
     * administrator.
     */
    NOT_GLOBAL_ADMINISTRATOR,
    /**
     * Timeout reading the configuration of a particular server.
     */
    TIMEOUT,
    /**
     * Unexpected error.
     */
    BUG
  }
  /**
   * Constructor for the exception that must be generated when an
   * ADSContextException occurs.
   * @param ace the exception which is the cause of this exception.
   */
  public TopologyCacheException(ADSContextException ace)
  {
    type = Type.GENERIC_READING_ADS;
    initCause(ace);
  }
  /**
  * Constructor for a generic Exception.
  * @param type the type of this exception.
  * @param t the cause of this exception.
  */
  public TopologyCacheException(Type type, Throwable t)
  {
    this.type = type;
    initCause(t);
  }
  /**
   * Constructor for the exception that must be generated when a
   * NamingException occurs.
   * @param type the type of this exception.
   * @param ne the NamingException that generated this exception.
   * @param trustManager the ApplicationTrustManager used when the
   * NamingException occurred.
   * @param ldapUrl the LDAP URL of the server we where connected to (or trying
   * to connect) when the NamingException was generated.
   */
  public TopologyCacheException(Type type, NamingException ne,
      ApplicationTrustManager trustManager, String ldapUrl)
  {
    this.type = type;
    initCause(ne);
    this.ldapUrl = ldapUrl;
    this.trustManager = trustManager;
  }
  /**
   * Returns the type of this exception.
   * @return the type of this exception.
   */
  public Type getType()
  {
    return type;
  }
  /**
   * Returns the LDAP URL of the server we where connected to (or trying
   * to connect) when this exception was generated.
   * @return the LDAP URL of the server we where connected to (or trying
   * to connect) when this exception was generated.
   */
  public String getLdapUrl()
  {
    return ldapUrl;
  }
  /**
   * Returns the ApplicationTrustManager that we were using when this exception
   * was generated.
   * @return the ApplicationTrustManager that we were using when this exception
   * was generated.
   */
  public ApplicationTrustManager getTrustManager()
  {
    return trustManager;
  }
}
opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java
New file
@@ -0,0 +1,402 @@
/*
 * 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.admin.ads.util;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
 * This class is in charge of checking whether the certificates that are
 * presented are trusted or not.
 * This implementation tries to check also that the subject DN of the
 * certificate corresponds to the host passed using the setHostName method.
 *
 * The constructor tries to use a default TrustManager from the system and if
 * it cannot be retrieved this class will only accept the certificates
 * explicitly accepted by the user (and specified by calling acceptCertificate).
 *
 * NOTE: this class is not aimed to be used when we have connections in paralel.
 */
public class ApplicationTrustManager implements X509TrustManager
{
  /**
   * The enumeration for the different causes for which the trust manager can
   * refuse to accept a certificate.
   */
  public enum Cause
  {
    /**
     * The certificate was not trusted.
     */
    NOT_TRUSTED,
    /**
     * The certificate's subject DN's value and the host name we tried to
     * connect to do not match.
     */
    HOST_NAME_MISMATCH
  }
  static private final Logger LOG =
    Logger.getLogger(ApplicationTrustManager.class.getName());
  private X509TrustManager sunJSSEX509TrustManager;
  private String lastRefusedAuthType;
  private X509Certificate[] lastRefusedChain;
  private Cause lastRefusedCause;
  /*
   * The following ArrayList contain information about the certificates
   * explicitly accepted by the user.
   */
  private ArrayList<X509Certificate[]> acceptedChains =
    new ArrayList<X509Certificate[]>();
  private ArrayList<String> acceptedAuthTypes = new ArrayList<String>();
  private ArrayList<String> acceptedHosts = new ArrayList<String>();
  private String host;
  /**
   * The default constructor.
   */
  public ApplicationTrustManager()
  {
    TrustManagerFactory tmf = null;
    String algo = "SunX509";
    String provider = "SunJSSE";
    try
    {
      tmf = TrustManagerFactory.getInstance(algo, provider);
      tmf.init((KeyStore)null);
      sunJSSEX509TrustManager =
        (X509TrustManager)(tmf.getTrustManagers())[0];
    }
    catch (NoSuchAlgorithmException e)
    {
      // Nothing to do: if this occurs we will systematically refuse the
      // certificates.  Maybe we should avoid this and be strict, but we are
      // in a best effor mode.
      LOG.log(Level.WARNING, "Error with the algorithm", e);
    }
    catch (NoSuchProviderException e)
    {
      // Nothing to do: if this occurs we will systematically refuse the
      // certificates.  Maybe we should avoid this and be strict, but we are
      // in a best effor mode.
      LOG.log(Level.WARNING, "Error with the provider", e);
    }
    catch (KeyStoreException e)
    {
      // Nothing to do: if this occurs we will systematically refuse the
      // certificates.  Maybe we should avoid this and be strict, but we are
      // in a best effor mode.
      LOG.log(Level.WARNING, "Error with the keystore", e);
    }
  }
  /**
   * {@inheritDoc}
   */
  public void checkClientTrusted(X509Certificate[] chain, String authType)
  throws CertificateException
  {
    boolean explicitlyAccepted = false;
    try
    {
      if (sunJSSEX509TrustManager != null)
      {
        try
        {
          sunJSSEX509TrustManager.checkClientTrusted(chain, authType);
        }
        catch (CertificateException ce)
        {
          verifyAcceptedCertificates(chain, authType);
          explicitlyAccepted = true;
        }
      }
      else
      {
        verifyAcceptedCertificates(chain, authType);
        explicitlyAccepted = true;
      }
    }
    catch (CertificateException ce)
    {
      lastRefusedChain = chain;
      lastRefusedAuthType = authType;
      lastRefusedCause = Cause.NOT_TRUSTED;
      throw ce;
    }
    if (!explicitlyAccepted)
    {
      try
      {
        verifyHostName(chain, authType);
      }
      catch (CertificateException ce)
      {
        lastRefusedChain = chain;
        lastRefusedAuthType = authType;
        lastRefusedCause = Cause.HOST_NAME_MISMATCH;
        throw ce;
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public void checkServerTrusted(X509Certificate[] chain,
      String authType) throws CertificateException
  {
    boolean explicitlyAccepted = false;
    try
    {
      if (sunJSSEX509TrustManager != null)
      {
        try
        {
          sunJSSEX509TrustManager.checkServerTrusted(chain, authType);
        }
        catch (CertificateException ce)
        {
          verifyAcceptedCertificates(chain, authType);
          explicitlyAccepted = true;
        }
      }
      else
      {
        verifyAcceptedCertificates(chain, authType);
        explicitlyAccepted = true;
      }
    }
    catch (CertificateException ce)
    {
      lastRefusedChain = chain;
      lastRefusedAuthType = authType;
      lastRefusedCause = Cause.NOT_TRUSTED;
      throw ce;
    }
    if (!explicitlyAccepted)
    {
      try
      {
        verifyHostName(chain, authType);
      }
      catch (CertificateException ce)
      {
        lastRefusedChain = chain;
        lastRefusedAuthType = authType;
        lastRefusedCause = Cause.HOST_NAME_MISMATCH;
        throw ce;
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public X509Certificate[] getAcceptedIssuers()
  {
    if (sunJSSEX509TrustManager != null)
    {
      return sunJSSEX509TrustManager.getAcceptedIssuers();
    }
    else
    {
      return new X509Certificate[0];
    }
  }
  /**
   * This method is called when the user accepted a certificate.
   * @param chain the certificate chain accepted by the user.
   * @param authType the authentication type.
   * @param host the host we tried to connect and that presented the
   * certificate.
   */
  public void acceptCertificate(X509Certificate[] chain, String authType,
      String host)
  {
    acceptedChains.add(chain);
    acceptedAuthTypes.add(authType);
    acceptedHosts.add(host);
  }
  /**
   * Sets the host name we are trying to contact in a secure mode.  This
   * method is used if we want to verify the correspondance between the
   * hostname and the subject DN of the certificate that is being presented.
   * If this method is never called (or called passing null) no verification
   * will be made on the host name.
   * @param host the host name we are trying to contact in a secure mode.
   */
  public void setHost(String host)
  {
    this.host = host;
  }
  /**
   * This is a method used to set to null the different members that provide
   * information about the last refused certificate.  It is recommended to
   * call this method before trying to establish a connection using this
   * trust manager.
   */
  public void resetLastRefusedItems()
  {
    lastRefusedAuthType = null;
    lastRefusedChain = null;
    lastRefusedCause = null;
  }
  /**
   * Creates a copy of this ApplicationTrustManager.
   * @return a copy of this ApplicationTrustManager.
   */
  public ApplicationTrustManager createCopy()
  {
    ApplicationTrustManager copy = new ApplicationTrustManager();
    copy.lastRefusedAuthType = lastRefusedAuthType;
    copy.lastRefusedChain = lastRefusedChain;
    copy.lastRefusedCause = lastRefusedCause;
    copy.acceptedChains.addAll(acceptedChains);
    copy.acceptedAuthTypes.addAll(acceptedAuthTypes);
    copy.acceptedHosts.addAll(acceptedHosts);
    copy.host = host;
    return copy;
  }
  /**
   * Verifies whether the provided chain and authType have been already accepted
   * by the user or not.  If they have not a CertificateException is thrown.
   * @param chain the certificate chain to analyze.
   * @param authType the authentication type.
   * @throws CertificateException if the provided certificate chain and the
   * authentication type have not been accepted explicitly by the user.
   */
  private void verifyAcceptedCertificates(X509Certificate[] chain,
      String authType) throws CertificateException
  {
    boolean found = false;
    for (int i=0; i<acceptedChains.size() && !found; i++)
    {
      if (authType.equals(acceptedAuthTypes.get(i)))
      {
        X509Certificate[] current = acceptedChains.get(i);
        found = current.length == chain.length;
        for (int j=0; j<chain.length && found; j++)
        {
          found = chain[j].equals(current[j]);
        }
      }
    }
    if (!found)
    {
      throw new CertificateException(
          "Certificate not in list of accepted certificates");
    }
  }
  /**
   * Verifies that the provided certificate chains subject DN corresponds to the
   * host name specified with the setHost method.
   * @param chain the certificate chain to analyze.
   * @throws CertificateException if the subject DN of the certificate does
   * not match with the host name specified with the method setHost.
   */
  private void verifyHostName(X509Certificate[] chain, String authType)
  throws CertificateException
  {
    if (host != null)
    {
      boolean matches = false;
      try
      {
        LdapName dn =
          new LdapName(chain[0].getSubjectX500Principal().getName());
        Rdn rdn = dn.getRdn(0);
        String value = rdn.getValue().toString();
        matches = host.equalsIgnoreCase(value);
      }
      catch (Throwable t)
      {
        LOG.log(Level.WARNING, "Error parsing subject dn: "+
            chain[0].getSubjectX500Principal(), t);
      }
      if (!matches)
      {
        throw new CertificateException("Hostname mismatch between host name "+
            host+" and subject DN: "+chain[0].getSubjectX500Principal());
      }
    }
  }
  /**
   * Returns the authentication type for the last refused certificate.
   * @return the authentication type for the last refused certificate.
   */
  public String getLastRefusedAuthType()
  {
    return lastRefusedAuthType;
  }
  /**
   * Returns the last cause for refusal of a certificate.
   * @return the last cause for refusal of a certificate.
   */
  public Cause getLastRefusedCause()
  {
    return lastRefusedCause;
  }
  /**
   * Returns the certificate chain for the last refused certificate.
   * @return the certificate chain for the last refused certificate.
   */
  public X509Certificate[] getLastRefusedChain()
  {
    return lastRefusedChain;
  }
}
opends/src/ads/org/opends/admin/ads/util/BlindHostnameVerifier.java
New file
@@ -0,0 +1,45 @@
/*
 * 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.admin.ads.util;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
/**
 * A HostnameVerifier which verifies nothing.
 */
class BlindHostnameVerifier implements HostnameVerifier
{
  /**
   * {@inheritDoc}
   */
  public boolean verify(String hostname, SSLSession session)
  {
    return true;
  }
}
opends/src/ads/org/opends/admin/ads/util/BlindTrustManager.java
New file
@@ -0,0 +1,63 @@
/*
 * 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.admin.ads.util;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
 * An X509TrustManager which trusts everything.
 */
class BlindTrustManager implements X509TrustManager {
  /**
   * {@inheritDoc}
   */
  public void checkClientTrusted(X509Certificate[] chain, String authType)
  throws CertificateException
  {
  }
  /**
   * {@inheritDoc}
   */
  public void checkServerTrusted(X509Certificate[] chain, String authType)
  throws CertificateException
  {
  }
  /**
   * {@inheritDoc}
   */
  public X509Certificate[] getAcceptedIssuers()
  {
    return new X509Certificate[0];
  }
}
opends/src/ads/org/opends/admin/ads/util/ConnectionUtils.java
New file
@@ -0,0 +1,548 @@
/*
 * 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.admin.ads.util;
import java.io.IOException;
import java.net.ConnectException;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.TrustManager;
/**
 * Class providing some utilities to create LDAP connections using JNDI and
 * to manage entries retrieved using JNDI.
 *
 */
public class ConnectionUtils
{
  private static final int DEFAULT_LDAP_CONNECT_TIMEOUT = 10000;
  /**
   * Private constructor: this class cannot be instantiated.
   */
  private ConnectionUtils()
  {
  }
  /**
   * Creates a clear LDAP connection and returns the corresponding LdapContext.
   * This methods uses the specified parameters to create a JNDI environment
   * hashtable and creates an InitialLdapContext instance.
   *
   * @param ldapURL
   *          the target LDAP URL
   * @param dn
   *          passed as Context.SECURITY_PRINCIPAL if not null
   * @param pwd
   *          passed as Context.SECURITY_CREDENTIALS if not null
   * @param timeout
   *          passed as com.sun.jndi.ldap.connect.timeout if > 0
   * @param env
   *          null or additional environment properties
   *
   * @throws NamingException
   *           the exception thrown when instantiating InitialLdapContext
   *
   * @return the created InitialLdapContext.
   * @see javax.naming.Context
   * @see javax.naming.ldap.InitialLdapContext
   */
  public static InitialLdapContext createLdapContext(String ldapURL, String dn,
      String pwd, int timeout, Hashtable<String, String> env)
      throws NamingException
  {
    if (env != null)
    { // We clone 'env' so that we can modify it freely
      env = new Hashtable<String, String>(env);
    } else
    {
      env = new Hashtable<String, String>();
    }
    env.put(Context.INITIAL_CONTEXT_FACTORY,
        "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, ldapURL);
    if (timeout >= 1)
    {
      env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(timeout));
    }
    if (dn != null)
    {
      env.put(Context.SECURITY_PRINCIPAL, dn);
    }
    if (pwd != null)
    {
      env.put(Context.SECURITY_CREDENTIALS, pwd);
    }
    /* Contains the DirContext and the Exception if any */
    final Object[] pair = new Object[]
      { null, null };
    final Hashtable fEnv = env;
    Thread t = new Thread(new Runnable()
    {
      public void run()
      {
        try
        {
          pair[0] = new InitialLdapContext(fEnv, null);
        } catch (NamingException ne)
        {
          pair[1] = ne;
        } catch (Throwable t)
        {
          pair[1] = t;
        }
      }
    });
    return getInitialLdapContext(t, pair, timeout);
  }
  /**
   * Creates an LDAPS connection and returns the corresponding LdapContext.
   * This method uses the TrusteSocketFactory class so that the specified
   * trust manager gets called during the SSL handshake. If trust manager is
   * null, certificates are not verified during SSL handshake.
   *
   * @param ldapsURL      the target *LDAPS* URL.
   * @param dn            passed as Context.SECURITY_PRINCIPAL if not null.
   * @param pwd           passed as Context.SECURITY_CREDENTIALS if not null.
   * @param timeout       passed as com.sun.jndi.ldap.connect.timeout if > 0.
   * @param env           null or additional environment properties.
   * @param trustManager  null or the trust manager to be invoked during SSL
   * negociation.
   *
   * @return the established connection with the given parameters.
   *
   * @throws NamingException the exception thrown when instantiating
   * InitialLdapContext.
   *
   * @see javax.naming.Context
   * @see javax.naming.ldap.InitialLdapContext
   * @see TrustedSocketFactory
   */
  public static InitialLdapContext createLdapsContext(String ldapsURL,
      String dn, String pwd, int timeout, Hashtable<String, String> env,
      TrustManager trustManager) throws NamingException {
    if (env != null)
    { // We clone 'env' so that we can modify it freely
      env = new Hashtable<String, String>(env);
    } else
    {
      env = new Hashtable<String, String>();
    }
    env.put(Context.INITIAL_CONTEXT_FACTORY,
        "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, ldapsURL);
    env.put("java.naming.ldap.factory.socket",
        org.opends.admin.ads.util.TrustedSocketFactory.class.getName());
    if (dn != null)
    {
      env.put(Context.SECURITY_PRINCIPAL, dn);
    }
    if (pwd != null)
    {
      env.put(Context.SECURITY_CREDENTIALS, pwd);
    }
    if (trustManager == null)
    {
      trustManager = new BlindTrustManager();
    }
    /* Contains the DirContext and the Exception if any */
    final Object[] pair = new Object[] {null, null};
    final Hashtable fEnv = env;
    final TrustManager fTrustManager = trustManager;
    Thread t = new Thread(new Runnable() {
      public void run() {
        try {
          TrustedSocketFactory.setCurrentThreadTrustManager(fTrustManager);
          pair[0] = new InitialLdapContext(fEnv, null);
        } catch (NamingException ne) {
          pair[1] = ne;
        } catch (RuntimeException re) {
          pair[1] = re;
        }
      }
    });
    return getInitialLdapContext(t, pair, timeout);
  }
  /**
   * Creates an LDAP+StartTLS connection and returns the corresponding
   * LdapContext.
   * This method first creates an LdapContext with anonymous bind. Then it
   * requests a StartTlsRequest extended operation. The StartTlsResponse is
   * setup with the specified hostname verifier. Negotiation is done using a
   * TrustSocketFactory so that the specified TrustManager gets called during
   * the SSL handshake.
   * If trust manager is null, certificates are not checked during SSL
   * handshake.
   *
   * @param ldapsURL      the target *LDAPS* URL.
   * @param dn            passed as Context.SECURITY_PRINCIPAL if not null.
   * @param pwd           passed as Context.SECURITY_CREDENTIALS if not null.
   * @param timeout       passed as com.sun.jndi.ldap.connect.timeout if > 0.
   * @param env           null or additional environment properties.
   * @param trustManager  null or the trust manager to be invoked during SSL.
   * negociation.
   * @param verifier      null or the hostname verifier to be setup in the
   * StartTlsResponse.
   *
   * @return the established connection with the given parameters.
   *
   * @throws NamingException the exception thrown when instantiating
   * InitialLdapContext.
   *
   * @see javax.naming.Context
   * @see javax.naming.ldap.InitialLdapContext
   * @see javax.naming.ldap.StartTlsRequest
   * @see javax.naming.ldap.StartTlsResponse
   * @see TrustedSocketFactory
   */
  public static InitialLdapContext createStartTLSContext(String ldapsURL,
      String dn, String pwd, int timeout, Hashtable<String, String> env,
      TrustManager trustManager, HostnameVerifier verifier)
  throws NamingException
  {
    if (trustManager == null)
    {
      trustManager = new BlindTrustManager();
    }
    if (verifier == null) {
      verifier = new BlindHostnameVerifier();
    }
    if (env != null)
    { // We clone 'env' to modify it freely
      env = new Hashtable<String, String>(env);
    }
    else
    {
      env = new Hashtable<String, String>();
    }
    env.put(Context.INITIAL_CONTEXT_FACTORY,
        "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, ldapsURL);
    env.put(Context.SECURITY_AUTHENTICATION , "none");
    /* Contains the DirContext and the Exception if any */
    final Object[] pair = new Object[] {null, null};
    final Hashtable fEnv = env;
    final String fDn = dn;
    final String fPwd = pwd;
    final TrustManager fTrustManager = trustManager;
    final HostnameVerifier fVerifier = verifier;
    Thread t = new Thread(new Runnable() {
      public void run() {
        try {
          StartTlsResponse tls;
          InitialLdapContext result = new InitialLdapContext(fEnv, null);
          tls = (StartTlsResponse) result.extendedOperation(
              new StartTlsRequest());
          tls.setHostnameVerifier(fVerifier);
          try
          {
            tls.negotiate(new TrustedSocketFactory(fTrustManager));
          }
          catch(IOException x) {
            NamingException xx;
            xx = new CommunicationException(
                "Failed to negotiate Start TLS operation");
            xx.initCause(x);
            result.close();
            throw xx;
          }
          if (fDn != null)
          {
            result.addToEnvironment(Context.SECURITY_AUTHENTICATION , "simple");
            result.addToEnvironment(Context.SECURITY_PRINCIPAL, fDn);
            if (fPwd != null)
            {
              result.addToEnvironment(Context.SECURITY_CREDENTIALS, fPwd);
            }
            result.reconnect(null);
          }
          pair[0] = result;
        } catch (NamingException ne)
        {
          pair[1] = ne;
        } catch (RuntimeException re)
        {
          pair[1] = re;
        }
      }
    });
    return getInitialLdapContext(t, pair, timeout);
  }
  /**
   * Method used to know if we can connect as administrator in a server with a
   * given password and dn.
   * @param ldapUrl the ldap URL of the server.
   * @param dn the dn to be used.
   * @param pwd the password to be used.
   * @return <CODE>true</CODE> if we can connect and read the configuration and
   * <CODE>false</CODE> otherwise.
   */
  public static boolean canConnectAsAdministrativeUser(String ldapUrl,
      String dn, String pwd)
  {
    boolean canConnectAsAdministrativeUser = false;
    try
    {
      InitialLdapContext ctx =
        createLdapContext(ldapUrl, dn, pwd, getDefaultLDAPTimeout(), null);
      /*
       * Search for the config to check that it is the directory manager.
       */
      SearchControls searchControls = new SearchControls();
      searchControls.setCountLimit(1);
      searchControls.setSearchScope(
      SearchControls. OBJECT_SCOPE);
      searchControls.setReturningAttributes(
      new String[] {"dn"});
      ctx.search("cn=config", "objectclass=*", searchControls);
      canConnectAsAdministrativeUser = true;
    } catch (NamingException ne)
    {
      // Nothing to do.
    } catch (Throwable t)
    {
      throw new IllegalStateException("Unexpected throwable.", t);
    }
    return canConnectAsAdministrativeUser;
  }/**
   * This is just a commodity method used to try to get an InitialLdapContext.
   * @param t the Thread to be used to create the InitialLdapContext.
   * @param pair an Object[] array that contains the InitialLdapContext and the
   * Throwable if any occurred.
   * @param timeout the timeout.  If we do not get to create the connection
   * before the timeout a CommunicationException will be thrown.
   * @return the created InitialLdapContext
   * @throws NamingException if something goes wrong during the creation.
   */
  private static InitialLdapContext getInitialLdapContext(Thread t,
      Object[] pair, int timeout) throws NamingException
  {
    try
    {
      if (timeout > 0)
      {
        t.start();
        t.join(timeout);
      } else
      {
        t.run();
      }
    } catch (InterruptedException x)
    {
      // This might happen for problems in sockets
      // so it does not necessarily imply a bug
    }
    boolean throwException = false;
    if ((timeout > 0) && t.isAlive())
    {
      t.interrupt();
      try
      {
        t.join(2000);
      } catch (InterruptedException x)
      {
        // This might happen for problems in sockets
        // so it does not necessarily imply a bug
      }
      throwException = true;
    }
    if ((pair[0] == null) && (pair[1] == null))
    {
      throwException = true;
    }
    if (throwException)
    {
      NamingException xx;
      ConnectException x = new ConnectException("Connection timed out");
      xx = new CommunicationException("Connection timed out");
      xx.initCause(x);
      throw xx;
    }
    if (pair[1] != null)
    {
      if (pair[1] instanceof NamingException)
      {
        throw (NamingException) pair[1];
      } else if (pair[1] instanceof RuntimeException)
      {
        throw (RuntimeException) pair[1];
      } else if (pair[1] instanceof Throwable)
      {
        throw new IllegalStateException("Unexpected throwable occurred",
            (Throwable) pair[1]);
      }
    }
    return (InitialLdapContext) pair[0];
  }
  /**
   * 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 String that can be used to represent a given host name in a
   * LDAP URL.
   * This method must be used when we have IPv6 addresses (the address in the
   * LDAP URL must be enclosed with brackets).
   * @param host the host name.
   * @return the String that can be used to represent a given host name in a
   * LDAP URL.
   */
  public static String getHostNameForLdapUrl(String host)
  {
    if ((host != null) && host.indexOf(":") != -1)
    {
      // Assume an IPv6 address has been specified and adds the brackets
      // for the URL.
      host = host.trim();
      if (!host.startsWith("["))
      {
        host = "["+host;
      }
      if (!host.endsWith("]"))
      {
        host = host + "]";
      }
    }
    return host;
  }
  /**
   * Returns the String representation of the first value of an attribute in a
   * LDAP entry.
   * @param entry the entry.
   * @param attrName the attribute name.
   * @return the String representation of the first value of an attribute in a
   * LDAP entry.
   * @throws NamingException if there is an error processing the entry.
   */
  static public String getFirstValue(SearchResult entry, String attrName)
  throws NamingException
  {
    String v = null;
    Attributes attrs = entry.getAttributes();
    if (attrs != null)
    {
      Attribute attr = attrs.get(attrName);
      if ((attr != null) && (attr.size() > 0))
      {
        Object o = attr.get();
        if (o instanceof String)
        {
          v = (String)o;
        }
        else
        {
          v = String.valueOf(o);
        }
      }
    }
    return v;
  }
  /**
   * Returns a Set with the String representation of the values of an attribute
   * in a LDAP entry.  The returned Set will never be null.
   * @param entry the entry.
   * @param attrName the attribute name.
   * @return a Set with the String representation of the values of an attribute
   * in a LDAP entry.
   * @throws NamingException if there is an error processing the entry.
   */
  static public Set<String> getValues(SearchResult entry, String attrName)
  throws NamingException
  {
    Set<String> values = new HashSet<String>();
    Attributes attrs = entry.getAttributes();
    if (attrs != null)
    {
      Attribute attr = attrs.get(attrName);
      if (attr != null)
      {
        for (int i=0; i<attr.size(); i++)
        {
          values.add((String)attr.get(i));
        }
      }
    }
    return values;
  }
}
opends/src/ads/org/opends/admin/ads/util/ServerLoader.java
New file
@@ -0,0 +1,349 @@
/*
 * 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.admin.ads.util;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.NamingException;
import javax.naming.NoPermissionException;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapName;
import org.opends.admin.ads.ADSContext;
import org.opends.admin.ads.ServerDescriptor;
import org.opends.admin.ads.TopologyCacheException;
import org.opends.admin.ads.ADSContext.ServerProperty;
/**
 * Class used to load the configuration of a server.  Basically the code
 * uses some provided properties and authentication information to connect
 * to the server and then generate a ServerDescriptor object based on the
 * read configuration.
 */
public class ServerLoader extends Thread
{
  private Map<ServerProperty,Object> serverProperties;
  private boolean isOver;
  private boolean isInterrupted;
  private String lastLdapUrl;
  private TopologyCacheException lastException;
  private ServerDescriptor serverDescriptor;
  private ApplicationTrustManager trustManager;
  private String dn;
  private String pwd;
  private static final Logger LOG =
    Logger.getLogger(ServerLoader.class.getName());
  /**
   * Constructor.
   * @param serverProperties the server properties of the server we want to
   * load.
   * @param dn the DN that we must use to bind to the server.
   * @param pwd the password that we must use to bind to the server.
   * @param trustManager the ApplicationTrustManager to be used when we try
   * to connect to the server.
   */
  public ServerLoader(Map<ServerProperty,Object> serverProperties,
      String dn, String pwd, ApplicationTrustManager trustManager)
  {
    this.serverProperties = serverProperties;
    this.dn = dn;
    this.pwd = pwd;
    this.trustManager = trustManager;
  }
  /**
   * Returns the ServerDescriptor that could be retrieved.
   * @return the ServerDescriptor that could be retrieved.
   */
  public ServerDescriptor getServerDescriptor()
  {
    if (serverDescriptor == null)
    {
      serverDescriptor = ServerDescriptor.createStandalone(serverProperties);
    }
    serverDescriptor.setLastException(lastException);
    return serverDescriptor;
  }
  /**
   * Returns the last exception that occurred while trying to generate
   * the ServerDescriptor object.
   * @return the last exception that occurred while trying to generate
   * the ServerDescriptor object.
   */
  public TopologyCacheException getLastException()
  {
    return lastException;
  }
  /**
   * {@inheritDoc}
   */
  public void interrupt()
  {
    if (!isOver)
    {
      isInterrupted = true;
      String ldapUrl = getLdapUrl(serverProperties);
      if (ldapUrl == null)
      {
        ldapUrl = getLdapsUrl(serverProperties);
      }
      lastException = new TopologyCacheException(
          TopologyCacheException.Type.TIMEOUT, null, trustManager, ldapUrl);
      LOG.log(Level.WARNING, "Timeout reading server: "+ldapUrl);
    }
    super.interrupt();
  }
  /**
   * The method where we try to generate the ServerDescriptor object.
   */
  public void run()
  {
    lastException = null;
    InitialLdapContext ctx = null;
    try
    {
      ctx = createContext();
      serverDescriptor = ServerDescriptor.createStandalone(ctx);
      serverDescriptor.setAdsProperties(serverProperties);
    }
    catch (NoPermissionException npe)
    {
      LOG.log(Level.WARNING,
          "Permissions error reading server: "+getLastLdapUrl(), npe);
      if (!isAdministratorDn())
      {
        lastException = new TopologyCacheException(
                TopologyCacheException.Type.NOT_GLOBAL_ADMINISTRATOR, npe,
                trustManager, getLastLdapUrl());
      }
    }
    catch (NamingException ne)
    {
      LOG.log(Level.WARNING,
          "NamingException error reading server: "+getLastLdapUrl(), ne);
      if (ctx == null)
      {
        lastException =
            new TopologyCacheException(
                TopologyCacheException.Type.GENERIC_CREATING_CONNECTION, ne,
                trustManager, getLastLdapUrl());
      }
      else
      {
        lastException =
          new TopologyCacheException(
              TopologyCacheException.Type.GENERIC_READING_SERVER, ne,
              trustManager, getLastLdapUrl());
      }
    }
    catch (Throwable t)
    {
      if (!isInterrupted)
      {
        LOG.log(Level.WARNING,
            "Generic error reading server: "+getLastLdapUrl(), t);
        lastException =
            new TopologyCacheException(TopologyCacheException.Type.BUG, t);
      }
    }
    finally
    {
      isOver = true;
      try
      {
        if (ctx != null)
        {
          ctx.close();
        }
      }
      catch (Throwable t)
      {
      }
    }
  }
  /**
   * Create an InitialLdapContext based in the provide server properties and
   * authentication data provided in the constructor.
   * @return an InitialLdapContext based in the provide server properties and
   * authentication data provided in the constructor.
   * @throws NamingException if an error occurred while creating the
   * InitialLdapContext.
   */
  public InitialLdapContext createContext() throws NamingException
  {
    InitialLdapContext ctx = null;
    trustManager.resetLastRefusedItems();
    String host = (String)serverProperties.get(ServerProperty.HOST_NAME);
    trustManager.setHost(host);
    lastLdapUrl = getLdapsUrl(serverProperties);
    if (lastLdapUrl == null)
    {
      lastLdapUrl = getStartTlsLdapUrl(serverProperties);
      if (lastLdapUrl == null)
      {
        lastLdapUrl = getLdapUrl(serverProperties);
        ctx = ConnectionUtils.createLdapContext(lastLdapUrl, dn, pwd,
            ConnectionUtils.getDefaultLDAPTimeout(), null);
      }
      else
      {
        ctx = ConnectionUtils.createStartTLSContext(lastLdapUrl, dn, pwd,
            ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager,
            null);
      }
    }
    else
    {
      ctx = ConnectionUtils.createLdapsContext(lastLdapUrl, dn, pwd,
          ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager);
    }
    return ctx;
  }
  /**
   * Returns the last LDAP URL to which we tried to connect.
   * @return the last LDAP URL to which we tried to connect.
   */
  private String getLastLdapUrl()
  {
    return lastLdapUrl;
  }
  /**
   * Returns the non-secure LDAP URL for the given server properties.  It
   * returns NULL if according to the server properties no non-secure LDAP URL
   * can be generated (LDAP disabled or port not defined).
   * @param serverProperties the server properties to be used to generate
   * the non-secure LDAP URL.
   * @return the non-secure LDAP URL for the given server properties.
   */
  private String getLdapUrl(Map<ServerProperty,Object> serverProperties)
  {
    String ldapUrl = null;
    Object v = serverProperties.get(ServerProperty.LDAP_ENABLED);
    boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString());
    if (ldapEnabled)
    {
      ldapUrl = "ldap://"+getHostNameForLdapUrl(serverProperties)+":"+
      serverProperties.get(ServerProperty.LDAP_PORT);
    }
    return ldapUrl;
  }
  /**
   * Returns the StartTLS LDAP URL for the given server properties.  It
   * returns NULL if according to the server properties no StartTLS LDAP URL
   * can be generated (StartTLS disabled or port not defined).
   * @param serverProperties the server properties to be used to generate
   * the StartTLS LDAP URL.
   * @return the StartTLS LDAP URL for the given server properties.
   */
  private String getStartTlsLdapUrl(Map<ServerProperty,Object> serverProperties)
  {
    String ldapUrl = null;
    Object v = serverProperties.get(ServerProperty.LDAP_ENABLED);
    boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString());
    v = serverProperties.get(ServerProperty.STARTTLS_ENABLED);
    boolean startTLSEnabled = (v != null) &&
    "true".equalsIgnoreCase(v.toString());
    if (ldapEnabled && startTLSEnabled)
    {
      ldapUrl = "ldaps://"+getHostNameForLdapUrl(serverProperties)+":"+
      serverProperties.get(ServerProperty.LDAP_PORT);
    }
    return ldapUrl;
  }
  /**
   * Returns the LDAPs URL for the given server properties.  It
   * returns NULL if according to the server properties no LDAPS URL
   * can be generated (LDAPS disabled or port not defined).
   * @param serverProperties the server properties to be used to generate
   * the LDAPS URL.
   * @return the LDAPS URL for the given server properties.
   */
  private String getLdapsUrl(Map<ServerProperty,Object> serverProperties)
  {
    String ldapsUrl = null;
    Object v = serverProperties.get(ServerProperty.LDAPS_ENABLED);
    boolean ldapsEnabled = (v != null) && "true".equalsIgnoreCase(v.toString());
    if (ldapsEnabled)
    {
      ldapsUrl = "ldaps://"+getHostNameForLdapUrl(serverProperties)+":"+
      serverProperties.get(ServerProperty.LDAPS_PORT);
    }
    return ldapsUrl;
  }
  /**
   * Returns the host name to be used to generate an LDAP URL based on the
   * contents of the provided server properties.
   * @param serverProperties the server properties.
   * @return the host name to be used to generate an LDAP URL based on the
   * contents of the provided server properties.
   */
  private String getHostNameForLdapUrl(
      Map<ServerProperty,Object> serverProperties)
  {
    String host = (String)serverProperties.get(ServerProperty.HOST_NAME);
    return ConnectionUtils.getHostNameForLdapUrl(host);
  }
  /**
   * Returns whether the DN provided in the constructor is a Global
   * Administrator DN or not.
   * @return <CODE>true</CODE> if the DN provided in the constructor is a Global
   * Administrator DN and <CODE>false</CODE> otherwise.
   */
  private boolean isAdministratorDn()
  {
    boolean isAdministratorDn = false;
    try
    {
      LdapName theDn = new LdapName(dn);
      LdapName containerDn =
        new LdapName(ADSContext.getAdministratorContainerDN());
      isAdministratorDn = theDn.endsWith(containerDn);
    }
    catch (Throwable t)
    {
      LOG.log(Level.WARNING, "Error parsing authentication DNs.", t);
    }
    return isAdministratorDn;
  }
}
opends/src/ads/org/opends/admin/ads/util/TrustedSocketFactory.java
New file
@@ -0,0 +1,237 @@
/*
 * 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.admin.ads.util;
import java.io.IOException;
import java.net.Socket;
import java.net.InetAddress;
import java.util.Map;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.security.GeneralSecurityException;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLKeyException;
import javax.net.ssl.TrustManager;
/**
 * An implementation of SSLSocketFactory.
 */
public class TrustedSocketFactory extends SSLSocketFactory
{
  static private final Logger LOG =
    Logger.getLogger(TrustedSocketFactory.class.getName());
  private static Map<Thread, TrustManager> hmTrustManager =
    new HashMap<Thread, TrustManager>();
  private static Map<TrustManager, SocketFactory> hmDefaultFactory =
    new HashMap<TrustManager, SocketFactory>();
  private SSLSocketFactory innerFactory;
  private TrustManager trustManager;
  /**
   * Constructor of the TrustedSocketFactory.
   * @param trustManager the trust manager to use.
   */
  public TrustedSocketFactory(TrustManager trustManager)
  {
    this.trustManager = trustManager;
  }
  /**
   * Sets the provided trust manager for the operations in the current thread.
   * @param trustManager the trust manager to use.
   */
  public static synchronized void setCurrentThreadTrustManager(
      TrustManager trustManager)
  {
    setThreadTrustManager(trustManager, Thread.currentThread());
  }
  /**
   * Sets the provided trust manager for the operations in the provided thread.
   * @param trustManager the trust manager to use.
   * @param thread the thread where we want to use the provided trust manager.
   */
  public static synchronized void setThreadTrustManager(
      TrustManager trustManager, Thread thread)
  {
    TrustManager currentTrustManager = hmTrustManager.get(thread);
    if (currentTrustManager != null) {
      hmDefaultFactory.remove(currentTrustManager);
      hmTrustManager.remove(thread);
    }
    if (trustManager != null) {
      hmTrustManager.put(thread, trustManager);
    }
  }
  //
  // SocketFactory implementation
  //
  /**
   * {@inheritDoc}
   */
  public static synchronized SocketFactory getDefault()
  {
    Thread currentThread = Thread.currentThread();
    TrustManager trustManager = hmTrustManager.get(currentThread);
    SocketFactory result;
    if (trustManager == null)
    {
      LOG.log(Level.SEVERE, "Can't find a trust manager associated to thread " +
          currentThread);
      result = new TrustedSocketFactory(null);
    }
    else
    {
      result = hmDefaultFactory.get(trustManager);
      if (result == null)
      {
        result = new TrustedSocketFactory(trustManager);
        hmDefaultFactory.put(trustManager, result);
      }
    }
    return result;
  }
  /**
   * {@inheritDoc}
   */
  public Socket createSocket(InetAddress address, int port) throws IOException {
    return getInnerFactory().createSocket(address, port);
  }
  /**
   * {@inheritDoc}
   */
  public Socket createSocket(InetAddress address, int port,
      InetAddress clientAddress, int clientPort) throws IOException
  {
    return getInnerFactory().createSocket(address, port, clientAddress,
        clientPort);
  }
  /**
   * {@inheritDoc}
   */
  public Socket createSocket(String host, int port) throws IOException
  {
    return getInnerFactory().createSocket(host, port);
  }
  /**
   * {@inheritDoc}
   */
  public Socket createSocket(String host, int port, InetAddress clientHost,
      int clientPort) throws IOException
  {
    return getInnerFactory().createSocket(host, port, clientHost, clientPort);
  }
  /**
   * {@inheritDoc}
   */
  public Socket createSocket(Socket s, String host, int port, boolean autoClose)
  throws IOException
  {
    return getInnerFactory().createSocket(s, host, port, autoClose);
  }
  /**
   * {@inheritDoc}
   */
  public String[] getDefaultCipherSuites()
  {
    try
    {
      return getInnerFactory().getDefaultCipherSuites();
    }
    catch(IOException x)
    {
      return new String[0];
    }
  }
  /**
   * {@inheritDoc}
   */
  public String[] getSupportedCipherSuites()
  {
    try
    {
      return getInnerFactory().getSupportedCipherSuites();
    }
    catch(IOException x)
    {
      return new String[0];
    }
  }
  //
  // Private
  //
  private SSLSocketFactory getInnerFactory() throws IOException {
    if (innerFactory == null)
    {
      String algorithm = "TLSv1";
      SSLKeyException xx;
      try {
        SSLContext sslCtx = SSLContext.getInstance(algorithm);
        if (trustManager == null)
        {
          LOG.log(Level.SEVERE, "Warning : no trust for this factory");
          sslCtx.init(null, null, null); // No certif -> no SSL connection
        }
        else {
          sslCtx.init(null, new TrustManager[] { trustManager }, null
          );
        }
        innerFactory = sslCtx.getSocketFactory();
      }
      catch(GeneralSecurityException x) {
        xx = new SSLKeyException("Failed to create SSLContext for " +
            algorithm);
        xx.initCause(x);
        throw xx;
      }
    }
    return innerFactory;
  }
}
opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
@@ -44,8 +44,6 @@
import org.opends.admin.ads.ADSContext;
import org.opends.admin.ads.ADSContextException;
import org.opends.admin.ads.ReplicaDescriptor;
import org.opends.admin.ads.ServerDescriptor;
import org.opends.admin.ads.SuffixDescriptor;
import org.opends.quicksetup.ui.*;
import org.opends.quicksetup.util.Utils;
@@ -1327,6 +1325,7 @@
      notifyListeners(getFormattedWithPoints(getMsg("creating-ads")));
      Map<ADSContext.ServerProperty, Object> serverProperties =
        getNewServerProperties();
      /*
      try
      {
        ADSContext.createOfflineAdminData(serverProperties,
@@ -1338,6 +1337,7 @@
            ApplicationException.Type.CONFIGURATION_ERROR,
            getMsg("local-ads-exception"), ace);
      }
      */
      notifyListeners(getFormattedDone());
    }
  }
@@ -1382,6 +1382,7 @@
    Map<ADSContext.ServerProperty, Object> serverProperties =
      new HashMap<ADSContext.ServerProperty, Object>();
    // TODO: this might not work
    /*
    try
    {
      serverProperties.put(ADSContext.ServerProperty.HOSTNAME,
@@ -1409,8 +1410,9 @@
    String serverID = serverProperties.get(ADSContext.ServerProperty.HOSTNAME)+
    ":"+getUserData().getServerPort();
    */
    /* TODO: do we want to ask this specifically to the user? */
    serverProperties.put(ADSContext.ServerProperty.ID, serverID);
    //serverProperties.put(ADSContext.ServerProperty.ID, serverID);
    serverProperties.put(ADSContext.ServerProperty.HOST_OS,
        Utils.getOSString());
@@ -2269,6 +2271,7 @@
  private Set<SuffixDescriptor> staticSuffixes()
  {
    /*
    ServerDescriptor server1 = new ServerDescriptor();
    Map<ADSContext.ServerProperty, Object> serverProp1 =
      new HashMap<ADSContext.ServerProperty, Object>();
@@ -2356,13 +2359,14 @@
    replicas4.add(replica8);
    suffix4.setReplicas(replicas4);
     */
    Set<SuffixDescriptor> suffixes = new HashSet<SuffixDescriptor>();
    /*
    suffixes.add(suffix1);
    suffixes.add(suffix2);
    suffixes.add(suffix3);
    suffixes.add(suffix4);
*/
    //suffixes.clear();
    return suffixes;
opends/src/quicksetup/org/opends/quicksetup/installer/ui/SuffixesToReplicatePanel.java
@@ -51,7 +51,6 @@
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import org.opends.admin.ads.ADSContext;
import org.opends.admin.ads.ReplicaDescriptor;
import org.opends.admin.ads.ServerDescriptor;
import org.opends.admin.ads.SuffixDescriptor;
@@ -442,53 +441,7 @@
  //TODO: only handles server that have ADSProperties.
  private String getServerDisplay(ServerDescriptor server)
  {
    String display;
    String hostName = (String)server.getAdsProperties().get(
        ADSContext.ServerProperty.HOSTNAME);
    boolean secure = false;
    int port = -1;
    if (hostName == null)
    {
      hostName = getMsg("suffix-list-unknown-label");
    }
    if (!"true".equalsIgnoreCase((String)server.getAdsProperties().get(
        ADSContext.ServerProperty.LDAP_ENABLED)))
    {
      if ("true".equalsIgnoreCase((String)server.getAdsProperties().get(
          ADSContext.ServerProperty.LDAPS_ENABLED)))
      {
        secure = true;
      }
    }
    try
    {
      if (secure)
      {
        port = Integer.parseInt((String)server.getAdsProperties().get(
            ADSContext.ServerProperty.SECURE_PORT));
      }
      else
      {
        port = Integer.parseInt((String)server.getAdsProperties().get(
            ADSContext.ServerProperty.PORT));
      }
    }
    catch (Throwable t)
    {
    }
    if (port == -1)
    {
      display = hostName+":"+getMsg("suffix-list-unknown-label");
    }
    else
    {
      display = hostName+":"+port;
    }
    return display;
    return server.getHostPort(true);
  }
  private TreeSet<SuffixDescriptor> orderSuffixes(