mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Violette Roche-Montane
03.27.2013 dcccc9d1b70be62288d1dd23f3084e6451354661
CR-2181 OPENDJ-1030 Upgrade : add a task to rebuild ds-sync-hist index when upgrading from 2.5.0-Xpress
10 files modified
667 ■■■■ changed files
opends/src/messages/messages/tools.properties 16 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/RebuildIndex.java 185 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/upgrade/AbstractUpgradeTask.java 39 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/upgrade/Upgrade.java 127 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/upgrade/UpgradeCli.java 10 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/upgrade/UpgradeLog.java 26 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/upgrade/UpgradeTask.java 39 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java 175 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java 43 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/ConsoleApplication.java 7 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/tools.properties
@@ -2537,6 +2537,16 @@
(if present)
SEVERE_ERR_UPGRADE_RENAME_SNMP_SECURITY_CONFIG_FILE_1838=An error occurred when \
trying to rename the SNMP security config file: %s
INFO_UPGRADE_REBUILD_INDEX_STARTS_1839=Rebuilding index(es) %s
INFO_UPGRADE_REBUILD_INDEX_ENDS_1840=Rebuild index task ends
INFO_UPGRADE_PERFORMING_POST_TASKS_1841=Performing post upgrade tasks
INFO_UPGRADE_POST_TASKS_COMPLETE_1842=Post upgrade tasks complete
SEVERE_ERR_UPGRADE_PERFORMING_POST_TASKS_FAIL_1843=An error occured during post \
upgrade task. Process aborted. Please check log for further details
INFO_UPGRADE_REBUILD_INDEX_DECLINED_1844 =You have to rebuild the '%s' index \
manually to get a fully functional server
SEVERE_ERR_UPGRADE_INVALID_LOG_FILE_1845=Invalid log file %s
INFO_UPGRADE_REBUILD_INDEX_ARGUMENTS_1846=The rebuild index tool arguments are %s
# Upgrade tasks
INFO_UPGRADE_TASK_6869_SUMMARY_10000=Fixing de-DE collation matching rule OID
@@ -2561,7 +2571,5 @@
INFO_UPGRADE_TASK_8985_1_SUMMARY_10017=Adding 'emailAddress' attribute
INFO_UPGRADE_TASK_8985_2_SUMMARY_10018=Updating subject attribute to user attribute configuration
INFO_UPGRADE_TASK_9013_DESCRIPTION_10019=OpenDJ 2.5.0-Xpress1 introduced a \
 regression in the ds-sync-hist ordering index. This index must be rebuilt \
 after the upgrade has completed and before restarting OpenDJ. Do you wish to \
 continue?
 regression in the ds-sync-hist ordering index. This index has to be rebuilt and this could take a long time \
 to proceed. Do you want to launch this process at automatically at the end of the upgrade ?
opends/src/server/org/opends/server/tools/RebuildIndex.java
@@ -36,6 +36,7 @@
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.util.BuildVersion;
import org.opends.server.util.StaticUtils;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.BooleanArgument;
import org.opends.server.util.args.LDAPConnectionArgumentParser;
@@ -91,6 +92,11 @@
      "org.opends.server.tools.RebuildIndex",
      INFO_REBUILDINDEX_TOOL_DESCRIPTION.get());
  private RebuildConfig rebuildConfig = new RebuildConfig();
  private Backend currentBackend = null;
  /**
   * Processes the command-line arguments and invokes the rebuild process.
   *
@@ -329,38 +335,52 @@
      setErrorAndDebugLogPublisher(out, err);
    }
    if (!configureRebuildProcess(baseDNString.getValue()))
    {
      return 1;
    }
    return rebuildIndex(currentBackend, rebuildConfig);
  }
  /**
   * Configures the rebuild index process. i.e.: decodes the selected DN and
   * retrieves the backend which holds it. Finally, initializes and sets the
   * rebuild configuration.
   *
   * @param dn
   *          User selected base DN.
   * @return A boolean representing the result of the process.
   */
  private boolean configureRebuildProcess(final String dn) {
    // Decodes the base DN provided by the user.
    DN rebuildBaseDN = null;
    try
    {
      rebuildBaseDN = DN.decode(baseDNString.getValue());
      rebuildBaseDN = DN.decode(dn);
    }
    catch (Exception e)
    {
      final Message message =
          ERR_CANNOT_DECODE_BASE_DN.get(baseDNString.getValue(),
          ERR_CANNOT_DECODE_BASE_DN.get(dn,
              getExceptionMessage(e));
      logError(message);
      return 1;
      return false;
    }
    // Retrieves the backend which holds the selected base DN.
    Backend backend = null;
    try
    {
      backend = retrieveBackend(rebuildBaseDN);
      setCurrentBackend(retrieveBackend(rebuildBaseDN));
    }
    catch (Exception e)
    {
      logError(Message.raw(e.getMessage()));
      return 1;
      return false;
    }
    // Sets the rebuild index configuration.
    final RebuildConfig rebuildConfig =
        initializeRebuildIndexConfiguration(rebuildBaseDN);
    return rebuildIndex(backend, rebuildConfig);
    setRebuildConfig(initializeRebuildIndexConfiguration(rebuildBaseDN));
    return true;
  }
  /**
@@ -396,6 +416,7 @@
  /**
   * Initializes the directory server.<br />
   * Processes to :
   * - bootstrapClient
   * - initializeJMX
   * - initializeConfiguration
@@ -405,10 +426,10 @@
   *
   * @param directoryServer
   *          The current instance.
   * @param outStream
   * @param out
   *          The output stream to use for standard output, or {@code null} if
   *          standard output is not needed.
   * @param errStream
   * @param err
   *          The output stream to use for standard error, or {@code null} if
   *          standard error is not needed.
   * @return The error code.
@@ -527,32 +548,32 @@
  private RebuildConfig initializeRebuildIndexConfiguration(
      final DN rebuildBaseDN)
  {
    final RebuildConfig rebuildConfig = new RebuildConfig();
    rebuildConfig.setBaseDN(rebuildBaseDN);
    final RebuildConfig config = new RebuildConfig();
    config.setBaseDN(rebuildBaseDN);
    for (final String s : indexList.getValues())
    {
      rebuildConfig.addRebuildIndex(s);
      config.addRebuildIndex(s);
    }
    if (rebuildAll.isPresent())
    {
      rebuildConfig.setRebuildMode(RebuildMode.ALL);
      config.setRebuildMode(RebuildMode.ALL);
    }
    else if (rebuildDegraded.isPresent())
    {
      rebuildConfig.setRebuildMode(RebuildMode.DEGRADED);
      config.setRebuildMode(RebuildMode.DEGRADED);
    }
    else
    {
      if (clearDegradedState.isPresent())
      {
        rebuildConfig.isClearDegradedState(true);
        config.isClearDegradedState(true);
      }
      rebuildConfig.setRebuildMode(RebuildMode.USER_DEFINED);
      config.setRebuildMode(RebuildMode.USER_DEFINED);
    }
    rebuildConfig.setTmpDirectory(tmpDirectory.getValue());
    return rebuildConfig;
    config.setTmpDirectory(tmpDirectory.getValue());
    return config;
  }
  /**
@@ -635,7 +656,7 @@
  /**
   * Gets information about the backends defined in the server. Iterates through
   * them, finding the one backend to be verified.
   * them, finding the one that holds the base DN.
   *
   * @param selectedDN
   *          The user selected DN.
@@ -699,6 +720,82 @@
  }
  /**
   * This function allow internal use of the rebuild index tools. This function
   * rebuilds indexes shared by multiple backends.
   *
   * @param initializeServer
   *          Indicates whether to initialize the server.
   * @param out
   *          The print stream which is used to display errors/debug lines.
   *          Usually redirected into a logger if the tool is used as external.
   * @param args
   *          The arguments used to launch the rebuild index process.
   * @return An integer indicating the result of this action.
   */
  public int rebuildIndexesWithinMultipleBackends(
      final boolean initializeServer, final PrintStream out,
      final String... args)
  {
    try
    {
      setErrorAndDebugLogPublisher(out, out);
      try
      {
        initializeArguments(true);
      }
      catch (ArgumentException ae)
      {
        final Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
        out.println(wrapText(message, MAX_LINE_WIDTH));
        return 1;
      }
      try
      {
        argParser.parseArguments(args);
      }
      catch (ArgumentException ae)
      {
        final Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
        out.println(wrapText(message, MAX_LINE_WIDTH));
        return 1;
      }
      final DirectoryServer directoryServer = DirectoryServer.getInstance();
      if (initializeServer)
      {
        initializeServer(directoryServer, out, out);
      }
      for (final String dn : baseDNString.getValues())
      {
        if (!configureRebuildProcess(dn))
        {
          return 1;
        }
        final int result =
            rebuildIndex(getCurrentBackend(), getRebuildConfig());
        // If the rebuild index is going bad, process is stopped.
        if (result != 0)
        {
          out.println(String.format(
                  "An error occurs during the rebuild index process" +
                  " in %s, rebuild index(es) aborted.",
                  dn));
          return 1;
        }
      }
    }
    finally
    {
      StaticUtils.close(out);
    }
    return 0;
  }
  /**
   * {@inheritDoc}
   */
  public String getTaskId()
@@ -781,4 +878,46 @@
  {
    return RebuildTask.class;
  }
  /**
   * Returns the rebuild configuration.
   *
   * @return The rebuild configuration.
   */
  public RebuildConfig getRebuildConfig()
  {
    return rebuildConfig;
  }
  /**
   * Sets the rebuild configuration.
   *
   * @param rebuildConfig
   *          The rebuild configuration to set.
   */
  public void setRebuildConfig(RebuildConfig rebuildConfig)
  {
    this.rebuildConfig = rebuildConfig;
  }
  /**
   * Returns the current backend.
   *
   * @return The current backend.
   */
  public Backend getCurrentBackend()
  {
    return currentBackend;
  }
  /**
   * Sets the current backend.
   *
   * @param currentBackend
   *          The current backend to set.
   */
  public void setCurrentBackend(Backend currentBackend)
  {
    this.currentBackend = currentBackend;
  }
}
opends/src/server/org/opends/server/tools/upgrade/AbstractUpgradeTask.java
@@ -45,16 +45,6 @@
   * {@inheritDoc}
   */
  @Override
  public void end(UpgradeContext context)
      throws ClientException
  {
    // Nothing to do.
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void interact(UpgradeContext context)
      throws ClientException
  {
@@ -75,9 +65,38 @@
   * {@inheritDoc}
   */
  @Override
  public void perform(UpgradeContext context) throws ClientException
  {
    // Must be implemented.
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void verify(UpgradeContext context)
      throws ClientException
  {
    // Nothing to do.
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void postUpgrade(UpgradeContext context)
      throws ClientException
  {
    // Nothing to do.
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void postponePostUpgrade(UpgradeContext context)
      throws ClientException
  {
    // Nothing to do.
  }
}
opends/src/server/org/opends/server/tools/upgrade/Upgrade.java
@@ -32,6 +32,7 @@
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.tools.upgrade.FormattedNotificationCallback.*;
import static org.opends.server.tools.upgrade.UpgradeTasks.*;
import static org.opends.server.tools.upgrade.LicenseFile.*;
import java.io.File;
import java.io.FileWriter;
@@ -89,6 +90,11 @@
  static final int EXIT_CODE_MANUAL_INTERVENTION = 2;
  /**
   * If the upgrade contains some post upgrade tasks to do.
   */
  static boolean hasPostUpgradeTask = false;
  /**
   * Developers should register upgrade tasks below.
   */
  private static final NavigableMap<BuildVersion, List<UpgradeTask>> TASKS =
@@ -292,7 +298,8 @@
    /* See OPENDJ-992 */
    register("2.5.0.9013",
        regressionInVersion("2.5.0.7640",
            rebuildSingleIndex(INFO_UPGRADE_TASK_9013_DESCRIPTION.get())));
            rebuildSingleIndex(INFO_UPGRADE_TASK_9013_DESCRIPTION.get(),
                "ds-sync-hist")));
    /*
     * All upgrades will refresh the server configuration schema and generate
@@ -300,11 +307,8 @@
     */
    registerLast(
        copySchemaFile("02-config.ldif"),
        updateConfigUpgradeFolder());
    // TODO for tests.
    /*register("2.5.0.8657",
       rebuildAllIndexes(Message.raw("This is fake Rebuild Task")));*/
        updateConfigUpgradeFolder(),
        postUpgradeRebuildIndexes());
    // @formatter:on
  }
@@ -322,7 +326,7 @@
   * @return A list containing all the tasks which are required in order to
   *         upgrade from {@code fromVersion} to {@code toVersion}.
   */
 private static List<UpgradeTask> getUpgradeTasks(
  private static List<UpgradeTask> getUpgradeTasks(
      final BuildVersion fromVersion, final BuildVersion toVersion)
  {
    final List<UpgradeTask> tasks = new LinkedList<UpgradeTask>();
@@ -347,12 +351,10 @@
  public static void upgrade(final UpgradeContext context)
      throws ClientException
  {
    // Checks and validate the version number.
    // Checks and validates the version number.
    isVersionCanBeUpdated(context);
    // Server offline ?
    // Server must be offline.
    checkIfServerIsRunning();
    context.notify( INFO_UPGRADE_TITLE.get(), TITLE_CALLBACK);
@@ -378,7 +380,7 @@
    /*
     * Verify tasks requirements.
     * Eg. if a task requires mandatory user interaction, like rebuild index,
     * E.g. if a task requires mandatory user interaction
     * and the application is non-interactive then, the process
     * may abort immediately.
     */
@@ -407,21 +409,49 @@
      context.notify(INFO_UPGRADE_PERFORMING_TASKS.get(),
          TITLE_CALLBACK);
      perform(context, tasks);
      /*
       * Notify each task that the upgrade is about to be started.
       */
      for (final UpgradeTask task : tasks)
      {
        task.start(context);
      }
      /*
       * Perform each task.
       */
      for (final UpgradeTask task : tasks)
      {
        task.perform(context);
      }
      if (UpgradeTasks.countErrors == 0)
      {
        // At the end, and if only if succeed, we need to change the buildInfo
        // file with the version number updated.
        /*
         * The end of a successful upgrade is marked up with the build info
         * file update and the license, if present, requires the creation of
         * an approval file.
         */
        changeBuildInfoVersion(context);
        // Writes the license if needed.
        LicenseFile.createFileLicenseApproved();
        createFileLicenseApproved();
      }
      else
      {
        context.notify(
            ERR_UPGRADE_FAILS.get(UpgradeTasks.countErrors), TITLE_CALLBACK);
      }
      /*
       * Performs the post upgrade tasks.
       */
      if (hasPostUpgradeTask && (UpgradeTasks.countErrors == 0))
      {
        context
            .notify(INFO_UPGRADE_PERFORMING_POST_TASKS.get(), TITLE_CALLBACK);
        performPostUpgradeTasks(context, tasks);
        context.notify(INFO_UPGRADE_POST_TASKS_COMPLETE.get(), TITLE_CALLBACK);
      }
    }
    catch (final ClientException e)
    {
@@ -443,35 +473,29 @@
    }
  }
  private static void perform(final UpgradeContext context,
  private static void performPostUpgradeTasks(final UpgradeContext context,
      final List<UpgradeTask> tasks)
      throws ClientException
  {
    /*
     * Notify each task that the upgrade is about to be started.
     */
    boolean isOk = true;
    for (final UpgradeTask task : tasks)
    {
      task.start(context);
    }
    /*
     * Perform each task.
     */
    for (final UpgradeTask task : tasks)
    {
      task.perform(context);
    }
    /*
     * Notify each task that the upgrade has completed. Tasks may do cleanup
     * work here, such as removing files.
     */
    for (final UpgradeTask task : tasks)
    {
      task.end(context);
      if (isOk)
      {
        try
        {
          task.postUpgrade(context);
        }
        catch (ClientException e)
        {
          LOG.log(Level.SEVERE, e.getMessage());
          isOk = false;
        }
      }
      else
      {
        task.postponePostUpgrade(context);
      }
    }
  }
@@ -673,6 +697,27 @@
    }
  }
  /**
   * Returns {@code true} if the current upgrade contains post upgrade tasks.
   *
   * @return {@code true} if the current upgrade contains post upgrade tasks.
   */
  static boolean hasPostUpgradeTask()
  {
    return hasPostUpgradeTask;
  }
  /**
   * Sets {@code true} if the current upgrade contains post upgrade tasks.
   *
   * @param hasPostUpgradeTask
   *          {@code true} if the current upgrade contains post upgrade tasks.
   */
  static void setHasPostUpgradeTask(boolean hasPostUpgradeTask)
  {
    Upgrade.hasPostUpgradeTask = hasPostUpgradeTask;
  }
  // Prevent instantiation.
  private Upgrade()
  {
opends/src/server/org/opends/server/tools/upgrade/UpgradeCli.java
@@ -41,6 +41,7 @@
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.callback.Callback;
@@ -405,25 +406,32 @@
        // Displays formatted notifications.
        final FormattedNotificationCallback fnc =
            (FormattedNotificationCallback) c;
        LOG.log(INFO, fnc.getMessage());
        switch (fnc.getMessageSubType())
        {
        case TITLE_CALLBACK:
          println(Style.TITLE, Message.raw(fnc.getMessage()), 0);
          LOG.log(INFO, fnc.getMessage());
          break;
        case SUBTITLE_CALLBACK:
          println(Style.SUBTITLE, Message.raw(fnc.getMessage()),
              4);
          LOG.log(INFO, fnc.getMessage());
          break;
        case NOTICE_CALLBACK:
          println(Style.NOTICE, Message.raw(fnc.getMessage()), 1);
          LOG.log(INFO, fnc.getMessage());
          break;
        case ERROR_CALLBACK:
          println(Style.ERROR, Message.raw(fnc.getMessage()), 1);
          LOG.log(Level.SEVERE, fnc.getMessage());
          break;
        case BREAKLINE:
          println(Style.BREAKLINE, Message.raw(fnc.getMessage()), 1);
          break;
        case WARNING:
          println(Style.WARNING, Message.raw(fnc.getMessage()), 2);
          LOG.log(Level.WARNING, fnc.getMessage());
          break;
        default:
          LOG.log(SEVERE, "Unsupported message type: "
            + fnc.getMessage());
opends/src/server/org/opends/server/tools/upgrade/UpgradeLog.java
@@ -28,7 +28,10 @@
package org.opends.server.tools.upgrade;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
@@ -37,6 +40,9 @@
import java.util.logging.Logger;
import org.opends.messages.RuntimeMessages;
import org.opends.server.tools.ClientException;
import static org.opends.messages.ToolMessages.ERR_UPGRADE_INVALID_LOG_FILE;
/**
 * Creates a historical log about the upgrade. If file does not exist an attempt
@@ -104,4 +110,24 @@
    logger.log(Level.CONFIG, RuntimeMessages.NOTE_INSTANCE_DIRECTORY.get(
        UpgradeUtils.getInstancePath()).toString());
  }
  /**
   * Returns the print stream of the current logger.
   *
   * @return the print stream of the current logger.
   * @throws ClientException
   *           If the file defined by the logger is not found or invalid.
   */
  static PrintStream getPrintStream() throws ClientException
  {
    try
    {
      return new PrintStream(new FileOutputStream(logFile, true));
    }
    catch (FileNotFoundException e)
    {
      throw new ClientException(1, ERR_UPGRADE_INVALID_LOG_FILE.get(e
          .getMessage()));
    }
  }
}
opends/src/server/org/opends/server/tools/upgrade/UpgradeTask.java
@@ -62,20 +62,6 @@
  }
  /**
   * Notifies this task that the upgrade has completed. This method will be
   * invoked after all upgrade tasks have completed successfully. Most task
   * implementation will not need to do anything.
   *
   * @param context
   *          Context through which tasks can interact with the server
   *          installation.
   * @throws ClientException
   *           If an error occurred while performing the task.
   */
  void end(UpgradeContext context)
      throws ClientException;
  /**
   * Performs this upgrade task.
   *
   * @param context
@@ -126,4 +112,29 @@
   */
  void interact(UpgradeContext context)
      throws ClientException;
  /**
   * This method will be invoked after all upgrade tasks have completed
   * successfully The post upgrade tasks are processes which should be launched
   * after a successful upgrade.
   *
   * @param context
   *          Context through which tasks can interact with the server
   *          installation.
   * @throws ClientException
   *           If the task cannot proceed.
   */
  void postUpgrade(UpgradeContext context) throws ClientException;
  /**
   * This method will be invoked only if one of the previous post upgrade task
   * has failed.
   *
   * @param context
   *          Context through which tasks can interact with the server
   *          installation.
   * @throws ClientException
   *           If the task cannot proceed.
   */
  void postponePostUpgrade(UpgradeContext context) throws ClientException;
}
opends/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java
@@ -31,20 +31,28 @@
import static org.opends.server.tools.ToolConstants.OPTION_LONG_FORCE_UPGRADE;
import static org.opends.server.tools.ToolConstants.OPTION_LONG_NO_PROMPT;
import static org.opends.server.tools.upgrade.FileManager.copy;
import static org.opends.server.tools.upgrade.Installation
.CURRENT_CONFIG_FILE_NAME;
import static org.opends.server.tools.upgrade.Upgrade.*;
import static org.opends.server.tools.upgrade.UpgradeUtils.*;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.callback.ConfirmationCallback;
import javax.security.auth.callback.TextOutputCallback;
import org.opends.messages.Message;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.tools.ClientException;
import org.opends.server.tools.RebuildIndex;
import org.opends.server.tools.upgrade.UpgradeTask.TaskType;
import org.opends.server.util.BuildVersion;
import org.opends.server.util.ChangeOperationType;
@@ -66,6 +74,16 @@
      .getLogger(UpgradeCli.class.getName());
  /**
   * The indexes list to rebuild are united here.
   */
  static Set<String> indexesListToRebuild = new HashSet<String>();
  /**
   * A flag to avoid rebuild indexes if all already selected.
   */
  static boolean isRebuildAllIndexes = false;
  /**
   * Returns a new upgrade task which applies an LDIF record to all
   * configuration entries matching the provided filter.
   *
@@ -412,19 +430,34 @@
      }
      @Override
      public void end(UpgradeContext context) throws ClientException
      public void postUpgrade(UpgradeContext context) throws ClientException
      {
        if (currentVersionEqualToOrMoreRecentThan(context, version))
        {
          for (UpgradeTask task : tasks)
          boolean isOk = true;
          for (final UpgradeTask task : tasks)
          {
            task.end(context);
            if (isOk)
            {
              try
              {
                task.postUpgrade(context);
              }
              catch (ClientException e)
              {
                LOG.log(Level.SEVERE, e.getMessage());
                isOk = false;
              }
            }
            else
            {
              task.postponePostUpgrade(context);
            }
          }
        }
      }
      private boolean currentVersionEqualToOrMoreRecentThan(
          UpgradeContext context, final BuildVersion version)
      {
@@ -447,7 +480,7 @@
      @Override
      public void perform(final UpgradeContext context) throws ClientException
      {
        // TODO
        // NYI.
      }
      @Override
@@ -467,48 +500,136 @@
  /**
   * Creates a rebuild index task for a single index. At the moment this is
   * implemented as a simple stub which displays a message which should prompt
   * the user to rebuild the index manually once the upgrade has completed.
   * <p>
   * In future this task should register the index to be rebuilt in a table. A
   * subsequent task executed at the end of the upgrade process will then obtain
   * the set of indexes to be rebuilt, optimize it (e.g. removing duplicates),
   * and perform the rebuild.
   * Creates a rebuild index task for a given single index. As this task is
   * possibly lengthy, it's considered as a post upgrade task. This task is not
   * mandatory; e.g not require user interaction, but could be required to get a
   * fully functional server. <br />
   * The post upgrade task just register the task. The rebuild indexes tasks are
   * completed at the end of the upgrade process.
   *
   * @param summary
   *          A message describing why the index needs to be rebuilt and asking
   *          them whether or not they wish to continue.
   *          them whether or not they wish to perform this task after the
   *          upgrade.
   * @param index
   *          The index to rebuild.
   * @return The rebuild index task.
   */
  public static UpgradeTask rebuildSingleIndex(final Message summary)
  public static UpgradeTask rebuildSingleIndex(final Message summary,
      final String index)
  {
    return new AbstractUpgradeTask()
    {
      @Override
      public void verify(final UpgradeContext context) throws ClientException
      {
        verifyTaskType(TaskType.MANDATORY_USER_INTERACTION, context);
      }
      private boolean isATaskToPerform = false;
      @Override
      public void interact(UpgradeContext context) throws ClientException
      {
        // Require acknowledgment from the user.
        Upgrade.setHasPostUpgradeTask(true);
        // Requires answer from the user.
        final int answer = context.confirmYN(summary, ConfirmationCallback.NO);
        isATaskToPerform = (answer == ConfirmationCallback.YES);
      }
        // The user refused to perform this task.
        if (answer == ConfirmationCallback.NO)
      @Override
      public void postUpgrade(final UpgradeContext context)
          throws ClientException
      {
        if (isATaskToPerform)
        {
          throw new ClientException(EXIT_CODE_MANUAL_INTERVENTION,
              INFO_UPGRADE_ABORTED_BY_USER.get());
          indexesListToRebuild.add(index);
        }
        else
        {
          postponePostUpgrade(context);
        }
      }
      @Override
      public void perform(final UpgradeContext context) throws ClientException
      public void postponePostUpgrade(UpgradeContext context)
          throws ClientException
      {
        // TODO: automatic rebuild is not supported yet.
        context.notify(INFO_UPGRADE_REBUILD_INDEX_DECLINED.get(index),
            TextOutputCallback.WARNING);
      }
    };
  }
  /**
   * This task is processed at the end of the upgrade, rebuilding indexes. If a
   * rebuild all indexes has been registered before, it takes the flag
   * relatively to single rebuild index.
   *
   * @return The post upgrade rebuild indexes task.
   */
  public static UpgradeTask postUpgradeRebuildIndexes()
  {
    return new AbstractUpgradeTask()
    {
      @Override
      public void postUpgrade(final UpgradeContext context)
          throws ClientException
      {
        if (isRebuildAllIndexes)
        {
          // TODO To implement
        }
        else if (!indexesListToRebuild.isEmpty())
        {
          final Message message = INFO_UPGRADE_REBUILD_INDEX_STARTS.get(Arrays
              .toString(indexesListToRebuild.toArray()));
          final ProgressNotificationCallback pnc =
              new ProgressNotificationCallback(0, message, 25);
          LOG.log(Level.INFO, message.toString());
          context.notifyProgress(pnc);
          // Sets the arguments like the rebuild index command line.
          final List<String> args = new LinkedList<String>();
          args.addAll(Arrays.asList(
              "-f",
              new File(configDirectory, CURRENT_CONFIG_FILE_NAME)
                .getAbsolutePath()));
          // Adding all requested indexes.
          for (final String indexToRebuild : indexesListToRebuild)
          {
            args.add("-i");
            args.add(indexToRebuild);
          }
          // Index(es) could be contained in several backends.
          for (final String be : UpgradeUtils.getLocalBackendsFromConfig())
          {
            args.add("-b");
            args.add(be);
          }
          final String[] commandLineArgs =
              args.toArray(new String[args.size()]);
          // Displays info about command line args for log only.
          LOG.log(Level.INFO, INFO_UPGRADE_REBUILD_INDEX_ARGUMENTS.get(Arrays
              .toString(commandLineArgs)).toString());
          /*
           * The rebuild-index process just display a status ok / fails. The
           * logger stream contains all the log linked to this process. The
           * complete process is not displayed in the upgrade console.
           */
          final int result =
              new RebuildIndex().rebuildIndexesWithinMultipleBackends(true,
                  UpgradeLog.getPrintStream(), commandLineArgs);
          if (result == 0)
          {
            LOG.log(Level.INFO, INFO_UPGRADE_REBUILD_INDEX_ENDS.get()
                .toString());
            context.notifyProgress(pnc.setProgress(100));
          }
          else
          {
            LOG.log(Level.SEVERE, ERR_UPGRADE_PERFORMING_POST_TASKS_FAIL.get()
                .toString());
            context.notifyProgress(pnc.setProgress(-100));
          }
        }
      }
    };
  }
opends/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java
@@ -28,8 +28,7 @@
import java.io.*;
import java.util.LinkedList;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -386,21 +385,21 @@
    return installationPath;
  }
  // This function is not in use actually but may be useful later
  // eg. for rebuild index task.
  @SuppressWarnings("unused")
  private static SortedMap<String, LinkedList<String>> getLocalBackends()
  /**
   * Retrieves the backends from the current configuration file.
   *
   * @return A backend list.
   */
  static List<String> getLocalBackendsFromConfig()
  {
    // Config.ldif path
    final File configLdif = new File(configDirectory,
        CURRENT_CONFIG_FILE_NAME);
    SortedMap<String, LinkedList<String>> result =
        new TreeMap<String, LinkedList<String>>();
    final List<String> listBackends = new LinkedList<String>();
    LDIFEntryReader entryReader = null;
    try
    {
      entryReader = new LDIFEntryReader(new FileInputStream(configLdif));
      entryReader =
          new LDIFEntryReader(new FileInputStream(new File(configDirectory,
              CURRENT_CONFIG_FILE_NAME)));
      final Filter filter =
          Filter.equality("objectclass", "ds-cfg-local-db-backend");
      final Matcher includeFilter = filter.matcher();
@@ -408,21 +407,9 @@
      while (entryReader.hasNext())
      {
        LinkedList<String> dataRelativesToBck = new LinkedList<String>();
        Entry entry = entryReader.readEntry();
        // Backend dn
        dataRelativesToBck.add(entry.getAttribute("ds-cfg-base-dn")
        final Entry entry = entryReader.readEntry();
        listBackends.add(entry.getAttribute("ds-cfg-base-dn")
            .firstValueAsString());
        // db path
        dataRelativesToBck.add(entry.getAttribute("ds-cfg-db-directory")
            .firstValueAsString());
        // enabled ?
        dataRelativesToBck.add(entry.getAttribute("ds-cfg-enabled")
            .firstValueAsString());
        // backend name
        result.put(
            entry.getAttribute("ds-cfg-backend-id").firstValueAsString(),
            dataRelativesToBck);
      }
    }
    catch (Exception ex)
@@ -434,7 +421,7 @@
      StaticUtils.close(entryReader);
    }
    return result;
    return listBackends;
  }
  /**
opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
@@ -129,6 +129,10 @@
     * Defines a breakline.
     */
    BREAKLINE,
    /**
     * Defines a warning.
     */
    WARNING
  }
  // The error stream which this application should use.
@@ -448,6 +452,9 @@
      case BREAKLINE:
        out.println();
        break;
      case WARNING:
        out.println(wrapText("[!] " + msg, MAX_LINE_WIDTH, indent));
        break;
      default:
        out.println(wrapText(msg, MAX_LINE_WIDTH, indent));
        break;