From 75a571bfa9d382b75e664eba3dfd58d17c694e11 Mon Sep 17 00:00:00 2001
From: Violette Roche-Montane <violette.roche-montane@forgerock.com>
Date: Tue, 03 Sep 2013 09:27:02 +0000
Subject: [PATCH] CR-2181 OPENDJ-1030 Upgrade : add a task to rebuild ds-sync-hist index when upgrading from 2.5.0-Xpress

---
 opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTask.java         |   39 ++-
 opendj-sdk/opends/src/messages/messages/tools.properties                              |   16 +
 opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/AbstractUpgradeTask.java |   39 ++
 opendj-sdk/opends/src/server/org/opends/server/tools/RebuildIndex.java                |  185 +++++++++++++-
 opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/Upgrade.java             |  127 +++++++---
 opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeCli.java          |   10 
 opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeLog.java          |   26 ++
 opendj-sdk/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java       |    7 
 opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java        |  175 ++++++++++++--
 opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java        |   43 +--
 10 files changed, 519 insertions(+), 148 deletions(-)

diff --git a/opendj-sdk/opends/src/messages/messages/tools.properties b/opendj-sdk/opends/src/messages/messages/tools.properties
index 1bad9ed..8e8c789 100644
--- a/opendj-sdk/opends/src/messages/messages/tools.properties
+++ b/opendj-sdk/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 ?
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/RebuildIndex.java b/opendj-sdk/opends/src/server/org/opends/server/tools/RebuildIndex.java
index 1c4ef96..1cf7ab7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/RebuildIndex.java
+++ b/opendj-sdk/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;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/AbstractUpgradeTask.java b/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/AbstractUpgradeTask.java
index 59e9479..048a72c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/AbstractUpgradeTask.java
+++ b/opendj-sdk/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.
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/Upgrade.java b/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/Upgrade.java
index 0ebd39d..c946268 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/Upgrade.java
+++ b/opendj-sdk/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()
   {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeCli.java b/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeCli.java
index da69d34..2dd0f9c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeCli.java
+++ b/opendj-sdk/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());
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeLog.java b/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeLog.java
index 1e03d1d..7b7bb70 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeLog.java
+++ b/opendj-sdk/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()));
+    }
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTask.java b/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTask.java
index f433ddc..968cacd 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTask.java
+++ b/opendj-sdk/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;
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java b/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java
index 9181072..de87f2a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java
+++ b/opendj-sdk/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));
+          }
+        }
       }
     };
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java b/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java
index b03c56c..c73e57c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java
+++ b/opendj-sdk/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;
   }
 
   /**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java b/opendj-sdk/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
index f475576..e09a564 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
+++ b/opendj-sdk/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;

--
Gitblit v1.10.0