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