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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS.
026 */
027package org.opends.server.tools;
028
029import static org.opends.messages.ToolMessages.*;
030import static org.opends.server.config.ConfigConstants.*;
031import static org.opends.server.util.StaticUtils.*;
032
033import static com.forgerock.opendj.cli.ArgumentConstants.*;
034import static com.forgerock.opendj.cli.Utils.*;
035
036import java.io.File;
037import java.io.OutputStream;
038import java.io.PrintStream;
039import java.util.ArrayList;
040import java.util.HashSet;
041import java.util.List;
042import java.util.Random;
043import java.util.Set;
044
045import org.forgerock.i18n.LocalizableMessage;
046import org.forgerock.i18n.slf4j.LocalizedLogger;
047import org.forgerock.opendj.config.server.ConfigException;
048import org.forgerock.opendj.ldap.ResultCode;
049import org.opends.server.admin.std.server.BackendCfg;
050import org.opends.server.api.Backend;
051import org.opends.server.api.Backend.BackendOperation;
052import org.opends.server.api.plugin.PluginType;
053import org.opends.server.core.CoreConfigManager;
054import org.opends.server.core.DirectoryServer;
055import org.opends.server.core.LockFileManager;
056import org.opends.server.core.PluginConfigManager;
057import org.opends.server.extensions.ConfigFileHandler;
058import org.opends.server.loggers.ErrorLogPublisher;
059import org.opends.server.loggers.ErrorLogger;
060import org.opends.server.loggers.JDKLogging;
061import org.opends.server.loggers.TextErrorLogPublisher;
062import org.opends.server.loggers.TextWriter;
063import org.opends.server.protocols.ldap.LDAPAttribute;
064import org.opends.server.tasks.ImportTask;
065import org.opends.server.tools.makeldif.TemplateFile;
066import org.opends.server.tools.tasks.TaskTool;
067import org.opends.server.types.AttributeType;
068import org.opends.server.types.DN;
069import org.opends.server.types.DirectoryException;
070import org.opends.server.types.ExistingFileBehavior;
071import org.opends.server.types.InitializationException;
072import org.opends.server.types.LDIFImportConfig;
073import org.opends.server.types.LDIFImportResult;
074import org.opends.server.types.NullOutputStream;
075import org.opends.server.types.RawAttribute;
076import org.opends.server.types.SearchFilter;
077import org.opends.server.util.args.LDAPConnectionArgumentParser;
078
079import com.forgerock.opendj.cli.Argument;
080import com.forgerock.opendj.cli.ArgumentException;
081import com.forgerock.opendj.cli.BooleanArgument;
082import com.forgerock.opendj.cli.ClientException;
083import com.forgerock.opendj.cli.CommonArguments;
084import com.forgerock.opendj.cli.IntegerArgument;
085import com.forgerock.opendj.cli.StringArgument;
086
087/**
088 * This program provides a utility that may be used to import the contents of an
089 * LDIF file into a Directory Server backend.  This will be a process that is
090 * intended to run separate from Directory Server and not internally within the
091 * server process (e.g., via the tasks interface).
092 */
093public class ImportLDIF extends TaskTool {
094
095  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
096
097  /**
098   * The buffer size that should be used when reading data from LDIF.
099   */
100  public static final int LDIF_BUFFER_SIZE = 1048576;
101
102
103  /**
104   * The main method for ImportLDIF tool.
105   *
106   * @param  args  The command-line arguments provided to this program.
107   */
108  public static void main(String[] args)
109  {
110    int retCode = mainImportLDIF(args, true, System.out, System.err);
111
112    if(retCode != 0)
113    {
114      System.exit(filterExitCode(retCode));
115    }
116  }
117
118  /**
119   * Processes the command-line arguments and invokes the import process.
120   *
121   * @param  args  The command-line arguments provided to this program.
122   * @return The error code.
123   */
124  public static int mainImportLDIF(String[] args)
125  {
126    return mainImportLDIF(args, true, System.out, System.err);
127  }
128
129  /**
130   * Processes the command-line arguments and invokes the import process.
131   *
132   * @param  args              The command-line arguments provided to this
133   *                           program.
134   * @param  initializeServer  Indicates whether to initialize the server.
135   * @param  outStream         The output stream to use for standard output, or
136   *                           {@code null} if standard output is not needed.
137   * @param  errStream         The output stream to use for standard error, or
138   *                           {@code null} if standard error is not needed.
139   *
140   * @return The error code.
141   */
142  public static int mainImportLDIF(String[] args, boolean initializeServer,
143                                   OutputStream outStream,
144                                   OutputStream errStream)
145  {
146    ImportLDIF tool = new ImportLDIF();
147    return tool.process(args, initializeServer, outStream, errStream);
148  }
149
150  /** Define the command-line arguments that may be used with this program. */
151  private BooleanArgument countRejects;
152  private BooleanArgument displayUsage;
153  private BooleanArgument isCompressed;
154  private BooleanArgument isEncrypted;
155  private BooleanArgument overwrite;
156  private BooleanArgument quietMode;
157  private BooleanArgument skipSchemaValidation;
158  private BooleanArgument clearBackend;
159  private IntegerArgument randomSeed;
160  private StringArgument  backendID;
161  private StringArgument  configClass;
162  private StringArgument  configFile;
163  private StringArgument  excludeAttributeStrings;
164  private StringArgument  excludeBranchStrings;
165  private StringArgument  excludeFilterStrings;
166  private StringArgument  includeAttributeStrings;
167  private StringArgument  includeBranchStrings;
168  private StringArgument  includeFilterStrings;
169  private StringArgument  ldifFiles;
170  private StringArgument  rejectFile;
171  private StringArgument  skipFile;
172  private StringArgument  templateFile;
173  private BooleanArgument skipDNValidation;
174  private IntegerArgument threadCount;
175  private StringArgument  tmpDirectory;
176
177  private int process(String[] args, boolean initializeServer,
178                      OutputStream outStream, OutputStream errStream) {
179
180    PrintStream out = NullOutputStream.wrapOrNullStream(outStream);
181    PrintStream err = NullOutputStream.wrapOrNullStream(errStream);
182    JDKLogging.disableLogging();
183
184    // FIXME -- Need to add a mechanism for verifying the file signature.
185
186
187    // Create the command-line argument parser for use with this program.
188    LDAPConnectionArgumentParser argParser =
189            createArgParser("org.opends.server.tools.ImportLDIF", INFO_LDIFIMPORT_TOOL_DESCRIPTION.get());
190    argParser.setShortToolDescription(REF_SHORT_DESC_IMPORT_LDIF.get());
191
192    // Initialize all the command-line argument types and register them with the
193    // parser.
194    try
195    {
196      createArguments(argParser);
197    }
198    catch (ArgumentException ae)
199    {
200      printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
201      return 1;
202    }
203
204    // Init the default values so that they can appear also on the usage.
205    try
206    {
207      argParser.getArguments().initArgumentsWithConfiguration();
208    }
209    catch (ConfigException ce)
210    {
211      // Ignore.
212    }
213
214    // Parse the command-line arguments provided to this program.
215    try
216    {
217      argParser.parseArguments(args);
218      validateTaskArgs();
219    }
220    catch (ArgumentException ae)
221    {
222      argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
223      return 1;
224    }
225    catch (ClientException ce)
226    {
227      // No need to display the usage since the problem comes with a provided value.
228      printWrappedText(err, ce.getMessageObject());
229      return 1;
230    }
231
232
233    if (argParser.usageOrVersionDisplayed())
234    {
235      return 0;
236    }
237
238
239    // Make sure that either the "ldifFile" argument or the "templateFile"
240    // argument was provided, but not both.
241    if (ldifFiles.isPresent())
242    {
243      if (templateFile.isPresent())
244      {
245        printWrappedText(err,
246            ERR_LDIFIMPORT_CONFLICTING_OPTIONS.get(ldifFiles.getLongIdentifier(), templateFile.getLongIdentifier()));
247        return 1;
248      }
249    }
250    else if (! templateFile.isPresent())
251    {
252      argParser.displayMessageAndUsageReference(err, ERR_LDIFIMPORT_MISSING_REQUIRED_ARGUMENT.get(
253          ldifFiles.getLongIdentifier(), templateFile.getLongIdentifier()));
254      return 1;
255    }
256
257    // Make sure that either the "includeBranchStrings" argument or the
258    // "backendID" argument was provided.
259    if(!includeBranchStrings.isPresent() && !backendID.isPresent())
260    {
261      argParser.displayMessageAndUsageReference(err, ERR_LDIFIMPORT_MISSING_BACKEND_ARGUMENT.get(
262          includeBranchStrings.getLongIdentifier(), backendID.getLongIdentifier()));
263      return 1;
264    }
265
266    // Count rejects option requires the ability to return result codes
267    // which are potentially greater than 1. This is not supported by
268    // the task framework.
269    if (countRejects.isPresent() && argParser.connectionArgumentsPresent())
270    {
271      argParser.displayMessageAndUsageReference(err, ERR_LDIFIMPORT_COUNT_REJECTS_REQUIRES_OFFLINE.get(
272          countRejects.getLongIdentifier()));
273      return 1;
274    }
275
276    // Don't write non-error messages to console if quite
277    if (quietMode.isPresent()) {
278      out = new PrintStream(NullOutputStream.instance());
279    }
280
281    // Checks the version - if upgrade required, the tool is unusable
282    try
283    {
284      checkVersion();
285    }
286    catch (InitializationException e)
287    {
288      printWrappedText(err, e.getMessage());
289      return 1;
290    }
291
292    return process(argParser, initializeServer, out, err);
293  }
294
295  private void createArguments(LDAPConnectionArgumentParser argParser) throws ArgumentException
296  {
297      configClass =
298           new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS,
299                              OPTION_LONG_CONFIG_CLASS, true, false,
300                              true, INFO_CONFIGCLASS_PLACEHOLDER.get(),
301                              ConfigFileHandler.class.getName(), null,
302                              INFO_DESCRIPTION_CONFIG_CLASS.get());
303      configClass.setHidden(true);
304      argParser.addArgument(configClass);
305
306
307      configFile =
308           new StringArgument("configfile", 'f', "configFile", true, false,
309                              true, INFO_CONFIGFILE_PLACEHOLDER.get(), null,
310                              null,
311                              INFO_DESCRIPTION_CONFIG_FILE.get());
312      configFile.setHidden(true);
313      argParser.addArgument(configFile);
314
315
316      ldifFiles =
317           new StringArgument("ldiffile", OPTION_SHORT_LDIF_FILE,
318                              OPTION_LONG_LDIF_FILE, false, true, true,
319                              INFO_LDIFFILE_PLACEHOLDER.get(), null, null,
320                              INFO_LDIFIMPORT_DESCRIPTION_LDIF_FILE.get());
321      argParser.addArgument(ldifFiles);
322
323
324      templateFile =
325           new StringArgument("templatefile", 'A', "templateFile", false, false,
326                              true, INFO_TEMPLATE_FILE_PLACEHOLDER.get(), null,
327                              null,
328                              INFO_LDIFIMPORT_DESCRIPTION_TEMPLATE_FILE.get());
329      argParser.addArgument(templateFile);
330
331      backendID =
332           new StringArgument("backendid", 'n', "backendID", false, false, true,
333                              INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
334                              INFO_LDIFIMPORT_DESCRIPTION_BACKEND_ID.get());
335      argParser.addArgument(backendID);
336
337      clearBackend =
338          new BooleanArgument("clearbackend", 'F', "clearBackend",
339                              INFO_LDIFIMPORT_DESCRIPTION_CLEAR_BACKEND.get());
340      argParser.addArgument(clearBackend);
341
342
343      includeBranchStrings =
344           new StringArgument("includebranch", 'b', "includeBranch", false,
345                              true, true, INFO_BRANCH_DN_PLACEHOLDER.get(),
346                              null, null,
347                              INFO_LDIFIMPORT_DESCRIPTION_INCLUDE_BRANCH.get());
348      argParser.addArgument(includeBranchStrings);
349
350
351      excludeBranchStrings =
352           new StringArgument("excludebranch", 'B', "excludeBranch", false,
353                              true, true, INFO_BRANCH_DN_PLACEHOLDER.get(),
354                              null, null,
355                              INFO_LDIFIMPORT_DESCRIPTION_EXCLUDE_BRANCH.get());
356      argParser.addArgument(excludeBranchStrings);
357
358
359      includeAttributeStrings =
360           new StringArgument(
361                   "includeattribute", 'i', "includeAttribute",
362                   false, true, true, INFO_ATTRIBUTE_PLACEHOLDER.get(), null,
363                   null,
364                   INFO_LDIFIMPORT_DESCRIPTION_INCLUDE_ATTRIBUTE.get());
365      argParser.addArgument(includeAttributeStrings);
366
367
368      excludeAttributeStrings =
369           new StringArgument(
370                   "excludeattribute", 'e', "excludeAttribute",
371                   false, true, true, INFO_ATTRIBUTE_PLACEHOLDER.get(), null,
372                   null,
373                   INFO_LDIFIMPORT_DESCRIPTION_EXCLUDE_ATTRIBUTE.get());
374      argParser.addArgument(excludeAttributeStrings);
375
376
377      includeFilterStrings =
378           new StringArgument(
379                   "includefilter", 'I', "includeFilter",
380                   false, true, true, INFO_FILTER_PLACEHOLDER.get(), null, null,
381                   INFO_LDIFIMPORT_DESCRIPTION_INCLUDE_FILTER.get());
382      argParser.addArgument(includeFilterStrings);
383
384
385      excludeFilterStrings =
386           new StringArgument("excludefilter", 'E', "excludeFilter",
387                              false, true, true, INFO_FILTER_PLACEHOLDER.get(),
388                              null, null,
389                              INFO_LDIFIMPORT_DESCRIPTION_EXCLUDE_FILTER.get());
390      argParser.addArgument(excludeFilterStrings);
391
392
393      rejectFile =
394           new StringArgument("rejectfile", 'R', "rejectFile", false, false,
395                              true, INFO_REJECT_FILE_PLACEHOLDER.get(), null,
396                              null,
397                              INFO_LDIFIMPORT_DESCRIPTION_REJECT_FILE.get());
398      argParser.addArgument(rejectFile);
399
400
401      skipFile =
402           new StringArgument("skipfile", null, "skipFile", false, false,
403                              true, INFO_SKIP_FILE_PLACEHOLDER.get(), null,
404                              null,
405                              INFO_LDIFIMPORT_DESCRIPTION_SKIP_FILE.get());
406      argParser.addArgument(skipFile);
407
408
409      overwrite =
410           new BooleanArgument("overwrite", 'O', "overwrite",
411                               INFO_LDIFIMPORT_DESCRIPTION_OVERWRITE.get());
412      argParser.addArgument(overwrite);
413
414
415      randomSeed =
416           new IntegerArgument("randomseed", OPTION_SHORT_RANDOM_SEED,
417                               OPTION_LONG_RANDOM_SEED, false, false,
418                               true, INFO_SEED_PLACEHOLDER.get(),
419                               0, null, false, 0, false, 0,
420                               INFO_LDIFIMPORT_DESCRIPTION_RANDOM_SEED.get());
421      argParser.addArgument(randomSeed);
422
423
424      skipSchemaValidation =
425           new BooleanArgument("skipschema", 'S', "skipSchemaValidation",
426                    INFO_LDIFIMPORT_DESCRIPTION_SKIP_SCHEMA_VALIDATION.get());
427      argParser.addArgument(skipSchemaValidation);
428
429
430      skipDNValidation =
431           new BooleanArgument("skipDNValidation", null, "skipDNValidation",
432                    INFO_LDIFIMPORT_DESCRIPTION_DN_VALIDATION.get());
433      argParser.addArgument(skipDNValidation);
434
435
436      threadCount = new IntegerArgument("threadCount", null, "threadCount",
437              false, false, true,
438              INFO_LDIFIMPORT_THREAD_COUNT_PLACEHOLDER.get(),
439              0, null,
440              true, 1, true, Integer.MAX_VALUE,
441              INFO_LDIFIMPORT_DESCRIPTION_THREAD_COUNT.get());
442      argParser.addArgument(threadCount);
443
444      tmpDirectory =
445           new StringArgument("tmpdirectory", null, "tmpdirectory", false,
446                   false, true, INFO_LDIFIMPORT_TEMP_DIR_PLACEHOLDER.get(),
447                   "import-tmp",
448                    null, INFO_LDIFIMPORT_DESCRIPTION_TEMP_DIRECTORY.get());
449      argParser.addArgument(tmpDirectory);
450
451
452      countRejects =
453           new BooleanArgument("countrejects", null, "countRejects",
454                               INFO_LDIFIMPORT_DESCRIPTION_COUNT_REJECTS.get());
455      argParser.addArgument(countRejects);
456
457
458      isCompressed =
459           new BooleanArgument("iscompressed", 'c', "isCompressed",
460                               INFO_LDIFIMPORT_DESCRIPTION_IS_COMPRESSED.get());
461      argParser.addArgument(isCompressed);
462
463
464      isEncrypted =
465           new BooleanArgument("isencrypted", 'y', "isEncrypted",
466                               INFO_LDIFIMPORT_DESCRIPTION_IS_ENCRYPTED.get());
467      isEncrypted.setHidden(true); //See issue #27
468      argParser.addArgument(isEncrypted);
469
470
471      quietMode = new BooleanArgument("quietmode", OPTION_SHORT_QUIET,
472                                      OPTION_LONG_QUIET,
473                                      INFO_LDIFIMPORT_DESCRIPTION_QUIET.get());
474      argParser.addArgument(quietMode);
475
476
477      displayUsage = CommonArguments.getShowUsage();
478      argParser.addArgument(displayUsage);
479      argParser.setUsageArgument(displayUsage);
480  }
481
482  @Override
483  public void addTaskAttributes(List<RawAttribute> attributes)
484  {
485    // Required attributes
486    addAttribute(attributes, ATTR_IMPORT_LDIF_FILE, ldifFiles.getValues());
487    addAttribute(attributes, ATTR_IMPORT_TEMPLATE_FILE, templateFile.getValue());
488    addAttribute(attributes, ATTR_IMPORT_RANDOM_SEED, randomSeed.getValue());
489    addAttribute(attributes, ATTR_IMPORT_THREAD_COUNT, threadCount.getValue());
490
491    // Optional attributes
492    addAttribute2(attributes, ATTR_IMPORT_BACKEND_ID, backendID);
493    addAttribute(attributes, ATTR_IMPORT_INCLUDE_ATTRIBUTE, includeAttributeStrings.getValues());
494    addAttribute(attributes, ATTR_IMPORT_EXCLUDE_ATTRIBUTE, excludeAttributeStrings.getValues());
495    addAttribute(attributes, ATTR_IMPORT_INCLUDE_FILTER, includeFilterStrings.getValues());
496    addAttribute(attributes, ATTR_IMPORT_EXCLUDE_FILTER, excludeFilterStrings.getValues());
497    addAttribute(attributes, ATTR_IMPORT_INCLUDE_BRANCH, includeBranchStrings.getValues());
498    addAttribute(attributes, ATTR_IMPORT_EXCLUDE_BRANCH, excludeBranchStrings.getValues());
499    addAttribute2(attributes, ATTR_IMPORT_REJECT_FILE, rejectFile);
500    addAttribute2(attributes, ATTR_IMPORT_SKIP_FILE, skipFile);
501    addAttribute2(attributes, ATTR_IMPORT_OVERWRITE, overwrite);
502    addAttribute2(attributes, ATTR_IMPORT_SKIP_SCHEMA_VALIDATION, skipSchemaValidation);
503    addAttribute2(attributes, ATTR_IMPORT_TMP_DIRECTORY, tmpDirectory);
504    addAttribute2(attributes, ATTR_IMPORT_SKIP_DN_VALIDATION, skipDNValidation);
505    addAttribute2(attributes, ATTR_IMPORT_IS_COMPRESSED, isCompressed);
506    addAttribute2(attributes, ATTR_IMPORT_IS_ENCRYPTED, isEncrypted);
507    addAttribute2(attributes, ATTR_IMPORT_CLEAR_BACKEND, clearBackend);
508  }
509
510  private void addAttribute(List<RawAttribute> attributes, String attrName, String value)
511  {
512    if (value != null)
513    {
514      attributes.add(new LDAPAttribute(attrName, value));
515    }
516  }
517
518  private void addAttribute2(List<RawAttribute> attributes, String attrName, Argument arg)
519  {
520    final String value = arg.getValue();
521    if (value != null && !value.equals(arg.getDefaultValue()))
522    {
523      attributes.add(new LDAPAttribute(attrName, value));
524    }
525  }
526
527  private void addAttribute(List<RawAttribute> attributes, String attrName, List<String> attrValues)
528  {
529    if (attrValues != null && !attrValues.isEmpty())
530    {
531      attributes.add(new LDAPAttribute(attrName, attrValues));
532    }
533  }
534
535  @Override
536  public String getTaskObjectclass() {
537    return "ds-task-import";
538  }
539
540  @Override
541  public Class<?> getTaskClass() {
542    return ImportTask.class;
543  }
544
545  @Override
546  protected int processLocal(boolean initializeServer,
547                           PrintStream out,
548                           PrintStream err) {
549
550
551    // Perform the initial bootstrap of the Directory Server and process the configuration.
552    DirectoryServer directoryServer = DirectoryServer.getInstance();
553    if (initializeServer)
554    {
555      try
556      {
557        DirectoryServer.bootstrapClient();
558        DirectoryServer.initializeJMX();
559      }
560      catch (Exception e)
561      {
562        printWrappedText(err, ERR_SERVER_BOOTSTRAP_ERROR.get(getExceptionMessage(e)));
563        return 1;
564      }
565
566      try
567      {
568        directoryServer.initializeConfiguration(configClass.getValue(),
569                                                configFile.getValue());
570      }
571      catch (InitializationException ie)
572      {
573        printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(ie.getMessage()));
574        return 1;
575      }
576      catch (Exception e)
577      {
578        printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(getExceptionMessage(e)));
579        return 1;
580      }
581
582
583
584      // Initialize the Directory Server schema elements.
585      try
586      {
587        directoryServer.initializeSchema();
588      }
589      catch (Exception e)
590      {
591        printWrappedText(err, ERR_CANNOT_LOAD_SCHEMA.get(getMessage(e)));
592        return 1;
593      }
594
595
596      // Initialize the Directory Server core configuration.
597      try
598      {
599        CoreConfigManager coreConfigManager = new CoreConfigManager(directoryServer.getServerContext());
600        coreConfigManager.initializeCoreConfig();
601      }
602      catch (Exception e)
603      {
604        printWrappedText(err, ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(getMessage(e)));
605        return 1;
606      }
607
608
609      // Initialize the Directory Server crypto manager.
610      try
611      {
612        directoryServer.initializeCryptoManager();
613      }
614      catch (Exception e)
615      {
616        printWrappedText(err, ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(getMessage(e)));
617        return 1;
618      }
619
620
621      if (! quietMode.isPresent())
622      {
623        try
624        {
625          ErrorLogPublisher errorLogPublisher =
626              TextErrorLogPublisher.getToolStartupTextErrorPublisher(
627                  new TextWriter.STREAM(out));
628          ErrorLogger.getInstance().addLogPublisher(errorLogPublisher);
629        }
630        catch(Exception e)
631        {
632          err.println("Error installing the custom error logger: " +
633              stackTraceToSingleLineString(e));
634        }
635      }
636
637      // Initialize the root DNs.
638      try
639      {
640        directoryServer.initializeRootDNConfigManager();
641      }
642      catch (Exception e)
643      {
644        printWrappedText(err, ERR_CANNOT_INITIALIZE_ROOTDN_MANAGER.get(getMessage(e)));
645        return 1;
646      }
647
648      // Initialize the plugin manager.
649      try
650      {
651        HashSet<PluginType> pluginTypes = new HashSet<>(1);
652        directoryServer.initializePlugins(pluginTypes);
653      }
654      catch (Exception e)
655      {
656        printWrappedText(err, ERR_LDIFIMPORT_CANNOT_INITIALIZE_PLUGINS.get(getMessage(e)));
657        return 1;
658      }
659
660      // Initialize the subentry manager.
661      try
662      {
663        directoryServer.initializeSubentryManager();
664      }
665      catch (InitializationException ie)
666      {
667        printWrappedText(err, ERR_CANNOT_INITIALIZE_SUBENTRY_MANAGER.get(ie.getMessage()));
668        return 1;
669      }
670
671      // Initialize all the password policy information.
672      try
673      {
674        directoryServer.initializeAuthenticationPolicyComponents();
675      }
676      catch (Exception e)
677      {
678        printWrappedText(err, ERR_LDIFIMPORT_CANNOT_INITIALIZE_PWPOLICY.get(getMessage(e)));
679        return 1;
680      }
681    }
682
683    // Make sure that the plugin initialization is performed.
684    try
685    {
686      HashSet<PluginType> pluginTypes = new HashSet<>(1);
687      pluginTypes.add(PluginType.LDIF_IMPORT);
688      PluginConfigManager pluginConfigManager =
689              DirectoryServer.getPluginConfigManager();
690      pluginConfigManager.initializeUserPlugins(pluginTypes);
691    }
692    catch (Exception e)
693    {
694      printWrappedText(err, ERR_LDIFIMPORT_CANNOT_INITIALIZE_PLUGINS.get(getMessage(e)));
695      return 1;
696    }
697
698    // See if there were any user-defined sets of include/exclude attributes or
699    // filters.  If so, then process them.
700    HashSet<AttributeType> excludeAttributes;
701    boolean excludeAllUserAttributes = false;
702    boolean excludeAllOperationalAttributes = false;
703    if (excludeAttributeStrings == null)
704    {
705      excludeAttributes = null;
706    }
707    else
708    {
709      excludeAttributes = new HashSet<>();
710      for (String attrName : excludeAttributeStrings.getValues())
711      {
712        String lowerName = attrName.toLowerCase();
713        if (lowerName.equals("*"))
714        {
715          excludeAllUserAttributes = true;
716        }
717        else if (lowerName.equals("+"))
718        {
719          excludeAllOperationalAttributes = true;
720        }
721        else
722        {
723          excludeAttributes.add(DirectoryServer.getAttributeTypeOrDefault(lowerName, attrName));
724        }
725      }
726    }
727
728    HashSet<AttributeType> includeAttributes;
729    boolean includeAllUserAttributes = false;
730    boolean includeAllOperationalAttributes = false;
731    if (includeAttributeStrings == null)
732    {
733      includeAttributes = null;
734    }
735    else
736    {
737      includeAttributes = new HashSet<>();
738      for (String attrName : includeAttributeStrings.getValues())
739      {
740        String lowerName = attrName.toLowerCase();
741        if (lowerName.equals("*"))
742        {
743          includeAllUserAttributes = true;
744        }
745        else if (lowerName.equals("+"))
746        {
747          includeAllOperationalAttributes = true;
748        }
749        else
750        {
751          includeAttributes.add(DirectoryServer.getAttributeTypeOrDefault(lowerName, attrName));
752        }
753      }
754    }
755
756    ArrayList<SearchFilter> excludeFilters;
757    if (excludeFilterStrings == null)
758    {
759      excludeFilters = null;
760    }
761    else
762    {
763      excludeFilters = new ArrayList<>();
764      for (String filterString : excludeFilterStrings.getValues())
765      {
766        try
767        {
768          excludeFilters.add(SearchFilter.createFilterFromString(filterString));
769        }
770        catch (DirectoryException de)
771        {
772          logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER, filterString, de.getMessageObject());
773          return 1;
774        }
775        catch (Exception e)
776        {
777          logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER, filterString, getExceptionMessage(e));
778          return 1;
779        }
780      }
781    }
782
783    ArrayList<SearchFilter> includeFilters;
784    if (includeFilterStrings == null)
785    {
786      includeFilters = null;
787    }
788    else
789    {
790      includeFilters = new ArrayList<>();
791      for (String filterString : includeFilterStrings.getValues())
792      {
793        try
794        {
795          includeFilters.add(SearchFilter.createFilterFromString(filterString));
796        }
797        catch (DirectoryException de)
798        {
799          logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER, filterString, de.getMessageObject());
800          return 1;
801        }
802        catch (Exception e)
803        {
804          logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER, filterString, getExceptionMessage(e));
805          return 1;
806        }
807      }
808    }
809
810
811    // Get information about the backends defined in the server.  Iterate
812    // through them, finding the one backend into which the LDIF should be
813    // imported and finding backends with subordinate base DNs that should be
814    // excluded from the import.
815    Backend<?> backend = null;
816    Set<DN> defaultIncludeBranches = null;
817    Set<DN> excludeBranches = new HashSet<>();
818    Set<DN> includeBranches = new HashSet<>();
819
820    if (includeBranchStrings.isPresent())
821    {
822      for (String s : includeBranchStrings.getValues())
823      {
824        DN includeBranch;
825        try
826        {
827          includeBranch = DN.valueOf(s);
828        }
829        catch (DirectoryException de)
830        {
831          logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, de.getMessageObject());
832          return 1;
833        }
834        catch (Exception e)
835        {
836          logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE, s, getExceptionMessage(e));
837          return 1;
838        }
839
840        includeBranches.add(includeBranch);
841      }
842    }
843
844    ArrayList<Backend>     backendList = new ArrayList<>();
845    ArrayList<BackendCfg>  entryList   = new ArrayList<>();
846    ArrayList<List<DN>> dnList = new ArrayList<>();
847    int code = BackendToolUtils.getBackends(backendList, entryList, dnList);
848    if (code != 0)
849    {
850      return code;
851    }
852
853    int numBackends = backendList.size();
854    for (int i=0; i < numBackends; i++)
855    {
856      Backend<?> b = backendList.get(i);
857
858      if(backendID.isPresent())
859      {
860        if (! backendID.getValue().equals(b.getBackendID()))
861        {
862          continue;
863        }
864      }
865      else
866      {
867        if (!useBackend(includeBranches, dnList.get(i)))
868        {
869          continue;
870        }
871      }
872
873      if (backend == null)
874      {
875        backend                = b;
876        defaultIncludeBranches = new HashSet<>(dnList.get(i));
877      }
878      else
879      {
880        logger.error(ERR_LDIFIMPORT_MULTIPLE_BACKENDS_FOR_ID);
881        return 1;
882      }
883    }
884
885    if (backend == null)
886    {
887      logger.error(ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID);
888      return 1;
889    }
890    else if (!backend.supports(BackendOperation.LDIF_IMPORT))
891    {
892      logger.error(ERR_LDIFIMPORT_CANNOT_IMPORT, backendID.getValue());
893      return 1;
894    }
895
896    for (List<DN> baseList : dnList)
897    {
898      for (DN baseDN : baseList)
899      {
900        for (DN importBase : defaultIncludeBranches)
901        {
902          if (!baseDN.equals(importBase) && baseDN.isDescendantOf(importBase))
903          {
904            excludeBranches.add(baseDN);
905            break;
906          }
907        }
908      }
909    }
910
911    for (String s : excludeBranchStrings.getValues())
912    {
913      DN excludeBranch;
914      try
915      {
916        excludeBranch = DN.valueOf(s);
917      }
918      catch (DirectoryException de)
919      {
920        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE, s, de.getMessageObject());
921        return 1;
922      }
923      catch (Exception e)
924      {
925        logger.error(ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE, s, getExceptionMessage(e));
926        return 1;
927      }
928
929      excludeBranches.add(excludeBranch);
930    }
931
932    if (! includeBranchStrings.isPresent())
933    {
934      includeBranches = defaultIncludeBranches;
935    }
936    else
937    {
938      // Make sure the selected backend will handle all the include branches
939      for(DN includeBranch : includeBranches)
940      {
941        if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches,
942                                   excludeBranches))
943        {
944          logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch, backendID.getValue());
945          return 1;
946        }
947      }
948    }
949
950
951    // See if the data should be read from LDIF files or generated via MakeLDIF.
952    LDIFImportConfig importConfig;
953    if (ldifFiles.isPresent())
954    {
955      ArrayList<String> fileList = new ArrayList<>(ldifFiles.getValues());
956      int badFileCount = 0;
957      for (String pathname : fileList)
958      {
959        File f = new File(pathname);
960        if (!f.canRead())
961        {
962          logger.error(ERR_LDIFIMPORT_CANNOT_READ_FILE, pathname);
963          badFileCount++;
964        }
965      }
966      if (badFileCount > 0)
967      {
968        return 1;
969      }
970      importConfig = new LDIFImportConfig(fileList);
971    }
972    else
973    {
974      Random random = newRandom();
975
976      String resourcePath = DirectoryServer.getInstanceRoot() + File.separator +
977                            PATH_MAKELDIF_RESOURCE_DIR;
978      TemplateFile tf = new TemplateFile(resourcePath, random);
979
980      ArrayList<LocalizableMessage> warnings = new ArrayList<>();
981      try
982      {
983        tf.parse(templateFile.getValue(), warnings);
984      }
985      catch (Exception e)
986      {
987        logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_TEMPLATE_FILE, templateFile.getValue(), e.getMessage());
988        return 1;
989      }
990
991      importConfig = new LDIFImportConfig(tf);
992    }
993
994
995      // Create the LDIF import configuration to use when reading the LDIF.
996      importConfig.setCompressed(isCompressed.isPresent());
997      importConfig.setClearBackend(clearBackend.isPresent());
998      importConfig.setEncrypted(isEncrypted.isPresent());
999      importConfig.setExcludeAttributes(excludeAttributes);
1000      importConfig.setExcludeBranches(excludeBranches);
1001      importConfig.setExcludeFilters(excludeFilters);
1002      importConfig.setIncludeAttributes(includeAttributes);
1003      importConfig.setIncludeBranches(includeBranches);
1004      importConfig.setIncludeFilters(includeFilters);
1005      importConfig.setValidateSchema(!skipSchemaValidation.isPresent());
1006      importConfig.setSkipDNValidation(skipDNValidation.isPresent());
1007      importConfig.setTmpDirectory(tmpDirectory.getValue());
1008
1009      try
1010      {
1011          importConfig.setThreadCount(threadCount.getIntValue());
1012      }
1013      catch(Exception e)
1014      {
1015          logger.error(ERR_LDIFIMPORT_CANNOT_PARSE_THREAD_COUNT,
1016                  threadCount.getValue(), e.getMessage());
1017          return 1;
1018      }
1019
1020    importConfig.setBufferSize(LDIF_BUFFER_SIZE);
1021    importConfig.setExcludeAllUserAttributes(excludeAllUserAttributes);
1022    importConfig.setExcludeAllOperationalAttributes(excludeAllOperationalAttributes);
1023    importConfig.setIncludeAllOpAttributes(includeAllOperationalAttributes);
1024    importConfig.setIncludeAllUserAttributes(includeAllUserAttributes);
1025
1026    // FIXME -- Should this be conditional?
1027    importConfig.setInvokeImportPlugins(true);
1028
1029    if (rejectFile != null)
1030    {
1031      try
1032      {
1033        ExistingFileBehavior existingBehavior = overwrite.isPresent()
1034            ? ExistingFileBehavior.OVERWRITE
1035            : ExistingFileBehavior.APPEND;
1036
1037        importConfig.writeRejectedEntries(rejectFile.getValue(),
1038                                          existingBehavior);
1039      }
1040      catch (Exception e)
1041      {
1042        logger.error(ERR_LDIFIMPORT_CANNOT_OPEN_REJECTS_FILE, rejectFile.getValue(), getExceptionMessage(e));
1043        return 1;
1044      }
1045    }
1046
1047    if (skipFile != null)
1048    {
1049      try
1050      {
1051        ExistingFileBehavior existingBehavior = overwrite.isPresent()
1052            ? ExistingFileBehavior.OVERWRITE
1053            : ExistingFileBehavior.APPEND;
1054
1055        importConfig.writeSkippedEntries(skipFile.getValue(),
1056                                          existingBehavior);
1057      }
1058      catch (Exception e)
1059      {
1060        logger.error(ERR_LDIFIMPORT_CANNOT_OPEN_SKIP_FILE, skipFile.getValue(), getExceptionMessage(e));
1061        return 1;
1062      }
1063    }
1064
1065    // Get the set of base DNs for the backend as an array.
1066    DN[] baseDNs = new DN[defaultIncludeBranches.size()];
1067    defaultIncludeBranches.toArray(baseDNs);
1068
1069
1070    // Acquire an exclusive lock for the backend.
1071    try
1072    {
1073      String lockFile = LockFileManager.getBackendLockFileName(backend);
1074      StringBuilder failureReason = new StringBuilder();
1075      if (! LockFileManager.acquireExclusiveLock(lockFile, failureReason))
1076      {
1077        logger.error(ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), failureReason);
1078        return 1;
1079      }
1080    }
1081    catch (Exception e)
1082    {
1083      logger.error(ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
1084      return 1;
1085    }
1086
1087
1088    // Launch the import.
1089    int retCode = 0;
1090    try
1091    {
1092      LDIFImportResult importResult =
1093          backend.importLDIF(importConfig, DirectoryServer.getInstance().getServerContext());
1094      if (countRejects.isPresent())
1095      {
1096        if (importResult.getEntriesRejected() > Integer.MAX_VALUE)
1097        {
1098          retCode = Integer.MAX_VALUE;
1099        }
1100        else
1101        {
1102          retCode = (int) importResult.getEntriesRejected();
1103        }
1104      }
1105    }
1106    catch (DirectoryException de)
1107    {
1108      LocalizableMessage msg;
1109      if (de.getResultCode() == ResultCode.CONSTRAINT_VIOLATION)
1110      {
1111        msg = ERR_LDIFIMPORT_ERROR_CONSTRAINT_VIOLATION.get();
1112      }
1113      else
1114      {
1115        msg = de.getMessageObject();
1116      }
1117      logger.error(ERR_LDIFIMPORT_ERROR_DURING_IMPORT.get(msg));
1118      retCode = 1;
1119    }
1120    catch (Exception e)
1121    {
1122      logger.error(ERR_LDIFIMPORT_ERROR_DURING_IMPORT, getExceptionMessage(e));
1123      retCode = 1;
1124    }
1125
1126
1127    // Release the exclusive lock on the backend.
1128    try
1129    {
1130      String lockFile = LockFileManager.getBackendLockFileName(backend);
1131      StringBuilder failureReason = new StringBuilder();
1132      if (! LockFileManager.releaseLock(lockFile, failureReason))
1133      {
1134        logger.warn(WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), failureReason);
1135        retCode = 1;
1136      }
1137    }
1138    catch (Exception e)
1139    {
1140      logger.warn(WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
1141      retCode = 1;
1142    }
1143
1144
1145    // Clean up after the import by closing the import config.
1146    importConfig.close();
1147    return retCode;
1148  }
1149
1150  private Object getMessage(Exception e)
1151  {
1152    try
1153    {
1154      throw e;
1155    }
1156    catch (ConfigException | InitializationException e2)
1157    {
1158      return e2.getMessage();
1159    }
1160    catch (Exception e2)
1161    {
1162      return getExceptionMessage(e2);
1163    }
1164  }
1165
1166  private boolean useBackend(Set<DN> includeBranches, List<DN> dnlist)
1167  {
1168    for (DN baseDN : dnlist)
1169    {
1170      for (DN includeDN : includeBranches)
1171      {
1172        if (baseDN.isAncestorOf(includeDN))
1173        {
1174          return true;
1175        }
1176      }
1177    }
1178    return false;
1179  }
1180
1181  private Random newRandom()
1182  {
1183    if (randomSeed.isPresent())
1184    {
1185      try
1186      {
1187        return new Random(randomSeed.getIntValue());
1188      }
1189      catch (Exception ignored)
1190      {
1191        // ignore
1192      }
1193    }
1194    return new Random();
1195  }
1196
1197  @Override
1198  public String getTaskId() {
1199    // NYI.
1200    return null;
1201  }
1202}