/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2015 ForgeRock AS
 */
package org.opends.server.tasks;

import static org.opends.messages.TaskMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.core.DirectoryServer.*;
import static org.opends.server.util.StaticUtils.*;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.messages.Severity;
import org.opends.messages.TaskMessages;
import org.opends.server.api.Backend;
import org.opends.server.api.Backend.BackendOperation;
import org.opends.server.api.ClientConnection;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.LockFileManager;
import org.opends.server.tools.makeldif.TemplateFile;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.Operation;
import org.opends.server.types.Privilege;
import org.opends.server.types.SearchFilter;

/**
 * This class provides an implementation of a Directory Server task that can
 * be used to import data from an LDIF file into a backend.
 */
public class ImportTask extends Task
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();

  /** Stores mapping between configuration attribute name and its label. */
  private static final Map<String, LocalizableMessage> argDisplayMap = new HashMap<>();
  static
  {
    argDisplayMap.put(ATTR_IMPORT_LDIF_FILE, INFO_IMPORT_ARG_LDIF_FILE.get());
    argDisplayMap.put(ATTR_IMPORT_TEMPLATE_FILE, INFO_IMPORT_ARG_TEMPLATE_FILE.get());
    argDisplayMap.put(ATTR_IMPORT_RANDOM_SEED, INFO_IMPORT_ARG_RANDOM_SEED.get());
    argDisplayMap.put(ATTR_IMPORT_BACKEND_ID, INFO_IMPORT_ARG_BACKEND_ID.get());
    argDisplayMap.put(ATTR_IMPORT_INCLUDE_BRANCH, INFO_IMPORT_ARG_INCL_BRANCH.get());
    argDisplayMap.put(ATTR_IMPORT_EXCLUDE_BRANCH, INFO_IMPORT_ARG_EXCL_BRANCH.get());
    argDisplayMap.put(ATTR_IMPORT_INCLUDE_ATTRIBUTE, INFO_IMPORT_ARG_INCL_ATTR.get());
    argDisplayMap.put(ATTR_IMPORT_EXCLUDE_ATTRIBUTE, INFO_IMPORT_ARG_EXCL_ATTR.get());
    argDisplayMap.put(ATTR_IMPORT_INCLUDE_FILTER, INFO_IMPORT_ARG_INCL_FILTER.get());
    argDisplayMap.put(ATTR_IMPORT_EXCLUDE_FILTER, INFO_IMPORT_ARG_EXCL_FILTER.get());
    argDisplayMap.put(ATTR_IMPORT_REJECT_FILE, INFO_IMPORT_ARG_REJECT_FILE.get());
    argDisplayMap.put(ATTR_IMPORT_SKIP_FILE, INFO_IMPORT_ARG_SKIP_FILE.get());
    argDisplayMap.put(ATTR_IMPORT_OVERWRITE, INFO_IMPORT_ARG_OVERWRITE.get());
    argDisplayMap.put(ATTR_IMPORT_SKIP_SCHEMA_VALIDATION, INFO_IMPORT_ARG_SKIP_SCHEMA_VALIDATION.get());
    argDisplayMap.put(ATTR_IMPORT_IS_COMPRESSED, INFO_IMPORT_ARG_IS_COMPRESSED.get());
    argDisplayMap.put(ATTR_IMPORT_IS_ENCRYPTED, INFO_IMPORT_ARG_IS_ENCRYPTED.get());
    argDisplayMap.put(ATTR_IMPORT_CLEAR_BACKEND, INFO_IMPORT_ARG_CLEAR_BACKEND.get());
  }


  private boolean isCompressed;
  private boolean isEncrypted;
  private boolean overwrite;
  private boolean skipSchemaValidation;
  private boolean clearBackend;
  private boolean skipDNValidation;
  private String tmpDirectory;
  private int threadCount;
  private String backendID;
  private String rejectFile;
  private String skipFile;
  private ArrayList<String> excludeAttributeStrings;
  private ArrayList<String> excludeBranchStrings;
  private ArrayList<String> excludeFilterStrings;
  private ArrayList<String> includeAttributeStrings;
  private ArrayList<String> includeBranchStrings;
  private ArrayList<String> includeFilterStrings;
  private ArrayList<String> ldifFiles;
  private String templateFile;
  private int randomSeed;
  private LDIFImportConfig importConfig;

  @Override
  public LocalizableMessage getDisplayName() {
    return INFO_TASK_IMPORT_NAME.get();
  }

  @Override
  public LocalizableMessage getAttributeDisplayName(String name) {
    return argDisplayMap.get(name);
  }

  @Override public void initializeTask() throws DirectoryException
  {
    // If the client connection is available, then make sure the associated
    // client has the LDIF_IMPORT privilege.
    Operation operation = getOperation();
    if (operation != null)
    {
      ClientConnection clientConnection = operation.getClientConnection();
      if (! clientConnection.hasPrivilege(Privilege.LDIF_IMPORT, operation))
      {
        LocalizableMessage message = ERR_TASK_LDIFIMPORT_INSUFFICIENT_PRIVILEGES.get();
        throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message);
      }
    }


    Entry taskEntry = getTaskEntry();

    AttributeType typeLdifFile = getAttributeTypeOrDefault(ATTR_IMPORT_LDIF_FILE);
    AttributeType typeTemplateFile = getAttributeTypeOrDefault(ATTR_IMPORT_TEMPLATE_FILE);
    AttributeType typeBackendID = getAttributeTypeOrDefault(ATTR_IMPORT_BACKEND_ID);
    AttributeType typeIncludeBranch = getAttributeTypeOrDefault(ATTR_IMPORT_INCLUDE_BRANCH);
    AttributeType typeExcludeBranch = getAttributeTypeOrDefault(ATTR_IMPORT_EXCLUDE_BRANCH);
    AttributeType typeIncludeAttribute = getAttributeTypeOrDefault(ATTR_IMPORT_INCLUDE_ATTRIBUTE);
    AttributeType typeExcludeAttribute = getAttributeTypeOrDefault(ATTR_IMPORT_EXCLUDE_ATTRIBUTE);
    AttributeType typeIncludeFilter = getAttributeTypeOrDefault(ATTR_IMPORT_INCLUDE_FILTER);
    AttributeType typeExcludeFilter = getAttributeTypeOrDefault(ATTR_IMPORT_EXCLUDE_FILTER);
    AttributeType typeRejectFile = getAttributeTypeOrDefault(ATTR_IMPORT_REJECT_FILE);
    AttributeType typeSkipFile = getAttributeTypeOrDefault(ATTR_IMPORT_SKIP_FILE);
    AttributeType typeOverwrite = getAttributeTypeOrDefault(ATTR_IMPORT_OVERWRITE);
    AttributeType typeSkipSchemaValidation = getAttributeTypeOrDefault(ATTR_IMPORT_SKIP_SCHEMA_VALIDATION);
    AttributeType typeIsCompressed = getAttributeTypeOrDefault(ATTR_IMPORT_IS_COMPRESSED);
    AttributeType typeIsEncrypted = getAttributeTypeOrDefault(ATTR_IMPORT_IS_ENCRYPTED);
    AttributeType typeClearBackend = getAttributeTypeOrDefault(ATTR_IMPORT_CLEAR_BACKEND);
    AttributeType typeRandomSeed = getAttributeTypeOrDefault(ATTR_IMPORT_RANDOM_SEED);
    AttributeType typeThreadCount = getAttributeTypeOrDefault(ATTR_IMPORT_THREAD_COUNT);
    AttributeType typeTmpDirectory = getAttributeTypeOrDefault(ATTR_IMPORT_TMP_DIRECTORY);
    AttributeType typeDNCheckPhase2 = getAttributeTypeOrDefault(ATTR_IMPORT_SKIP_DN_VALIDATION);

    ArrayList<String> ldifFilestmp = asListOfStrings(taskEntry, typeLdifFile);
    ldifFiles = new ArrayList<>(ldifFilestmp.size());
    for (String s : ldifFilestmp)
    {
      File f = new File (s);
      if (!f.isAbsolute())
      {
        f = new File(DirectoryServer.getInstanceRoot(), s);
        try
        {
          s = f.getCanonicalPath();
        }
        catch (Exception ex)
        {
          s = f.getAbsolutePath();
        }
      }
      if (!f.canRead()) {
        LocalizableMessage message = ERR_LDIFIMPORT_LDIF_FILE_DOESNT_EXIST.get(s);
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
      ldifFiles.add(s);
    }

    templateFile = asString(taskEntry, typeTemplateFile);
    if (templateFile != null)
    {
      File f = new File(templateFile);
      if (!f.isAbsolute())
      {
        templateFile = new File(DirectoryServer.getInstanceRoot(), templateFile).getAbsolutePath();
      }
    }

    skipDNValidation = asBoolean(taskEntry, typeDNCheckPhase2);
    tmpDirectory = asString(taskEntry, typeTmpDirectory);
    backendID = asString(taskEntry, typeBackendID);
    includeBranchStrings = asListOfStrings(taskEntry, typeIncludeBranch);
    excludeBranchStrings = asListOfStrings(taskEntry, typeExcludeBranch);
    includeAttributeStrings = asListOfStrings(taskEntry, typeIncludeAttribute);
    excludeAttributeStrings = asListOfStrings(taskEntry, typeExcludeAttribute);
    includeFilterStrings = asListOfStrings(taskEntry, typeIncludeFilter);
    excludeFilterStrings = asListOfStrings(taskEntry, typeExcludeFilter);
    rejectFile = asString(taskEntry, typeRejectFile);
    skipFile = asString(taskEntry, typeSkipFile);
    overwrite = asBoolean(taskEntry, typeOverwrite);
    skipSchemaValidation = asBoolean(taskEntry, typeSkipSchemaValidation);
    isCompressed = asBoolean(taskEntry, typeIsCompressed);
    isEncrypted = asBoolean(taskEntry, typeIsEncrypted);
    clearBackend = asBoolean(taskEntry, typeClearBackend);
    randomSeed = asInt(taskEntry, typeRandomSeed);
    threadCount = asInt(taskEntry, typeThreadCount);

    // Make sure that either the "includeBranchStrings" argument or the
    // "backendID" argument was provided.
    if(includeBranchStrings.isEmpty() && backendID == null)
    {
      LocalizableMessage message = ERR_LDIFIMPORT_MISSING_BACKEND_ARGUMENT.get(
          typeIncludeBranch.getNameOrOID(), typeBackendID.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
    }

    Backend<?> backend = null;
    ArrayList<DN> defaultIncludeBranches;
    HashSet<DN> excludeBranches = new HashSet<>(excludeBranchStrings.size());
    HashSet<DN> includeBranches = new HashSet<>(includeBranchStrings.size());

    for (String s : includeBranchStrings)
    {
      DN includeBranch;
      try
      {
        includeBranch = DN.valueOf(s);
      }
      catch (DirectoryException de)
      {
        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
            s, de.getMessageObject());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
      catch (Exception e)
      {
        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
            s, getExceptionMessage(e));
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }

      includeBranches.add(includeBranch);
    }
    for (String s : excludeBranchStrings)
    {
      DN excludeBranch;
      try
      {
        excludeBranch = DN.valueOf(s);
      }
      catch (DirectoryException de)
      {
        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
            s, de.getMessageObject());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
      catch (Exception e)
      {
        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
            s, getExceptionMessage(e));
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }

      excludeBranches.add(excludeBranch);
    }

    for (String filterString : excludeFilterStrings)
    {
      try
      {
        SearchFilter.createFilterFromString(filterString);
      }
      catch (DirectoryException de)
      {
        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER.get(
            filterString, de.getMessageObject());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
    }

    for (String filterString : includeFilterStrings)
    {
      try
      {
        SearchFilter.createFilterFromString(filterString);
      }
      catch (DirectoryException de)
      {
        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER.get(
            filterString, de.getMessageObject());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
    }

    if(backendID != null)
    {
      backend = DirectoryServer.getBackend(backendID);
      if (backend == null)
      {
        LocalizableMessage message = ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID.get();
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
      else if (!backend.supports(BackendOperation.LDIF_IMPORT))
      {
        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_IMPORT.get(backendID);
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
    }
    else
    {
      // Find the backend that includes all the branches.
      for(DN includeBranch : includeBranches)
      {
        Backend<?> locatedBackend = DirectoryServer.getBackend(includeBranch);
        if(locatedBackend != null)
        {
          if(backend == null)
          {
            backend = locatedBackend;
          }
          else if(backend != locatedBackend)
          {
            // The include branches span across multiple backends.
            LocalizableMessage message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
                includeBranch, backend.getBackendID());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
          }
        }
        else
        {
          // The include branch is not associated with any backend.
          LocalizableMessage message = ERR_NO_BACKENDS_FOR_BASE.get(includeBranch);
          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
        }
      }
    }

    // Make sure the selected backend will handle all the include branches
    defaultIncludeBranches = new ArrayList<>(backend.getBaseDNs().length);
    Collections.addAll(defaultIncludeBranches, backend.getBaseDNs());

    for(DN includeBranch : includeBranches)
    {
      if (!Backend.handlesEntry(includeBranch, defaultIncludeBranches, excludeBranches))
      {
        LocalizableMessage message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
            includeBranch, backend.getBackendID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
    }
  }

  private int asInt(Entry taskEntry, AttributeType attributeType)
  {
    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
    return TaskUtils.getSingleValueInteger(attrList, 0);
  }

  private boolean asBoolean(Entry taskEntry, AttributeType attributeType)
  {
    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
    return TaskUtils.getBoolean(attrList, false);
  }

  private String asString(Entry taskEntry, AttributeType attributeType)
  {
    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
    return TaskUtils.getSingleValueString(attrList);
  }

  private ArrayList<String> asListOfStrings(Entry taskEntry, AttributeType attributeType)
  {
    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
    return TaskUtils.getMultiValueString(attrList);
  }

  @Override
  public void interruptTask(TaskState interruptState, LocalizableMessage interruptReason)
  {
    if (TaskState.STOPPED_BY_ADMINISTRATOR.equals(interruptState) && importConfig != null)
    {
      addLogMessage(Severity.INFORMATION, TaskMessages.INFO_TASK_STOPPED_BY_ADMIN.get(
      interruptReason));
      setTaskInterruptState(interruptState);
      importConfig.cancel();
    }
  }

  @Override
  public boolean isInterruptable()
  {
    return true;
  }

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

    ArrayList<SearchFilter> excludeFilters = new ArrayList<>(excludeFilterStrings.size());
    for (String filterString : excludeFilterStrings)
    {
      try
      {
        excludeFilters.add(SearchFilter.createFilterFromString(filterString));
      }
      catch (DirectoryException de)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER, filterString, de.getMessageObject());
        return TaskState.STOPPED_BY_ERROR;
      }
    }

    ArrayList<SearchFilter> includeFilters = new ArrayList<>(includeFilterStrings.size());
    for (String filterString : includeFilterStrings)
    {
      try
      {
        includeFilters.add(SearchFilter.createFilterFromString(filterString));
      }
      catch (DirectoryException de)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER, filterString, de.getMessageObject());
        return TaskState.STOPPED_BY_ERROR;
      }
    }


    // Get the backend into which the LDIF should be imported.
    Backend<?> backend = null;
    HashSet<DN> defaultIncludeBranches;
    HashSet<DN> excludeBranches = new HashSet<>(excludeBranchStrings.size());
    HashSet<DN> includeBranches = new HashSet<>(includeBranchStrings.size());

    for (String s : includeBranchStrings)
    {
      DN includeBranch;
      try
      {
        includeBranch = DN.valueOf(s);
      }
      catch (DirectoryException de)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, de.getMessageObject());
        return TaskState.STOPPED_BY_ERROR;
      }
      catch (Exception e)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, getExceptionMessage(e));
        return TaskState.STOPPED_BY_ERROR;
      }

      includeBranches.add(includeBranch);
    }

    if(backendID != null)
    {
      backend = DirectoryServer.getBackend(backendID);

      if (backend == null)
      {
        logger.error(ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID);
        return TaskState.STOPPED_BY_ERROR;
      }
      else if (!backend.supports(BackendOperation.LDIF_IMPORT))
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_IMPORT, backendID);
        return TaskState.STOPPED_BY_ERROR;
      }
    }
    else
    {
      // Find the backend that includes all the branches.
      for(DN includeBranch : includeBranches)
      {
        Backend<?> locatedBackend = DirectoryServer.getBackend(includeBranch);
        if(locatedBackend != null)
        {
          if(backend == null)
          {
            backend = locatedBackend;
          }
          else if(backend != locatedBackend)
          {
            // The include branches span across multiple backends.
            logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch, backend.getBackendID());
            return TaskState.STOPPED_BY_ERROR;
          }
        }
      }
    }

    // Find backends with subordinate base DNs that should be excluded from the import.
    defaultIncludeBranches = new HashSet<>(backend.getBaseDNs().length);
    Collections.addAll(defaultIncludeBranches, backend.getBaseDNs());

    if (backend.getSubordinateBackends() != null)
    {
      for (Backend<?> subBackend : backend.getSubordinateBackends())
      {
        for (DN baseDN : subBackend.getBaseDNs())
        {
          for (DN importBase : defaultIncludeBranches)
          {
            if (!baseDN.equals(importBase) && baseDN.isDescendantOf(importBase))
            {
              excludeBranches.add(baseDN);
              break;
            }
          }
        }
      }
    }

    for (String s : excludeBranchStrings)
    {
      DN excludeBranch;
      try
      {
        excludeBranch = DN.valueOf(s);
      }
      catch (DirectoryException de)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE, s, de.getMessageObject());
        return TaskState.STOPPED_BY_ERROR;
      }
      catch (Exception e)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE, s, getExceptionMessage(e));
        return TaskState.STOPPED_BY_ERROR;
      }

      excludeBranches.add(excludeBranch);
    }

    if (includeBranchStrings.isEmpty())
    {
      includeBranches = defaultIncludeBranches;
    }
    else
    {
      // Make sure the selected backend will handle all the include branches
      for (DN includeBranch : includeBranches)
      {
        if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches,
                                   excludeBranches))
        {
          logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch, backend.getBackendID());
          return TaskState.STOPPED_BY_ERROR;
        }
      }
    }

    // Create the LDIF import configuration to use when reading the LDIF.
    if (templateFile != null)
    {
      Random random;
      try
      {
        random = new Random(randomSeed);
      }
      catch (Exception e)
      {
        random = new Random();
      }

      String resourcePath = DirectoryServer.getInstanceRoot() + File.separator +
                            PATH_MAKELDIF_RESOURCE_DIR;
      TemplateFile tf = new TemplateFile(resourcePath, random);

      ArrayList<LocalizableMessage> warnings = new ArrayList<>();
      try
      {
        tf.parse(templateFile, warnings);
      }
      catch (Exception e)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_TEMPLATE_FILE, templateFile, e.getMessage());
        return TaskState.STOPPED_BY_ERROR;
      }

      importConfig = new LDIFImportConfig(tf);
    }
    else
    {
      ArrayList<String> fileList = new ArrayList<>(ldifFiles);
      importConfig = new LDIFImportConfig(fileList);
    }
    if(tmpDirectory == null)
    {
      tmpDirectory = "import-tmp";
    }
    importConfig.setCompressed(isCompressed);
    importConfig.setEncrypted(isEncrypted);
    importConfig.setClearBackend(clearBackend);
    importConfig.setExcludeAttributes(excludeAttributes);
    importConfig.setExcludeBranches(excludeBranches);
    importConfig.setExcludeFilters(excludeFilters);
    importConfig.setIncludeAttributes(includeAttributes);
    importConfig.setIncludeBranches(includeBranches);
    importConfig.setIncludeFilters(includeFilters);
    importConfig.setValidateSchema(!skipSchemaValidation);
    importConfig.setSkipDNValidation(skipDNValidation);
    importConfig.setTmpDirectory(tmpDirectory);
    importConfig.setThreadCount(threadCount);

    // FIXME -- Should this be conditional?
    importConfig.setInvokeImportPlugins(true);

    if (rejectFile != null)
    {
      try
      {
        ExistingFileBehavior existingBehavior =
            overwrite ? ExistingFileBehavior.OVERWRITE : ExistingFileBehavior.APPEND;

        importConfig.writeRejectedEntries(rejectFile, existingBehavior);
      }
      catch (Exception e)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_OPEN_REJECTS_FILE, rejectFile, getExceptionMessage(e));
        return TaskState.STOPPED_BY_ERROR;
      }
    }

    if (skipFile != null)
    {
      try
      {
        ExistingFileBehavior existingBehavior =
            overwrite ? ExistingFileBehavior.OVERWRITE : ExistingFileBehavior.APPEND;
        importConfig.writeSkippedEntries(skipFile, existingBehavior);
      }
      catch (Exception e)
      {
        logger.error(ERR_LDIFIMPORT_CANNOT_OPEN_SKIP_FILE, skipFile, getExceptionMessage(e));
        return TaskState.STOPPED_BY_ERROR;
      }
    }

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

    // Notify the task listeners that an import is going to start
    // this must be done before disabling the backend to allow
    // listeners to get access to the backend configuration
    // and to take appropriate actions.
    DirectoryServer.notifyImportBeginning(backend, importConfig);

    // Disable the backend.
    try
    {
      TaskUtils.disableBackend(backend.getBackendID());
    }
    catch (DirectoryException e)
    {
      logger.traceException(e);

      logger.error(e.getMessageObject());
      return TaskState.STOPPED_BY_ERROR;
    }


    try
    {
      // Acquire an exclusive lock for the backend.
      try
      {
        String lockFile = LockFileManager.getBackendLockFileName(backend);
        StringBuilder failureReason = new StringBuilder();
        if (! LockFileManager.acquireExclusiveLock(lockFile, failureReason))
        {
          logger.error(ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), failureReason);
          return TaskState.STOPPED_BY_ERROR;
        }
      }
      catch (Exception e)
      {
        logger.traceException(e);

        logger.error(ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
        return TaskState.STOPPED_BY_ERROR;
      }


      // Launch the import.
      try
      {
        backend.importLDIF(importConfig, DirectoryServer.getInstance().getServerContext());
      }
      catch (DirectoryException de)
      {
        logger.traceException(de);

        DirectoryServer.notifyImportEnded(backend, importConfig, false);
        LocalizableMessage msg;
        if (de.getResultCode() == ResultCode.CONSTRAINT_VIOLATION)
        {
          msg = ERR_LDIFIMPORT_ERROR_CONSTRAINT_VIOLATION.get();
        }
        else
        {
          msg = de.getMessageObject();
        }
        logger.error(ERR_LDIFIMPORT_ERROR_DURING_IMPORT.get(msg));
        return TaskState.STOPPED_BY_ERROR;
      }
      catch (Exception e)
      {
        logger.traceException(e);

        DirectoryServer.notifyImportEnded(backend, importConfig, false);
        logger.error(ERR_LDIFIMPORT_ERROR_DURING_IMPORT, getExceptionMessage(e));
        return TaskState.STOPPED_BY_ERROR;
      }
      finally
      {
        // Release the exclusive lock on the backend.
        try
        {
          String lockFile = LockFileManager.getBackendLockFileName(backend);
          StringBuilder failureReason = new StringBuilder();
          if (! LockFileManager.releaseLock(lockFile, failureReason))
          {
            logger.warn(WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), failureReason);
            return TaskState.COMPLETED_WITH_ERRORS;
          }
        }
        catch (Exception e)
        {
          logger.traceException(e);

          logger.warn(WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
          return TaskState.COMPLETED_WITH_ERRORS;
        }

      }
    }
    finally
    {
      // Enable the backend.
      try
      {
        TaskUtils.enableBackend(backend.getBackendID());
        // It is necessary to retrieve the backend structure again
        // because disabling and enabling it again may have resulted
        // in a new backend being registered to the server.
        backend = DirectoryServer.getBackend(backend.getBackendID());
      }
      catch (DirectoryException e)
      {
        logger.traceException(e);

        logger.error(e.getMessageObject());
        return TaskState.STOPPED_BY_ERROR;
      }
      DirectoryServer.notifyImportEnded(backend, importConfig, true);
    }


    // Clean up after the import by closing the import config.
    importConfig.close();
    return getFinalTaskState();
  }

  private HashSet<AttributeType> toAttributeTypes(ArrayList<String> attrNames)
  {
    final HashSet<AttributeType> attrTypes = new HashSet<>(attrNames.size());
    for (String attrName : attrNames)
    {
      attrTypes.add(DirectoryServer.getAttributeTypeOrDefault(attrName.toLowerCase(), attrName));
    }
    return attrTypes;
  }
}
