Example #1
0
  @Override
  public LDIFImportResult importLDIF(LDIFImportConfig importConfig, ServerContext serverContext)
      throws DirectoryException {
    try (LDIFReader reader = getReader(importConfig)) {
      while (true) {
        Entry e = null;
        try {
          e = reader.readEntry();
          if (e == null) {
            break;
          }
        } catch (LDIFException le) {
          if (le.canContinueReading()) {
            continue;
          }
          throw newDirectoryException(le);
        }

        try {
          addEntry(e, null);
        } catch (DirectoryException de) {
          reader.rejectLastEntry(de.getMessageObject());
        }
      }

      return new LDIFImportResult(
          reader.getEntriesRead(), reader.getEntriesRejected(), reader.getEntriesIgnored());
    } catch (DirectoryException de) {
      throw de;
    } catch (Exception e) {
      throw newDirectoryException(e);
    }
  }
Example #2
0
  @Override
  public boolean isConfigurationAcceptable(
      ConnectionHandlerCfg configuration, List<LocalizableMessage> unacceptableReasons) {
    HTTPConnectionHandlerCfg config = (HTTPConnectionHandlerCfg) configuration;

    if (currentConfig == null || (!this.enabled && config.isEnabled())) {
      // Attempt to bind to the listen port on all configured addresses to
      // verify whether the connection handler will be able to start.
      LocalizableMessage errorMessage =
          checkAnyListenAddressInUse(
              config.getListenAddress(),
              config.getListenPort(),
              config.isAllowTCPReuseAddress(),
              config.dn());
      if (errorMessage != null) {
        unacceptableReasons.add(errorMessage);
        return false;
      }
    }

    if (config.isEnabled() && config.isUseSSL()) {
      try {
        createSSLEngineConfigurator(config);
      } catch (DirectoryException e) {
        logger.traceException(e);
        unacceptableReasons.add(e.getMessageObject());
        return false;
      }
    }

    return true;
  }
  /** {@inheritDoc} */
  @Override()
  public DynamicGroup newInstance(Entry groupEntry) throws DirectoryException {
    ensureNotNull(groupEntry);

    // Get the memberURL attribute from the entry, if there is one, and parse
    // out the LDAP URLs that it contains.
    LinkedHashSet<LDAPURL> memberURLs = new LinkedHashSet<LDAPURL>();
    AttributeType memberURLType = DirectoryConfig.getAttributeType(ATTR_MEMBER_URL_LC, true);
    List<Attribute> attrList = groupEntry.getAttribute(memberURLType);
    if (attrList != null) {
      for (Attribute a : attrList) {
        for (AttributeValue v : a) {
          try {
            memberURLs.add(LDAPURL.decode(v.getValue().toString(), true));
          } catch (DirectoryException de) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }

            Message message =
                ERR_DYNAMICGROUP_CANNOT_DECODE_MEMBERURL.get(
                    v.getValue().toString(),
                    String.valueOf(groupEntry.getDN()),
                    de.getMessageObject());
            ErrorLogger.logError(message);
          }
        }
      }
    }

    return new DynamicGroup(groupEntry.getDN(), memberURLs);
  }
  /** {@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);
  }
Example #5
0
 /**
  * Returns a new connection factory for a specified user.
  *
  * @param userDN The specified user's DN.
  * @return a new connection factory.
  */
 public static ConnectionFactory newConnectionFactoryForUser(final DN userDN) {
   InternalClientConnection icc = null;
   try {
     icc = new InternalClientConnection(to(userDN));
   } catch (DirectoryException e) {
     throw new IllegalStateException(e.getMessage(), e);
   }
   return newConnectionFactory(icc);
 }
  /** {@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);
  }
 /**
  * Retrieves the default ordering matching rule that will be used for attributes with this
  * syntax.
  *
  * @return The default ordering matching rule that will be used for attributes with this syntax,
  *     or <CODE>null</CODE> if ordering matches will not be allowed for this type by default.
  */
 @Override
 public OrderingMatchingRule getOrderingMatchingRule() {
   if (orderingMatchingRule == null) {
     orderingMatchingRule = new EnumOrderingMatchingRule(this, oid);
     try {
       DirectoryServer.registerMatchingRule(orderingMatchingRule, false);
     } catch (DirectoryException de) {
       logError(de.getMessageObject());
     }
   }
   return orderingMatchingRule;
 }
Example #8
0
  @Override
  public synchronized void openBackend() throws ConfigException, InitializationException {
    baseDNSet = new HashSet<>();
    Collections.addAll(baseDNSet, baseDNs);

    // Register base DNs.
    for (DN dn : baseDNs) {
      try {
        DirectoryServer.registerBaseDN(dn, this, false);
      } catch (Exception e) {
        logger.traceException(e);

        LocalizableMessage message =
            ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(dn, getExceptionMessage(e));
        throw new InitializationException(message, e);
      }
    }

    // Initialize null entry object classes.
    objectClasses = new HashMap<>();

    String topOCName = "top";
    ObjectClass topOC = DirectoryServer.getObjectClass(topOCName);
    if (topOC == null) {
      throw new InitializationException(
          LocalizableMessage.raw(
              "Unable to locate " + topOCName + " objectclass in the current server schema"));
    }
    objectClasses.put(topOC, topOCName);

    String nulOCName = "nullbackendobject";
    ObjectClass nulOC = DirectoryServer.getDefaultObjectClass(nulOCName);
    try {
      DirectoryServer.registerObjectClass(nulOC, false);
    } catch (DirectoryException de) {
      logger.traceException(de);
      throw new InitializationException(de.getMessageObject());
    }
    objectClasses.put(nulOC, nulOCName);

    String extOCName = "extensibleobject";
    ObjectClass extOC = DirectoryServer.getObjectClass(extOCName);
    if (extOC == null) {
      throw new InitializationException(
          LocalizableMessage.raw(
              "Unable to locate " + extOCName + " objectclass in the current server schema"));
    }
    objectClasses.put(extOC, extOCName);
  }
  /**
   * Indicates whether the provided value is acceptable for use in an attribute with this syntax. If
   * it is not, then the reason may be appended to the provided buffer.
   *
   * @param value The value for which to make the determination.
   * @param invalidReason The buffer to which the invalid reason should be appended.
   * @return <CODE>true</CODE> if the provided value is acceptable for use with this syntax, or
   *     <CODE>false</CODE> if not.
   */
  @Override
  public boolean valueIsAcceptable(ByteSequence value, MessageBuilder invalidReason) {
    // We'll use the decodeAttributeType method to determine if the value is
    // acceptable.
    try {
      decodeLDAPSyntax(value, DirectoryServer.getSchema(), true);
      return true;
    } catch (DirectoryException de) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, de);
      }

      invalidReason.append(de.getMessageObject());
      return false;
    }
  }
Example #10
0
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationChange(LDAPConnectionHandlerCfg config) {
    final ConfigChangeResult ccr = new ConfigChangeResult();

    // Note that the following properties cannot be modified:
    //
    // * listen port and addresses
    // * use ssl
    // * ssl policy
    // * ssl cert nickname
    // * accept backlog
    // * tcp reuse address
    // * num request handler

    // Clear the stat tracker if LDAPv2 is being enabled.
    if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2() && config.isAllowLDAPV2()) {
      statTracker.clearStatistics();
    }

    // Apply the changes.
    currentConfig = config;
    enabled = config.isEnabled();
    allowedClients = config.getAllowedClient();
    deniedClients = config.getDeniedClient();

    // Reconfigure SSL if needed.
    try {
      configureSSL(config);
    } catch (DirectoryException e) {
      logger.traceException(e);
      ccr.setResultCode(e.getResultCode());
      ccr.addMessage(e.getMessageObject());
      return ccr;
    }

    if (config.isAllowLDAPV2()) {
      DirectoryServer.registerSupportedLDAPVersion(2, this);
    } else {
      DirectoryServer.deregisterSupportedLDAPVersion(2, this);
    }

    return ccr;
  }
Example #11
0
  @Override
  public ConfigChangeResult applyConfigurationChange(HTTPConnectionHandlerCfg config) {
    final ConfigChangeResult ccr = new ConfigChangeResult();

    if (anyChangeRequiresRestart(config)) {
      ccr.setAdminActionRequired(true);
      ccr.addMessage(ERR_CONNHANDLER_CONFIG_CHANGES_REQUIRE_RESTART.get("HTTP"));
    }

    // Reconfigure SSL if needed.
    try {
      configureSSL(config);
    } catch (DirectoryException e) {
      logger.traceException(e);
      ccr.setResultCode(e.getResultCode());
      ccr.addMessage(e.getMessageObject());
      return ccr;
    }

    if (config.isEnabled() && this.currentConfig.isEnabled() && isListening()) {
      // Server was running and will still be running if the "enabled" was flipped,
      // leave it to the stop / start server to handle it.
      if (!this.currentConfig.isKeepStats() && config.isKeepStats()) {
        // It must now keep stats while it was not previously.
        setHttpStatsProbe(this.httpServer);
      } else if (this.currentConfig.isKeepStats()
          && !config.isKeepStats()
          && this.httpProbe != null) {
        // It must NOT keep stats anymore.
        getHttpConfig(this.httpServer).removeProbes(this.httpProbe);
        this.httpProbe = null;
      }
    }

    this.initConfig = config;
    this.currentConfig = config;
    this.enabled = this.currentConfig.isEnabled();

    return ccr;
  }
  /** {@inheritDoc} */
  @Override
  public VirtualStaticGroup newInstance(ServerContext serverContext, Entry groupEntry)
      throws DirectoryException {
    ifNull(groupEntry);

    // Get the target group DN attribute from the entry, if there is one.
    DN targetDN = null;
    AttributeType targetType = DirectoryServer.getAttributeType(ATTR_TARGET_GROUP_DN, true);
    List<Attribute> attrList = groupEntry.getAttribute(targetType);
    if (attrList != null) {
      for (Attribute a : attrList) {
        for (ByteString v : a) {
          if (targetDN != null) {
            LocalizableMessage message =
                ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS.get(groupEntry.getName());
            throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message);
          }

          try {
            targetDN = DN.decode(v);
          } catch (DirectoryException de) {
            logger.traceException(de);

            LocalizableMessage message =
                ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET.get(
                    v, groupEntry.getName(), de.getMessageObject());
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, de);
          }
        }
      }
    }

    if (targetDN == null) {
      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET.get(groupEntry.getName());
      throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message);
    }

    return new VirtualStaticGroup(groupEntry.getName(), targetDN);
  }
  /**
   * Performs the checks and processing necessary for the current bind operation (simple or SASL).
   */
  private void processBind() {
    // Check to see if the client has permission to perform the
    // bind.

    // FIXME: for now assume that this will check all permission
    // pertinent to the operation. This includes any controls
    // specified.
    try {
      if (!AccessControlConfigManager.getInstance().getAccessControlHandler().isAllowed(this)) {
        setResultCode(ResultCode.INVALID_CREDENTIALS);
        setAuthFailureReason(ERR_BIND_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get());
        return;
      }
    } catch (DirectoryException e) {
      setResultCode(e.getResultCode());
      setAuthFailureReason(e.getMessageObject());
      return;
    }

    // Check to see if there are any controls in the request. If so, then see
    // if there is any special processing required.
    try {
      handleRequestControls();
    } catch (DirectoryException de) {
      logger.traceException(de);

      setResponseData(de);
      return;
    }

    // Check to see if this is a simple bind or a SASL bind and process
    // accordingly.
    try {
      switch (getAuthenticationType()) {
        case SIMPLE:
          processSimpleBind();
          break;

        case SASL:
          processSASLBind();
          break;

        default:
          // Send a protocol error response to the client and disconnect.
          // We should never come here.
          setResultCode(ResultCode.PROTOCOL_ERROR);
      }
    } catch (DirectoryException de) {
      logger.traceException(de);

      if (de.getResultCode() == ResultCode.INVALID_CREDENTIALS) {
        setResultCode(ResultCode.INVALID_CREDENTIALS);
        setAuthFailureReason(de.getMessageObject());
      } else {
        setResponseData(de);
      }
    }
  }
Example #14
0
  @Override
  public void initializeConnectionHandler(HTTPConnectionHandlerCfg config)
      throws ConfigException, InitializationException {
    this.enabled = config.isEnabled();

    if (friendlyName == null) {
      friendlyName = config.dn().rdn().getAttributeValue(0).toString();
    }

    int listenPort = config.getListenPort();
    for (InetAddress a : config.getListenAddress()) {
      listeners.add(new HostPort(a.getHostAddress(), listenPort));
    }

    handlerName = getHandlerName(config);

    // Configure SSL if needed.
    try {
      // This call may disable the connector if wrong SSL settings
      configureSSL(config);
    } catch (DirectoryException e) {
      logger.traceException(e);
      throw new InitializationException(e.getMessageObject());
    }

    // Create and register monitors.
    statTracker = new HTTPStatistics(handlerName + " Statistics");
    DirectoryServer.registerMonitorProvider(statTracker);

    connMonitor = new ClientConnectionMonitorProvider(this);
    DirectoryServer.registerMonitorProvider(connMonitor);

    // Register this as a change listener.
    config.addHTTPChangeListener(this);

    this.initConfig = config;
    this.currentConfig = config;
  }
  /**
   * 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);
    }
  }
Example #16
0
  /** {@inheritDoc} */
  @Override
  protected int processLocal(boolean initializeServer, PrintStream out, PrintStream err) {

    // Perform the initial bootstrap of the Directory Server and process the
    // configuration.
    DirectoryServer directoryServer = DirectoryServer.getInstance();
    if (initializeServer) {
      try {
        DirectoryServer.bootstrapClient();
        DirectoryServer.initializeJMX();
      } catch (Exception e) {
        printWrappedText(err, ERR_SERVER_BOOTSTRAP_ERROR.get(getExceptionMessage(e)));
        return 1;
      }

      try {
        directoryServer.initializeConfiguration(configClass.getValue(), configFile.getValue());
      } catch (InitializationException ie) {
        printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(ie.getMessage()));
        return 1;
      } catch (Exception e) {
        printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(getExceptionMessage(e)));
        return 1;
      }

      // Initialize the Directory Server schema elements.
      try {
        directoryServer.initializeSchema();
      } catch (ConfigException | InitializationException e) {
        printWrappedText(err, ERR_CANNOT_LOAD_SCHEMA.get(e.getMessage()));
        return 1;
      } catch (Exception e) {
        printWrappedText(err, ERR_CANNOT_LOAD_SCHEMA.get(getExceptionMessage(e)));
        return 1;
      }

      // Initialize the Directory Server core configuration.
      try {
        CoreConfigManager coreConfigManager =
            new CoreConfigManager(directoryServer.getServerContext());
        coreConfigManager.initializeCoreConfig();
      } catch (ConfigException | InitializationException e) {
        printWrappedText(err, ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(e.getMessage()));
        return 1;
      } catch (Exception e) {
        printWrappedText(err, ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(getExceptionMessage(e)));
        return 1;
      }

      // Initialize the Directory Server crypto manager.
      try {
        directoryServer.initializeCryptoManager();
      } catch (ConfigException | InitializationException e) {
        printWrappedText(err, ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(e.getMessage()));
        return 1;
      } catch (Exception e) {
        printWrappedText(err, ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(getExceptionMessage(e)));
        return 1;
      }

      try {
        ErrorLogPublisher errorLogPublisher =
            TextErrorLogPublisher.getToolStartupTextErrorPublisher(new TextWriter.STREAM(out));
        ErrorLogger.getInstance().addLogPublisher(errorLogPublisher);

        DebugLogger.getInstance().addPublisherIfRequired(new TextWriter.STREAM(out));
      } catch (Exception e) {
        err.println("Error installing the custom error logger: " + stackTraceToSingleLineString(e));
      }

      // Make sure that the Directory Server plugin initialization is performed.
      try {
        HashSet<PluginType> pluginTypes = new HashSet<>(1);
        pluginTypes.add(PluginType.LDIF_EXPORT);
        directoryServer.initializePlugins(pluginTypes);
      } catch (ConfigException | InitializationException e) {
        printWrappedText(err, ERR_LDIFEXPORT_CANNOT_INITIALIZE_PLUGINS.get(e.getMessage()));
        return 1;
      } catch (Exception e) {
        printWrappedText(err, ERR_LDIFEXPORT_CANNOT_INITIALIZE_PLUGINS.get(getExceptionMessage(e)));
        return 1;
      }
    }

    // See if there were any user-defined sets of include/exclude attributes or
    // filters.  If so, then process them.
    Set<AttributeType> excludeAttributes = toAttributeTypes(excludeAttributeStrings);
    Set<AttributeType> includeAttributes = toAttributeTypes(includeAttributeStrings);

    ArrayList<SearchFilter> excludeFilters;
    if (excludeFilterStrings == null) {
      excludeFilters = null;
    } else {
      excludeFilters = new ArrayList<>();
      for (String filterString : excludeFilterStrings.getValues()) {
        try {
          excludeFilters.add(SearchFilter.createFilterFromString(filterString));
        } catch (DirectoryException de) {
          logger.error(
              ERR_LDIFEXPORT_CANNOT_PARSE_EXCLUDE_FILTER, filterString, de.getMessageObject());
          return 1;
        } catch (Exception e) {
          logger.error(
              ERR_LDIFEXPORT_CANNOT_PARSE_EXCLUDE_FILTER, filterString, getExceptionMessage(e));
          return 1;
        }
      }
    }

    ArrayList<SearchFilter> includeFilters;
    if (includeFilterStrings == null) {
      includeFilters = null;
    } else {
      includeFilters = new ArrayList<>();
      for (String filterString : includeFilterStrings.getValues()) {
        try {
          includeFilters.add(SearchFilter.createFilterFromString(filterString));
        } catch (DirectoryException de) {
          logger.error(
              ERR_LDIFEXPORT_CANNOT_PARSE_INCLUDE_FILTER, filterString, de.getMessageObject());
          return 1;
        } catch (Exception e) {
          logger.error(
              ERR_LDIFEXPORT_CANNOT_PARSE_INCLUDE_FILTER, filterString, getExceptionMessage(e));
          return 1;
        }
      }
    }

    // Get information about the backends defined in the server.  Iterate
    // through them, finding the one backend that should be used for the export,
    // and also finding backends with subordinate base DNs that should be
    // excluded from the export.
    Backend backend = null;
    List<DN> baseDNList = null;
    List<DN> defaultIncludeBranches = null;
    ArrayList<DN> excludeBranches = null;

    ArrayList<Backend> backendList = new ArrayList<>();
    ArrayList<BackendCfg> entryList = new ArrayList<>();
    ArrayList<List<DN>> dnList = new ArrayList<>();
    BackendToolUtils.getBackends(backendList, entryList, dnList);

    int numBackends = backendList.size();
    for (int i = 0; i < numBackends; i++) {
      Backend b = backendList.get(i);
      if (!backendID.getValue().equals(b.getBackendID())) {
        continue;
      }

      if (backend == null) {
        backend = b;
        baseDNList = dnList.get(i);
        defaultIncludeBranches = dnList.get(i);
      } else {
        logger.error(ERR_LDIFEXPORT_MULTIPLE_BACKENDS_FOR_ID, backendID.getValue());
        return 1;
      }
    }

    if (backend == null) {
      logger.error(ERR_LDIFEXPORT_NO_BACKENDS_FOR_ID, backendID.getValue());
      return 1;
    } else if (!backend.supports(BackendOperation.RESTORE)) {
      logger.error(ERR_LDIFEXPORT_CANNOT_EXPORT_BACKEND, backendID.getValue());
      return 1;
    }

    if (excludeBranchStrings.isPresent()) {
      excludeBranches = new ArrayList<>();
      for (String s : excludeBranchStrings.getValues()) {
        DN excludeBranch;
        try {
          excludeBranch = DN.valueOf(s);
        } catch (DirectoryException de) {
          logger.error(ERR_LDIFEXPORT_CANNOT_DECODE_EXCLUDE_BASE, s, de.getMessageObject());
          return 1;
        } catch (Exception e) {
          logger.error(ERR_LDIFEXPORT_CANNOT_DECODE_EXCLUDE_BASE, s, getExceptionMessage(e));
          return 1;
        }

        if (!excludeBranches.contains(excludeBranch)) {
          excludeBranches.add(excludeBranch);
        }
      }
    }

    List<DN> includeBranches;
    if (includeBranchStrings.isPresent()) {
      includeBranches = new ArrayList<>();
      for (String s : includeBranchStrings.getValues()) {
        DN includeBranch;
        try {
          includeBranch = DN.valueOf(s);
        } catch (DirectoryException de) {
          logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, de.getMessageObject());
          return 1;
        } catch (Exception e) {
          logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, getExceptionMessage(e));
          return 1;
        }

        if (!Backend.handlesEntry(includeBranch, defaultIncludeBranches, excludeBranches)) {
          logger.error(ERR_LDIFEXPORT_INVALID_INCLUDE_BASE, s, backendID.getValue());
          return 1;
        }

        includeBranches.add(includeBranch);
      }
    } else {
      includeBranches = defaultIncludeBranches;
    }

    // Create the LDIF export configuration to use when reading the LDIF.
    ExistingFileBehavior existingBehavior;
    if (appendToLDIF.isPresent()) {
      existingBehavior = ExistingFileBehavior.APPEND;
    } else {
      existingBehavior = ExistingFileBehavior.OVERWRITE;
    }

    LDIFExportConfig exportConfig = new LDIFExportConfig(ldifFile.getValue(), existingBehavior);
    exportConfig.setCompressData(compressLDIF.isPresent());
    exportConfig.setEncryptData(encryptLDIF.isPresent());
    exportConfig.setExcludeAttributes(excludeAttributes);
    exportConfig.setExcludeBranches(excludeBranches);
    exportConfig.setExcludeFilters(excludeFilters);
    exportConfig.setIncludeAttributes(includeAttributes);
    exportConfig.setIncludeBranches(includeBranches);
    exportConfig.setIncludeFilters(includeFilters);
    exportConfig.setSignHash(signHash.isPresent());
    exportConfig.setIncludeOperationalAttributes(!excludeOperationalAttrs.isPresent());

    // FIXME -- Should this be conditional?
    exportConfig.setInvokeExportPlugins(true);

    try {
      exportConfig.setWrapColumn(wrapColumn.getIntValue());
    } catch (ArgumentException ae) {
      logger.error(ERR_LDIFEXPORT_CANNOT_DECODE_WRAP_COLUMN_AS_INTEGER, wrapColumn.getValue());
      return 1;
    }

    // Get the set of base DNs for the backend as an array.
    DN[] baseDNs = new DN[baseDNList.size()];
    baseDNList.toArray(baseDNs);

    // Acquire a shared lock for the backend.
    try {
      String lockFile = LockFileManager.getBackendLockFileName(backend);
      StringBuilder failureReason = new StringBuilder();
      if (!LockFileManager.acquireSharedLock(lockFile, failureReason)) {
        logger.error(ERR_LDIFEXPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), failureReason);
        return 1;
      }
    } catch (Exception e) {
      logger.error(
          ERR_LDIFEXPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
      return 1;
    }

    boolean errorOccurred = false;

    // Launch the export.
    try {
      backend.exportLDIF(exportConfig);
    } catch (DirectoryException de) {
      logger.error(ERR_LDIFEXPORT_ERROR_DURING_EXPORT, de.getMessageObject());
      errorOccurred = true;
    } catch (Exception e) {
      logger.error(ERR_LDIFEXPORT_ERROR_DURING_EXPORT, getExceptionMessage(e));
      errorOccurred = true;
    }

    // Release the shared lock on the backend.
    try {
      String lockFile = LockFileManager.getBackendLockFileName(backend);
      StringBuilder failureReason = new StringBuilder();
      if (!LockFileManager.releaseLock(lockFile, failureReason)) {
        logger.warn(WARN_LDIFEXPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), failureReason);
      }
    } catch (Exception e) {
      logger.warn(
          WARN_LDIFEXPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
    }

    // Clean up after the export by closing the export config.
    exportConfig.close();
    return !errorOccurred ? 0 : 1;
  }
Example #17
0
  /** {@inheritDoc} */
  @Override
  public void initializeConnectionHandler(LDAPConnectionHandlerCfg config)
      throws ConfigException, InitializationException {
    if (friendlyName == null) {
      friendlyName = config.dn().rdn().getAttributeValue(0).toString();
    }

    // Open the selector.
    try {
      selector = Selector.open();
    } catch (Exception e) {
      logger.traceException(e);

      LocalizableMessage message =
          ERR_LDAP_CONNHANDLER_OPEN_SELECTOR_FAILED.get(
              config.dn(), stackTraceToSingleLineString(e));
      throw new InitializationException(message, e);
    }

    // Save this configuration for future reference.
    currentConfig = config;
    enabled = config.isEnabled();
    requestHandlerIndex = 0;
    allowedClients = config.getAllowedClient();
    deniedClients = config.getDeniedClient();

    // Configure SSL if needed.
    try {
      // This call may disable the connector if wrong SSL settings
      configureSSL(config);
    } catch (DirectoryException e) {
      logger.traceException(e);
      throw new InitializationException(e.getMessageObject());
    }

    // Save properties that cannot be dynamically modified.
    allowReuseAddress = config.isAllowTCPReuseAddress();
    backlog = config.getAcceptBacklog();
    listenAddresses = config.getListenAddress();
    listenPort = config.getListenPort();
    numRequestHandlers = getNumRequestHandlers(config.getNumRequestHandlers(), friendlyName);

    // Construct a unique name for this connection handler, and put
    // together the set of listeners.
    listeners = new LinkedList<>();
    StringBuilder nameBuffer = new StringBuilder();
    nameBuffer.append(friendlyName);
    for (InetAddress a : listenAddresses) {
      listeners.add(new HostPort(a.getHostAddress(), listenPort));
      nameBuffer.append(" ");
      nameBuffer.append(a.getHostAddress());
    }
    nameBuffer.append(" port ");
    nameBuffer.append(listenPort);
    handlerName = nameBuffer.toString();

    // Attempt to bind to the listen port on all configured addresses to
    // verify whether the connection handler will be able to start.
    LocalizableMessage errorMessage =
        checkAnyListenAddressInUse(listenAddresses, listenPort, allowReuseAddress, config.dn());
    if (errorMessage != null) {
      logger.error(errorMessage);
      throw new InitializationException(errorMessage);
    }

    // Create a system property to store the LDAP(S) port the server is
    // listening to. This information can be displayed with jinfo.
    System.setProperty(protocol + "_port", String.valueOf(listenPort));

    // Create and start a connection finalizer thread for this
    // connection handler.
    connectionFinalizer =
        Executors.newSingleThreadScheduledExecutor(
            new DirectoryThread.Factory(
                "LDAP Connection Finalizer for connection handler " + toString()));

    connectionFinalizerActiveJobQueue = new ArrayList<>();
    connectionFinalizerPendingJobQueue = new ArrayList<>();

    connectionFinalizer.scheduleWithFixedDelay(
        new ConnectionFinalizerRunnable(), 100, 100, TimeUnit.MILLISECONDS);

    // Create and start the request handlers.
    requestHandlers = new LDAPRequestHandler[numRequestHandlers];
    for (int i = 0; i < numRequestHandlers; i++) {
      requestHandlers[i] = new LDAPRequestHandler(this, i);
    }

    for (int i = 0; i < numRequestHandlers; i++) {
      requestHandlers[i].start();
    }

    // Register the set of supported LDAP versions.
    DirectoryServer.registerSupportedLDAPVersion(3, this);
    if (config.isAllowLDAPV2()) {
      DirectoryServer.registerSupportedLDAPVersion(2, this);
    }

    // Create and register monitors.
    statTracker = new LDAPStatistics(handlerName + " Statistics");
    DirectoryServer.registerMonitorProvider(statTracker);

    connMonitor = new ClientConnectionMonitorProvider(this);
    DirectoryServer.registerMonitorProvider(connMonitor);

    // Register this as a change listener.
    config.addLDAPChangeListener(this);
  }
  /**
   * Performs the processing necessary for a simple bind operation.
   *
   * @return {@code true} if processing should continue for the operation, or {@code false} if not.
   * @throws DirectoryException If a problem occurs that should cause the bind operation to fail.
   */
  protected boolean processSimpleBind() throws DirectoryException {
    // See if this is an anonymous bind.  If so, then determine whether
    // to allow it.
    ByteString simplePassword = getSimplePassword();
    if (simplePassword == null || simplePassword.length() == 0) {
      return processAnonymousSimpleBind();
    }

    // See if the bind DN is actually one of the alternate root DNs
    // defined in the server.  If so, then replace it with the actual DN
    // for that user.
    DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN);
    if (actualRootDN != null) {
      bindDN = actualRootDN;
    }

    Entry userEntry;
    try {
      userEntry = backend.getEntry(bindDN);
    } catch (DirectoryException de) {
      logger.traceException(de);

      userEntry = null;

      if (de.getResultCode() == ResultCode.REFERRAL) {
        // Re-throw referral exceptions - these should be passed back
        // to the client.
        throw de;
      } else {
        // Replace other exceptions in case they expose any sensitive
        // information.
        throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, de.getMessageObject());
      }
    }

    if (userEntry == null) {
      throw new DirectoryException(
          ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_UNKNOWN_USER.get());
    } else {
      setUserEntryDN(userEntry.getName());
    }

    // Check to see if the user has a password. If not, then fail.
    // FIXME -- We need to have a way to enable/disable debugging.
    authPolicyState = AuthenticationPolicyState.forUser(userEntry, false);
    if (authPolicyState.isPasswordPolicy()) {
      // Account is managed locally.
      PasswordPolicyState pwPolicyState = (PasswordPolicyState) authPolicyState;
      PasswordPolicy policy = pwPolicyState.getAuthenticationPolicy();

      AttributeType pwType = policy.getPasswordAttribute();
      List<Attribute> pwAttr = userEntry.getAttribute(pwType);
      if (pwAttr == null || pwAttr.isEmpty()) {
        throw new DirectoryException(
            ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_NO_PASSWORD.get());
      }

      // Perform a number of password policy state checks for the
      // non-authenticated user.
      checkUnverifiedPasswordPolicyState(userEntry, null);

      // Invoke pre-operation plugins.
      if (!invokePreOpPlugins()) {
        return false;
      }

      // Determine whether the provided password matches any of the stored
      // passwords for the user.
      if (pwPolicyState.passwordMatches(simplePassword)) {
        setResultCode(ResultCode.SUCCESS);

        checkVerifiedPasswordPolicyState(userEntry, null);

        if (DirectoryServer.lockdownMode()
            && !ClientConnection.hasPrivilege(userEntry, BYPASS_LOCKDOWN)) {
          throw new DirectoryException(
              ResultCode.INVALID_CREDENTIALS, ERR_BIND_REJECTED_LOCKDOWN_MODE.get());
        }
        setAuthenticationInfo(
            new AuthenticationInfo(
                userEntry, getBindDN(), DirectoryServer.isRootDN(userEntry.getName())));

        // Set resource limits for the authenticated user.
        setResourceLimits(userEntry);

        // Perform any remaining processing for a successful simple
        // authentication.
        pwPolicyState.handleDeprecatedStorageSchemes(simplePassword);
        pwPolicyState.clearFailureLockout();

        if (isFirstWarning) {
          pwPolicyState.setWarnedTime();

          int numSeconds = pwPolicyState.getSecondsUntilExpiration();
          LocalizableMessage m = WARN_BIND_PASSWORD_EXPIRING.get(secondsToTimeString(numSeconds));

          pwPolicyState.generateAccountStatusNotification(
              AccountStatusNotificationType.PASSWORD_EXPIRING,
              userEntry,
              m,
              AccountStatusNotification.createProperties(
                  pwPolicyState, false, numSeconds, null, null));
        }

        if (isGraceLogin) {
          pwPolicyState.updateGraceLoginTimes();
        }

        pwPolicyState.setLastLoginTime();
      } else {
        setResultCode(ResultCode.INVALID_CREDENTIALS);
        setAuthFailureReason(ERR_BIND_OPERATION_WRONG_PASSWORD.get());

        if (policy.getLockoutFailureCount() > 0) {
          generateAccountStatusNotificationForLockedBindAccount(userEntry, pwPolicyState);
        }
      }
    } else {
      // Check to see if the user is administratively disabled or locked.
      if (authPolicyState.isDisabled()) {
        throw new DirectoryException(
            ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_ACCOUNT_DISABLED.get());
      }

      // Invoke pre-operation plugins.
      if (!invokePreOpPlugins()) {
        return false;
      }

      if (authPolicyState.passwordMatches(simplePassword)) {
        setResultCode(ResultCode.SUCCESS);

        if (DirectoryServer.lockdownMode()
            && !ClientConnection.hasPrivilege(userEntry, BYPASS_LOCKDOWN)) {
          throw new DirectoryException(
              ResultCode.INVALID_CREDENTIALS, ERR_BIND_REJECTED_LOCKDOWN_MODE.get());
        }
        setAuthenticationInfo(
            new AuthenticationInfo(
                userEntry, getBindDN(), DirectoryServer.isRootDN(userEntry.getName())));

        // Set resource limits for the authenticated user.
        setResourceLimits(userEntry);
      } else {
        setResultCode(ResultCode.INVALID_CREDENTIALS);
        setAuthFailureReason(ERR_BIND_OPERATION_WRONG_PASSWORD.get());
      }
    }

    return true;
  }
Example #19
0
  /**
   * Parses the provided command-line arguments and makes the appropriate changes to the Directory
   * Server configuration.
   *
   * @param args The command-line arguments provided to this program.
   * @param outStream Output stream.
   * @param errStream Error stream.
   * @return The exit code from the configuration processing. A nonzero value indicates that there
   *     was some kind of problem during the configuration processing.
   */
  public static int configMain(String[] args, OutputStream outStream, OutputStream errStream) {
    BooleanArgument showUsage;
    BooleanArgument enableStartTLS;
    FileBasedArgument rootPasswordFile;
    StringArgument hostName;
    IntegerArgument ldapPort;
    IntegerArgument adminConnectorPort;
    IntegerArgument ldapsPort;
    IntegerArgument jmxPort;
    StringArgument baseDNString;
    StringArgument configClass;
    StringArgument configFile;
    StringArgument rootDNString;
    StringArgument rootPassword;
    StringArgument keyManagerProviderDN;
    StringArgument trustManagerProviderDN;
    StringArgument certNickName;
    StringArgument keyManagerPath;
    StringArgument serverRoot;
    PrintStream out, err;

    if (outStream != null) {
      out = new PrintStream(outStream);
    } else {
      out = NullOutputStream.printStream();
    }

    if (errStream != null) {
      err = new PrintStream(errStream);
    } else {
      err = NullOutputStream.printStream();
    }
    Message toolDescription = INFO_CONFIGDS_TOOL_DESCRIPTION.get();
    ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription, false);
    try {
      configFile =
          new StringArgument(
              "configfile",
              'c',
              "configFile",
              true,
              false,
              true,
              INFO_CONFIGFILE_PLACEHOLDER.get(),
              null,
              null,
              INFO_DESCRIPTION_CONFIG_FILE.get());
      configFile.setHidden(true);
      argParser.addArgument(configFile);

      configClass =
          new StringArgument(
              "configclass",
              OPTION_SHORT_CONFIG_CLASS,
              OPTION_LONG_CONFIG_CLASS,
              false,
              false,
              true,
              INFO_CONFIGCLASS_PLACEHOLDER.get(),
              ConfigFileHandler.class.getName(),
              null,
              INFO_DESCRIPTION_CONFIG_CLASS.get());
      configClass.setHidden(true);
      argParser.addArgument(configClass);

      String defaultHostName;
      try {
        defaultHostName = InetAddress.getLocalHost().getHostName();
      } catch (Exception e) {
        // Not much we can do here.
        defaultHostName = "localhost";
      }
      hostName =
          new StringArgument(
              OPTION_LONG_HOST.toLowerCase(),
              OPTION_SHORT_HOST,
              OPTION_LONG_HOST,
              false,
              false,
              true,
              INFO_HOST_PLACEHOLDER.get(),
              defaultHostName,
              null,
              INFO_INSTALLDS_DESCRIPTION_HOST_NAME.get());
      argParser.addArgument(hostName);

      ldapPort =
          new IntegerArgument(
              "ldapport",
              OPTION_SHORT_PORT,
              "ldapPort",
              false,
              false,
              true,
              INFO_LDAPPORT_PLACEHOLDER.get(),
              389,
              null,
              true,
              1,
              true,
              65535,
              INFO_CONFIGDS_DESCRIPTION_LDAP_PORT.get());
      argParser.addArgument(ldapPort);

      adminConnectorPort =
          new IntegerArgument(
              "adminConnectorPort".toLowerCase(),
              null,
              "adminConnectorPort",
              false,
              false,
              true,
              INFO_PORT_PLACEHOLDER.get(),
              4444,
              "adminConnectorPort",
              true,
              1,
              true,
              65535,
              INFO_INSTALLDS_DESCRIPTION_ADMINCONNECTORPORT.get());
      argParser.addArgument(adminConnectorPort);

      ldapsPort =
          new IntegerArgument(
              "ldapsPort",
              'P',
              "ldapsPort",
              false,
              false,
              true,
              INFO_LDAPPORT_PLACEHOLDER.get(),
              636,
              null,
              true,
              1,
              true,
              65535,
              INFO_CONFIGDS_DESCRIPTION_LDAPS_PORT.get());
      argParser.addArgument(ldapsPort);

      enableStartTLS =
          new BooleanArgument(
              "enableStartTLS",
              OPTION_SHORT_START_TLS,
              "enableStartTLS",
              INFO_CONFIGDS_DESCRIPTION_ENABLE_START_TLS.get());
      argParser.addArgument(enableStartTLS);

      jmxPort =
          new IntegerArgument(
              "jmxport",
              'x',
              "jmxPort",
              false,
              false,
              true,
              INFO_JMXPORT_PLACEHOLDER.get(),
              SetupUtils.getDefaultJMXPort(),
              null,
              true,
              1,
              true,
              65535,
              INFO_CONFIGDS_DESCRIPTION_JMX_PORT.get());
      argParser.addArgument(jmxPort);

      keyManagerProviderDN =
          new StringArgument(
              "keymanagerproviderdn",
              'k',
              "keyManagerProviderDN",
              false,
              false,
              true,
              INFO_KEY_MANAGER_PROVIDER_DN_PLACEHOLDER.get(),
              null,
              null,
              INFO_CONFIGDS_DESCRIPTION_KEYMANAGER_PROVIDER_DN.get());
      argParser.addArgument(keyManagerProviderDN);

      trustManagerProviderDN =
          new StringArgument(
              "trustmanagerproviderdn",
              't',
              "trustManagerProviderDN",
              false,
              false,
              true,
              INFO_TRUST_MANAGER_PROVIDER_DN_PLACEHOLDER.get(),
              null,
              null,
              INFO_CONFIGDS_DESCRIPTION_TRUSTMANAGER_PROVIDER_DN.get());
      argParser.addArgument(trustManagerProviderDN);

      keyManagerPath =
          new StringArgument(
              "keymanagerpath",
              'm',
              "keyManagerPath",
              false,
              false,
              true,
              INFO_KEY_MANAGER_PATH_PLACEHOLDER.get(),
              null,
              null,
              INFO_CONFIGDS_DESCRIPTION_KEYMANAGER_PATH.get());
      argParser.addArgument(keyManagerPath);

      certNickName =
          new StringArgument(
              "certnickname",
              'a',
              "certNickName",
              false,
              false,
              true,
              INFO_NICKNAME_PLACEHOLDER.get(),
              null,
              null,
              INFO_CONFIGDS_DESCRIPTION_CERTNICKNAME.get());
      argParser.addArgument(certNickName);

      baseDNString =
          new StringArgument(
              "basedn",
              OPTION_SHORT_BASEDN,
              OPTION_LONG_BASEDN,
              false,
              true,
              true,
              INFO_BASEDN_PLACEHOLDER.get(),
              "dc=example,dc=com",
              null,
              INFO_CONFIGDS_DESCRIPTION_BASE_DN.get());
      argParser.addArgument(baseDNString);

      rootDNString =
          new StringArgument(
              "rootdn",
              OPTION_SHORT_ROOT_USER_DN,
              OPTION_LONG_ROOT_USER_DN,
              false,
              false,
              true,
              INFO_ROOT_USER_DN_PLACEHOLDER.get(),
              "cn=Directory Manager",
              null,
              INFO_CONFIGDS_DESCRIPTION_ROOT_DN.get());
      argParser.addArgument(rootDNString);

      rootPassword =
          new StringArgument(
              "rootpw",
              OPTION_SHORT_BINDPWD,
              "rootPassword",
              false,
              false,
              true,
              INFO_ROOT_USER_PWD_PLACEHOLDER.get(),
              null,
              null,
              INFO_CONFIGDS_DESCRIPTION_ROOT_PW.get());
      argParser.addArgument(rootPassword);

      rootPasswordFile =
          new FileBasedArgument(
              "rootpwfile",
              OPTION_SHORT_BINDPWD_FILE,
              "rootPasswordFile",
              false,
              false,
              INFO_FILE_PLACEHOLDER.get(),
              null,
              null,
              INFO_CONFIGDS_DESCRIPTION_ROOT_PW_FILE.get());
      argParser.addArgument(rootPasswordFile);

      showUsage =
          new BooleanArgument(
              "showusage", OPTION_SHORT_HELP, OPTION_LONG_HELP, INFO_DESCRIPTION_USAGE.get());
      argParser.addArgument(showUsage);
      argParser.setUsageArgument(showUsage);

      serverRoot =
          new StringArgument(
              "serverRoot",
              ToolConstants.OPTION_SHORT_SERVER_ROOT,
              ToolConstants.OPTION_LONG_SERVER_ROOT,
              false,
              false,
              true,
              INFO_SERVER_ROOT_DIR_PLACEHOLDER.get(),
              null,
              null,
              null);
      serverRoot.setHidden(true);
      argParser.addArgument(serverRoot);
    } catch (ArgumentException ae) {
      Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return 1;
    }

    // Parse the command-line arguments provided to the program.
    try {
      argParser.parseArguments(args);
    } catch (ArgumentException ae) {
      Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());

      err.println(wrapText(message, MAX_LINE_WIDTH));
      err.println(argParser.getUsage());
      return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
    }

    // If we should just display usage or version information,
    // then print it and exit.
    if (argParser.usageOrVersionDisplayed()) {
      return 0;
    }

    // Make sure that the user actually tried to configure something.
    if (!(baseDNString.isPresent()
        || ldapPort.isPresent()
        || jmxPort.isPresent()
        || rootDNString.isPresent())) {
      Message message = ERR_CONFIGDS_NO_CONFIG_CHANGES.get();
      err.println(wrapText(message, MAX_LINE_WIDTH));
      err.println(argParser.getUsage());
      return 1;
    }

    try {
      Set<Integer> ports = new HashSet<Integer>();
      if (ldapPort.isPresent()) {
        ports.add(ldapPort.getIntValue());
      }
      if (adminConnectorPort.isPresent()) {
        if (ports.contains(adminConnectorPort.getIntValue())) {
          Message message =
              ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(
                  String.valueOf(adminConnectorPort.getIntValue()));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          err.println(argParser.getUsage());
          return 1;
        } else {
          ports.add(adminConnectorPort.getIntValue());
        }
      }
      if (ldapsPort.isPresent()) {
        if (ports.contains(ldapsPort.getIntValue())) {
          Message message =
              ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(String.valueOf(ldapsPort.getIntValue()));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          err.println(argParser.getUsage());
          return 1;
        } else {
          ports.add(ldapsPort.getIntValue());
        }
      }
      if (jmxPort.isPresent()) {
        if (ports.contains(jmxPort.getIntValue())) {
          Message message =
              ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(String.valueOf(jmxPort.getIntValue()));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          err.println(argParser.getUsage());
          return 1;
        } else {
          ports.add(jmxPort.getIntValue());
        }
      }
    } catch (ArgumentException ae) {
      Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return 1;
    }

    if (serverRoot.isPresent()) {
      DirectoryEnvironmentConfig env = DirectoryServer.getEnvironmentConfig();
      String root = serverRoot.getValue();
      try {
        env.setServerRoot(new File(serverRoot.getValue()));
      } catch (InitializationException e) {
        ERR_INITIALIZE_SERVER_ROOT.get(root, e.getMessageObject());
      }
    }

    // Initialize the Directory Server configuration handler using the
    // information that was provided.
    DirectoryServer directoryServer = DirectoryServer.getInstance();
    DirectoryServer.bootstrapClient();

    try {
      DirectoryServer.initializeJMX();
    } catch (Exception e) {
      Message message =
          ERR_CONFIGDS_CANNOT_INITIALIZE_JMX.get(
              String.valueOf(configFile.getValue()), e.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return 1;
    }

    try {
      directoryServer.initializeConfiguration(configClass.getValue(), configFile.getValue());
    } catch (Exception e) {
      Message message =
          ERR_CONFIGDS_CANNOT_INITIALIZE_CONFIG.get(
              String.valueOf(configFile.getValue()), e.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return 1;
    }

    try {
      directoryServer.initializeSchema();
    } catch (Exception e) {
      Message message =
          ERR_CONFIGDS_CANNOT_INITIALIZE_SCHEMA.get(
              String.valueOf(configFile.getValue()), e.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return 1;
    }

    // Make sure that we can get an exclusive lock for the Directory Server, so
    // that no other operation will be allowed while this is in progress.
    String serverLockFileName = LockFileManager.getServerLockFileName();
    StringBuilder failureReason = new StringBuilder();
    if (!LockFileManager.acquireExclusiveLock(serverLockFileName, failureReason)) {
      Message message =
          ERR_CONFIGDS_CANNOT_ACQUIRE_SERVER_LOCK.get(
              String.valueOf(serverLockFileName), String.valueOf(failureReason));
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return 1;
    }

    try {
      // If one or more base DNs were provided, then make sure that they can be
      // parsed as valid DNs.
      LinkedList<DN> baseDNs = null;
      if (baseDNString.isPresent()) {
        baseDNs = new LinkedList<DN>();
        for (String dnString : baseDNString.getValues()) {
          try {
            baseDNs.add(DN.decode(dnString));
          } catch (DirectoryException de) {
            Message message =
                ERR_CONFIGDS_CANNOT_PARSE_BASE_DN.get(
                    String.valueOf(dnString), de.getMessageObject());
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        }
      }

      // If a root user DN was provided, then make sure it can be parsed.  Also,
      // make sure that either a password or password file was specified.
      DN rootDN = null;
      String rootPW = null;
      if (rootDNString.isPresent()) {
        try {
          rootDN = DN.decode(rootDNString.getValue());
        } catch (DirectoryException de) {
          Message message =
              ERR_CONFIGDS_CANNOT_PARSE_ROOT_DN.get(
                  String.valueOf(rootDNString.getValue()), de.getMessageObject());
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }

        if (rootPassword.isPresent()) {
          rootPW = rootPassword.getValue();
        } else if (rootPasswordFile.isPresent()) {
          rootPW = rootPasswordFile.getValue();
        } else {
          Message message = ERR_CONFIGDS_NO_ROOT_PW.get();
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // Get the Directory Server configuration handler and use it to make the
      // appropriate configuration changes.
      ConfigHandler configHandler = DirectoryServer.getConfigHandler();

      // Check that the key manager provided is valid.
      if (keyManagerProviderDN.isPresent()) {
        DN dn = null;
        DN JCEKSProviderDN = null;
        try {
          dn = DN.decode(keyManagerProviderDN.getValue());
          JCEKSProviderDN = DN.decode("cn=JCEKS,cn=Key Manager Providers,cn=config");
        } catch (DirectoryException de) {
          Message message =
              ERR_CONFIGDS_CANNOT_PARSE_KEYMANAGER_PROVIDER_DN.get(
                  keyManagerProviderDN.getValue(), de.getMessageObject());
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }

        if (dn.equals(JCEKSProviderDN)) {
          // Create the JCEKSProvider entry
          try {
            String ldif =
                "dn: cn=JCEKS,cn=Key Manager Providers,cn=config\n"
                    + "objectClass: top\n"
                    + "objectClass: ds-cfg-key-manager-provider\n"
                    + "objectClass: ds-cfg-file-based-key-manager-provider\n"
                    + "cn: JCEKS\n"
                    + "ds-cfg-java-class: org.opends.server.extensions."
                    + "FileBasedKeyManagerProvider\n"
                    + "ds-cfg-enabled: true\n"
                    + "ds-cfg-key-store-type: JCEKS\n"
                    + "ds-cfg-key-store-file: config/keystore.jceks\n"
                    + "ds-cfg-key-store-pin-file: config/keystore.pin";

            LDIFImportConfig ldifImportConfig = new LDIFImportConfig(new StringReader(ldif));
            LDIFReader reader = new LDIFReader(ldifImportConfig);
            Entry providerConfigEntry;
            while ((providerConfigEntry = reader.readEntry()) != null) {
              configHandler.addEntry(providerConfigEntry, null);
            }
          } catch (Exception e) {
            Message message =
                ERR_CONFIG_KEYMANAGER_CANNOT_CREATE_JCEKS_PROVIDER.get(String.valueOf(e));
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        } else {
          try {
            configHandler.getConfigEntry(dn);
          } catch (Exception e) {
            Message message = ERR_CONFIG_KEYMANAGER_CANNOT_GET_BASE.get(String.valueOf(e));
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        }
      }

      // Check that the trust manager provided is valid.
      if (trustManagerProviderDN.isPresent()) {
        DN dn = null;
        DN JCEKSTrustManagerDN = null;
        try {
          dn = DN.decode(trustManagerProviderDN.getValue());
          JCEKSTrustManagerDN = DN.decode("cn=JCEKS,cn=Trust Manager Providers,cn=config");
        } catch (DirectoryException de) {
          Message message =
              ERR_CONFIGDS_CANNOT_PARSE_TRUSTMANAGER_PROVIDER_DN.get(
                  trustManagerProviderDN.getValue(), de.getMessageObject());
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }

        if (dn.equals(JCEKSTrustManagerDN)) {
          try {
            String ldif =
                "dn: cn=JCEKS,cn=Trust Manager Providers,cn=config\n"
                    + "objectClass: top\n"
                    + "objectClass: ds-cfg-trust-manager-provider\n"
                    + "objectClass: ds-cfg-file-based-trust-manager-provider\n"
                    + "cn: JCEKS\n"
                    + "ds-cfg-java-class: org.opends.server.extensions."
                    + "FileBasedTrustManagerProvider\n"
                    + "ds-cfg-enabled: false\n"
                    + "ds-cfg-trust-store-type: JCEKS\n"
                    + "ds-cfg-trust-store-file: config/truststore\n";

            LDIFImportConfig ldifImportConfig = new LDIFImportConfig(new StringReader(ldif));
            LDIFReader reader = new LDIFReader(ldifImportConfig);
            Entry trustManagerConfigEntry;
            while ((trustManagerConfigEntry = reader.readEntry()) != null) {
              configHandler.addEntry(trustManagerConfigEntry, null);
            }
          } catch (Exception e) {
            Message message = ERR_CONFIG_KEYMANAGER_CANNOT_GET_BASE.get(String.valueOf(e));
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        } else {
          try {
            configHandler.getConfigEntry(dn);
          } catch (Exception e) {
            Message message = ERR_CONFIG_TRUSTMANAGER_CANNOT_GET_BASE.get(String.valueOf(e));
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        }
      }

      // Check that the keystore path values are valid.
      if (keyManagerPath.isPresent()) {
        if (!keyManagerProviderDN.isPresent()) {
          Message message =
              ERR_CONFIGDS_KEYMANAGER_PROVIDER_DN_REQUIRED.get(
                  keyManagerProviderDN.getLongIdentifier(), keyManagerPath.getLongIdentifier());
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // If one or more base DNs were specified, then update the config
      // accordingly.
      if (baseDNs != null) {
        try {
          DN jeBackendDN = DN.decode(DN_JE_BACKEND);
          ConfigEntry configEntry = configHandler.getConfigEntry(jeBackendDN);

          DNConfigAttribute baseDNAttr =
              new DNConfigAttribute(
                  ATTR_BACKEND_BASE_DN,
                  INFO_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS.get(),
                  true,
                  true,
                  false,
                  baseDNs);
          configEntry.putConfigAttribute(baseDNAttr);
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_BASE_DN.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // If an LDAP port was specified, then update the config accordingly.
      if (ldapPort.isPresent()) {
        try {
          DN ldapListenerDN = DN.decode(DN_LDAP_CONNECTION_HANDLER);
          ConfigEntry configEntry = configHandler.getConfigEntry(ldapListenerDN);

          IntegerConfigAttribute portAttr =
              new IntegerConfigAttribute(
                  ATTR_LISTEN_PORT,
                  INFO_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_PORT.get(),
                  true,
                  false,
                  true,
                  true,
                  1,
                  true,
                  65535,
                  ldapPort.getIntValue());
          configEntry.putConfigAttribute(portAttr);
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_LDAP_PORT.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // If an Admin Connector port was specified, then update the config
      // accordingly.
      if (adminConnectorPort.isPresent()) {
        try {
          DN adminConnectorDN = DN.decode(DN_ADMIN_CONNECTOR);
          ConfigEntry configEntry = configHandler.getConfigEntry(adminConnectorDN);

          IntegerConfigAttribute portAttr =
              new IntegerConfigAttribute(
                  ATTR_LISTEN_PORT,
                  INFO_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_PORT.get(),
                  true,
                  false,
                  true,
                  true,
                  1,
                  true,
                  65535,
                  adminConnectorPort.getIntValue());
          configEntry.putConfigAttribute(portAttr);
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_ADMIN_CONNECTOR_PORT.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      //    If an LDAPS port was specified, then update the config accordingly.
      if (ldapsPort.isPresent()) {
        try {
          DN ldapListenerDN = DN.decode(DN_LDAPS_CONNECTION_HANDLER);
          ConfigEntry configEntry = configHandler.getConfigEntry(ldapListenerDN);

          IntegerConfigAttribute portAttr =
              new IntegerConfigAttribute(
                  ATTR_LISTEN_PORT,
                  INFO_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_PORT.get(),
                  true,
                  false,
                  true,
                  true,
                  1,
                  true,
                  65535,
                  ldapsPort.getIntValue());
          configEntry.putConfigAttribute(portAttr);

          BooleanConfigAttribute enablePortAttr =
              new BooleanConfigAttribute(
                  ATTR_CONNECTION_HANDLER_ENABLED,
                  INFO_LDAPS_CONNHANDLER_DESCRIPTION_ENABLE.get(),
                  true,
                  true);
          configEntry.putConfigAttribute(enablePortAttr);
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_LDAPS_PORT.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      //    If an JMX port was specified, then update the config accordingly.
      if (jmxPort.isPresent()) {
        try {
          DN jmxListenerDN = DN.decode(DN_JMX_CONNECTION_HANDLER);
          ConfigEntry configEntry = configHandler.getConfigEntry(jmxListenerDN);

          IntegerConfigAttribute portAttr =
              new IntegerConfigAttribute(
                  ATTR_LISTEN_PORT,
                  INFO_JMX_CONNHANDLER_DESCRIPTION_LISTEN_PORT.get(),
                  true,
                  false,
                  true,
                  true,
                  1,
                  true,
                  65535,
                  jmxPort.getIntValue());
          configEntry.putConfigAttribute(portAttr);

          BooleanConfigAttribute enablePortAttr =
              new BooleanConfigAttribute(
                  ATTR_CONNECTION_HANDLER_ENABLED,
                  INFO_JMX_CONNHANDLER_DESCRIPTION_ENABLE.get(),
                  true,
                  true);
          configEntry.putConfigAttribute(enablePortAttr);
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_JMX_PORT.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // Start TLS configuration
      if (enableStartTLS.isPresent()) {
        try {
          DN ldapListenerDN = DN.decode(DN_LDAP_CONNECTION_HANDLER);
          ConfigEntry configEntry = configHandler.getConfigEntry(ldapListenerDN);

          BooleanConfigAttribute startTLS =
              new BooleanConfigAttribute(
                  ATTR_ALLOW_STARTTLS,
                  INFO_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_STARTTLS.get(),
                  true,
                  true);
          configEntry.putConfigAttribute(startTLS);
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_ENABLE_STARTTLS.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // Key manager provider
      if (keyManagerProviderDN.isPresent()) {
        if (enableStartTLS.isPresent() || ldapsPort.isPresent()) {
          try {
            // Enable the key manager
            DN dn = DN.decode(keyManagerProviderDN.getValue());
            ConfigEntry configEntry = configHandler.getConfigEntry(dn);

            BooleanConfigAttribute enableAttr =
                new BooleanConfigAttribute(
                    ATTR_KEYMANAGER_ENABLED,
                    INFO_CONFIG_KEYMANAGER_DESCRIPTION_ENABLED.get(),
                    true,
                    true);
            configEntry.putConfigAttribute(enableAttr);
          } catch (Exception e) {
            Message message = ERR_CONFIGDS_CANNOT_ENABLE_KEYMANAGER.get(String.valueOf(e));
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        }

        try {
          if (enableStartTLS.isPresent()) {
            // Use the key manager specified for the LDAP connection handler.
            DN ldapListenerDN = DN.decode(DN_LDAP_CONNECTION_HANDLER);
            ConfigEntry configEntry = configHandler.getConfigEntry(ldapListenerDN);

            StringConfigAttribute keyManagerProviderAttr =
                new StringConfigAttribute(
                    ATTR_KEYMANAGER_DN,
                    INFO_LDAP_CONNHANDLER_DESCRIPTION_KEYMANAGER_DN.get(),
                    false,
                    false,
                    true,
                    keyManagerProviderDN.getValue());
            configEntry.putConfigAttribute(keyManagerProviderAttr);
          }

          if (ldapsPort.isPresent()) {
            // Use the key manager specified for the LDAPS connection handler.
            DN ldapsListenerDN = DN.decode(DN_LDAPS_CONNECTION_HANDLER);
            ConfigEntry configEntry = configHandler.getConfigEntry(ldapsListenerDN);

            StringConfigAttribute keyManagerProviderAttr =
                new StringConfigAttribute(
                    ATTR_KEYMANAGER_DN,
                    INFO_LDAP_CONNHANDLER_DESCRIPTION_KEYMANAGER_DN.get(),
                    false,
                    false,
                    true,
                    keyManagerProviderDN.getValue());
            configEntry.putConfigAttribute(keyManagerProviderAttr);
          }
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_KEYMANAGER_REFERENCE.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }

        if (keyManagerPath.isPresent()) {
          try {
            // Enable the key manager
            DN dn = DN.decode(keyManagerProviderDN.getValue());
            ConfigEntry configEntry = configHandler.getConfigEntry(dn);

            StringConfigAttribute pathAttr =
                new StringConfigAttribute(
                    ATTR_KEYSTORE_FILE,
                    INFO_FILE_KEYMANAGER_DESCRIPTION_FILE.get(),
                    true,
                    true,
                    true,
                    keyManagerPath.getValue());
            configEntry.putConfigAttribute(pathAttr);
          } catch (Exception e) {
            String message = String.valueOf(e);
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        }
      }
      if (trustManagerProviderDN.isPresent()) {
        if (enableStartTLS.isPresent() || ldapsPort.isPresent()) {
          // Enable the trust manager
          try {
            DN dn = DN.decode(trustManagerProviderDN.getValue());
            ConfigEntry configEntry = configHandler.getConfigEntry(dn);

            BooleanConfigAttribute enableAttr =
                new BooleanConfigAttribute(
                    ATTR_TRUSTMANAGER_ENABLED,
                    ERR_CONFIG_TRUSTMANAGER_DESCRIPTION_ENABLED.get(),
                    true,
                    true);
            configEntry.putConfigAttribute(enableAttr);
          } catch (Exception e) {
            Message message = ERR_CONFIGDS_CANNOT_ENABLE_TRUSTMANAGER.get(String.valueOf(e));
            err.println(wrapText(message, MAX_LINE_WIDTH));
            return 1;
          }
        }

        try {
          if (enableStartTLS.isPresent()) {
            // Use the trust manager specified for the LDAP connection handler.
            DN ldapListenerDN = DN.decode(DN_LDAP_CONNECTION_HANDLER);
            ConfigEntry configEntry = configHandler.getConfigEntry(ldapListenerDN);

            StringConfigAttribute trustManagerProviderAttr =
                new StringConfigAttribute(
                    ATTR_TRUSTMANAGER_DN,
                    INFO_LDAP_CONNHANDLER_DESCRIPTION_TRUSTMANAGER_DN.get(),
                    false,
                    false,
                    true,
                    trustManagerProviderDN.getValue());
            configEntry.putConfigAttribute(trustManagerProviderAttr);
          }

          if (ldapsPort.isPresent()) {
            // Use the trust manager specified for the LDAPS connection handler.
            DN ldapsListenerDN = DN.decode(DN_LDAPS_CONNECTION_HANDLER);
            ConfigEntry configEntry = configHandler.getConfigEntry(ldapsListenerDN);

            StringConfigAttribute trustManagerProviderAttr =
                new StringConfigAttribute(
                    ATTR_TRUSTMANAGER_DN,
                    INFO_LDAP_CONNHANDLER_DESCRIPTION_TRUSTMANAGER_DN.get(),
                    false,
                    false,
                    true,
                    trustManagerProviderDN.getValue());
            configEntry.putConfigAttribute(trustManagerProviderAttr);
          }
        } catch (Exception e) {
          Message message =
              ERR_CONFIGDS_CANNOT_UPDATE_TRUSTMANAGER_REFERENCE.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      if (certNickName.isPresent()) {
        try {
          StringConfigAttribute certNickNameAttr =
              new StringConfigAttribute(
                  ATTR_SSL_CERT_NICKNAME,
                  INFO_LDAP_CONNHANDLER_DESCRIPTION_SSL_CERT_NICKNAME.get(),
                  false,
                  false,
                  true,
                  certNickName.getValue());

          DN ldapListenerDN = DN.decode(DN_LDAP_CONNECTION_HANDLER);
          ConfigEntry configEntry = configHandler.getConfigEntry(ldapListenerDN);
          if (ldapPort.isPresent()) {
            // Use the key manager specified for the LDAP connection handler.
            configEntry.putConfigAttribute(certNickNameAttr);
          } else {
            configEntry.removeConfigAttribute(ATTR_SSL_CERT_NICKNAME.toLowerCase());
          }

          // Use the key manager specified for the LDAPS connection handler.
          DN ldapsListenerDN = DN.decode(DN_LDAPS_CONNECTION_HANDLER);
          configEntry = configHandler.getConfigEntry(ldapsListenerDN);
          if (ldapsPort.isPresent()) {
            configEntry.putConfigAttribute(certNickNameAttr);
          } else {
            configEntry.removeConfigAttribute(ATTR_SSL_CERT_NICKNAME.toLowerCase());
          }

          certNickNameAttr =
              new StringConfigAttribute(
                  ATTR_SSL_CERT_NICKNAME,
                  INFO_JMX_CONNHANDLER_DESCRIPTION_SSL_CERT_NICKNAME.get(),
                  false,
                  false,
                  true,
                  certNickName.getValue());

          // Use the key manager specified for the JMX connection handler.
          DN jmxListenerDN = DN.decode(DN_JMX_CONNECTION_HANDLER);
          configEntry = configHandler.getConfigEntry(jmxListenerDN);
          if (jmxPort.isPresent()) {
            configEntry.putConfigAttribute(certNickNameAttr);
          } else {
            configEntry.removeConfigAttribute(ATTR_SSL_CERT_NICKNAME.toLowerCase());
          }
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_CERT_NICKNAME.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      } else {
        try {
          // Use the key manager specified for the LDAP connection handler.
          DN ldapListenerDN = DN.decode(DN_LDAP_CONNECTION_HANDLER);
          ConfigEntry configEntry = configHandler.getConfigEntry(ldapListenerDN);

          configEntry.removeConfigAttribute(ATTR_SSL_CERT_NICKNAME.toLowerCase());

          // Use the key manager specified for the LDAPS connection handler.
          DN ldapsListenerDN = DN.decode(DN_LDAPS_CONNECTION_HANDLER);
          configEntry = configHandler.getConfigEntry(ldapsListenerDN);

          configEntry.removeConfigAttribute(ATTR_SSL_CERT_NICKNAME.toLowerCase());

          // Use the key manager specified for the JMX connection handler.
          DN jmxListenerDN = DN.decode(DN_JMX_CONNECTION_HANDLER);
          configEntry = configHandler.getConfigEntry(jmxListenerDN);

          configEntry.removeConfigAttribute(ATTR_SSL_CERT_NICKNAME.toLowerCase());
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_CERT_NICKNAME.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // If a root user DN and password were specified, then update the config
      // accordingly.
      if (rootDN != null) {
        try {
          DN rootUserDN = DN.decode(DN_ROOT_USER);
          ConfigEntry configEntry = configHandler.getConfigEntry(rootUserDN);

          DNConfigAttribute bindDNAttr =
              new DNConfigAttribute(
                  ATTR_ROOTDN_ALTERNATE_BIND_DN,
                  INFO_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN.get(),
                  false,
                  true,
                  false,
                  rootDN);
          configEntry.putConfigAttribute(bindDNAttr);

          byte[] rootPWBytes = getBytes(rootPW);
          String encodedPassword = SaltedSHA512PasswordStorageScheme.encodeOffline(rootPWBytes);
          StringConfigAttribute bindPWAttr =
              new StringConfigAttribute(
                  ATTR_USER_PASSWORD, Message.EMPTY, false, false, false, encodedPassword);
          configEntry.putConfigAttribute(bindPWAttr);
        } catch (Exception e) {
          Message message = ERR_CONFIGDS_CANNOT_UPDATE_ROOT_USER.get(String.valueOf(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return 1;
        }
      }

      // Set the FQDN for the DIGEST-MD5 SASL mechanism.
      try {
        DN digestMD5DN = DN.decode(DN_DIGEST_MD5_SASL_MECHANISM);
        ConfigEntry configEntry = configHandler.getConfigEntry(digestMD5DN);
        StringConfigAttribute fqdnAttr =
            new StringConfigAttribute(
                "ds-cfg-server-fqdn", Message.EMPTY, false, false, false, hostName.getValue());
        configEntry.putConfigAttribute(fqdnAttr);
      } catch (Exception e) {
        Message message = ERR_CONFIGDS_CANNOT_UPDATE_DIGEST_MD5_FQDN.get(String.valueOf(e));
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return 1;
      }

      // Check that the cipher specified is supported.  This is intended to
      // fix issues with JVM that do not support the default cipher (see
      // issue 3075 for instance).
      CryptoManagerCfgDefn cryptoManager = CryptoManagerCfgDefn.getInstance();
      StringPropertyDefinition prop =
          cryptoManager.getKeyWrappingTransformationPropertyDefinition();
      String defaultCipher = null;
      DefaultBehaviorProvider<?> p = prop.getDefaultBehaviorProvider();
      if (p instanceof DefinedDefaultBehaviorProvider) {
        Collection<?> defaultValues = ((DefinedDefaultBehaviorProvider<?>) p).getDefaultValues();
        if (!defaultValues.isEmpty()) {
          defaultCipher = defaultValues.iterator().next().toString();
        }
      }
      if (defaultCipher != null) {
        // Check that the default cipher is supported by the JVM.
        try {
          Cipher.getInstance(defaultCipher);
        } catch (GeneralSecurityException ex) {
          // The cipher is not supported: try to find an alternative one.
          String alternativeCipher = getAlternativeCipher();
          if (alternativeCipher != null) {
            try {
              DN cipherDN = DN.decode(DN_CRYPTO_MANAGER);
              ConfigEntry configEntry = configHandler.getConfigEntry(cipherDN);

              // Set the alternative cipher
              StringConfigAttribute keyWrappingTransformation =
                  new StringConfigAttribute(
                      ATTR_CRYPTO_CIPHER_KEY_WRAPPING_TRANSFORMATION,
                      Message.EMPTY,
                      false,
                      false,
                      true,
                      alternativeCipher);
              configEntry.putConfigAttribute(keyWrappingTransformation);
            } catch (Exception e) {
              Message message = ERR_CONFIGDS_CANNOT_UPDATE_CRYPTO_MANAGER.get(String.valueOf(e));
              err.println(wrapText(message, MAX_LINE_WIDTH));
              return 1;
            }
          }
        }
      }

      // Write the updated configuration.
      try {
        configHandler.writeUpdatedConfig();

        Message message = INFO_CONFIGDS_WROTE_UPDATED_CONFIG.get();
        out.println(wrapText(message, MAX_LINE_WIDTH));
      } catch (DirectoryException de) {
        Message message = ERR_CONFIGDS_CANNOT_WRITE_UPDATED_CONFIG.get(de.getMessageObject());
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return 1;
      }
    } finally {
      LockFileManager.releaseLock(serverLockFileName, failureReason);
    }

    // If we've gotten here, then everything was successful.
    return 0;
  }
  /**
   * Handles any controls contained in the request.
   *
   * @throws DirectoryException If there is a problem with any of the request controls.
   */
  private void handleRequestControls() throws DirectoryException {
    LocalBackendWorkflowElement.removeAllDisallowedControls(baseDN, this);

    List<Control> requestControls = getRequestControls();
    if (requestControls != null && !requestControls.isEmpty()) {
      for (Control c : requestControls) {
        String oid = c.getOID();

        if (OID_LDAP_ASSERTION.equals(oid)) {
          LDAPAssertionRequestControl assertControl =
              getRequestControl(LDAPAssertionRequestControl.DECODER);

          SearchFilter assertionFilter;
          try {
            assertionFilter = assertControl.getSearchFilter();
          } catch (DirectoryException de) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }

            throw new DirectoryException(
                de.getResultCode(),
                ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(de.getMessageObject()),
                de);
          }

          Entry entry;
          try {
            entry = DirectoryServer.getEntry(baseDN);
          } catch (DirectoryException de) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }

            throw new DirectoryException(
                de.getResultCode(),
                ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get(de.getMessageObject()));
          }

          if (entry == null) {
            throw new DirectoryException(
                ResultCode.NO_SUCH_OBJECT, ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get());
          }

          // Check if the current user has permission to make
          // this determination.
          if (!AccessControlConfigManager.getInstance()
              .getAccessControlHandler()
              .isAllowed(this, entry, assertionFilter)) {
            throw new DirectoryException(
                ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
                ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
          }

          try {
            if (!assertionFilter.matchesEntry(entry)) {
              throw new DirectoryException(
                  ResultCode.ASSERTION_FAILED, ERR_SEARCH_ASSERTION_FAILED.get());
            }
          } catch (DirectoryException de) {
            if (de.getResultCode() == ResultCode.ASSERTION_FAILED) {
              throw de;
            }

            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }

            throw new DirectoryException(
                de.getResultCode(),
                ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(de.getMessageObject()),
                de);
          }
        } else if (OID_PROXIED_AUTH_V1.equals(oid)) {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(
              AdditionalLogItem.keyOnly(getClass(), "obsoleteProxiedAuthzV1Control"));

          // The requester must have the PROXIED_AUTH privilege in order to be
          // able to use this control.
          if (!clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) {
            throw new DirectoryException(
                ResultCode.AUTHORIZATION_DENIED, ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }

          ProxiedAuthV1Control proxyControl = getRequestControl(ProxiedAuthV1Control.DECODER);

          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getDN(authorizationEntry));
        } else if (OID_PROXIED_AUTH_V2.equals(oid)) {
          // The requester must have the PROXIED_AUTH privilege in order to be
          // able to use this control.
          if (!clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) {
            throw new DirectoryException(
                ResultCode.AUTHORIZATION_DENIED, ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }

          ProxiedAuthV2Control proxyControl = getRequestControl(ProxiedAuthV2Control.DECODER);

          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getDN(authorizationEntry));
        } else if (OID_PERSISTENT_SEARCH.equals(oid)) {
          final PersistentSearchControl ctrl = getRequestControl(PersistentSearchControl.DECODER);

          persistentSearch =
              new PersistentSearch(
                  this, ctrl.getChangeTypes(), ctrl.getChangesOnly(), ctrl.getReturnECs());
        } else if (OID_LDAP_SUBENTRIES.equals(oid)) {
          SubentriesControl subentriesControl = getRequestControl(SubentriesControl.DECODER);
          setReturnSubentriesOnly(subentriesControl.getVisibility());
        } else if (OID_LDUP_SUBENTRIES.equals(oid)) {
          // Support for legacy draft-ietf-ldup-subentry.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(), "obsoleteSubentryControl"));

          setReturnSubentriesOnly(true);
        } else if (OID_MATCHED_VALUES.equals(oid)) {
          MatchedValuesControl matchedValuesControl =
              getRequestControl(MatchedValuesControl.DECODER);
          setMatchedValuesControl(matchedValuesControl);
        } else if (OID_ACCOUNT_USABLE_CONTROL.equals(oid)) {
          setIncludeUsableControl(true);
        } else if (OID_REAL_ATTRS_ONLY.equals(oid)) {
          setRealAttributesOnly(true);
        } else if (OID_VIRTUAL_ATTRS_ONLY.equals(oid)) {
          setVirtualAttributesOnly(true);
        } else if (OID_GET_EFFECTIVE_RIGHTS.equals(oid)
            && DirectoryServer.isSupportedControl(OID_GET_EFFECTIVE_RIGHTS)) {
          // Do nothing here and let AciHandler deal with it.
        }
        // NYI -- Add support for additional controls.

        else if (c.isCritical() && !backendSupportsControl(oid)) {
          throw new DirectoryException(
              ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
              ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
        }
      }
    }
  }
  private void processSearch(BooleanHolder executePostOpPlugins) throws CanceledOperationException {
    // Process the search base and filter to convert them from their raw forms
    // as provided by the client to the forms required for the rest of the
    // search processing.
    baseDN = getBaseDN();
    filter = getFilter();

    if (baseDN == null || filter == null) {
      return;
    }

    // Check to see if there are any controls in the request. If so, then
    // see if there is any special processing required.
    try {
      handleRequestControls();
    } catch (DirectoryException de) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, de);
      }

      setResponseData(de);
      return;
    }

    // Check to see if the client has permission to perform the
    // search.

    // FIXME: for now assume that this will check all permission
    // pertinent to the operation. This includes proxy authorization
    // and any other controls specified.
    try {
      if (!AccessControlConfigManager.getInstance().getAccessControlHandler().isAllowed(this)) {
        setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
        appendErrorMessage(ERR_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(String.valueOf(baseDN)));
        return;
      }
    } catch (DirectoryException e) {
      setResultCode(e.getResultCode());
      appendErrorMessage(e.getMessageObject());
      return;
    }

    // Check for a request to cancel this operation.
    checkIfCanceled(false);

    // Invoke the pre-operation search plugins.
    executePostOpPlugins.value = true;
    PluginResult.PreOperation preOpResult =
        DirectoryServer.getPluginConfigManager().invokePreOperationSearchPlugins(this);
    if (!preOpResult.continueProcessing()) {
      setResultCode(preOpResult.getResultCode());
      appendErrorMessage(preOpResult.getErrorMessage());
      setMatchedDN(preOpResult.getMatchedDN());
      setReferralURLs(preOpResult.getReferralURLs());
      return;
    }

    // Check for a request to cancel this operation.
    checkIfCanceled(false);

    // Get the backend that should hold the search base. If there is none,
    // then fail.
    if (backend == null) {
      setResultCode(ResultCode.NO_SUCH_OBJECT);
      appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(String.valueOf(baseDN)));
      return;
    }

    // We'll set the result code to "success". If a problem occurs, then it
    // will be overwritten.
    setResultCode(ResultCode.SUCCESS);

    try {
      // If there's a persistent search, then register it with the server.
      boolean processSearchNow = true;
      if (persistentSearch != null) {
        // If we're only interested in changes, then we do not actually want
        // to process the search now.
        processSearchNow = !persistentSearch.isChangesOnly();

        // The Core server maintains the count of concurrent persistent searches
        // so that all the backends (Remote and Local) are aware of it. Verify
        // with the core if we have already reached the threshold.
        if (!DirectoryServer.allowNewPersistentSearch()) {
          setResultCode(ResultCode.ADMIN_LIMIT_EXCEEDED);
          appendErrorMessage(ERR_MAX_PSEARCH_LIMIT_EXCEEDED.get());
          return;
        }
        backend.registerPersistentSearch(persistentSearch);
        persistentSearch.enable();
      }

      if (processSearchNow) {
        // Process the search in the backend and all its subordinates.
        backend.search(this);
      }
    } catch (DirectoryException de) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.VERBOSE, de);
      }

      setResponseData(de);

      if (persistentSearch != null) {
        persistentSearch.cancel();
        setSendResponse(true);
      }

      return;
    } catch (CanceledOperationException coe) {
      if (persistentSearch != null) {
        persistentSearch.cancel();
        setSendResponse(true);
      }

      throw coe;
    } catch (Exception e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }

      setResultCode(DirectoryServer.getServerErrorResultCode());
      appendErrorMessage(ERR_SEARCH_BACKEND_EXCEPTION.get(getExceptionMessage(e)));

      if (persistentSearch != null) {
        persistentSearch.cancel();
        setSendResponse(true);
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public LDIFImportResult importLDIF(
      LDIFImportConfig importConfig, RootContainer rootContainer, ServerContext serverContext)
      throws DirectoryException {
    try {
      ScheduledThreadPoolExecutor timerService = new ScheduledThreadPoolExecutor(1);
      try {
        final LDIFReader reader;
        try {
          reader = new LDIFReader(importConfig);
        } catch (Exception e) {
          LocalizableMessage m =
              ERR_LDIF_BACKEND_CANNOT_CREATE_LDIF_READER.get(stackTraceToSingleLineString(e));
          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), m, e);
        }

        long importCount = 0;
        final long startTime = System.currentTimeMillis();
        timerService.scheduleAtFixedRate(
            new ImportProgress(reader),
            IMPORT_PROGRESS_INTERVAL,
            IMPORT_PROGRESS_INTERVAL,
            TimeUnit.MILLISECONDS);
        while (true) {
          final Entry entry;
          try {
            entry = reader.readEntry();
            if (entry == null) {
              break;
            }
          } catch (LDIFException le) {
            if (!le.canContinueReading()) {
              LocalizableMessage m =
                  ERR_LDIF_BACKEND_ERROR_READING_LDIF.get(stackTraceToSingleLineString(le));
              throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), m, le);
            }
            continue;
          }

          final DN dn = entry.getName();
          final EntryContainer ec = rootContainer.getEntryContainer(dn);
          if (ec == null) {
            final LocalizableMessage m = ERR_LDIF_SKIP.get(dn);
            logger.error(m);
            reader.rejectLastEntry(m);
            continue;
          }

          try {
            ec.addEntry(entry, null);
            importCount++;
          } catch (DirectoryException e) {
            switch (e.getResultCode().asEnum()) {
              case ENTRY_ALREADY_EXISTS:
                if (importConfig.replaceExistingEntries()) {
                  final Entry oldEntry = ec.getEntry(entry.getName());
                  ec.replaceEntry(oldEntry, entry, null);
                } else {
                  reader.rejectLastEntry(WARN_IMPORT_ENTRY_EXISTS.get());
                }
                break;
              case NO_SUCH_OBJECT:
                reader.rejectLastEntry(ERR_IMPORT_PARENT_NOT_FOUND.get(dn.parent()));
                break;
              default:
                // Not sure why it failed.
                reader.rejectLastEntry(e.getMessageObject());
                break;
            }
          }
        }
        final long finishTime = System.currentTimeMillis();

        waitForShutdown(timerService);

        final long importTime = finishTime - startTime;
        float rate = 0;
        if (importTime > 0) {
          rate = 1000f * reader.getEntriesRead() / importTime;
        }
        logger.info(
            NOTE_IMPORT_FINAL_STATUS,
            reader.getEntriesRead(),
            importCount,
            reader.getEntriesIgnored(),
            reader.getEntriesRejected(),
            0,
            importTime / 1000,
            rate);
        return new LDIFImportResult(
            reader.getEntriesRead(), reader.getEntriesRejected(), reader.getEntriesIgnored());
      } finally {
        rootContainer.close();

        // if not already stopped, then stop it
        waitForShutdown(timerService);
      }
    } catch (DirectoryException e) {
      logger.traceException(e);
      throw e;
    } catch (OpenDsException e) {
      logger.traceException(e);
      throw new DirectoryException(getServerErrorResultCode(), e.getMessageObject());
    } catch (Exception e) {
      logger.traceException(e);
      throw new DirectoryException(
          getServerErrorResultCode(), LocalizableMessage.raw(e.getMessage()));
    }
  }
  /** Test PersistentSearchControl. */
  @Test(dataProvider = "persistentSearchControl")
  public void checkPersistentSearchControlTest(
      boolean isCritical, boolean changesOnly, boolean returnECs) throws Exception {
    // Test constructor
    // CheclPersistentSearchControlTest(Set<PersistentSearchChangeType>
    // changeTypes, boolean changesOnly, boolean returnECs
    for (int i = 1; i <= 15; i++) {
      Set<PersistentSearchChangeType> returnTypes = PersistentSearchChangeType.intToTypes(i);
      PersistentSearchControl psc =
          new PersistentSearchControl(returnTypes, changesOnly, returnECs);
      assertNotNull(psc);
      assertEquals(changesOnly, psc.getChangesOnly());
      assertEquals(returnECs, psc.getReturnECs());
      assertEquals(returnTypes.size(), psc.getChangeTypes().size());
      assertEquals(OID_PERSISTENT_SEARCH, psc.getOID());
    }

    // Test constructor
    // CString oid, boolean isCritical,
    // Set<PersistentSearchChangeType> changeTypes,
    //    boolean changesOnly, boolean returnECs
    for (int i = 1; i <= 15; i++) {
      Set<PersistentSearchChangeType> returnTypes = PersistentSearchChangeType.intToTypes(i);
      PersistentSearchControl psc =
          new PersistentSearchControl(isCritical, returnTypes, changesOnly, returnECs);
      assertNotNull(psc);
      assertEquals(isCritical, psc.isCritical());
      assertEquals(OID_PERSISTENT_SEARCH, psc.getOID());
      assertEquals(changesOnly, psc.getChangesOnly());
      assertEquals(returnECs, psc.getReturnECs());
      assertEquals(returnTypes.size(), psc.getChangeTypes().size());
    }

    // Test encode/decode
    ByteStringBuilder bsb = new ByteStringBuilder();
    ASN1Writer writer = ASN1.getWriter(bsb);
    for (int i = 1; i <= 15; i++) {
      bsb.clear();
      Set<PersistentSearchChangeType> returnTypes = PersistentSearchChangeType.intToTypes(i);
      PersistentSearchControl psc =
          new PersistentSearchControl(isCritical, returnTypes, changesOnly, returnECs);
      psc.write(writer);
      LDAPControl control = LDAPReader.readControl(ASN1.getReader(bsb));
      psc = PersistentSearchControl.DECODER.decode(control.isCritical(), control.getValue());
      assertNotNull(psc);
      assertEquals(isCritical, psc.isCritical());
      assertEquals(OID_PERSISTENT_SEARCH, psc.getOID());
      assertEquals(changesOnly, psc.getChangesOnly());
      assertEquals(returnECs, psc.getReturnECs());
      assertEquals(returnTypes.size(), psc.getChangeTypes().size());

      // Check the toString
      String changeTypes = PersistentSearchChangeType.changeTypesToString(psc.getChangeTypes());
      String toString =
          "PersistentSearchControl(changeTypes=\""
              + changeTypes
              + "\",changesOnly="
              + psc.getChangesOnly()
              + ",returnECs="
              + psc.getReturnECs()
              + ")";
      assertEquals(psc.toString(), toString);

      // check null value for the control
      try {
        control = new LDAPControl(OID_PERSISTENT_SEARCH, isCritical);
        psc = PersistentSearchControl.DECODER.decode(control.isCritical(), control.getValue());
        fail();
      } catch (DirectoryException expected) {
        assertEquals(expected.getMessage(), CANNOT_DECODE_PERSISTENT_SEARCH_CONTROL_NO_VALUE);
      }

      // check invalid value for the control
      try {
        control =
            new LDAPControl(OID_PERSISTENT_SEARCH, isCritical, ByteString.valueOf("invalid value"));
        psc = PersistentSearchControl.DECODER.decode(control.isCritical(), control.getValue());
        fail();
      } catch (DirectoryException expected) {
        assertThat(expected.getMessage())
            .contains("Cannot decode the provided persistent search control");
      }
    }
  }
  /** Test EntryChangeNotificationControl. */
  @Test(dataProvider = "entryChangeNotificationControl")
  public void checkEntryChangeNotificationControlTest(
      boolean isCritical, long changeNumber, String dnString) throws Exception {
    // Test constructor EntryChangeNotificationControl
    // (PersistentSearchChangeType changeType,long changeNumber)
    PersistentSearchChangeType[] types = PersistentSearchChangeType.values();
    EntryChangeNotificationControl ecnc = null;
    EntryChangeNotificationControl newEcnc;
    ByteStringBuilder bsb = new ByteStringBuilder();
    ASN1Writer writer = ASN1.getWriter(bsb);
    for (PersistentSearchChangeType type : types) {
      ecnc = new EntryChangeNotificationControl(type, changeNumber);
      assertNotNull(ecnc);
      assertEquals(OID_ENTRY_CHANGE_NOTIFICATION, ecnc.getOID());
      assertEquals(changeNumber, ecnc.getChangeNumber());
      assertEquals(type, ecnc.getChangeType());
      assertNull(ecnc.getPreviousDN());
      assertEquals(false, ecnc.isCritical());
      checkEntryChangeNotificationControlToString(ecnc);

      // also check encode/decode
      try {
        bsb.clear();
        ecnc.write(writer);
        LDAPControl control = LDAPReader.readControl(ASN1.getReader(bsb));
        newEcnc =
            EntryChangeNotificationControl.DECODER.decode(control.isCritical(), control.getValue());
        assertNotNull(newEcnc);
        assertEquals(ecnc.getOID(), newEcnc.getOID());
        assertEquals(ecnc.getChangeNumber(), newEcnc.getChangeNumber());
        assertEquals(ecnc.getChangeType(), newEcnc.getChangeType());
        assertNull(newEcnc.getPreviousDN());
        assertEquals(ecnc.isCritical(), newEcnc.isCritical());
      } catch (DirectoryException e) {
        fail();
      }
    }

    // Test constructor EntryChangeNotificationControl
    // (PersistentSearchChangeType changeType, DN previousDN, long
    // changeNumber)
    DN dn = DN.valueOf(dnString);
    for (PersistentSearchChangeType type : types) {
      ecnc = new EntryChangeNotificationControl(type, dn, changeNumber);
      assertNotNull(ecnc);
      assertEquals(OID_ENTRY_CHANGE_NOTIFICATION, ecnc.getOID());
      assertEquals(changeNumber, ecnc.getChangeNumber());
      assertEquals(type, ecnc.getChangeType());
      assertEquals(dn, ecnc.getPreviousDN());
      assertEquals(false, ecnc.isCritical());
      checkEntryChangeNotificationControlToString(ecnc);

      // also check encode/decode
      try {
        bsb.clear();
        ecnc.write(writer);
        LDAPControl control = LDAPReader.readControl(ASN1.getReader(bsb));
        newEcnc =
            EntryChangeNotificationControl.DECODER.decode(control.isCritical(), control.getValue());
        assertNotNull(newEcnc);
        assertEquals(ecnc.getOID(), newEcnc.getOID());
        assertEquals(ecnc.getChangeNumber(), newEcnc.getChangeNumber());
        assertEquals(ecnc.getChangeType(), newEcnc.getChangeType());
        assertEquals(ecnc.getPreviousDN(), newEcnc.getPreviousDN());
        assertEquals(ecnc.isCritical(), newEcnc.isCritical());
      } catch (DirectoryException e) {
        assertNotEquals(
            type.compareTo(MODIFY_DN),
            0,
            "couldn't decode a control with previousDN not null and type=modDN");
      }
    }

    // Test constructor EntryChangeNotificationControl(boolean
    // isCritical, PersistentSearchChangeType changeType,
    // DN previousDN, long changeNumber)
    for (PersistentSearchChangeType type : types) {
      ecnc = new EntryChangeNotificationControl(isCritical, type, dn, changeNumber);
      assertNotNull(ecnc);
      assertEquals(OID_ENTRY_CHANGE_NOTIFICATION, ecnc.getOID());
      assertEquals(changeNumber, ecnc.getChangeNumber());
      assertEquals(type, ecnc.getChangeType());
      assertEquals(dn, ecnc.getPreviousDN());
      assertEquals(isCritical, ecnc.isCritical());
      checkEntryChangeNotificationControlToString(ecnc);

      // also check encode/decode
      try {
        bsb.clear();
        ecnc.write(writer);
        LDAPControl control = LDAPReader.readControl(ASN1.getReader(bsb));
        newEcnc =
            EntryChangeNotificationControl.DECODER.decode(control.isCritical(), control.getValue());
        assertNotNull(newEcnc);
        assertEquals(ecnc.getOID(), newEcnc.getOID());
        assertEquals(ecnc.getChangeNumber(), newEcnc.getChangeNumber());
        assertEquals(ecnc.getChangeType(), newEcnc.getChangeType());
        assertEquals(ecnc.getPreviousDN(), newEcnc.getPreviousDN());
        assertEquals(ecnc.isCritical(), newEcnc.isCritical());
      } catch (DirectoryException e) {
        assertNotEquals(
            type.compareTo(PersistentSearchChangeType.MODIFY_DN),
            0,
            "couldn't decode a control with previousDN not null and type=modDN");
      }
    }

    // Check error on decode
    try {
      LDAPControl control = new LDAPControl(OID_ENTRY_CHANGE_NOTIFICATION, isCritical);
      newEcnc =
          EntryChangeNotificationControl.DECODER.decode(control.isCritical(), control.getValue());
      fail();
    } catch (DirectoryException expected) {
      assertEquals(expected.getMessage(), CANNOT_DECODE_CHANGE_NOTIF_CONTROL_NO_VALUE);
    }
  }