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 2011-2016 ForgeRock AS.
016 */
017package org.opends.server.tools;
018
019import static org.opends.messages.ToolMessages.*;
020import static org.opends.server.config.ConfigConstants.*;
021import static org.opends.server.util.StaticUtils.*;
022
023import static com.forgerock.opendj.cli.Utils.*;
024import static com.forgerock.opendj.cli.CommonArguments.*;
025
026import java.io.OutputStream;
027import java.io.PrintStream;
028import java.util.ArrayList;
029import java.util.List;
030import java.util.logging.Level;
031
032import org.forgerock.i18n.LocalizableMessage;
033import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1;
034import org.forgerock.i18n.slf4j.LocalizedLogger;
035import org.forgerock.opendj.config.server.ConfigException;
036import org.opends.server.admin.std.server.BackendCfg;
037import org.opends.server.api.Backend;
038import org.opends.server.api.Backend.BackendOperation;
039import org.opends.server.backends.RebuildConfig;
040import org.opends.server.backends.RebuildConfig.RebuildMode;
041import org.opends.server.core.CoreConfigManager;
042import org.opends.server.core.DirectoryServer;
043import org.opends.server.core.LockFileManager;
044import org.opends.server.extensions.ConfigFileHandler;
045import org.opends.server.loggers.DebugLogger;
046import org.opends.server.loggers.ErrorLogPublisher;
047import org.opends.server.loggers.ErrorLogger;
048import org.opends.server.loggers.JDKLogging;
049import org.opends.server.loggers.TextErrorLogPublisher;
050import org.opends.server.loggers.TextWriter;
051import org.opends.server.protocols.ldap.LDAPAttribute;
052import org.opends.server.tasks.RebuildTask;
053import org.opends.server.tools.tasks.TaskTool;
054import org.forgerock.opendj.ldap.DN;
055import org.opends.server.types.InitializationException;
056import org.opends.server.types.NullOutputStream;
057import org.opends.server.types.RawAttribute;
058import org.opends.server.util.StaticUtils;
059import org.opends.server.util.cli.LDAPConnectionArgumentParser;
060
061import com.forgerock.opendj.cli.ArgumentException;
062import com.forgerock.opendj.cli.BooleanArgument;
063import com.forgerock.opendj.cli.StringArgument;
064
065/**
066 * This program provides a utility to rebuild the contents of the indexes of a
067 * Directory Server backend. This will be a process that is intended to run
068 * separate from Directory Server and not internally within the server process
069 * (e.g., via the tasks interface).
070 */
071public class RebuildIndex extends TaskTool
072{
073
074  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
075
076  private StringArgument configClass;
077  private StringArgument configFile;
078  private StringArgument baseDNString;
079  private StringArgument indexList;
080  private StringArgument tmpDirectory;
081  private BooleanArgument rebuildAll;
082  private BooleanArgument rebuildDegraded;
083  private BooleanArgument clearDegradedState;
084
085  private final LDAPConnectionArgumentParser argParser = createArgParser(
086      "org.opends.server.tools.RebuildIndex",
087      INFO_REBUILDINDEX_TOOL_DESCRIPTION.get());
088
089  private RebuildConfig rebuildConfig = new RebuildConfig();
090  private Backend<?> currentBackend;
091
092  /**
093   * Processes the command-line arguments and invokes the rebuild process.
094   *
095   * @param args
096   *          The command-line arguments provided to this program.
097   */
098  public static void main(final String[] args)
099  {
100    final int retCode =
101        mainRebuildIndex(args, true, System.out, System.err);
102    if (retCode != 0)
103    {
104      System.exit(filterExitCode(retCode));
105    }
106  }
107
108  /**
109   * Processes the command-line arguments and invokes the rebuild process.
110   *
111   * @param args
112   *          The command-line arguments provided to this program.
113   * @param initializeServer
114   *          Indicates whether to initialize the server.
115   * @param outStream
116   *          The output stream to use for standard output, or {@code null} if
117   *          standard output is not needed.
118   * @param errStream
119   *          The output stream to use for standard error, or {@code null} if
120   *          standard error is not needed.
121   * @return The error code.
122   */
123  public static int mainRebuildIndex(final String[] args,
124      final boolean initializeServer, final OutputStream outStream,
125      final OutputStream errStream)
126  {
127    final RebuildIndex tool = new RebuildIndex();
128    return tool.process(args, initializeServer, outStream, errStream);
129  }
130
131  private int process(final String[] args, final boolean initializeServer,
132      final OutputStream outStream, final OutputStream errStream )
133  {
134    final PrintStream out = NullOutputStream.wrapOrNullStream(outStream);
135    final PrintStream err = NullOutputStream.wrapOrNullStream(errStream);
136    JDKLogging.enableConsoleLoggingForOpenDJ(Level.FINE);
137
138    // Initialize all the command-line argument types and register them with the
139    // parser.
140    try
141    {
142      initializeArguments(false);
143    }
144    catch (ArgumentException ae)
145    {
146      printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
147      return 1;
148    }
149
150    // Parse the command-line arguments provided to this program.
151    try
152    {
153      argParser.parseArguments(args);
154    }
155    catch (ArgumentException ae)
156    {
157      argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
158      return 1;
159    }
160
161    // If we should just display usage or version information,
162    // then print it and exit.
163    if (argParser.usageOrVersionDisplayed())
164    {
165      return 0;
166    }
167
168    if (indexList.getValues().isEmpty()
169        && !rebuildAll.isPresent()
170        && !rebuildDegraded.isPresent())
171    {
172      argParser.displayMessageAndUsageReference(err, ERR_REBUILDINDEX_REQUIRES_AT_LEAST_ONE_INDEX.get());
173      return 1;
174    }
175
176    if (rebuildAll.isPresent() && indexList.isPresent())
177    {
178      argParser.displayMessageAndUsageReference(err, ERR_REBUILDINDEX_REBUILD_ALL_ERROR.get());
179      return 1;
180    }
181
182    if (rebuildDegraded.isPresent() && indexList.isPresent())
183    {
184      argParser.displayMessageAndUsageReference(err, ERR_REBUILDINDEX_REBUILD_DEGRADED_ERROR.get("index"));
185      return 1;
186    }
187
188    if (rebuildDegraded.isPresent() && clearDegradedState.isPresent())
189    {
190      argParser.displayMessageAndUsageReference(err, ERR_REBUILDINDEX_REBUILD_DEGRADED_ERROR.get("clearDegradedState"));
191      return 1;
192    }
193
194    if (rebuildAll.isPresent() && rebuildDegraded.isPresent())
195    {
196      argParser.displayMessageAndUsageReference(err,
197          ERR_REBUILDINDEX_REBUILD_ALL_DEGRADED_ERROR.get("rebuildDegraded"));
198      return 1;
199    }
200
201    if (rebuildAll.isPresent() && clearDegradedState.isPresent())
202    {
203      argParser.displayMessageAndUsageReference(err,
204          ERR_REBUILDINDEX_REBUILD_ALL_DEGRADED_ERROR.get("clearDegradedState"));
205      return 1;
206    }
207
208    // Checks the version - if upgrade required, the tool is unusable
209    try
210    {
211      checkVersion();
212    }
213    catch (InitializationException e)
214    {
215      printWrappedText(err, e.getMessage());
216      return 1;
217    }
218    return process(argParser, initializeServer, out, err);
219  }
220
221  /**
222   * Initializes the arguments for the rebuild index tool.
223   *
224   * @param isMultipleBackends
225   *          {@code true} if the tool is used as internal.
226   * @throws ArgumentException
227   *           If the initialization fails.
228   */
229  private void initializeArguments(final boolean isMultipleBackends)
230      throws ArgumentException
231  {
232    argParser.setShortToolDescription(REF_SHORT_DESC_REBUILD_INDEX.get());
233
234    configClass =
235            StringArgument.builder("configClass")
236                    .shortIdentifier('C')
237                    .description(INFO_DESCRIPTION_CONFIG_CLASS.get())
238                    .hidden()
239                    .required()
240                    .defaultValue(ConfigFileHandler.class.getName())
241                    .valuePlaceholder(INFO_CONFIGCLASS_PLACEHOLDER.get())
242                    .buildAndAddToParser(argParser);
243    configFile =
244            StringArgument.builder("configFile")
245                    .shortIdentifier('f')
246                    .description(INFO_DESCRIPTION_CONFIG_FILE.get())
247                    .hidden()
248                    .required()
249                    .valuePlaceholder(INFO_CONFIGFILE_PLACEHOLDER.get())
250                    .buildAndAddToParser(argParser);
251
252    final StringArgument.Builder builder =
253            StringArgument.builder("baseDN")
254                    .shortIdentifier('b')
255                    .description(INFO_REBUILDINDEX_DESCRIPTION_BASE_DN.get())
256                    .required()
257                    .valuePlaceholder(INFO_BASEDN_PLACEHOLDER.get());
258    if (isMultipleBackends) {
259      builder.multiValued();
260    }
261    baseDNString = builder.buildAndAddToParser(argParser);
262
263    indexList =
264            StringArgument.builder("index")
265                    .shortIdentifier('i')
266                    .description(INFO_REBUILDINDEX_DESCRIPTION_INDEX_NAME.get())
267                    .multiValued()
268                    .valuePlaceholder(INFO_INDEX_PLACEHOLDER.get())
269                    .buildAndAddToParser(argParser);
270    rebuildAll =
271            BooleanArgument.builder("rebuildAll")
272                    .description(INFO_REBUILDINDEX_DESCRIPTION_REBUILD_ALL.get())
273                    .buildAndAddToParser(argParser);
274    rebuildDegraded =
275            BooleanArgument.builder("rebuildDegraded")
276                    .description(INFO_REBUILDINDEX_DESCRIPTION_REBUILD_DEGRADED.get())
277                    .buildAndAddToParser(argParser);
278    clearDegradedState =
279            BooleanArgument.builder("clearDegradedState")
280                    .description(INFO_REBUILDINDEX_DESCRIPTION_CLEAR_DEGRADED_STATE.get())
281                    .buildAndAddToParser(argParser);
282    tmpDirectory =
283            StringArgument.builder("tmpdirectory")
284                    .description(INFO_REBUILDINDEX_DESCRIPTION_TEMP_DIRECTORY.get())
285                    .defaultValue("import-tmp")
286                    .valuePlaceholder(INFO_REBUILDINDEX_TEMP_DIR_PLACEHOLDER.get())
287                    .buildAndAddToParser(argParser);
288
289    final BooleanArgument displayUsage = showUsageArgument();
290    argParser.addArgument(displayUsage);
291    argParser.setUsageArgument(displayUsage);
292  }
293
294  /** {@inheritDoc} */
295  @Override
296  protected int processLocal(final boolean initializeServer,
297      final PrintStream out, final PrintStream err)
298  {
299    // Performs the initial bootstrap of the Directory Server and processes the
300    // configuration.
301    final DirectoryServer directoryServer = DirectoryServer.getInstance();
302    if (initializeServer)
303    {
304      final int init = initializeServer(directoryServer, out, err);
305      if (init != 0)
306      {
307        return init;
308      }
309      setErrorAndDebugLogPublisher(out, err);
310    }
311
312    if (!configureRebuildProcess(baseDNString.getValue()))
313    {
314      return 1;
315    }
316
317    return rebuildIndex(currentBackend, rebuildConfig);
318  }
319
320  /**
321   * Configures the rebuild index process. i.e.: decodes the selected DN and
322   * retrieves the backend which holds it. Finally, initializes and sets the
323   * rebuild configuration.
324   *
325   * @param dn
326   *          User selected base DN.
327   * @return A boolean representing the result of the process.
328   */
329  private boolean configureRebuildProcess(final String dn) {
330    // Decodes the base DN provided by the user.
331    DN rebuildBaseDN = null;
332    try
333    {
334      rebuildBaseDN = DN.valueOf(dn);
335    }
336    catch (Exception e)
337    {
338      logger.error(ERR_CANNOT_DECODE_BASE_DN, dn,
339              getExceptionMessage(e));
340      return false;
341    }
342
343    // Retrieves the backend which holds the selected base DN.
344    try
345    {
346      setCurrentBackend(retrieveBackend(rebuildBaseDN));
347    }
348    catch (Exception e)
349    {
350      logger.error(LocalizableMessage.raw(e.getMessage()));
351      return false;
352    }
353
354    setRebuildConfig(initializeRebuildIndexConfiguration(rebuildBaseDN));
355    return true;
356  }
357
358  /**
359   * Defines the error and the debug log publisher used in this tool.
360   *
361   * @param out
362   *          The output stream to use for standard output, or {@code null} if
363   *          standard output is not needed.
364   * @param err
365   *          The output stream to use for standard error, or {@code null} if
366   *          standard error is not needed.
367   */
368  private void setErrorAndDebugLogPublisher(final PrintStream out,
369      final PrintStream err)
370  {
371    try
372    {
373      final ErrorLogPublisher errorLogPublisher =
374          TextErrorLogPublisher
375              .getToolStartupTextErrorPublisher(new TextWriter.STREAM(out));
376      ErrorLogger.getInstance().addLogPublisher(errorLogPublisher);
377      DebugLogger.getInstance().addPublisherIfRequired(new TextWriter.STREAM(out));
378    }
379    catch (Exception e)
380    {
381      err.println("Error installing the custom error logger: "
382          + stackTraceToSingleLineString(e));
383    }
384  }
385
386  /**
387   * Initializes the directory server.<br />
388   * Processes to :
389   * - bootstrapClient
390   * - initializeJMX
391   * - initializeConfiguration
392   * - initializeSchema
393   * - coreConfigManager.initializeCoreConfig()
394   * - initializeCryptoManager
395   *
396   * @param directoryServer
397   *          The current instance.
398   * @param out
399   *          The output stream to use for standard output, or {@code null} if
400   *          standard output is not needed.
401   * @param err
402   *          The output stream to use for standard error, or {@code null} if
403   *          standard error is not needed.
404   * @return The result code.
405   */
406  private int initializeServer(final DirectoryServer directoryServer,
407      final PrintStream out, final PrintStream err)
408  {
409    try
410    {
411      DirectoryServer.bootstrapClient();
412      DirectoryServer.initializeJMX();
413    }
414    catch (Exception e)
415    {
416      printWrappedText(err, ERR_SERVER_BOOTSTRAP_ERROR.get(getExceptionMessage(e)));
417      return 1;
418    }
419
420    try
421    {
422      directoryServer.initializeConfiguration(configClass.getValue(),
423          configFile.getValue());
424    }
425    catch (Exception ex)
426    {
427      printWrappedText(err, toErrorMsg(ERR_CANNOT_LOAD_CONFIG, ex));
428      return 1;
429    }
430
431    // Initializes the Directory Server schema elements.
432    try
433    {
434      directoryServer.initializeSchema();
435    }
436    catch (Exception e)
437    {
438      printWrappedText(err, toErrorMsg(ERR_CANNOT_LOAD_SCHEMA, e));
439      return 1;
440    }
441
442    // Initializes the Directory Server core configuration.
443    try
444    {
445      final CoreConfigManager coreConfigManager = new CoreConfigManager(directoryServer.getServerContext());
446      coreConfigManager.initializeCoreConfig();
447    }
448    catch (Exception ex)
449    {
450      printWrappedText(err, toErrorMsg(ERR_CANNOT_INITIALIZE_CORE_CONFIG, ex));
451      return 1;
452    }
453
454    // Initializes the Directory Server crypto manager.
455    try
456    {
457      directoryServer.initializeCryptoManager();
458    }
459    catch (Exception ex)
460    {
461      printWrappedText(err, toErrorMsg(ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER, ex));
462      return 1;
463    }
464
465    return 0;
466  }
467
468  private String toErrorMsg(Arg1<Object> errorMsg, Exception ex)
469  {
470    final LocalizableMessage message = getErrorMsg(ex, errorMsg);
471    return wrapText(message, MAX_LINE_WIDTH);
472  }
473
474  private LocalizableMessage getErrorMsg(Exception ex, Arg1<Object> errorMsg)
475  {
476    if (ex instanceof ConfigException || ex instanceof InitializationException)
477    {
478      return errorMsg.get(ex.getMessage());
479    }
480    return errorMsg.get(getExceptionMessage(ex));
481  }
482
483  /**
484   * Initializes and sets the rebuild index configuration.
485   *
486   * @param rebuildBaseDN
487   *          The selected base DN.
488   * @return A rebuild configuration.
489   */
490  private RebuildConfig initializeRebuildIndexConfiguration(
491      final DN rebuildBaseDN)
492  {
493    final RebuildConfig config = new RebuildConfig();
494    config.setBaseDN(rebuildBaseDN);
495    for (final String s : indexList.getValues())
496    {
497      config.addRebuildIndex(s);
498    }
499
500    if (rebuildAll.isPresent())
501    {
502      config.setRebuildMode(RebuildMode.ALL);
503    }
504    else if (rebuildDegraded.isPresent())
505    {
506      config.setRebuildMode(RebuildMode.DEGRADED);
507    }
508    else
509    {
510      if (clearDegradedState.isPresent())
511      {
512        config.isClearDegradedState(true);
513      }
514      config.setRebuildMode(RebuildMode.USER_DEFINED);
515    }
516
517    config.setTmpDirectory(tmpDirectory.getValue());
518    return config;
519  }
520
521  /**
522   * Launches the rebuild index process.
523   *
524   * @param backend
525   *          The directory server backend.
526   * @param rebuildConfig
527   *          The configuration which is going to be used by the rebuild index
528   *          process.
529   * @return An integer representing the result of the process.
530   */
531  private int rebuildIndex(final Backend<?> backend, final RebuildConfig rebuildConfig)
532  {
533    // Acquire an exclusive lock for the backend.
534    //TODO: Find a way to do this with the server online.
535    try
536    {
537      final String lockFile = LockFileManager.getBackendLockFileName(backend);
538      final StringBuilder failureReason = new StringBuilder();
539      if (!LockFileManager.acquireExclusiveLock(lockFile, failureReason))
540      {
541        logger.error(ERR_REBUILDINDEX_CANNOT_EXCLUSIVE_LOCK_BACKEND, backend.getBackendID(), failureReason);
542        return 1;
543      }
544    }
545    catch (Exception e)
546    {
547      logger.error(ERR_REBUILDINDEX_CANNOT_EXCLUSIVE_LOCK_BACKEND, backend
548              .getBackendID(), getExceptionMessage(e));
549      return 1;
550    }
551
552    int returnCode = 0;
553    try
554    {
555      backend.rebuildBackend(rebuildConfig, DirectoryServer.getInstance().getServerContext());
556    }
557    catch (InitializationException e)
558    {
559      logger.error(ERR_REBUILDINDEX_ERROR_DURING_REBUILD, e.getMessage());
560      returnCode = 1;
561    }
562    catch (Exception e)
563    {
564      logger.error(ERR_REBUILDINDEX_ERROR_DURING_REBUILD, getExceptionMessage(e));
565      returnCode = 1;
566    }
567    finally
568    {
569      // Release the shared lock on the backend.
570      try
571      {
572        final String lockFile = LockFileManager.getBackendLockFileName(backend);
573        final StringBuilder failureReason = new StringBuilder();
574        if (!LockFileManager.releaseLock(lockFile, failureReason))
575        {
576          logger.warn(WARN_REBUILDINDEX_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), failureReason);
577        }
578      }
579      catch (Exception e)
580      {
581        logger.error(WARN_REBUILDINDEX_CANNOT_UNLOCK_BACKEND, backend.getBackendID(), getExceptionMessage(e));
582      }
583    }
584
585    return returnCode;
586  }
587
588  /**
589   * Gets information about the backends defined in the server. Iterates through
590   * them, finding the one that holds the base DN.
591   *
592   * @param selectedDN
593   *          The user selected DN.
594   * @return The backend which holds the selected base DN.
595   * @throws ConfigException
596   *           If the backend is poorly configured.
597   * @throws Exception
598   *           If an exception occurred during the backend search.
599   */
600  private Backend<?> retrieveBackend(final DN selectedDN) throws ConfigException, Exception
601  {
602    final ArrayList<Backend> backendList = new ArrayList<>();
603    final ArrayList<BackendCfg> entryList = new ArrayList<>();
604    final ArrayList<List<DN>> dnList = new ArrayList<>();
605    BackendToolUtils.getBackends(backendList, entryList, dnList);
606
607    Backend<?> backend = null;
608    final int numBackends = backendList.size();
609    for (int i = 0; i < numBackends; i++)
610    {
611      final Backend<?> b = backendList.get(i);
612      final List<DN> baseDNs = dnList.get(i);
613      if (baseDNs.contains(selectedDN))
614      {
615        if (backend != null)
616        {
617          throw new ConfigException(ERR_MULTIPLE_BACKENDS_FOR_BASE.get(baseDNString.getValue()));
618        }
619        backend = b;
620      }
621    }
622
623    if (backend == null)
624    {
625      throw new ConfigException(ERR_NO_BACKENDS_FOR_BASE.get(baseDNString.getValue()));
626    }
627    if (!backend.supports(BackendOperation.INDEXING))
628    {
629      throw new ConfigException(ERR_BACKEND_NO_INDEXING_SUPPORT.get());
630    }
631    return backend;
632  }
633
634  /**
635   * This function allow internal use of the rebuild index tools. This function
636   * rebuilds indexes shared by multiple backends.
637   *
638   * @param initializeServer
639   *          Indicates whether to initialize the server.
640   * @param out
641   *          The print stream which is used to display errors/debug lines.
642   *          Usually redirected into a logger if the tool is used as external.
643   * @param args
644   *          The arguments used to launch the rebuild index process.
645   * @return An integer indicating the result of this action.
646   */
647  public int rebuildIndexesWithinMultipleBackends(
648      final boolean initializeServer, final PrintStream out,
649      final String... args)
650  {
651    try
652    {
653      setErrorAndDebugLogPublisher(out, out);
654
655      try
656      {
657        initializeArguments(true);
658      }
659      catch (ArgumentException ae)
660      {
661        printWrappedText(out, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
662        return 1;
663      }
664
665      try
666      {
667        argParser.parseArguments(args);
668      }
669      catch (ArgumentException ae)
670      {
671        argParser.displayMessageAndUsageReference(out, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
672        return 1;
673      }
674
675      final DirectoryServer directoryServer = DirectoryServer.getInstance();
676      if (initializeServer)
677      {
678        final int init = initializeServer(directoryServer, out, out);
679        if (init != 0)
680        {
681          return init;
682        }
683      }
684
685      for (final String dn : baseDNString.getValues())
686      {
687        if (!configureRebuildProcess(dn))
688        {
689          return 1;
690        }
691
692        final int result =
693            rebuildIndex(getCurrentBackend(), getRebuildConfig());
694        // If the rebuild index is going bad, process is stopped.
695        if (result != 0)
696        {
697          out.println(String.format(
698                  "An error occurs during the rebuild index process" +
699                  " in %s, rebuild index(es) aborted.",
700                  dn));
701          return 1;
702        }
703      }
704    }
705    finally
706    {
707      StaticUtils.close(out);
708    }
709    return 0;
710  }
711
712  /** {@inheritDoc} */
713  @Override
714  public String getTaskId()
715  {
716    // NYI.
717    return null;
718  }
719
720  /** {@inheritDoc} */
721  @Override
722  public void addTaskAttributes(List<RawAttribute> attributes)
723  {
724    // Required attributes
725    addLdapAttribute(attributes, ATTR_REBUILD_BASE_DN, baseDNString.getValue());
726
727    attributes.add(new LDAPAttribute(ATTR_REBUILD_INDEX, indexList.getValues()));
728
729    if (hasNonDefaultValue(tmpDirectory))
730    {
731      addLdapAttribute(attributes, ATTR_REBUILD_TMP_DIRECTORY, tmpDirectory.getValue());
732    }
733
734    if (hasNonDefaultValue(rebuildAll))
735    {
736      addLdapAttribute(attributes, ATTR_REBUILD_INDEX, REBUILD_ALL);
737    }
738
739    if (hasNonDefaultValue(rebuildDegraded))
740    {
741      addLdapAttribute(attributes, ATTR_REBUILD_INDEX, REBUILD_DEGRADED);
742    }
743
744    if (hasNonDefaultValue(clearDegradedState))
745    {
746      addLdapAttribute(attributes, ATTR_REBUILD_INDEX_CLEARDEGRADEDSTATE, "true");
747    }
748  }
749
750  private void addLdapAttribute(List<RawAttribute> attributes, String attrType, String attrValue)
751  {
752    attributes.add(new LDAPAttribute(attrType, attrValue));
753  }
754
755  private boolean hasNonDefaultValue(BooleanArgument arg)
756  {
757    return arg.getValue() != null
758        && !arg.getValue().equals(arg.getDefaultValue());
759  }
760
761  private boolean hasNonDefaultValue(StringArgument arg)
762  {
763    return arg.getValue() != null
764        && !arg.getValue().equals(arg.getDefaultValue());
765  }
766
767  /** {@inheritDoc} */
768  @Override
769  public String getTaskObjectclass()
770  {
771    return "ds-task-rebuild";
772  }
773
774  /** {@inheritDoc} */
775  @Override
776  public Class<?> getTaskClass()
777  {
778    return RebuildTask.class;
779  }
780
781  /**
782   * Returns the rebuild configuration.
783   *
784   * @return The rebuild configuration.
785   */
786  public RebuildConfig getRebuildConfig()
787  {
788    return rebuildConfig;
789  }
790
791  /**
792   * Sets the rebuild configuration.
793   *
794   * @param rebuildConfig
795   *          The rebuild configuration to set.
796   */
797  public void setRebuildConfig(RebuildConfig rebuildConfig)
798  {
799    this.rebuildConfig = rebuildConfig;
800  }
801
802  /**
803   * Returns the current backend.
804   *
805   * @return The current backend.
806   */
807  public Backend<?> getCurrentBackend()
808  {
809    return currentBackend;
810  }
811
812  /**
813   * Sets the current backend.
814   *
815   * @param currentBackend
816   *          The current backend to set.
817   */
818  public void setCurrentBackend(Backend<?> currentBackend)
819  {
820    this.currentBackend = currentBackend;
821  }
822}