001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.tasks;
028
029import static org.opends.messages.TaskMessages.*;
030import static org.opends.messages.ToolMessages.*;
031import static org.opends.server.config.ConfigConstants.*;
032import static org.opends.server.core.DirectoryServer.*;
033import static org.opends.server.util.StaticUtils.*;
034
035import java.io.File;
036import java.util.ArrayList;
037import java.util.Collections;
038import java.util.HashMap;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Map;
042import java.util.Random;
043
044import org.forgerock.i18n.LocalizableMessage;
045import org.forgerock.i18n.slf4j.LocalizedLogger;
046import org.forgerock.opendj.ldap.ResultCode;
047import org.opends.messages.Severity;
048import org.opends.messages.TaskMessages;
049import org.opends.server.api.Backend;
050import org.opends.server.api.Backend.BackendOperation;
051import org.opends.server.api.ClientConnection;
052import org.opends.server.backends.task.Task;
053import org.opends.server.backends.task.TaskState;
054import org.opends.server.core.DirectoryServer;
055import org.opends.server.core.LockFileManager;
056import org.opends.server.tools.makeldif.TemplateFile;
057import org.opends.server.types.Attribute;
058import org.opends.server.types.AttributeType;
059import org.opends.server.types.DN;
060import org.opends.server.types.DirectoryException;
061import org.opends.server.types.Entry;
062import org.opends.server.types.ExistingFileBehavior;
063import org.opends.server.types.LDIFImportConfig;
064import org.opends.server.types.Operation;
065import org.opends.server.types.Privilege;
066import org.opends.server.types.SearchFilter;
067
068/**
069 * This class provides an implementation of a Directory Server task that can
070 * be used to import data from an LDIF file into a backend.
071 */
072public class ImportTask extends Task
073{
074  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
075
076  /** Stores mapping between configuration attribute name and its label. */
077  private static final Map<String, LocalizableMessage> argDisplayMap = new HashMap<>();
078  static
079  {
080    argDisplayMap.put(ATTR_IMPORT_LDIF_FILE, INFO_IMPORT_ARG_LDIF_FILE.get());
081    argDisplayMap.put(ATTR_IMPORT_TEMPLATE_FILE, INFO_IMPORT_ARG_TEMPLATE_FILE.get());
082    argDisplayMap.put(ATTR_IMPORT_RANDOM_SEED, INFO_IMPORT_ARG_RANDOM_SEED.get());
083    argDisplayMap.put(ATTR_IMPORT_BACKEND_ID, INFO_IMPORT_ARG_BACKEND_ID.get());
084    argDisplayMap.put(ATTR_IMPORT_INCLUDE_BRANCH, INFO_IMPORT_ARG_INCL_BRANCH.get());
085    argDisplayMap.put(ATTR_IMPORT_EXCLUDE_BRANCH, INFO_IMPORT_ARG_EXCL_BRANCH.get());
086    argDisplayMap.put(ATTR_IMPORT_INCLUDE_ATTRIBUTE, INFO_IMPORT_ARG_INCL_ATTR.get());
087    argDisplayMap.put(ATTR_IMPORT_EXCLUDE_ATTRIBUTE, INFO_IMPORT_ARG_EXCL_ATTR.get());
088    argDisplayMap.put(ATTR_IMPORT_INCLUDE_FILTER, INFO_IMPORT_ARG_INCL_FILTER.get());
089    argDisplayMap.put(ATTR_IMPORT_EXCLUDE_FILTER, INFO_IMPORT_ARG_EXCL_FILTER.get());
090    argDisplayMap.put(ATTR_IMPORT_REJECT_FILE, INFO_IMPORT_ARG_REJECT_FILE.get());
091    argDisplayMap.put(ATTR_IMPORT_SKIP_FILE, INFO_IMPORT_ARG_SKIP_FILE.get());
092    argDisplayMap.put(ATTR_IMPORT_OVERWRITE, INFO_IMPORT_ARG_OVERWRITE.get());
093    argDisplayMap.put(ATTR_IMPORT_SKIP_SCHEMA_VALIDATION, INFO_IMPORT_ARG_SKIP_SCHEMA_VALIDATION.get());
094    argDisplayMap.put(ATTR_IMPORT_IS_COMPRESSED, INFO_IMPORT_ARG_IS_COMPRESSED.get());
095    argDisplayMap.put(ATTR_IMPORT_IS_ENCRYPTED, INFO_IMPORT_ARG_IS_ENCRYPTED.get());
096    argDisplayMap.put(ATTR_IMPORT_CLEAR_BACKEND, INFO_IMPORT_ARG_CLEAR_BACKEND.get());
097  }
098
099
100  private boolean isCompressed;
101  private boolean isEncrypted;
102  private boolean overwrite;
103  private boolean skipSchemaValidation;
104  private boolean clearBackend;
105  private boolean skipDNValidation;
106  private String tmpDirectory;
107  private int threadCount;
108  private String backendID;
109  private String rejectFile;
110  private String skipFile;
111  private ArrayList<String> excludeAttributeStrings;
112  private ArrayList<String> excludeBranchStrings;
113  private ArrayList<String> excludeFilterStrings;
114  private ArrayList<String> includeAttributeStrings;
115  private ArrayList<String> includeBranchStrings;
116  private ArrayList<String> includeFilterStrings;
117  private ArrayList<String> ldifFiles;
118  private String templateFile;
119  private int randomSeed;
120  private LDIFImportConfig importConfig;
121
122  @Override
123  public LocalizableMessage getDisplayName() {
124    return INFO_TASK_IMPORT_NAME.get();
125  }
126
127  @Override
128  public LocalizableMessage getAttributeDisplayName(String name) {
129    return argDisplayMap.get(name);
130  }
131
132  @Override public void initializeTask() throws DirectoryException
133  {
134    // If the client connection is available, then make sure the associated
135    // client has the LDIF_IMPORT privilege.
136    Operation operation = getOperation();
137    if (operation != null)
138    {
139      ClientConnection clientConnection = operation.getClientConnection();
140      if (! clientConnection.hasPrivilege(Privilege.LDIF_IMPORT, operation))
141      {
142        LocalizableMessage message = ERR_TASK_LDIFIMPORT_INSUFFICIENT_PRIVILEGES.get();
143        throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message);
144      }
145    }
146
147
148    Entry taskEntry = getTaskEntry();
149
150    AttributeType typeLdifFile = getAttributeTypeOrDefault(ATTR_IMPORT_LDIF_FILE);
151    AttributeType typeTemplateFile = getAttributeTypeOrDefault(ATTR_IMPORT_TEMPLATE_FILE);
152    AttributeType typeBackendID = getAttributeTypeOrDefault(ATTR_IMPORT_BACKEND_ID);
153    AttributeType typeIncludeBranch = getAttributeTypeOrDefault(ATTR_IMPORT_INCLUDE_BRANCH);
154    AttributeType typeExcludeBranch = getAttributeTypeOrDefault(ATTR_IMPORT_EXCLUDE_BRANCH);
155    AttributeType typeIncludeAttribute = getAttributeTypeOrDefault(ATTR_IMPORT_INCLUDE_ATTRIBUTE);
156    AttributeType typeExcludeAttribute = getAttributeTypeOrDefault(ATTR_IMPORT_EXCLUDE_ATTRIBUTE);
157    AttributeType typeIncludeFilter = getAttributeTypeOrDefault(ATTR_IMPORT_INCLUDE_FILTER);
158    AttributeType typeExcludeFilter = getAttributeTypeOrDefault(ATTR_IMPORT_EXCLUDE_FILTER);
159    AttributeType typeRejectFile = getAttributeTypeOrDefault(ATTR_IMPORT_REJECT_FILE);
160    AttributeType typeSkipFile = getAttributeTypeOrDefault(ATTR_IMPORT_SKIP_FILE);
161    AttributeType typeOverwrite = getAttributeTypeOrDefault(ATTR_IMPORT_OVERWRITE);
162    AttributeType typeSkipSchemaValidation = getAttributeTypeOrDefault(ATTR_IMPORT_SKIP_SCHEMA_VALIDATION);
163    AttributeType typeIsCompressed = getAttributeTypeOrDefault(ATTR_IMPORT_IS_COMPRESSED);
164    AttributeType typeIsEncrypted = getAttributeTypeOrDefault(ATTR_IMPORT_IS_ENCRYPTED);
165    AttributeType typeClearBackend = getAttributeTypeOrDefault(ATTR_IMPORT_CLEAR_BACKEND);
166    AttributeType typeRandomSeed = getAttributeTypeOrDefault(ATTR_IMPORT_RANDOM_SEED);
167    AttributeType typeThreadCount = getAttributeTypeOrDefault(ATTR_IMPORT_THREAD_COUNT);
168    AttributeType typeTmpDirectory = getAttributeTypeOrDefault(ATTR_IMPORT_TMP_DIRECTORY);
169    AttributeType typeDNCheckPhase2 = getAttributeTypeOrDefault(ATTR_IMPORT_SKIP_DN_VALIDATION);
170
171    ArrayList<String> ldifFilestmp = asListOfStrings(taskEntry, typeLdifFile);
172    ldifFiles = new ArrayList<>(ldifFilestmp.size());
173    for (String s : ldifFilestmp)
174    {
175      File f = new File (s);
176      if (!f.isAbsolute())
177      {
178        f = new File(DirectoryServer.getInstanceRoot(), s);
179        try
180        {
181          s = f.getCanonicalPath();
182        }
183        catch (Exception ex)
184        {
185          s = f.getAbsolutePath();
186        }
187      }
188      if (!f.canRead()) {
189        LocalizableMessage message = ERR_LDIFIMPORT_LDIF_FILE_DOESNT_EXIST.get(s);
190        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
191      }
192      ldifFiles.add(s);
193    }
194
195    templateFile = asString(taskEntry, typeTemplateFile);
196    if (templateFile != null)
197    {
198      File f = new File(templateFile);
199      if (!f.isAbsolute())
200      {
201        templateFile = new File(DirectoryServer.getInstanceRoot(), templateFile).getAbsolutePath();
202      }
203    }
204
205    skipDNValidation = asBoolean(taskEntry, typeDNCheckPhase2);
206    tmpDirectory = asString(taskEntry, typeTmpDirectory);
207    backendID = asString(taskEntry, typeBackendID);
208    includeBranchStrings = asListOfStrings(taskEntry, typeIncludeBranch);
209    excludeBranchStrings = asListOfStrings(taskEntry, typeExcludeBranch);
210    includeAttributeStrings = asListOfStrings(taskEntry, typeIncludeAttribute);
211    excludeAttributeStrings = asListOfStrings(taskEntry, typeExcludeAttribute);
212    includeFilterStrings = asListOfStrings(taskEntry, typeIncludeFilter);
213    excludeFilterStrings = asListOfStrings(taskEntry, typeExcludeFilter);
214    rejectFile = asString(taskEntry, typeRejectFile);
215    skipFile = asString(taskEntry, typeSkipFile);
216    overwrite = asBoolean(taskEntry, typeOverwrite);
217    skipSchemaValidation = asBoolean(taskEntry, typeSkipSchemaValidation);
218    isCompressed = asBoolean(taskEntry, typeIsCompressed);
219    isEncrypted = asBoolean(taskEntry, typeIsEncrypted);
220    clearBackend = asBoolean(taskEntry, typeClearBackend);
221    randomSeed = asInt(taskEntry, typeRandomSeed);
222    threadCount = asInt(taskEntry, typeThreadCount);
223
224    // Make sure that either the "includeBranchStrings" argument or the
225    // "backendID" argument was provided.
226    if(includeBranchStrings.isEmpty() && backendID == null)
227    {
228      LocalizableMessage message = ERR_LDIFIMPORT_MISSING_BACKEND_ARGUMENT.get(
229          typeIncludeBranch.getNameOrOID(), typeBackendID.getNameOrOID());
230      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
231    }
232
233    Backend<?> backend = null;
234    ArrayList<DN> defaultIncludeBranches;
235    HashSet<DN> excludeBranches = new HashSet<>(excludeBranchStrings.size());
236    HashSet<DN> includeBranches = new HashSet<>(includeBranchStrings.size());
237
238    for (String s : includeBranchStrings)
239    {
240      DN includeBranch;
241      try
242      {
243        includeBranch = DN.valueOf(s);
244      }
245      catch (DirectoryException de)
246      {
247        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
248            s, de.getMessageObject());
249        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
250      }
251      catch (Exception e)
252      {
253        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
254            s, getExceptionMessage(e));
255        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
256      }
257
258      includeBranches.add(includeBranch);
259    }
260    for (String s : excludeBranchStrings)
261    {
262      DN excludeBranch;
263      try
264      {
265        excludeBranch = DN.valueOf(s);
266      }
267      catch (DirectoryException de)
268      {
269        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
270            s, de.getMessageObject());
271        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
272      }
273      catch (Exception e)
274      {
275        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
276            s, getExceptionMessage(e));
277        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
278      }
279
280      excludeBranches.add(excludeBranch);
281    }
282
283    for (String filterString : excludeFilterStrings)
284    {
285      try
286      {
287        SearchFilter.createFilterFromString(filterString);
288      }
289      catch (DirectoryException de)
290      {
291        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER.get(
292            filterString, de.getMessageObject());
293        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
294      }
295    }
296
297    for (String filterString : includeFilterStrings)
298    {
299      try
300      {
301        SearchFilter.createFilterFromString(filterString);
302      }
303      catch (DirectoryException de)
304      {
305        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER.get(
306            filterString, de.getMessageObject());
307        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
308      }
309    }
310
311    if(backendID != null)
312    {
313      backend = DirectoryServer.getBackend(backendID);
314      if (backend == null)
315      {
316        LocalizableMessage message = ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID.get();
317        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
318      }
319      else if (!backend.supports(BackendOperation.LDIF_IMPORT))
320      {
321        LocalizableMessage message = ERR_LDIFIMPORT_CANNOT_IMPORT.get(backendID);
322        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
323      }
324    }
325    else
326    {
327      // Find the backend that includes all the branches.
328      for(DN includeBranch : includeBranches)
329      {
330        Backend<?> locatedBackend = DirectoryServer.getBackend(includeBranch);
331        if(locatedBackend != null)
332        {
333          if(backend == null)
334          {
335            backend = locatedBackend;
336          }
337          else if(backend != locatedBackend)
338          {
339            // The include branches span across multiple backends.
340            LocalizableMessage message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
341                includeBranch, backend.getBackendID());
342            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
343          }
344        }
345        else
346        {
347          // The include branch is not associated with any backend.
348          LocalizableMessage message = ERR_NO_BACKENDS_FOR_BASE.get(includeBranch);
349          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
350        }
351      }
352    }
353
354    // Make sure the selected backend will handle all the include branches
355    defaultIncludeBranches = new ArrayList<>(backend.getBaseDNs().length);
356    Collections.addAll(defaultIncludeBranches, backend.getBaseDNs());
357
358    for(DN includeBranch : includeBranches)
359    {
360      if (!Backend.handlesEntry(includeBranch, defaultIncludeBranches, excludeBranches))
361      {
362        LocalizableMessage message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
363            includeBranch, backend.getBackendID());
364        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
365      }
366    }
367  }
368
369  private int asInt(Entry taskEntry, AttributeType attributeType)
370  {
371    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
372    return TaskUtils.getSingleValueInteger(attrList, 0);
373  }
374
375  private boolean asBoolean(Entry taskEntry, AttributeType attributeType)
376  {
377    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
378    return TaskUtils.getBoolean(attrList, false);
379  }
380
381  private String asString(Entry taskEntry, AttributeType attributeType)
382  {
383    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
384    return TaskUtils.getSingleValueString(attrList);
385  }
386
387  private ArrayList<String> asListOfStrings(Entry taskEntry, AttributeType attributeType)
388  {
389    final List<Attribute> attrList = taskEntry.getAttribute(attributeType);
390    return TaskUtils.getMultiValueString(attrList);
391  }
392
393  @Override
394  public void interruptTask(TaskState interruptState, LocalizableMessage interruptReason)
395  {
396    if (TaskState.STOPPED_BY_ADMINISTRATOR.equals(interruptState) && importConfig != null)
397    {
398      addLogMessage(Severity.INFORMATION, TaskMessages.INFO_TASK_STOPPED_BY_ADMIN.get(
399      interruptReason));
400      setTaskInterruptState(interruptState);
401      importConfig.cancel();
402    }
403  }
404
405  @Override
406  public boolean isInterruptable()
407  {
408    return true;
409  }
410
411  @Override
412  protected TaskState runTask()
413  {
414    // See if there were any user-defined sets of include/exclude attributes or
415    // filters.  If so, then process them.
416    HashSet<AttributeType> excludeAttributes = toAttributeTypes(excludeAttributeStrings);
417    HashSet<AttributeType> includeAttributes = toAttributeTypes(includeAttributeStrings);
418
419    ArrayList<SearchFilter> excludeFilters = new ArrayList<>(excludeFilterStrings.size());
420    for (String filterString : excludeFilterStrings)
421    {
422      try
423      {
424        excludeFilters.add(SearchFilter.createFilterFromString(filterString));
425      }
426      catch (DirectoryException de)
427      {
428        logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER, filterString, de.getMessageObject());
429        return TaskState.STOPPED_BY_ERROR;
430      }
431    }
432
433    ArrayList<SearchFilter> includeFilters = new ArrayList<>(includeFilterStrings.size());
434    for (String filterString : includeFilterStrings)
435    {
436      try
437      {
438        includeFilters.add(SearchFilter.createFilterFromString(filterString));
439      }
440      catch (DirectoryException de)
441      {
442        logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER, filterString, de.getMessageObject());
443        return TaskState.STOPPED_BY_ERROR;
444      }
445    }
446
447
448    // Get the backend into which the LDIF should be imported.
449    Backend<?> backend = null;
450    HashSet<DN> defaultIncludeBranches;
451    HashSet<DN> excludeBranches = new HashSet<>(excludeBranchStrings.size());
452    HashSet<DN> includeBranches = new HashSet<>(includeBranchStrings.size());
453
454    for (String s : includeBranchStrings)
455    {
456      DN includeBranch;
457      try
458      {
459        includeBranch = DN.valueOf(s);
460      }
461      catch (DirectoryException de)
462      {
463        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, de.getMessageObject());
464        return TaskState.STOPPED_BY_ERROR;
465      }
466      catch (Exception e)
467      {
468        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, getExceptionMessage(e));
469        return TaskState.STOPPED_BY_ERROR;
470      }
471
472      includeBranches.add(includeBranch);
473    }
474
475    if(backendID != null)
476    {
477      backend = DirectoryServer.getBackend(backendID);
478
479      if (backend == null)
480      {
481        logger.error(ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID);
482        return TaskState.STOPPED_BY_ERROR;
483      }
484      else if (!backend.supports(BackendOperation.LDIF_IMPORT))
485      {
486        logger.error(ERR_LDIFIMPORT_CANNOT_IMPORT, backendID);
487        return TaskState.STOPPED_BY_ERROR;
488      }
489    }
490    else
491    {
492      // Find the backend that includes all the branches.
493      for(DN includeBranch : includeBranches)
494      {
495        Backend<?> locatedBackend = DirectoryServer.getBackend(includeBranch);
496        if(locatedBackend != null)
497        {
498          if(backend == null)
499          {
500            backend = locatedBackend;
501          }
502          else if(backend != locatedBackend)
503          {
504            // The include branches span across multiple backends.
505            logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch, backend.getBackendID());
506            return TaskState.STOPPED_BY_ERROR;
507          }
508        }
509      }
510    }
511
512    // Find backends with subordinate base DNs that should be excluded from the import.
513    defaultIncludeBranches = new HashSet<>(backend.getBaseDNs().length);
514    Collections.addAll(defaultIncludeBranches, backend.getBaseDNs());
515
516    if (backend.getSubordinateBackends() != null)
517    {
518      for (Backend<?> subBackend : backend.getSubordinateBackends())
519      {
520        for (DN baseDN : subBackend.getBaseDNs())
521        {
522          for (DN importBase : defaultIncludeBranches)
523          {
524            if (!baseDN.equals(importBase) && baseDN.isDescendantOf(importBase))
525            {
526              excludeBranches.add(baseDN);
527              break;
528            }
529          }
530        }
531      }
532    }
533
534    for (String s : excludeBranchStrings)
535    {
536      DN excludeBranch;
537      try
538      {
539        excludeBranch = DN.valueOf(s);
540      }
541      catch (DirectoryException de)
542      {
543        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE, s, de.getMessageObject());
544        return TaskState.STOPPED_BY_ERROR;
545      }
546      catch (Exception e)
547      {
548        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE, s, getExceptionMessage(e));
549        return TaskState.STOPPED_BY_ERROR;
550      }
551
552      excludeBranches.add(excludeBranch);
553    }
554
555    if (includeBranchStrings.isEmpty())
556    {
557      includeBranches = defaultIncludeBranches;
558    }
559    else
560    {
561      // Make sure the selected backend will handle all the include branches
562      for (DN includeBranch : includeBranches)
563      {
564        if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches,
565                                   excludeBranches))
566        {
567          logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch, backend.getBackendID());
568          return TaskState.STOPPED_BY_ERROR;
569        }
570      }
571    }
572
573    // Create the LDIF import configuration to use when reading the LDIF.
574    if (templateFile != null)
575    {
576      Random random;
577      try
578      {
579        random = new Random(randomSeed);
580      }
581      catch (Exception e)
582      {
583        random = new Random();
584      }
585
586      String resourcePath = DirectoryServer.getInstanceRoot() + File.separator +
587                            PATH_MAKELDIF_RESOURCE_DIR;
588      TemplateFile tf = new TemplateFile(resourcePath, random);
589
590      ArrayList<LocalizableMessage> warnings = new ArrayList<>();
591      try
592      {
593        tf.parse(templateFile, warnings);
594      }
595      catch (Exception e)
596      {
597        logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_TEMPLATE_FILE, templateFile, e.getMessage());
598        return TaskState.STOPPED_BY_ERROR;
599      }
600
601      importConfig = new LDIFImportConfig(tf);
602    }
603    else
604    {
605      ArrayList<String> fileList = new ArrayList<>(ldifFiles);
606      importConfig = new LDIFImportConfig(fileList);
607    }
608    if(tmpDirectory == null)
609    {
610      tmpDirectory = "import-tmp";
611    }
612    importConfig.setCompressed(isCompressed);
613    importConfig.setEncrypted(isEncrypted);
614    importConfig.setClearBackend(clearBackend);
615    importConfig.setExcludeAttributes(excludeAttributes);
616    importConfig.setExcludeBranches(excludeBranches);
617    importConfig.setExcludeFilters(excludeFilters);
618    importConfig.setIncludeAttributes(includeAttributes);
619    importConfig.setIncludeBranches(includeBranches);
620    importConfig.setIncludeFilters(includeFilters);
621    importConfig.setValidateSchema(!skipSchemaValidation);
622    importConfig.setSkipDNValidation(skipDNValidation);
623    importConfig.setTmpDirectory(tmpDirectory);
624    importConfig.setThreadCount(threadCount);
625
626    // FIXME -- Should this be conditional?
627    importConfig.setInvokeImportPlugins(true);
628
629    if (rejectFile != null)
630    {
631      try
632      {
633        ExistingFileBehavior existingBehavior =
634            overwrite ? ExistingFileBehavior.OVERWRITE : ExistingFileBehavior.APPEND;
635
636        importConfig.writeRejectedEntries(rejectFile, existingBehavior);
637      }
638      catch (Exception e)
639      {
640        logger.error(ERR_LDIFIMPORT_CANNOT_OPEN_REJECTS_FILE, rejectFile, getExceptionMessage(e));
641        return TaskState.STOPPED_BY_ERROR;
642      }
643    }
644
645    if (skipFile != null)
646    {
647      try
648      {
649        ExistingFileBehavior existingBehavior =
650            overwrite ? ExistingFileBehavior.OVERWRITE : ExistingFileBehavior.APPEND;
651        importConfig.writeSkippedEntries(skipFile, existingBehavior);
652      }
653      catch (Exception e)
654      {
655        logger.error(ERR_LDIFIMPORT_CANNOT_OPEN_SKIP_FILE, skipFile, getExceptionMessage(e));
656        return TaskState.STOPPED_BY_ERROR;
657      }
658    }
659
660    // Get the set of base DNs for the backend as an array.
661    DN[] baseDNs = new DN[defaultIncludeBranches.size()];
662    defaultIncludeBranches.toArray(baseDNs);
663
664    // Notify the task listeners that an import is going to start
665    // this must be done before disabling the backend to allow
666    // listeners to get access to the backend configuration
667    // and to take appropriate actions.
668    DirectoryServer.notifyImportBeginning(backend, importConfig);
669
670    // Disable the backend.
671    try
672    {
673      TaskUtils.disableBackend(backend.getBackendID());
674    }
675    catch (DirectoryException e)
676    {
677      logger.traceException(e);
678
679      logger.error(e.getMessageObject());
680      return TaskState.STOPPED_BY_ERROR;
681    }
682
683
684    try
685    {
686      // Acquire an exclusive lock for the backend.
687      try
688      {
689        String lockFile = LockFileManager.getBackendLockFileName(backend);
690        StringBuilder failureReason = new StringBuilder();
691        if (! LockFileManager.acquireExclusiveLock(lockFile, failureReason))
692        {
693          logger.error(ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), failureReason);
694          return TaskState.STOPPED_BY_ERROR;
695        }
696      }
697      catch (Exception e)
698      {
699        logger.traceException(e);
700
701        logger.error(ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
702        return TaskState.STOPPED_BY_ERROR;
703      }
704
705
706      // Launch the import.
707      try
708      {
709        backend.importLDIF(importConfig, DirectoryServer.getInstance().getServerContext());
710      }
711      catch (DirectoryException de)
712      {
713        logger.traceException(de);
714
715        DirectoryServer.notifyImportEnded(backend, importConfig, false);
716        LocalizableMessage msg;
717        if (de.getResultCode() == ResultCode.CONSTRAINT_VIOLATION)
718        {
719          msg = ERR_LDIFIMPORT_ERROR_CONSTRAINT_VIOLATION.get();
720        }
721        else
722        {
723          msg = de.getMessageObject();
724        }
725        logger.error(ERR_LDIFIMPORT_ERROR_DURING_IMPORT.get(msg));
726        return TaskState.STOPPED_BY_ERROR;
727      }
728      catch (Exception e)
729      {
730        logger.traceException(e);
731
732        DirectoryServer.notifyImportEnded(backend, importConfig, false);
733        logger.error(ERR_LDIFIMPORT_ERROR_DURING_IMPORT, getExceptionMessage(e));
734        return TaskState.STOPPED_BY_ERROR;
735      }
736      finally
737      {
738        // Release the exclusive lock on the backend.
739        try
740        {
741          String lockFile = LockFileManager.getBackendLockFileName(backend);
742          StringBuilder failureReason = new StringBuilder();
743          if (! LockFileManager.releaseLock(lockFile, failureReason))
744          {
745            logger.warn(WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), failureReason);
746            return TaskState.COMPLETED_WITH_ERRORS;
747          }
748        }
749        catch (Exception e)
750        {
751          logger.traceException(e);
752
753          logger.warn(WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
754          return TaskState.COMPLETED_WITH_ERRORS;
755        }
756
757      }
758    }
759    finally
760    {
761      // Enable the backend.
762      try
763      {
764        TaskUtils.enableBackend(backend.getBackendID());
765        // It is necessary to retrieve the backend structure again
766        // because disabling and enabling it again may have resulted
767        // in a new backend being registered to the server.
768        backend = DirectoryServer.getBackend(backend.getBackendID());
769      }
770      catch (DirectoryException e)
771      {
772        logger.traceException(e);
773
774        logger.error(e.getMessageObject());
775        return TaskState.STOPPED_BY_ERROR;
776      }
777      DirectoryServer.notifyImportEnded(backend, importConfig, true);
778    }
779
780
781    // Clean up after the import by closing the import config.
782    importConfig.close();
783    return getFinalTaskState();
784  }
785
786  private HashSet<AttributeType> toAttributeTypes(ArrayList<String> attrNames)
787  {
788    final HashSet<AttributeType> attrTypes = new HashSet<>(attrNames.size());
789    for (String attrName : attrNames)
790    {
791      attrTypes.add(DirectoryServer.getAttributeTypeOrDefault(attrName.toLowerCase(), attrName));
792    }
793    return attrTypes;
794  }
795}