/** {@inheritDoc} */
  public ConfigChangeResult applyConfigurationAdd(RootDNUserCfg configuration) {
    configuration.addChangeListener(this);

    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();

    HashSet<DN> altBindDNs = new HashSet<DN>();
    for (DN altBindDN : configuration.getAlternateBindDN()) {
      try {
        DirectoryServer.registerAlternateRootDN(configuration.dn(), altBindDN);
        altBindDNs.add(altBindDN);
      } catch (DirectoryException de) {
        // This shouldn't happen, since the set of DNs should have already been
        // validated.
        resultCode = DirectoryServer.getServerErrorResultCode();
        messages.add(de.getMessageObject());

        for (DN dn : altBindDNs) {
          DirectoryServer.deregisterAlternateRootBindDN(dn);
        }
        break;
      }
    }

    if (resultCode == ResultCode.SUCCESS) {
      DirectoryServer.registerRootDN(configuration.dn());
      alternateBindDNs.put(configuration.dn(), altBindDNs);
    }

    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /** {@inheritDoc} */
  public ConfigChangeResult applyConfigurationAdd(LogRotationPolicyCfg config) {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();

    try {
      RotationPolicy rotationPolicy = getRotationPolicy(config);

      DirectoryServer.registerRotationPolicy(config.dn(), rotationPolicy);
    } catch (ConfigException e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      messages.add(e.getMessageObject());
      resultCode = DirectoryServer.getServerErrorResultCode();
    } catch (Exception e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }

      messages.add(
          ERR_CONFIG_ROTATION_POLICY_CANNOT_CREATE_POLICY.get(
              String.valueOf(config.dn().toString()), stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
    }

    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /** {@inheritDoc} */
  public ConfigChangeResult applyConfigurationChange(RootDNUserCfg configuration) {
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();

    HashSet<DN> setDNs = new HashSet<DN>();
    HashSet<DN> addDNs = new HashSet<DN>();
    HashSet<DN> delDNs = new HashSet<DN>(alternateBindDNs.get(configuration.dn()));

    for (DN altBindDN : configuration.getAlternateBindDN()) {
      setDNs.add(altBindDN);

      if (!delDNs.remove(altBindDN)) {
        addDNs.add(altBindDN);
      }
    }

    for (DN dn : delDNs) {
      DirectoryServer.deregisterAlternateRootBindDN(dn);
    }

    HashSet<DN> addedDNs = new HashSet<DN>(addDNs.size());
    for (DN dn : addDNs) {
      try {
        DirectoryServer.registerAlternateRootDN(configuration.dn(), dn);
        addedDNs.add(dn);
      } catch (DirectoryException de) {
        // This shouldn't happen, since the set of DNs should have already been
        // validated.
        resultCode = DirectoryServer.getServerErrorResultCode();
        messages.add(de.getMessageObject());

        for (DN addedDN : addedDNs) {
          DirectoryServer.deregisterAlternateRootBindDN(addedDN);
        }

        for (DN deletedDN : delDNs) {
          try {
            DirectoryServer.registerAlternateRootDN(configuration.dn(), deletedDN);
          } catch (Exception e) {
            // This should also never happen.
            alternateBindDNs.get(configuration.dn()).remove(deletedDN);
          }
        }
      }
    }

    if (resultCode == ResultCode.SUCCESS) {
      alternateBindDNs.put(configuration.dn(), setDNs);
    }

    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /** {@inheritDoc} */
  public ConfigChangeResult applyConfigurationDelete(LogRotationPolicyCfg config) {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();

    RotationPolicy policy = DirectoryServer.getRotationPolicy(config.dn());
    if (policy != null) {
      DirectoryServer.deregisterRotationPolicy(config.dn());
    } else {
      // TODO: Add message and check for usage
      resultCode = DirectoryServer.getServerErrorResultCode();
    }

    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /** {@inheritDoc} */
  public ConfigChangeResult applyConfigurationDelete(RootDNUserCfg configuration) {
    DirectoryServer.deregisterRootDN(configuration.dn());
    configuration.removeChangeListener(this);

    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();

    HashSet<DN> altBindDNs = alternateBindDNs.remove(configuration.dn());
    if (altBindDNs != null) {
      for (DN dn : altBindDNs) {
        DirectoryServer.deregisterAlternateRootBindDN(dn);
      }
    }

    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /** {@inheritDoc} */
  public ConfigChangeResult applyConfigurationChange(LogRotationPolicyCfg configuration) {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();

    RotationPolicy policy = DirectoryServer.getRotationPolicy(configuration.dn());
    String className = configuration.getJavaClass();
    if (!className.equals(policy.getClass().getName())) {
      adminActionRequired = true;
    }

    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * Initializes all of the root users currently defined in the Directory Server configuration, as
   * well as the set of privileges that root users will inherit by default.
   *
   * @throws ConfigException If a configuration problem causes the identity mapper initialization
   *     process to fail.
   * @throws InitializationException If a problem occurs while initializing the identity mappers
   *     that is not related to the server configuration.
   */
  public void initializeRootDNs() throws ConfigException, InitializationException {
    // Get the root configuration object.
    ServerManagementContext managementContext = ServerManagementContext.getInstance();
    RootCfg rootConfiguration = managementContext.getRootConfiguration();

    // Get the root DN configuration object, use it to set the default root
    // privileges, and register a change listener for it.
    RootDNCfg rootDNCfg = rootConfiguration.getRootDN();
    rootPrivilegeChangeListener.setDefaultRootPrivileges(rootDNCfg);
    rootDNCfg.addChangeListener(rootPrivilegeChangeListener);

    // Register as an add and delete listener for new root DN users.
    rootDNCfg.addRootDNUserAddListener(this);
    rootDNCfg.addRootDNUserDeleteListener(this);

    // Get the set of root users defined below "cn=Root DNs,cn=config".  For
    // each one, register as a change listener, and get the set of alternate
    // bind DNs.
    for (String name : rootDNCfg.listRootDNUsers()) {
      RootDNUserCfg rootUserCfg = rootDNCfg.getRootDNUser(name);
      rootUserCfg.addChangeListener(this);
      DirectoryServer.registerRootDN(rootUserCfg.dn());

      HashSet<DN> altBindDNs = new HashSet<DN>();
      for (DN alternateBindDN : rootUserCfg.getAlternateBindDN()) {
        try {
          altBindDNs.add(alternateBindDN);
          DirectoryServer.registerAlternateRootDN(rootUserCfg.dn(), alternateBindDN);
        } catch (DirectoryException de) {
          throw new InitializationException(de.getMessageObject());
        }
      }

      alternateBindDNs.put(rootUserCfg.dn(), altBindDNs);
    }
  }
  /**
   * Initializes all the log rotation policies.
   *
   * @throws ConfigException If an unrecoverable problem arises in the process of performing the
   *     initialization as a result of the server configuration.
   * @throws InitializationException If a problem occurs during initialization that is not related
   *     to the server configuration.
   */
  public void initializeLogRotationPolicyConfig() throws ConfigException, InitializationException {
    ServerManagementContext context = ServerManagementContext.getInstance();
    RootCfg root = context.getRootConfiguration();

    root.addLogRotationPolicyAddListener(this);
    root.addLogRotationPolicyDeleteListener(this);

    for (String name : root.listLogRotationPolicies()) {
      LogRotationPolicyCfg config = root.getLogRotationPolicy(name);

      RotationPolicy rotationPolicy = getRotationPolicy(config);

      DirectoryServer.registerRotationPolicy(config.dn(), rotationPolicy);
    }
  }
  /** {@inheritDoc} */
  public boolean isConfigurationChangeAcceptable(
      RootDNUserCfg configuration, List<Message> unacceptableReasons) {
    boolean configAcceptable = true;

    // There must not be any new alternate bind DNs that are already in use by
    // other root users.
    for (DN altBindDN : configuration.getAlternateBindDN()) {
      DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN);
      if ((existingRootDN != null) && (!existingRootDN.equals(configuration.dn()))) {
        Message message =
            ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get(
                String.valueOf(altBindDN),
                String.valueOf(configuration.dn()),
                String.valueOf(existingRootDN));
        unacceptableReasons.add(message);

        configAcceptable = false;
      }
    }

    return configAcceptable;
  }
  /** {@inheritDoc} */
  public boolean isConfigurationAddAcceptable(
      RootDNUserCfg configuration, List<Message> unacceptableReasons) {
    // The new root user must not have an alternate bind DN that is already
    // in use.
    boolean configAcceptable = true;
    for (DN altBindDN : configuration.getAlternateBindDN()) {
      DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN);
      if (existingRootDN != null) {

        Message message =
            ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get(
                String.valueOf(altBindDN),
                String.valueOf(configuration.dn()),
                String.valueOf(existingRootDN));
        unacceptableReasons.add(message);

        configAcceptable = false;
      }
    }

    return configAcceptable;
  }