From a9ded45b0acdc5bcdee105036d1e36f31a16f05c Mon Sep 17 00:00:00 2001
From: jvergara <jvergara@localhost>
Date: Wed, 19 Mar 2008 18:09:56 +0000
Subject: [PATCH] Fix for issue 2197 (dsconfig: interactive mode should display the effective dsconfig command)

---
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyEditorModification.java       |  198 +++++
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java     |   13 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java          |  308 ++++++++
 opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java                             |   12 
 opendj-sdk/opends/src/messages/messages/dsconfig.properties                                         |   15 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java          |   16 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java                |  106 ++
 opendj-sdk/opends/src/server/org/opends/server/util/cli/CommandBuilder.java                         |  287 ++++++++
 opendj-sdk/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java       |  148 ++++
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java         |    9 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java            |   11 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ManagementContextFactory.java         |    9 
 opendj-sdk/opends/src/server/org/opends/server/admin/condition/ContainsCondition.java               |   21 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java            |    6 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java         |  188 +++++
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java                         |  249 ++++++
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/InternalManagementContextFactory.java |    9 
 opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java              |  372 ++++++++++
 opendj-sdk/opends/src/server/org/opends/server/util/args/ArgumentParser.java                        |    4 
 19 files changed, 1,908 insertions(+), 73 deletions(-)

diff --git a/opendj-sdk/opends/src/messages/messages/dsconfig.properties b/opendj-sdk/opends/src/messages/messages/dsconfig.properties
index 3ff98ee..a3a744d 100644
--- a/opendj-sdk/opends/src/messages/messages/dsconfig.properties
+++ b/opendj-sdk/opends/src/messages/messages/dsconfig.properties
@@ -380,7 +380,7 @@
 INFO_DSCFG_CREATE_NAME_PROMPT_NAMING_CONT_1356=Enter a name for the %s that you want to create:
 INFO_DSCFG_HEADING_MAIN_MENU_TITLE_1357=>>>> OpenDS configuration console main menu
 INFO_DSCFG_HEADING_MAIN_MENU_PROMPT_1358=What do you want to configure?
-INFO_DSCFG_HEADING_COMPONENT_MENU_TITLE_1359=>>>> %s management menu 
+INFO_DSCFG_HEADING_COMPONENT_MENU_TITLE_1359=>>>> %s management menu
 INFO_DSCFG_HEADING_COMPONENT_MENU_PROMPT_1360=What would you like to do?
 INFO_DSCFG_OPTION_COMPONENT_MENU_CREATE_1361=Create a new %s
 INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_SINGULAR_1362=View and edit the %s
@@ -449,3 +449,16 @@
  implementation class
 INFO_DSCFG_GENERIC_TYPE_SYNOPSIS_149=A Generic %s
 INFO_DSCFG_CREATE_TYPE_HELP_HEADING_150=Help: %s
+INFO_DSCFG_NON_INTERACTIVE_151=The equivalent non-interactive command-line is:\
+ %n%s
+INFO_DSCFG_DESCRIPTION_DISPLAY_EQUIVALENT_152=Display the equivalent \
+ non-interactive argument in the standard output when this command is run in \
+ interactive mode
+INFO_DSCFG_DESCRIPTION_EQUIVALENT_COMMAND_FILE_PATH_153=The full path to the \
+ file where the equivalent non-interactive commands will be written when this \
+ command is run in interactive mode
+MILD_ERR_DSCFG_ERROR_WRITING_EQUIVALENT_COMMAND_LINE_154=An error occurred \
+ while attempting to write equivalent non-interactive command line to file \
+ %s.  Error details:  %s
+SEVERE_ERR_DSCFG_CANNOT_WRITE_EQUIVALENT_COMMAND_LINE_FILE_155=Cannot write \
+ to file %s.  Verify that you have access rights to that file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/condition/ContainsCondition.java b/opendj-sdk/opends/src/server/org/opends/server/admin/condition/ContainsCondition.java
index ef2a4c2..12035b6 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/condition/ContainsCondition.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/condition/ContainsCondition.java
@@ -58,10 +58,10 @@
   private static final class Impl<T> implements Condition {
 
     // The property.
-    private final PropertyDefinition<T> pd;
+    final PropertyDefinition<T> pd;
 
     // The required property value.
-    private final T value;
+    final T value;
 
 
 
@@ -194,4 +194,21 @@
     this.impl = new Impl<T>(pd, value);
   }
 
+  /**
+   * Returns the property definition associated with this condition.
+   * @return the property definition associated with this condition.
+   */
+  public PropertyDefinition<?> getPropertyDefinition()
+  {
+    return impl.pd;
+  }
+
+  /**
+   * Returns the value that must be set for this condition to be fulfilled.
+   * @return the value that must be set for this condition to be fulfilled.
+   */
+  public Object getValue()
+  {
+    return impl.value;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java b/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java
index ac4fe41..e15dd23 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java
@@ -702,5 +702,17 @@
    * The default separator to be used in tables.
    */
   public static final String LIST_TABLE_SEPARATOR = ":";
+
+  /**
+   * Display the equivalent non-interactive command.
+   */
+  public static final String OPTION_DSCFG_LONG_DISPLAY_EQUIVALENT =
+    "displayEquivalentCommand";
+
+  /**
+   * The path where we write the equivalent non-interactive command.
+   */
+  public static final String OPTION_LONG_EQUIVALENT_COMMAND_FILE_PATH =
+    "equivalentCommandFilePath";
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
index 8b32da6..8643ac7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
@@ -80,6 +80,7 @@
 import org.opends.server.protocols.ldap.LDAPResultCode;
 import org.opends.server.tools.ClientException;
 import org.opends.server.tools.ToolConstants;
+import org.opends.server.util.args.Argument;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.StringArgument;
 import org.opends.server.util.args.SubCommand;
@@ -356,6 +357,16 @@
   private static final Character OPTION_DSCFG_SHORT_TYPE = 't';
 
 
+  /**
+   * The value for the long option remove (this is used only internally).
+   */
+  private static final String OPTION_DSCFG_LONG_REMOVE = "remove";
+
+  /**
+   * The value for the long option reset (this is used only internally).
+   */
+  private static final String OPTION_DSCFG_LONG_RESET = "reset";
+
 
   /**
    * Creates a new create-xxx sub-command for an instantiable
@@ -410,7 +421,6 @@
   }
 
 
-
   /**
    * Interactively lets a user create a new managed object beneath a
    * parent.
@@ -445,6 +455,46 @@
       ConsoleApplication app, ManagementContext context,
       ManagedObject<?> parent, InstantiableRelationDefinition<C, S> rd)
       throws ClientException, CLIException {
+    return createManagedObject(app, context, parent, rd, null);
+  }
+
+  /**
+   * Interactively lets a user create a new managed object beneath a
+   * parent.
+   *
+   * @param <C>
+   *          The type of managed object which can be created.
+   * @param <S>
+   *          The type of server managed object which can be created.
+   * @param app
+   *          The console application.
+   * @param context
+   *          The management context.
+   * @param parent
+   *          The parent managed object.
+   * @param rd
+   *          The relation beneath which the child managed object
+   *          should be created.
+   * @param handler
+   *          The subcommand handler whose command builder must be updated.
+   * @return Returns a MenuResult.success() containing the name of the
+   *         created managed object if it was created successfully, or
+   *         MenuResult.quit(), or MenuResult.cancel(), if the managed
+   *         object was edited interactively and the user chose to
+   *         quit or cancel.
+   * @throws ClientException
+   *           If an unrecoverable client exception occurred whilst
+   *           interacting with the server.
+   * @throws CLIException
+   *           If an error occurred whilst interacting with the
+   *           console.
+   */
+  private static <C extends ConfigurationClient, S extends Configuration>
+      MenuResult<String> createManagedObject(
+      ConsoleApplication app, ManagementContext context,
+      ManagedObject<?> parent, InstantiableRelationDefinition<C, S> rd,
+      SubCommandHandler handler)
+      throws ClientException, CLIException {
     AbstractManagedObjectDefinition<C, S> d = rd.getChildDefinition();
 
     // First determine what type of component the user wants to create.
@@ -470,7 +520,7 @@
       createChildInteractively(app, parent, rd, mod, exceptions);
 
     // Let the user interactively configure the managed object and commit it.
-    MenuResult<Void> result2 = commitManagedObject(app, context, mo);
+    MenuResult<Void> result2 = commitManagedObject(app, context, mo, handler);
     if (result2.isCancel()) {
       return MenuResult.cancel();
     } else if (result2.isQuit()) {
@@ -485,7 +535,8 @@
   // Check that any referenced components are enabled if
   // required.
   private static MenuResult<Void> checkReferences(ConsoleApplication app,
-      ManagementContext context, ManagedObject<?> mo) throws ClientException,
+      ManagementContext context, ManagedObject<?> mo,
+      SubCommandHandler handler) throws ClientException,
       CLIException {
     ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
     Message ufn = d.getUserFriendlyName();
@@ -549,7 +600,7 @@
                     if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn),
                         true)) {
                       MenuResult<Void> result = SetPropSubCommandHandler
-                          .modifyManagedObject(app, context, ref);
+                          .modifyManagedObject(app, context, ref, handler);
                       if (result.isQuit()) {
                         return result;
                       } else if (result.isSuccess()) {
@@ -570,7 +621,7 @@
                     if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn),
                         true)) {
                       MenuResult<Void> result = SetPropSubCommandHandler
-                          .modifyManagedObject(app, context, ref);
+                          .modifyManagedObject(app, context, ref, handler);
                       if (result.isQuit()) {
                         return result;
                       } else if (result.isSuccess()) {
@@ -589,7 +640,7 @@
                 if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_TO_ENABLE.get(
                     rufn, name, ufn), true)) {
                   MenuResult<Void> result = SetPropSubCommandHandler
-                      .modifyManagedObject(app, context, ref);
+                      .modifyManagedObject(app, context, ref, handler);
                   if (result.isQuit()) {
                     return result;
                   } else if (result.isSuccess()) {
@@ -634,12 +685,14 @@
 
   // Commit a new managed object's configuration.
   private static MenuResult<Void> commitManagedObject(ConsoleApplication app,
-      ManagementContext context, ManagedObject<?> mo) throws ClientException,
+      ManagementContext context, ManagedObject<?> mo, SubCommandHandler handler)
+      throws ClientException,
       CLIException {
     ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
     Message ufn = d.getUserFriendlyName();
 
     while (true) {
+      PropertyValueEditor editor = new PropertyValueEditor(app, context);
       // Interactively set properties if applicable.
       if (app.isInteractive()) {
         SortedSet<PropertyDefinition<?>> properties =
@@ -654,12 +707,11 @@
           properties.add(pd);
         }
 
-        PropertyValueEditor editor = new PropertyValueEditor(app, context);
         MenuResult<Void> result = editor.edit(mo, properties, false);
 
         // Interactively enable/edit referenced components.
         if (result.isSuccess()) {
-          result = checkReferences(app, context, mo);
+          result = checkReferences(app, context, mo, handler);
           if (result.isAgain()) {
             // Edit again.
             continue;
@@ -687,6 +739,22 @@
         Message msg = INFO_DSCFG_CONFIRM_CREATE_SUCCESS.get(ufn);
         app.printVerboseMessage(msg);
 
+        for (PropertyEditorModification mod : editor.getModifications())
+        {
+          try
+          {
+            Argument arg = createArgument(mod);
+            handler.getCommandBuilder().addArgument(arg);
+          }
+          catch (ArgumentException ae)
+          {
+            // This is a bug
+            throw new RuntimeException(
+                "Unexpected error generating the command builder: "+ae, ae);
+          }
+        }
+        handler.setCommandBuilderUseful(true);
+
         return MenuResult.success();
       } catch (MissingMandatoryPropertiesException e) {
         if (app.isInteractive()) {
@@ -984,6 +1052,8 @@
 
 
 
+
+
   // Common constructor.
   private CreateSubCommandHandler(
       SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
@@ -1128,6 +1198,14 @@
     // Get the naming argument values.
     List<String> names = getNamingArgValues(app, namingArgs);
 
+    // Reset the command builder
+    getCommandBuilder().clearArguments();
+
+    setCommandBuilderUseful(false);
+
+    // Update the command builder.
+    updateCommandBuilderWithSubCommand();
+
     // Encode the provided properties.
     List<String> propertyArgs = propertySetArgument.getValues();
     MyPropertyProvider provider = new MyPropertyProvider(d,
@@ -1178,6 +1256,8 @@
     ManagedObject<? extends C> child;
     List<DefaultBehaviorException> exceptions =
       new LinkedList<DefaultBehaviorException>();
+    boolean isNameProvidedInteractively = false;
+    String providedNamingArgName = null;
     if (relation instanceof InstantiableRelationDefinition) {
       InstantiableRelationDefinition<C, S> irelation =
         (InstantiableRelationDefinition<C, S>) relation;
@@ -1188,6 +1268,9 @@
           app.println();
           child = createChildInteractively(app, parent, irelation,
               d, exceptions);
+          isNameProvidedInteractively = true;
+          providedNamingArgName =
+            CLIProfile.getInstance().getNamingArgument(irelation);
         } else {
           throw ArgumentExceptionFactory
               .missingMandatoryNonInteractiveArgument(namingArgs.get(names
@@ -1219,12 +1302,132 @@
     // Now the command line changes have been made, create the managed
     // object interacting with the user to fix any problems if
     // required.
-    MenuResult<Void> result2 = commitManagedObject(app, context, child);
+    MenuResult<Void> result2 = commitManagedObject(app, context, child, this);
     if (result2.isCancel()) {
       return MenuResult.cancel();
     } else if (result2.isQuit()) {
       return MenuResult.quit();
     } else {
+      if (typeArgument.hasValue())
+      {
+        getCommandBuilder().addArgument(typeArgument);
+      }
+      else
+      {
+        // Set the type provided by the user
+        StringArgument arg = new StringArgument(typeArgument.getName(),
+            OPTION_DSCFG_SHORT_TYPE,
+            OPTION_DSCFG_LONG_TYPE, false, false, true,
+            INFO_TYPE_PLACEHOLDER.get(), typeArgument.getDefaultValue(),
+            typeArgument.getPropertyName(),
+            typeArgument.getDescription());
+        arg.addValue(getTypeName(d));
+        getCommandBuilder().addArgument(arg);
+      }
+      if (propertySetArgument.hasValue())
+      {
+        /*
+          We might have some conflicts in terms of arguments: the user might
+           have provided some values that were not good and then these have
+           overwritten when asking for them interactively: filter them */
+        StringArgument filteredArg = new StringArgument(OPTION_DSCFG_LONG_SET,
+            OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, true,
+            INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+            INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
+        for (String value : propertySetArgument.getValues())
+        {
+          boolean addValue = true;
+          int index = value.indexOf(':');
+          if (index != -1)
+          {
+            String propName = value.substring(0, index);
+            for (Argument arg : getCommandBuilder().getArguments())
+            {
+              for (String value2 : arg.getValues())
+              {
+                String prop2Name;
+                if (arg.getName().equals(OPTION_DSCFG_LONG_SET) ||
+                    arg.getName().equals(OPTION_DSCFG_LONG_REMOVE))
+                {
+                  int index2 = value2.indexOf(':');
+                  if (index2 != -1)
+                  {
+                    prop2Name = value2.substring(0, index2);
+                  }
+                  else
+                  {
+                    prop2Name = null;
+                  }
+                }
+                else if (arg.getName().equals(OPTION_DSCFG_LONG_RESET))
+                {
+                  prop2Name = value2;
+                }
+                else
+                {
+                  prop2Name = null;
+                }
+                if (prop2Name != null)
+                {
+                  if (prop2Name.equalsIgnoreCase(propName))
+                  {
+                    addValue = false;
+                    break;
+                  }
+                }
+              }
+              if (!addValue)
+              {
+                break;
+              }
+            }
+          }
+          else
+          {
+            addValue = false;
+          }
+          if (addValue)
+          {
+            filteredArg.addValue(value);
+          }
+        }
+        if (filteredArg.hasValue())
+        {
+          getCommandBuilder().addArgument(filteredArg);
+        }
+      }
+
+      /* Filter the arguments that are used internally */
+      List<Argument> argsCopy = new LinkedList<Argument>(
+          getCommandBuilder().getArguments());
+      for (Argument arg : argsCopy)
+      {
+        if (arg.getName().equals(OPTION_DSCFG_LONG_RESET) ||
+            arg.getName().equals(OPTION_DSCFG_LONG_REMOVE))
+        {
+          getCommandBuilder().removeArgument(arg);
+        }
+      }
+
+      if (isNameProvidedInteractively)
+      {
+        StringArgument arg = new StringArgument(providedNamingArgName, null,
+            providedNamingArgName, false, true,
+            INFO_NAME_PLACEHOLDER.get(),
+            INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName()));
+        arg.addValue(child.getManagedObjectPath().getName());
+        getCommandBuilder().addArgument(arg);
+      }
+      else
+      {
+        for (StringArgument arg : namingArgs)
+        {
+          if (arg.isPresent())
+          {
+            getCommandBuilder().addArgument(arg);
+          }
+        }
+      }
       return MenuResult.success(0);
     }
   }
@@ -1240,4 +1443,89 @@
     // validated.
     mo.setPropertyValues(pd, values);
   }
+
+  /**
+   * Creates an argument (the one that the user should provide in the
+   * command-line) that is equivalent to the modification proposed by the user
+   * in the provided PropertyEditorModification object.
+   * @param mod the object describing the modification made.
+   * @return the argument representing the modification.
+   * @throws ArgumentException if there is a problem creating the argument.
+   */
+  private static Argument createArgument(PropertyEditorModification mod)
+  throws ArgumentException
+  {
+    StringArgument arg;
+
+    String propName = mod.getPropertyDefinition().getName();
+
+    switch (mod.getType())
+    {
+    case ADD:
+      arg = new StringArgument(OPTION_DSCFG_LONG_SET,
+          OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, true,
+          INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
+      for (Object value : mod.getModificationValues())
+      {
+        arg.addValue(propName+':'+value);
+      }
+      break;
+    case SET:
+      arg = new StringArgument(OPTION_DSCFG_LONG_SET,
+          OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, true,
+          INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
+      for (Object value : mod.getModificationValues())
+      {
+        arg.addValue(propName+':'+value);
+      }
+      break;
+    case RESET:
+      arg = new StringArgument(OPTION_DSCFG_LONG_RESET,
+          null, OPTION_DSCFG_LONG_RESET, false, true, true,
+          INFO_PROPERTY_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_RESET_PROP.get());
+      arg.addValue(propName);
+      break;
+    case REMOVE:
+      arg = new StringArgument(OPTION_DSCFG_LONG_REMOVE,
+          null, OPTION_DSCFG_LONG_REMOVE, false, true,
+          true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL.get());
+      for (Object value : mod.getModificationValues())
+      {
+        arg.addValue(propName+':'+value);
+      }
+      arg = null;
+      break;
+    default:
+      // Bug
+      throw new IllegalStateException("Unknown modification type: "+
+          mod.getType());
+    }
+    return arg;
+  }
+
+  /**
+   * Returns the type name for a given ManagedObjectDefinition.
+   * @param d the ManagedObjectDefinition.
+   * @return the type name for the provided ManagedObjectDefinition.
+   */
+  private String getTypeName(
+      ManagedObjectDefinition<? extends C, ? extends S> d)
+  {
+    String name = d.getName();
+    for (String key : types.keySet())
+    {
+      ManagedObjectDefinition<? extends C, ? extends S> current =
+        types.get(key);
+      if (current.equals(d))
+      {
+        name = key;
+        break;
+      }
+    }
+    return name;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
index d0bcc6d..57e8843 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
@@ -35,6 +35,9 @@
 import static org.opends.server.tools.dsconfig.ArgumentExceptionFactory.*;
 import static org.opends.server.util.StaticUtils.*;
 
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Comparator;
@@ -46,6 +49,7 @@
 import java.util.TreeSet;
 
 import org.opends.messages.Message;
+import org.opends.quicksetup.util.Utils;
 import org.opends.server.admin.AttributeTypePropertyDefinition;
 import org.opends.server.admin.ClassLoaderProvider;
 import org.opends.server.admin.ClassPropertyDefinition;
@@ -60,6 +64,7 @@
 import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.InitializationException;
 import org.opends.server.util.EmbeddedUtils;
+import org.opends.server.util.ServerConstants;
 import org.opends.server.util.StaticUtils;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.BooleanArgument;
@@ -68,6 +73,7 @@
 import org.opends.server.util.args.SubCommandArgumentParser;
 import org.opends.server.util.args.ArgumentGroup;
 import org.opends.server.util.cli.CLIException;
+import org.opends.server.util.cli.CommandBuilder;
 import org.opends.server.util.cli.ConsoleApplication;
 import org.opends.server.util.cli.Menu;
 import org.opends.server.util.cli.MenuBuilder;
@@ -116,6 +122,11 @@
         if (result.isQuit()) {
           return result;
         } else {
+          if (result.isSuccess() && isInteractive() &&
+              handler.isCommandBuilderUseful())
+          {
+            printCommandBuilder(getCommandBuilder(handler));
+          }
           // Success or cancel.
           app.println();
           app.pressReturnToContinue();
@@ -364,6 +375,14 @@
   // behavior.
   private BooleanArgument noPromptArgument;
 
+  // The argument that the user must set to display the equivalent
+  // non-interactive mode argument
+  private BooleanArgument displayEquivalentArgument;
+
+  // The argument that allows the user to dump the equivalent non-interactive
+  // command to a file.
+  private StringArgument equivalentCommandFileArgument;
+
   // The command-line argument parser.
   private final SubCommandArgumentParser parser;
 
@@ -387,6 +406,10 @@
   // properties file.
   private BooleanArgument noPropertiesFileArgument;
 
+  // The boolean that is used to know if data must be appended to the file
+  // containing equivalent non-interactive commands.
+  private boolean alreadyWroteEquivalentCommand;
+
   /**
    * Creates a new dsconfig application instance.
    *
@@ -540,6 +563,19 @@
           OPTION_LONG_HELP, INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_SUMMARY
               .get());
 
+      displayEquivalentArgument = new BooleanArgument(
+          OPTION_DSCFG_LONG_DISPLAY_EQUIVALENT,
+          null, OPTION_DSCFG_LONG_DISPLAY_EQUIVALENT,
+          INFO_DSCFG_DESCRIPTION_DISPLAY_EQUIVALENT.get());
+      advancedModeArgument.setPropertyName(
+          OPTION_DSCFG_LONG_DISPLAY_EQUIVALENT);
+
+      equivalentCommandFileArgument = new StringArgument(
+          OPTION_LONG_EQUIVALENT_COMMAND_FILE_PATH, null,
+          OPTION_LONG_EQUIVALENT_COMMAND_FILE_PATH, false, false, true,
+          INFO_PATH_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_EQUIVALENT_COMMAND_FILE_PATH.get());
+
       propertiesFileArgument = new StringArgument("propertiesFilePath",
           null, OPTION_LONG_PROP_FILE_PATH,
           false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
@@ -561,6 +597,8 @@
       parser.addGlobalArgument(quietArgument);
       parser.addGlobalArgument(scriptFriendlyArgument);
       parser.addGlobalArgument(noPromptArgument);
+      parser.addGlobalArgument(displayEquivalentArgument);
+      parser.addGlobalArgument(equivalentCommandFileArgument);
       parser.addGlobalArgument(propertiesFileArgument);
       parser.setFilePropertiesArgument(propertiesFileArgument);
       parser.addGlobalArgument(noPropertiesFileArgument);
@@ -711,6 +749,18 @@
       return 1;
     }
 
+    // Check that we can write on the provided path where we write the
+    // equivalent non-interactive commands.
+    if (equivalentCommandFileArgument.isPresent())
+    {
+      String file = equivalentCommandFileArgument.getValue();
+      if (!Utils.canWrite(file))
+      {
+        println(ERR_DSCFG_CANNOT_WRITE_EQUIVALENT_COMMAND_LINE_FILE.get(file));
+        return 1;
+      }
+    }
+
     // Make sure that management context's arguments are valid.
     try {
       factory.validateGlobalArguments();
@@ -724,32 +774,32 @@
       hasSubCommand = false;
 
       if (isInteractive()) {
-          // Top-level interactive mode.
-          retCode = runInteractiveMode();
-        } else {
-          Message message = ERR_ERROR_PARSING_ARGS
-              .get(ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get());
-          displayMessageAndUsageReference(message);
-          retCode = 1;
-        }
+        // Top-level interactive mode.
+        retCode = runInteractiveMode();
       } else {
-        hasSubCommand = true;
-
-        // Retrieve the sub-command implementation and run it.
-        SubCommandHandler handler = handlers.get(parser.getSubCommand());
-        retCode = runSubCommand(handler);
+        Message message = ERR_ERROR_PARSING_ARGS
+        .get(ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get());
+        displayMessageAndUsageReference(message);
+        retCode = 1;
       }
+    } else {
+      hasSubCommand = true;
 
-      try {
-        // Close the Management context ==> an LDAP UNBIND is sent
-        factory.close();
-      } catch (Exception e) {
-      // Nothing to report in this case
-      }
-
-      return retCode;
+      // Retrieve the sub-command implementation and run it.
+      SubCommandHandler handler = handlers.get(parser.getSubCommand());
+      retCode = runSubCommand(handler);
     }
 
+    try {
+      // Close the Management context ==> an LDAP UNBIND is sent
+      factory.close();
+    } catch (Exception e) {
+      // Nothing to report in this case
+    }
+
+    return retCode;
+  }
+
 
 
   // Run the top-level interactive console.
@@ -874,6 +924,13 @@
       MenuResult<Integer> result = handler.run(this, factory);
 
       if (result.isSuccess()) {
+        if (isInteractive())
+        {
+          println();
+          println(INFO_DSCFG_NON_INTERACTIVE.get(
+              getCommandBuilder(handler).toString()));
+        }
+
         return result.getValue();
       } else {
         // User must have quit.
@@ -918,4 +975,154 @@
       return 1;
     }
   }
+
+  /**
+   * Updates the command builder with the global options: script friendly,
+   * verbose, etc. for a given subcommand.  It also adds systematically the
+   * no-prompt option.
+   * @param handler the subcommand handler.
+   */
+  private CommandBuilder getCommandBuilder(SubCommandHandler handler)
+  {
+    String commandName =
+      System.getProperty(ServerConstants.PROPERTY_SCRIPT_NAME);
+    if (commandName == null)
+    {
+      commandName = "dsconfig";
+    }
+
+    CommandBuilder commandBuilder =
+      new CommandBuilder(commandName, handler.getSubCommand().getName());
+
+    if (advancedModeArgument.isPresent())
+    {
+      commandBuilder.addArgument(advancedModeArgument);
+    }
+
+    commandBuilder.append(handler.getCommandBuilder());
+
+    if ((factory != null) && (factory.getContextCommandBuilder() != null))
+    {
+      commandBuilder.append(factory.getContextCommandBuilder());
+    }
+
+    if (verboseArgument.isPresent())
+    {
+      commandBuilder.addArgument(verboseArgument);
+    }
+
+    if (scriptFriendlyArgument.isPresent())
+    {
+      commandBuilder.addArgument(scriptFriendlyArgument);
+    }
+
+    commandBuilder.addArgument(noPromptArgument);
+
+    if (propertiesFileArgument.isPresent())
+    {
+      commandBuilder.addArgument(propertiesFileArgument);
+    }
+
+    if (noPropertiesFileArgument.isPresent())
+    {
+      commandBuilder.addArgument(noPropertiesFileArgument);
+    }
+
+    return commandBuilder;
+  }
+
+  /**
+   * Creates a command builder with the global options: script friendly,
+   * verbose, etc. for a given subcommand name.  It also adds systematically the
+   * no-prompt option.
+   * @param subcommandName the subcommand name.
+   * @return the command builder that has been created with the specified
+   * subcommandName.
+   */
+  CommandBuilder getCommandBuilder(String subcommandName)
+  {
+    String commandName =
+      System.getProperty(ServerConstants.PROPERTY_SCRIPT_NAME);
+    if (commandName == null)
+    {
+      commandName = "dsconfig";
+    }
+
+    CommandBuilder commandBuilder =
+      new CommandBuilder(commandName, subcommandName);
+
+    if (advancedModeArgument.isPresent())
+    {
+      commandBuilder.addArgument(advancedModeArgument);
+    }
+
+    if ((factory != null) && (factory.getContextCommandBuilder() != null))
+    {
+      commandBuilder.append(factory.getContextCommandBuilder());
+    }
+
+    if (verboseArgument.isPresent())
+    {
+      commandBuilder.addArgument(verboseArgument);
+    }
+
+    if (scriptFriendlyArgument.isPresent())
+    {
+      commandBuilder.addArgument(scriptFriendlyArgument);
+    }
+
+    commandBuilder.addArgument(noPromptArgument);
+
+    if (propertiesFileArgument.isPresent())
+    {
+      commandBuilder.addArgument(propertiesFileArgument);
+    }
+
+    if (noPropertiesFileArgument.isPresent())
+    {
+      commandBuilder.addArgument(noPropertiesFileArgument);
+    }
+
+    return commandBuilder;
+  }
+
+  /**
+   * Prints the contents of a command builder.  This method has been created
+   * since SetPropSubCommandHandler calls it.  All the logic of DSConfig is on
+   * this method.  Currently it simply writes the content of the CommandBuilder
+   * to the standard output, but if we provide an option to write the content
+   * to a file only the implementation of this method must be changed.
+   * @param commandBuilder the command builder to be printed.
+   */
+  void printCommandBuilder(CommandBuilder commandBuilder)
+  {
+    if (displayEquivalentArgument.isPresent())
+    {
+      println();
+      // We assume that the app we are running is this one.
+      println(INFO_DSCFG_NON_INTERACTIVE.get(commandBuilder.toString()));
+    }
+    if (equivalentCommandFileArgument.isPresent())
+    {
+      // Write to the file.
+      boolean append = alreadyWroteEquivalentCommand;
+      String file = equivalentCommandFileArgument.getValue();
+      try
+      {
+        BufferedWriter writer =
+          new BufferedWriter(new FileWriter(file, append));
+        writer.write(commandBuilder.toString());
+        writer.newLine();
+
+        writer.flush();
+        writer.close();
+      }
+      catch (IOException ioe)
+      {
+        println(ERR_DSCFG_ERROR_WRITING_EQUIVALENT_COMMAND_LINE.get(file,
+            ioe.toString()));
+      }
+      alreadyWroteEquivalentCommand = true;
+    }
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java
index de5ea1a..cb0c959 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java
@@ -203,6 +203,10 @@
     // Get the naming argument values.
     List<String> names = getNamingArgValues(app, namingArgs);
 
+    // Reset the command builder
+    getCommandBuilder().clearArguments();
+    setCommandBuilderUseful(false);
+
     // Delete the child managed object.
     ManagementContext context = factory.getManagementContext(app);
     MenuResult<ManagedObject<?>> result;
@@ -276,6 +280,7 @@
         }
 
         if (confirmDeletion(app)) {
+          setCommandBuilderUseful(true);
           parent.removeChild(irelation, childName);
         } else {
           return MenuResult.cancel();
@@ -285,6 +290,8 @@
           (OptionalRelationDefinition<?, ?>) relation;
 
         if (confirmDeletion(app)) {
+          setCommandBuilderUseful(true);
+
           parent.removeChild(orelation);
         } else {
           return MenuResult.cancel();
@@ -336,6 +343,15 @@
       throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN, msg);
     }
 
+    // Add the naming arguments if they were provided.
+    for (StringArgument arg : namingArgs)
+    {
+      if (arg.isPresent())
+      {
+        getCommandBuilder().addArgument(arg);
+      }
+    }
+
     // Output success message.
     Message msg = INFO_DSCFG_CONFIRM_DELETE_SUCCESS.get(ufn);
     app.printVerboseMessage(msg);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java
index 9cc7017..d073944 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java
@@ -222,6 +222,14 @@
     // Get the naming argument values.
     List<String> names = getNamingArgValues(app, namingArgs);
 
+    //  Reset the command builder
+    getCommandBuilder().clearArguments();
+
+    setCommandBuilderUseful(false);
+
+    // Update the command builder.
+    updateCommandBuilderWithSubCommand();
+
     // Get the targeted managed object.
     Message ufn = path.getRelationDefinition().getUserFriendlyName();
     ManagementContext context = factory.getManagementContext(app);
@@ -287,6 +295,7 @@
 
       if (propertyNames.isEmpty() || propertyNames.contains(pd.getName())) {
         displayProperty(app, builder, child, pd, valuePrinter);
+        setCommandBuilderUseful(true);
       }
     }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java
index 1040af2..f04fda8 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java
@@ -741,6 +741,8 @@
       new TreeMap<String, Map<String, AbstractManagedObjectDefinition<?, ?>>>();
     this.tagMap =
       new HashMap<Tag, Map<String, AbstractManagedObjectDefinition<?, ?>>>();
+
+    setCommandBuilderUseful(false);
   }
 
 
@@ -816,11 +818,18 @@
   public MenuResult<Integer> run(ConsoleApplication app,
       ManagementContextFactory factory) throws ArgumentException,
       ClientException, CLIException {
+
     String categoryName = categoryArgument.getValue();
     String typeName = typeArgument.getValue();
     Tag tag = null;
     Set<String> propertyNames = getPropertyNames();
 
+    // Reset the command builder
+    getCommandBuilder().clearArguments();
+
+    // Update the command builder.
+    updateCommandBuilderWithSubCommand();
+
     List<AbstractManagedObjectDefinition<?, ?>> dlist =
       new LinkedList<AbstractManagedObjectDefinition<?, ?>>();
     AbstractManagedObjectDefinition<?, ?> tmp = null;
@@ -924,8 +933,6 @@
     return MenuResult.success(0);
   }
 
-
-
   // Output property summary table.
   private void displayNonVerbose(ConsoleApplication app, String categoryName,
       String typeName, Tag tag, Set<String> propertyNames) {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/InternalManagementContextFactory.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/InternalManagementContextFactory.java
index 10febec..0cfae9c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/InternalManagementContextFactory.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/InternalManagementContextFactory.java
@@ -32,6 +32,7 @@
 import org.opends.server.tools.ClientException;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.SubCommandArgumentParser;
+import org.opends.server.util.cli.CommandBuilder;
 import org.opends.server.util.cli.ConsoleApplication;
 
 
@@ -96,4 +97,12 @@
     // No implementation required.
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  public CommandBuilder getContextCommandBuilder() {
+    // No implementation required.
+    return new CommandBuilder(null, null);
+  }
+
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java
index 15bf29d..4c89e6d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java
@@ -47,6 +47,7 @@
 import org.opends.server.util.args.Argument;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.SubCommandArgumentParser;
+import org.opends.server.util.cli.CommandBuilder;
 import org.opends.server.util.cli.LDAPConnectionConsoleInteraction;
 import org.opends.server.util.cli.ConsoleApplication;
 
@@ -69,6 +70,9 @@
   // The management context.
   private ManagementContext context = null;
 
+  // The connection parameters command builder.
+  private CommandBuilder contextCommandBuilder;
+
   /**
    * Creates a new LDAP management context factory.
    */
@@ -89,6 +93,7 @@
         new LDAPConnectionConsoleInteraction(app, secureArgsList);
       ci.run();
       context = getManagementContext(app, ci);
+      contextCommandBuilder = ci.getCommandBuilder();
     }
     return context;
   }
@@ -105,6 +110,14 @@
   }
 
   /**
+   * {@inheritDoc}
+   */
+  public CommandBuilder getContextCommandBuilder()
+  {
+    return contextCommandBuilder;
+  }
+
+  /**
    * Gets the management context which sub-commands should use in
    * order to manage the directory server. Implementations can use the
    * application instance for retrieving passwords interactively.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
index 1624a4a..326a41a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
@@ -193,6 +193,12 @@
     // Get the property names.
     Set<String> propertyNames = getPropertyNames();
 
+    // Reset the command builder
+    getCommandBuilder().clearArguments();
+
+    // Update the command builder.
+    updateCommandBuilderWithSubCommand();
+
     if (propertyNames.isEmpty()) {
       // Use a default set of properties.
       propertyNames = CLIProfile.getInstance().getDefaultListPropertyNames(
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ManagementContextFactory.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ManagementContextFactory.java
index 3238c3d..578417b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ManagementContextFactory.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/ManagementContextFactory.java
@@ -32,6 +32,7 @@
 import org.opends.server.tools.ClientException;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.SubCommandArgumentParser;
+import org.opends.server.util.cli.CommandBuilder;
 import org.opends.server.util.cli.ConsoleApplication;
 
 
@@ -96,4 +97,12 @@
    *           If the global arguments are invalid for some reason.
    */
   void validateGlobalArguments() throws ArgumentException;
+
+  /**
+   * Returns the command builder that provides the equivalent arguments in
+   * interactive mode to get the management context.
+   * @return the command builder that provides the equivalent arguments in
+   * interactive mode to get the management context.
+   */
+  CommandBuilder getContextCommandBuilder();
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyEditorModification.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyEditorModification.java
new file mode 100644
index 0000000..083f78c
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyEditorModification.java
@@ -0,0 +1,198 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.tools.dsconfig;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.opends.server.admin.PropertyDefinition;
+
+/**
+ * This class is a data structure that can be used as an interface between
+ * PropertyValueEditor and the different handlers that call it.
+ * Since PropertyValueEditor is not aware of the different options used by the
+ * handlers it cannot directly construct a CommandBuilder, but
+ * PropertyValueEditor knows about the different changes (set, reset, delete)
+ * that can be performed against the ManagedObject and these changes can be
+ * used to generate a CommandBuilder.
+ *
+ * @param <T> The type of the underlying property associated with the
+ * modification.
+ */
+final class PropertyEditorModification<T>
+{
+  /**
+   * The enumeration that describes the different types of modifications that
+   * we can have.
+   */
+  enum Type
+  {
+    /** The user chose to set values. */
+    SET,
+    /** The user chose to reset values. */
+    RESET,
+    /** The user chose to add values. */
+    ADD,
+    /** The user chose to delete values. */
+    REMOVE
+  };
+
+  private PropertyDefinition<T> propertyDefinition;
+  private Type type;
+  private SortedSet<T> values;
+  private SortedSet<T> originalValues;
+
+  /**
+   * The private constructor of the PropertyEditorModification.
+   * @param propertyDefinition the property definition associated with the
+   * modification.
+   * @param type the type of the modification.
+   * @param values the values associated with the modifications.
+   * @param originalValues the original values of the property we are modifying.
+   */
+  private PropertyEditorModification(PropertyDefinition<T> propertyDefinition,
+      Type type, SortedSet<T> values, SortedSet<T> originalValues)
+  {
+    this.propertyDefinition = propertyDefinition;
+    this.type = type;
+    this.values = new TreeSet<T>();
+    this.values.addAll(values);
+    this.originalValues = new TreeSet<T>();
+    this.originalValues.addAll(originalValues);
+  }
+
+  /**
+   * Creates a reset modification.
+   * @param <T> The type of the underlying property.
+   * @param propertyDefinition the property that is modified.
+   * @param originalValues the original values of the property.
+   * @return a reset modification for a given property.
+   */
+  static <T> PropertyEditorModification<T> createResetModification(
+      PropertyDefinition<T> propertyDefinition, SortedSet<T> originalValues)
+  {
+    return new PropertyEditorModification<T>(propertyDefinition, Type.RESET,
+        new TreeSet<T>(), originalValues);
+  }
+
+  /**
+   * Creates an add modification.
+   * @param <T> The type of the underlying property.
+   * @param propertyDefinition the property that is modified.
+   * @param addedValues the values that are added in this modification.
+   * @param originalValues the original values of the property.
+   * @return a reset modification for a given property.
+   */
+  static <T> PropertyEditorModification<T> createAddModification(
+      PropertyDefinition<T> propertyDefinition,
+      SortedSet<T> addedValues, SortedSet<T> originalValues)
+  {
+    return new PropertyEditorModification<T>(propertyDefinition, Type.ADD,
+        addedValues, originalValues);
+  }
+
+  /**
+   * Creates a set modification.
+   * @param <T> The type of the underlying property.
+   * @param propertyDefinition the property that is modified.
+   * @param newValues the new values for the property.
+   * @param originalValues the original values of the property.
+   * @return a reset modification for a given property.
+   */
+  static <T> PropertyEditorModification<T> createSetModification(
+      PropertyDefinition<T> propertyDefinition,
+      SortedSet<T> newValues, SortedSet<T> originalValues)
+  {
+    return new PropertyEditorModification<T>(propertyDefinition, Type.SET,
+        newValues, originalValues);
+  }
+
+  /**
+   * Creates a remove modification.
+   * @param <T> The type of the underlying property.
+   * @param propertyDefinition the property that is modified.
+   * @param removedValues the values that are removed in this modification.
+   * @param originalValues the original values of the property.
+   * @return a reset modification for a given property.
+   */
+  static <T> PropertyEditorModification<T> createRemoveModification(
+      PropertyDefinition<T> propertyDefinition, SortedSet<T> removedValues,
+      SortedSet<T> originalValues)
+  {
+    return new PropertyEditorModification<T>(propertyDefinition, Type.REMOVE,
+        removedValues, originalValues);
+  }
+
+  /**
+   * Retuns the property definition associated with this modification.
+   * @return the property definition associated with this modification.
+   */
+  PropertyDefinition<T> getPropertyDefinition()
+  {
+    return propertyDefinition;
+  }
+
+  /**
+   * Returns the type of the modification.
+   * @return the type of the modification.
+   */
+  Type getType()
+  {
+    return type;
+  }
+
+  /**
+   * Returns the specific values associated with the modification.
+   * @return the specific values associated with the modification.
+   */
+  SortedSet<T> getModificationValues()
+  {
+    return values;
+  }
+
+  /**
+   * Returns the original values associated with the property.
+   * @return the original values associated with the property.
+   */
+  SortedSet<T> getOriginalValues()
+  {
+    return originalValues;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString()
+  {
+    return "Property name: "+getPropertyDefinition()+
+        "\nMod type: "+getType()+
+        "\nMod values: "+getModificationValues()+
+        "\nOriginal values: "+getOriginalValues();
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java
index 0e15cad..e253c44 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java
@@ -518,7 +518,13 @@
         } else if (result.isCancel()) {
           return MenuResult.cancel();
         } else {
-          mo.setPropertyValues(d, result.getValues());
+          Collection<String> newValues = result.getValues();
+          SortedSet<String> oldValues =
+            new TreeSet<String>(mo.getPropertyValues(d));
+          mo.setPropertyValues(d, newValues);
+          isLastChoiceReset = false;
+          registerModification(d, new TreeSet<String>(newValues),
+              oldValues);
           return MenuResult.success();
         }
       } catch (CLIException e) {
@@ -561,7 +567,13 @@
         } else if (result.isCancel()) {
           return MenuResult.cancel();
         } else {
-          mo.setPropertyValue(d, result.getValue());
+          Collection<Boolean> newValues = result.getValues();
+          SortedSet<Boolean> oldValues = new TreeSet<Boolean>(
+              mo.getPropertyValues(d));
+          mo.setPropertyValues(d, newValues);
+          isLastChoiceReset = false;
+          registerModification(d, new TreeSet<Boolean>(newValues),
+              oldValues);
           return MenuResult.success();
         }
       } catch (CLIException e) {
@@ -614,7 +626,11 @@
         } else if (result.isCancel()) {
           return MenuResult.cancel();
         } else {
-          mo.setPropertyValues(d, result.getValues());
+          Collection<E> newValues = result.getValues();
+          SortedSet<E> oldValues = new TreeSet<E>(mo.getPropertyValues(d));
+          mo.setPropertyValues(d, newValues);
+          isLastChoiceReset = false;
+          registerModification(d, new TreeSet<E>(newValues), oldValues);
           return MenuResult.success();
         }
       } catch (CLIException e) {
@@ -636,8 +652,12 @@
 
       // Set the new property value(s).
       try {
-        mo.setPropertyValues(d, readPropertyValues(app, mo
-            .getManagedObjectDefinition(), d));
+        SortedSet<T> values = readPropertyValues(app,
+            mo.getManagedObjectDefinition(), d);
+        SortedSet<T> oldValues = new TreeSet<T>(mo.getPropertyValues(d));
+        mo.setPropertyValues(d, values);
+        isLastChoiceReset = false;
+        registerModification(d, values, oldValues);
         return MenuResult.success();
       } catch (CLIException e) {
         this.e = e;
@@ -788,7 +808,10 @@
 
               if (result.isSuccess()) {
                 // Set the new property value(s).
-                currentValues.addAll(result.getValues());
+                Collection<String> addedValues = result.getValues();
+                currentValues.addAll(addedValues);
+
+                isLastChoiceReset = false;
                 app.println();
                 app.pressReturnToContinue();
                 return MenuResult.success(false);
@@ -835,7 +858,9 @@
 
             if (result.isSuccess()) {
               // Set the new property value(s).
-              currentValues.removeAll(result.getValues());
+              Collection<String> removedValues = result.getValues();
+              currentValues.removeAll(removedValues);
+              isLastChoiceReset = false;
               app.println();
               app.pressReturnToContinue();
               return MenuResult.success(false);
@@ -929,8 +954,11 @@
               MenuResult<T> result = menu.run();
 
               if (result.isSuccess()) {
+
                 // Set the new property value(s).
-                currentValues.addAll(result.getValues());
+                Collection<T> addedValues = result.getValues();
+                currentValues.addAll(addedValues);
+                isLastChoiceReset = false;
                 app.println();
                 app.pressReturnToContinue();
                 return MenuResult.success(false);
@@ -976,7 +1004,9 @@
 
             if (result.isSuccess()) {
               // Set the new property value(s).
-              currentValues.removeAll(result.getValues());
+              Collection<T> removedValues = result.getValues();
+              currentValues.removeAll(removedValues);
+              isLastChoiceReset = false;
               app.println();
               app.pressReturnToContinue();
               return MenuResult.success(false);
@@ -1037,8 +1067,12 @@
           public MenuResult<Boolean> invoke(ConsoleApplication app)
               throws CLIException {
             app.println();
+            SortedSet<T> previousValues = new TreeSet<T>(currentValues);
             readPropertyValues(app, mo.getManagedObjectDefinition(), d,
                 currentValues);
+            SortedSet<T> addedValues = new TreeSet<T>(currentValues);
+            addedValues.removeAll(previousValues);
+            isLastChoiceReset = false;
             return MenuResult.success(false);
           }
 
@@ -1074,7 +1108,9 @@
 
             if (result.isSuccess()) {
               // Set the new property value(s).
-              currentValues.removeAll(result.getValues());
+              Collection<T> removedValues = result.getValues();
+              currentValues.removeAll(removedValues);
+              isLastChoiceReset = false;
               app.println();
               app.pressReturnToContinue();
               return MenuResult.success(false);
@@ -1305,6 +1341,7 @@
 
             public MenuResult<Boolean> invoke(ConsoleApplication app)
                 throws CLIException {
+              isLastChoiceReset = false;
               currentValues.clear();
               app.println();
               app.pressReturnToContinue();
@@ -1325,6 +1362,7 @@
               throws CLIException {
             currentValues.clear();
             currentValues.addAll(defaultValues);
+            isLastChoiceReset = true;
             app.println();
             app.pressReturnToContinue();
             return MenuResult.success(false);
@@ -1343,6 +1381,7 @@
               throws CLIException {
             currentValues.clear();
             currentValues.addAll(oldValues);
+            isLastChoiceReset = false;
             app.println();
             app.pressReturnToContinue();
             return MenuResult.success(false);
@@ -1370,8 +1409,12 @@
 
       if (result.isSuccess()) {
         if (result.getValue() == true) {
+
           // Set the new property value(s).
           mo.setPropertyValues(d, currentValues);
+
+          registerModification(d, currentValues, oldValues);
+
           app.println();
           app.pressReturnToContinue();
           return MenuResult.success(false);
@@ -1923,7 +1966,19 @@
 
       if (result.isSuccess()) {
         // Set the new property value(s).
-        mo.setPropertyValues(d, result.getValues());
+        Collection<T> newValues = result.getValues();
+        SortedSet<T> oldValues = new TreeSet<T>(mo.getPropertyValues(d));
+        mo.setPropertyValues(d, newValues);
+        if (newValues.size() > 0)
+        {
+          isLastChoiceReset = false;
+        }
+        else
+        {
+          // There are no newValues when we do a reset.
+          isLastChoiceReset = true;
+        }
+        registerModification(d, new TreeSet<T>(newValues), oldValues);
         app.println();
         app.pressReturnToContinue();
         return MenuResult.success(false);
@@ -2158,6 +2213,15 @@
   // The management context.
   private final ManagementContext context;
 
+  // The modifications performed: we assume that at most there is one
+  // modification per property definition.
+  private final List<PropertyEditorModification> mods =
+    new ArrayList<PropertyEditorModification>();
+
+  // Whether the last type of choice made by the user in a menu is a
+  // reset
+  private boolean isLastChoiceReset;
+
 
 
   /**
@@ -2204,6 +2268,7 @@
   public MenuResult<Void> edit(ManagedObject<?> mo,
       Collection<PropertyDefinition<?>> c, boolean isCreate)
       throws CLIException {
+
     // Get values for this missing mandatory property.
     for (PropertyDefinition<?> pd : c) {
       if (pd.hasOption(PropertyOption.MANDATORY)) {
@@ -2308,4 +2373,289 @@
       }
     }
   }
+
+  /**
+   * Register the modification in the list of modifications.
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param pd the property definition.
+   * @param newValues the resulting values of the property once the
+   * modification is applied.
+   * @param previousValues the values we had before the modification is applied
+   * (these are not necessarily the *original* values if we already have other
+   * modifications applied to the same property).
+   */
+  private <T> void registerModification(PropertyDefinition<T> pd,
+      SortedSet<T> newValues, SortedSet<T> previousValues)
+  {
+
+    if (isLastChoiceReset)
+    {
+      registerResetModification(pd, previousValues);
+    }
+    else if (!newValues.equals(previousValues))
+    {
+      if (newValues.containsAll(previousValues))
+      {
+        registerAddModification(pd, newValues, previousValues);
+      }
+      else if (previousValues.containsAll(newValues))
+      {
+        registerRemoveModification(pd, newValues, previousValues);
+      }
+      else
+      {
+        registerSetModification(pd, newValues, previousValues);
+      }
+    }
+  }
+
+  /**
+   * Register a reset modification in the list of modifications.
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param pd the property definition.
+   * @param previousValues the values we had before the modification is applied
+   * (these are not necessarily the *original* values if we already have other
+   * modifications applied to the same property).
+   */
+  private <T> void registerResetModification(PropertyDefinition<T> pd,
+      SortedSet<T> previousValues)
+  {
+    PropertyEditorModification<?> mod = getModification(pd);
+    SortedSet<T> originalValues;
+    if (mod != null)
+    {
+      originalValues = new TreeSet<T>();
+      castAndAddValues(originalValues, mod.getOriginalValues(), pd);
+      removeModification(mod);
+    }
+    else
+    {
+      originalValues = new TreeSet<T>(previousValues);
+    }
+
+    addModification(PropertyEditorModification.createResetModification(pd,
+        originalValues));
+  }
+
+  /**
+   * Register a set modification in the list of modifications.
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param pd the property definition.
+   * @param newValues the resulting values of the property once the
+   * modification is applied.
+   * @param previousValues the values we had before the modification is applied
+   * (these are not necessarily the *original* values if we already have other
+   * modifications applied to the same property).
+   */
+  private <T> void registerSetModification(PropertyDefinition<T> pd,
+      SortedSet<T> newValues, SortedSet<T> previousValues)
+  {
+    PropertyEditorModification<?> mod = getModification(pd);
+    SortedSet<T> originalValues;
+    if (mod != null)
+    {
+      originalValues = new TreeSet<T>();
+      castAndAddValues(originalValues, mod.getOriginalValues(), pd);
+      removeModification(mod);
+    }
+    else
+    {
+      originalValues = new TreeSet<T>(previousValues);
+    }
+    addModification(PropertyEditorModification.createSetModification(pd,
+        newValues, originalValues));
+  }
+
+  /**
+   * Register an add modification in the list of modifications.
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param pd the property definition.
+   * @param newValues the resulting values of the property once the
+   * modification is applied.
+   * @param previousValues the values we had before the modification is applied
+   * (these are not necessarily the *original* values if we already have other
+   * modifications applied to the same property).
+   */
+  private <T> void registerAddModification(PropertyDefinition<T> pd,
+      SortedSet<T> newValues, SortedSet<T> previousValues)
+  {
+    PropertyEditorModification<?> mod = getModification(pd);
+    PropertyEditorModification<T> newMod;
+    SortedSet<T> originalValues;
+    if (mod != null)
+    {
+      originalValues = new TreeSet<T>();
+      castAndAddValues(originalValues, mod.getOriginalValues(), pd);
+      if (mod.getType() == PropertyEditorModification.Type.ADD)
+      {
+        SortedSet<T> addedValues = new TreeSet<T>(newValues);
+        addedValues.removeAll(originalValues);
+        newMod = PropertyEditorModification.createAddModification(pd,
+            addedValues, originalValues);
+      }
+      else
+      {
+        newMod = PropertyEditorModification.createSetModification(pd,
+            new TreeSet<T>(newValues), originalValues);
+      }
+      removeModification(mod);
+    }
+    else
+    {
+      originalValues = new TreeSet<T>(previousValues);
+      SortedSet<T> addedValues = new TreeSet<T>(newValues);
+      addedValues.removeAll(originalValues);
+      newMod = PropertyEditorModification.createAddModification(pd,
+          addedValues, originalValues);
+    }
+    addModification(newMod);
+  }
+
+  /**
+   * Register a remove modification in the list of modifications.
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param pd the property definition.
+   * @param newValues the resulting values of the property once the
+   * modification is applied.
+   * @param previousValues the values we had before the modification is applied
+   * (these are not necessarily the *original* values if we already have other
+   * modifications applied to the same property).
+   */
+  private <T> void registerRemoveModification(PropertyDefinition<T> pd,
+      SortedSet<T> newValues, SortedSet<T> previousValues)
+  {
+    PropertyEditorModification<?> mod = getModification(pd);
+    PropertyEditorModification<T> newMod;
+    SortedSet<T> originalValues;
+    if (mod != null)
+    {
+      originalValues = new TreeSet<T>();
+      castAndAddValues(originalValues, mod.getOriginalValues(), pd);
+      if (newValues.isEmpty())
+      {
+        newMod = PropertyEditorModification.createRemoveModification(pd,
+            originalValues, originalValues);
+      }
+      else if (mod.getType() == PropertyEditorModification.Type.REMOVE)
+      {
+        SortedSet<T> removedValues = new TreeSet<T>(originalValues);
+        removedValues.removeAll(newValues);
+        newMod = PropertyEditorModification.createRemoveModification(pd,
+            removedValues, originalValues);
+      }
+      else
+      {
+        newMod = PropertyEditorModification.createSetModification(pd,
+            new TreeSet<T>(newValues), originalValues);
+      }
+      removeModification(mod);
+    }
+    else
+    {
+      originalValues = new TreeSet<T>(previousValues);
+      SortedSet<T> removedValues = new TreeSet<T>(originalValues);
+      removedValues.removeAll(newValues);
+      newMod = PropertyEditorModification.createRemoveModification(pd,
+          removedValues, originalValues);
+    }
+    addModification(newMod);
+  }
+
+  /**
+   * Returns the modifications that have been applied during the last call of
+   * the method PropertyValueEditor.edit.
+   * @return the modifications that have been applied during the last call of
+   * the method PropertyValueEditor.edit.
+   */
+  public Collection<PropertyEditorModification> getModifications()
+  {
+    return mods;
+  }
+
+  /**
+   * Clears the list of modifications.
+   */
+  public void resetModifications()
+  {
+    mods.clear();
+  }
+
+  /**
+   * Adds a modification to the list of modifications that have been performed.
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param mod the modification to be added.
+   */
+  private <T> void addModification(PropertyEditorModification<T> mod)
+  {
+    mods.add(mod);
+  }
+
+  /**
+   * Removes a modification from the list of modifications that have been
+   * performed.
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param mod the modification to be removed.
+   */
+  private <T> boolean removeModification(PropertyEditorModification<T> mod)
+  {
+    return mods.remove(mod);
+  }
+
+  /**
+   * Returns the modification associated with a given property definition:
+   * we assume that we have only one modification per property definition (in
+   * the worst case we merge the modifications and generate a unique set
+   * modification).
+   * @param <T> The type of the underlying property associated with the
+   * modification.
+   * @param pd the property definition.
+   * @return the modification associated with the provided property definition
+   * and <CODE>null</CODE> if no modification could be found.
+   */
+  private <T> PropertyEditorModification<?> getModification(
+      PropertyDefinition<T> pd)
+  {
+    PropertyEditorModification<?> mod = null;
+
+    for (PropertyEditorModification<?> m : mods)
+    {
+      if (pd.equals(m.getPropertyDefinition()))
+      {
+        mod = m;
+        break;
+      }
+    }
+
+    return mod;
+  }
+
+  /**
+   * This method is required to avoid compilation warnings.  It basically adds
+   * the contents of a collection to another collection by explicitly casting
+   * its values.  This is done because the method getModification() returns
+   * au undefined type.
+   * @param <T>  The type of the destination values.
+   * @param destination the collection that we want to update.
+   * @param source the collection whose values we want to add (and cast) to the
+   * source collection.
+   * @param pd the PropertyDefinition we use to do the casting.
+   * @throws ClassCastException if an error occurs during the cast of the
+   * objects.
+   */
+  private <T> void castAndAddValues(Collection<T> destination,
+      Collection<?> source, PropertyDefinition<T> pd) throws ClassCastException
+  {
+    for (Object o : source)
+    {
+      destination.add(pd.castValue(o));
+    }
+  }
 }
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java
index c7125c0..a92718e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java
@@ -67,11 +67,13 @@
 import org.opends.server.admin.condition.ContainsCondition;
 import org.opends.server.protocols.ldap.LDAPResultCode;
 import org.opends.server.tools.ClientException;
+import org.opends.server.util.args.Argument;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.StringArgument;
 import org.opends.server.util.args.SubCommand;
 import org.opends.server.util.args.SubCommandArgumentParser;
 import org.opends.server.util.cli.CLIException;
+import org.opends.server.util.cli.CommandBuilder;
 import org.opends.server.util.cli.ConsoleApplication;
 import org.opends.server.util.cli.MenuResult;
 
@@ -146,8 +148,6 @@
    */
   private static final Character OPTION_DSCFG_SHORT_SET = null;
 
-
-
   /**
    * Creates a new set-xxx-prop sub-command for an instantiable
    * relation.
@@ -210,10 +210,9 @@
     return new SetPropSubCommandHandler(parser, path.child(r), r);
   }
 
-
-
   /**
-   * Configure the provided managed object.
+   * Configure the provided managed object and updates the command builder
+   * in the pased SetPropSubCommandHandler object.
    *
    * @param app
    *          The console application.
@@ -221,6 +220,9 @@
    *          The management context.
    * @param mo
    *          The managed object to be configured.
+   * @param handler
+   *          The SubCommandHandler whose command builder properties must be
+   *          updated.
    * @return Returns a MenuResult.success() if the managed object was
    *         configured successfully, or MenuResult.quit(), or
    *         MenuResult.cancel(), if the managed object was edited
@@ -233,12 +235,14 @@
    *           console.
    */
   public static MenuResult<Void> modifyManagedObject(ConsoleApplication app,
-      ManagementContext context, ManagedObject<?> mo) throws ClientException,
+      ManagementContext context, ManagedObject<?> mo,
+      SubCommandHandler handler) throws ClientException,
       CLIException {
     ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
     Message ufn = d.getUserFriendlyName();
 
     while (true) {
+      PropertyValueEditor editor = new PropertyValueEditor(app, context);
       // Interactively set properties if applicable.
       if (app.isInteractive()) {
         SortedSet<PropertyDefinition<?>> properties =
@@ -253,12 +257,11 @@
           properties.add(pd);
         }
 
-        PropertyValueEditor editor = new PropertyValueEditor(app, context);
         MenuResult<Void> result = editor.edit(mo, properties, false);
 
         // Interactively enable/edit referenced components.
         if (result.isSuccess()) {
-          result = checkReferences(app, context, mo);
+          result = checkReferences(app, context, mo, handler);
           if (result.isAgain()) {
             // Edit again.
             continue;
@@ -286,6 +289,22 @@
           app.println();
           Message msg = INFO_DSCFG_CONFIRM_MODIFY_SUCCESS.get(ufn);
           app.printVerboseMessage(msg);
+
+          for (PropertyEditorModification mod : editor.getModifications())
+          {
+            try
+            {
+              handler.getCommandBuilder().addArgument(createArgument(mod));
+            }
+            catch (ArgumentException ae)
+            {
+              // This is a bug
+              throw new RuntimeException(
+                  "Unexpected error generating the command builder: "+ae, ae);
+            }
+          }
+
+          handler.setCommandBuilderUseful(true);
         }
 
         return MenuResult.success();
@@ -339,7 +358,8 @@
   // Check that any referenced components are enabled if
   // required.
   private static MenuResult<Void> checkReferences(ConsoleApplication app,
-      ManagementContext context, ManagedObject<?> mo) throws ClientException,
+      ManagementContext context, ManagedObject<?> mo,
+      SubCommandHandler handler) throws ClientException,
       CLIException {
     ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
     Message ufn = d.getUserFriendlyName();
@@ -394,6 +414,59 @@
                   cvc.setPropertyValue(ref);
                   try {
                     ref.commit();
+
+                    // Try to create the command builder
+                    if ((app instanceof DSConfig) && app.isInteractive())
+                    {
+                      DSConfig dsConfig = (DSConfig)app;
+                      String subCommandName = "set-" +
+                      path.getRelationDefinition().getName() + "-prop";
+                      CommandBuilder builder =
+                        dsConfig.getCommandBuilder(subCommandName);
+
+                      if (path.getRelationDefinition() instanceof
+                          InstantiableRelationDefinition<?, ?>)
+                      {
+                        String argName =
+                          CLIProfile.getInstance().getNamingArgument(
+                              (InstantiableRelationDefinition<?, ?>)
+                              path.getRelationDefinition());
+                        try
+                        {
+                          StringArgument arg = new StringArgument(argName, null,
+                              argName, false, true,
+                              INFO_NAME_PLACEHOLDER.get(),
+                              INFO_DSCFG_DESCRIPTION_NAME.get(
+                                  d.getUserFriendlyName()));
+                          arg.addValue(name);
+                          builder.addArgument(arg);
+                        }
+                        catch (Throwable t)
+                        {
+                          // Bug
+                          throw new RuntimeException("Unexpected error: "+t, t);
+                        }
+                      }
+
+                      try
+                      {
+                        StringArgument arg = new StringArgument(
+                            OPTION_DSCFG_LONG_SET, OPTION_DSCFG_SHORT_SET,
+                            OPTION_DSCFG_LONG_SET, false, true, true,
+                            INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+                            INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
+                        arg.addValue(cvc.getPropertyDefinition().getName()+':'+
+                            cvc.getValue());
+                        builder.addArgument(arg);
+                      }
+                      catch (Throwable t)
+                      {
+                        // Bug
+                        throw new RuntimeException("Unexpected error: "+t, t);
+                      }
+                      dsConfig.printCommandBuilder(builder);
+                    }
+
                     isBadReference = false;
                   } catch (MissingMandatoryPropertiesException e) {
                     // Give the user the chance to fix the problems.
@@ -403,7 +476,7 @@
                     if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn),
                         true)) {
                       MenuResult<Void> result = modifyManagedObject(app,
-                          context, ref);
+                          context, ref, handler);
                       if (result.isQuit()) {
                         return result;
                       } else if (result.isSuccess()) {
@@ -424,7 +497,7 @@
                     if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn),
                         true)) {
                       MenuResult<Void> result = modifyManagedObject(app,
-                          context, ref);
+                          context, ref, handler);
                       if (result.isQuit()) {
                         return result;
                       } else if (result.isSuccess()) {
@@ -443,7 +516,7 @@
                 if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_TO_ENABLE.get(
                     rufn, name, ufn), true)) {
                   MenuResult<Void> result = SetPropSubCommandHandler
-                      .modifyManagedObject(app, context, ref);
+                      .modifyManagedObject(app, context, ref, handler);
                   if (result.isQuit()) {
                     return result;
                   } else if (result.isSuccess()) {
@@ -585,7 +658,6 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
@@ -597,6 +669,14 @@
     // Get the naming argument values.
     List<String> names = getNamingArgValues(app, namingArgs);
 
+    // Reset the command builder
+    getCommandBuilder().clearArguments();
+
+    setCommandBuilderUseful(false);
+
+    // Update the command builder.
+    updateCommandBuilderWithSubCommand();
+
     // Get the targeted managed object.
     Message ufn = path.getRelationDefinition().getUserFriendlyName();
     ManagementContext context = factory.getManagementContext(app);
@@ -790,22 +870,38 @@
       } catch (PropertyException e) {
         throw ArgumentExceptionFactory.adaptPropertyException(e, d);
       }
+      setCommandBuilderUseful(true);
     }
 
     // Now the command line changes have been made, apply the changes
     // interacting with the user to fix any problems if required.
-    MenuResult<Void> result2 = modifyManagedObject(app, context, child);
+    MenuResult<Void> result2 = modifyManagedObject(app, context, child, this);
     if (result2.isCancel()){
       return MenuResult.cancel();
     } else if (result2.isQuit()) {
       return MenuResult.quit();
     } else {
+      if (propertyResetArgument.hasValue())
+      {
+        getCommandBuilder().addArgument(propertyResetArgument);
+      }
+      if (propertySetArgument.hasValue())
+      {
+        getCommandBuilder().addArgument(propertySetArgument);
+      }
+      if (propertyAddArgument.hasValue())
+      {
+        getCommandBuilder().addArgument(propertyAddArgument);
+      }
+      if (propertyRemoveArgument.hasValue())
+      {
+        getCommandBuilder().addArgument(propertyRemoveArgument);
+      }
       return MenuResult.success(0);
     }
   }
 
 
-
   // Apply a single modification to the current change-set.
   @SuppressWarnings("unchecked")
   private <T> void modifyPropertyValues(ManagedObject<?> mo,
@@ -844,4 +940,66 @@
 
     changes.put(pd, values);
   }
+
+  /**
+   * Creates an argument (the one that the user should provide in the
+   * command-line) that is equivalent to the modification proposed by the user
+   * in the provided PropertyEditorModification object.
+   * @param mod the object describing the modification made.
+   * @return the argument representing the modification.
+   * @throws ArgumentException if there is a problem creating the argument.
+   */
+  private static Argument createArgument(PropertyEditorModification mod)
+  throws ArgumentException
+  {
+    StringArgument arg;
+
+    String propName = mod.getPropertyDefinition().getName();
+
+    switch (mod.getType())
+    {
+    case RESET:
+      arg = new StringArgument(OPTION_DSCFG_LONG_RESET,
+          OPTION_DSCFG_SHORT_RESET, OPTION_DSCFG_LONG_RESET, false, true, true,
+          INFO_PROPERTY_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_RESET_PROP.get());
+      arg.addValue(propName);
+      break;
+    case REMOVE:
+      arg = new StringArgument(OPTION_DSCFG_LONG_REMOVE,
+          OPTION_DSCFG_SHORT_REMOVE, OPTION_DSCFG_LONG_REMOVE, false, true,
+          true, INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL.get());
+      for (Object value : mod.getModificationValues())
+      {
+        arg.addValue(propName+':'+value);
+      }
+      break;
+    case ADD:
+      arg = new StringArgument(OPTION_DSCFG_LONG_ADD,
+          OPTION_DSCFG_SHORT_ADD, OPTION_DSCFG_LONG_ADD, false, true, true,
+          INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_ADD_PROP_VAL.get());
+      for (Object value : mod.getModificationValues())
+      {
+        arg.addValue(propName+':'+value);
+      }
+      break;
+    case SET:
+      arg = new StringArgument(OPTION_DSCFG_LONG_SET,
+          OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, true,
+          INFO_VALUE_SET_PLACEHOLDER.get(), null, null,
+          INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
+      for (Object value : mod.getModificationValues())
+      {
+        arg.addValue(propName+':'+value);
+      }
+      break;
+    default:
+      // Bug
+      throw new IllegalStateException("Unknown modification type: "+
+          mod.getType());
+    }
+    return arg;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java
index 0458732..dddb2b2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java
@@ -70,11 +70,14 @@
 import org.opends.server.admin.client.ManagedObjectDecodingException;
 import org.opends.server.admin.client.ManagementContext;
 import org.opends.server.tools.ClientException;
+import org.opends.server.util.ServerConstants;
+import org.opends.server.util.args.Argument;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.BooleanArgument;
 import org.opends.server.util.args.StringArgument;
 import org.opends.server.util.args.SubCommand;
 import org.opends.server.util.cli.CLIException;
+import org.opends.server.util.cli.CommandBuilder;
 import org.opends.server.util.cli.ConsoleApplication;
 import org.opends.server.util.cli.Menu;
 import org.opends.server.util.cli.MenuBuilder;
@@ -125,8 +128,6 @@
     // The current result.
     private MenuResult<ManagedObject<?>> result;
 
-
-
     /**
      * {@inheritDoc}
      */
@@ -383,7 +384,7 @@
    * A path serializer which is used to register a sub-command's
    * naming arguments.
    */
-  private static class NamingArgumentBuilder implements
+  protected static class NamingArgumentBuilder implements
       ManagedObjectPathSerializer {
 
     /**
@@ -580,9 +581,17 @@
   // The argument which should be used to request specific time units.
   private StringArgument unitTimeArgument;
 
+  // The command builder associated with this handler.
+  private CommandBuilder commandBuilder;
 
 
   /**
+   * The boolean that says whether is useful to display the command builder's
+   * contents after calling the run method or not.
+   */
+  private boolean isCommandBuilderUseful = true;
+
+  /**
    * Create a new sub-command handler.
    */
   protected SubCommandHandler() {
@@ -631,6 +640,44 @@
   public abstract SubCommand getSubCommand();
 
 
+  /**
+   * Gets the command builder associated with this handler.  The method should
+   * be called after calling <CODE>run()</CODE> method.
+   *
+   * @return Returns the sub-command associated with this handler.
+   */
+  public final CommandBuilder getCommandBuilder()
+  {
+    if (commandBuilder == null)
+    {
+      commandBuilder = new CommandBuilder(
+            System.getProperty(ServerConstants.PROPERTY_SCRIPT_NAME),
+            getSubCommand().getName());
+    }
+    return commandBuilder;
+  }
+
+  /**
+   * This method tells whether displaying the command builder contents makes
+   * sense or not.  For instance in the case of the help subcommand handler
+   * displaying information makes no much sense.
+   *
+   * @return <CODE>true</CODE> if displaying the command builder is useful and
+   * <CODE>false</CODE> otherwise.
+   */
+  public final boolean isCommandBuilderUseful()
+  {
+    return isCommandBuilderUseful;
+  }
+
+  /**
+   * Sets wheter the command builder is useful or not.
+   * @param isCommandBuilderUseful whether the command builder is useful or not.
+   */
+  protected final void setCommandBuilderUseful(boolean isCommandBuilderUseful)
+  {
+    this.isCommandBuilderUseful = isCommandBuilderUseful;
+  }
 
   /**
    * Gets the tags associated with this sub-command handler.
@@ -969,7 +1016,7 @@
    *           there are no children.
    */
   protected final <C extends ConfigurationClient, S extends Configuration>
-      MenuResult<String> readChildName(
+  MenuResult<String> readChildName(
       ConsoleApplication app, ManagedObject<?> parent,
       InstantiableRelationDefinition<C, S> r,
       AbstractManagedObjectDefinition<? extends C, ? extends S> d)
@@ -1019,7 +1066,7 @@
     case 0: {
       // No options available - abort.
       Message msg =
-          ERR_DSCFG_ERROR_FINDER_NO_CHILDREN.get(d.getUserFriendlyPluralName());
+        ERR_DSCFG_ERROR_FINDER_NO_CHILDREN.get(d.getUserFriendlyPluralName());
       app.println(msg);
       return MenuResult.cancel();
     }
@@ -1027,8 +1074,22 @@
       // Only one option available so confirm that the user wishes to
       // access it.
       Message msg = INFO_DSCFG_FINDER_PROMPT_SINGLE.get(
-              d.getUserFriendlyName(), children.first());
+          d.getUserFriendlyName(), children.first());
       if (app.confirmAction(msg, true)) {
+        try
+        {
+          String argName = CLIProfile.getInstance().getNamingArgument(r);
+          StringArgument arg = new StringArgument(argName, null, argName, false,
+              true, INFO_NAME_PLACEHOLDER.get(),
+              INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName()));
+          arg.addValue(children.first());
+          getCommandBuilder().addArgument(arg);
+        }
+        catch (Throwable t)
+        {
+          // Bug
+          new RuntimeException("Unexpected exception: "+t, t);
+        }
         return MenuResult.success(children.first());
       } else {
         return MenuResult.cancel();
@@ -1052,7 +1113,22 @@
       builder.addQuitOption();
 
       Menu<String> menu = builder.toMenu();
-      return menu.run();
+      MenuResult<String> result = menu.run();
+      try
+      {
+        String argName = CLIProfile.getInstance().getNamingArgument(r);
+        StringArgument arg = new StringArgument(argName, null, argName, false,
+            true, INFO_NAME_PLACEHOLDER.get(),
+            INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName()));
+        arg.addValue(result.getValue());
+        getCommandBuilder().addArgument(arg);
+      }
+      catch (Throwable t)
+      {
+        // Bug
+        throw new RuntimeException("Unexpected exception: "+t, t);
+      }
+      return result;
     }
     }
   }
@@ -1135,6 +1211,20 @@
     subCommand.addArgument(unitTimeArgument);
   }
 
-
+  /**
+   * Updates the command builder with the arguments defined in the sub command.
+   * This implies basically putting the arguments provided by the user in the
+   * command builder.
+   */
+  protected final void updateCommandBuilderWithSubCommand()
+  {
+    for (Argument arg : getSubCommand().getArguments())
+    {
+      if (arg.isPresent())
+      {
+        getCommandBuilder().addArgument(arg);
+      }
+    }
+  }
 
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/args/ArgumentParser.java b/opendj-sdk/opends/src/server/org/opends/server/util/args/ArgumentParser.java
index 9b86d2d..dbd90a2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/args/ArgumentParser.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/args/ArgumentParser.java
@@ -1745,7 +1745,9 @@
               OPTION_LONG_NO_PROP_FILE.equals(longId) ||
               OPTION_LONG_SCRIPT_FRIENDLY.equals(longId) ||
               OPTION_LONG_DONT_WRAP.equals(longId) ||
-              OPTION_LONG_ENCODING.equals(longId);
+              OPTION_LONG_ENCODING.equals(longId) ||
+              OPTION_DSCFG_LONG_DISPLAY_EQUIVALENT.equals(longId) ||
+              OPTION_LONG_EQUIVALENT_COMMAND_FILE_PATH.equals(longId);
     }
     return io;
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/cli/CommandBuilder.java b/opendj-sdk/opends/src/server/org/opends/server/util/cli/CommandBuilder.java
new file mode 100644
index 0000000..3c9bf11
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/cli/CommandBuilder.java
@@ -0,0 +1,287 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.util.cli;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.opends.server.util.SetupUtils;
+import org.opends.server.util.args.Argument;
+import org.opends.server.util.args.BooleanArgument;
+import org.opends.server.util.args.FileBasedArgument;
+
+/**
+ * Class used to be able to generate the non interactive mode.
+ *
+ */
+public class CommandBuilder
+{
+  // The command name.
+  private String commandName;
+
+  // The subcommand name.
+  private String subcommandName;
+
+  private ArrayList<Argument> args = new ArrayList<Argument>();
+  private HashSet<Argument> obfuscatedArgs = new HashSet<Argument>();
+
+  // The value used to display arguments that must be obfuscated (such as
+  // passwords).  This does not require localization (since the output of
+  // command builder by its nature is not localized).
+  private final static String OBFUSCATED_VALUE = "******";
+
+  /**
+   * The constructor for the CommandBuilder.
+   * @param commandName the command name.
+   */
+  public CommandBuilder(String commandName)
+  {
+    this(commandName, null);
+  }
+
+  /**
+   * The constructor for the CommandBuilder.
+   * @param commandName the command name.
+   * @param subcommandName the subcommand name.
+   */
+  public CommandBuilder(String commandName, String subcommandName)
+  {
+    this.commandName = commandName;
+    this.subcommandName = subcommandName;
+  }
+
+  /**
+   * Adds an argument to the list of the command builder.
+   * @param argument the argument to be added.
+   */
+  public void addArgument(Argument argument)
+  {
+    // We use an ArrayList to be able to provide the possibility of updating
+    // the position of the attributes.
+    if (!args.contains(argument))
+    {
+      args.add(argument);
+    }
+  }
+
+  /**
+   * Adds an argument whose values must be obfuscated (passwords for instance).
+   * @param argument the argument to be added.
+   */
+  public void addObfuscatedArgument(Argument argument)
+  {
+    addArgument(argument);
+    obfuscatedArgs.add(argument);
+  }
+
+  /**
+   * Removes the provided argument from this CommandBuilder.
+   * @param argument the argument to be removed.
+   * @return <CODE>true</CODE> if the attribute was present and removed and
+   * <CODE>false</CODE> otherwise.
+   */
+  public boolean removeArgument(Argument argument)
+  {
+    obfuscatedArgs.remove(argument);
+    return args.remove(argument);
+  }
+
+  /**
+   * Appends the arguments of another command builder to this command builder.
+   * @param builder the CommandBuilder to append.
+   */
+  public void append(CommandBuilder builder)
+  {
+    for (Argument arg : builder.args)
+    {
+      if (builder.isObfuscated(arg))
+      {
+        addObfuscatedArgument(arg);
+      }
+      else
+      {
+        addArgument(arg);
+      }
+    }
+  }
+
+  /**
+   * Returns the String representation of this command builder (i.e. what we
+   * want to show to the user).
+   * @return the String representation of this command builder (i.e. what we
+   * want to show to the user).
+   */
+  public String toString()
+  {
+    return toString(false);
+  }
+
+  /**
+   * Returns the String representation of this command builder (i.e. what we
+   * want to show to the user).
+   * @param showObfuscated displays in clear the obfuscated values.
+   * @return the String representation of this command builder (i.e. what we
+   * want to show to the user).
+   */
+  private String toString(boolean showObfuscated)
+  {
+    StringBuilder builder = new StringBuilder();
+    builder.append(commandName);
+    if (subcommandName != null)
+    {
+      builder.append(" "+subcommandName);
+    }
+    for (Argument arg : args)
+    {
+      String argName;
+      if (arg.getLongIdentifier() != null)
+      {
+        argName = "--"+arg.getLongIdentifier();
+      }
+      else
+      {
+        argName = "-"+arg.getShortIdentifier();
+      }
+      String separator;
+      if (SetupUtils.isWindows())
+      {
+        separator = " ";
+      }
+      else
+      {
+        separator = " \\\n          ";
+      }
+
+      if (arg instanceof BooleanArgument)
+      {
+        builder.append(separator+argName);
+      }
+      else if (arg instanceof FileBasedArgument)
+      {
+        for (String value :
+          ((FileBasedArgument)arg).getNameToValueMap().keySet())
+        {
+          builder.append(separator+argName+" ");
+          if (isObfuscated(arg) && !showObfuscated)
+          {
+            value = OBFUSCATED_VALUE;
+          }
+          else
+          {
+            value = escapeValue(value);
+          }
+          builder.append(value);
+        }
+      }
+      else
+      {
+        for (String value : arg.getValues())
+        {
+          builder.append(separator+argName+" ");
+          if (isObfuscated(arg) && !showObfuscated)
+          {
+            value = OBFUSCATED_VALUE;
+          }
+          else
+          {
+            value = escapeValue(value);
+          }
+          builder.append(value);
+        }
+      }
+    }
+    return builder.toString();
+  }
+
+  /**
+   * Clears the arguments.
+   */
+  public void clearArguments()
+  {
+    args.clear();
+    obfuscatedArgs.clear();
+  }
+
+  /**
+   * Returns the list of arguments.
+   * @return the list of arguments.
+   */
+  public List<Argument> getArguments()
+  {
+    return args;
+  }
+
+  /**
+   * Tells whether the provided argument's values must be obfuscated or not.
+   * @param argument the argument to handle.
+   * @return <CODE>true</CODE> if the attribute's values must be obfuscated and
+   * <CODE>false</CODE> otherwise.
+   */
+  private boolean isObfuscated(Argument argument)
+  {
+    return obfuscatedArgs.contains(argument);
+  }
+
+  // Chars that require special treatment when passing them to command-line.
+  private final char[] charsToEscape = {' ', '\t', '\n', '|', ';', '<', '>',
+      '(', ')', '$', '`', '\\', '"', '\''};
+  /**
+   * This method simply takes a value and tries to transform it (with escape or
+   * '"') characters so that it can be used in a command line.
+   * @param value the String to be treated.
+   * @return the transformed value.
+   */
+  private String escapeValue(String value)
+  {
+    StringBuilder b = new StringBuilder();
+    if (SetupUtils.isUnix())
+    {
+      for (int i=0 ; i<value.length(); i++)
+      {
+        char c = value.charAt(i);
+        boolean charToEscapeFound = false;
+        for (int j=0; j<charsToEscape.length && !charToEscapeFound; j++)
+        {
+          charToEscapeFound = c == charsToEscape[j];
+        }
+        if (charToEscapeFound)
+        {
+          b.append('\\');
+        }
+        b.append(c);
+      }
+    }
+    else
+    {
+      b.append('"').append(value).append('"');
+    }
+
+    return b.toString();
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java b/opendj-sdk/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
index c99f564..6599232 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
@@ -115,6 +115,12 @@
 
   private Message heading = INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get();
 
+  // A copy of the secureArgList for convenience.
+  private SecureConnectionCliArgs copySecureArgsList = null;
+
+  // The command builder that we can return with the connection information.
+  private CommandBuilder commandBuilder;
+
   /**
    * Enumeration description protocols for interactive CLI choices.
    */
@@ -274,6 +280,18 @@
                                           SecureConnectionCliArgs secureArgs) {
     this.app = app;
     this.secureArgsList = secureArgs;
+    this.commandBuilder = new CommandBuilder(null);
+    copySecureArgsList = new SecureConnectionCliArgs();
+    try
+    {
+      copySecureArgsList.createGlobalArguments();
+    }
+    catch (Throwable t)
+    {
+      // This is  a bug: we should always be able to create the global arguments
+      // no need to localize this one.
+      throw new RuntimeException("Unexpected error: "+t, t);
+    }
   }
 
   /**
@@ -301,6 +319,9 @@
   public void run(boolean canUseSSL, boolean canUseStartTLS)
           throws ArgumentException
   {
+    // Reset everything
+    commandBuilder.clearArguments();
+    copySecureArgsList.createGlobalArguments();
     boolean secureConnection = (canUseSSL || canUseStartTLS) &&
       (
           secureArgsList.useSSLArg.isPresent()
@@ -372,6 +393,10 @@
       }
     }
 
+    copySecureArgsList.hostNameArg.clearValues();
+    copySecureArgsList.hostNameArg.addValue(hostName);
+    commandBuilder.addArgument(copySecureArgsList.hostNameArg);
+
     useSSL = secureArgsList.useSSL();
     useStartTLS = secureArgsList.useStartTLS();
     boolean connectionTypeIsSet =
@@ -455,6 +480,15 @@
       }
     }
 
+    if (useSSL)
+    {
+      commandBuilder.addArgument(copySecureArgsList.useSSLArg);
+    }
+    else if (useStartTLS)
+    {
+      commandBuilder.addArgument(copySecureArgsList.useStartTLSArg);
+    }
+
     if ((useSSL || useStartTLS) && (trustManager == null))
     {
       initializeTrustManager();
@@ -528,6 +562,10 @@
       }
     }
 
+    copySecureArgsList.portArg.clearValues();
+    copySecureArgsList.portArg.addValue(String.valueOf(portNumber));
+    commandBuilder.addArgument(copySecureArgsList.portArg);
+
     // Get the LDAP bind credentials.
     bindDN = secureArgsList.bindDnArg.getValue();
     adminUID = secureArgsList.adminUidArg.getValue();
@@ -629,6 +667,18 @@
               .unableToReadConnectionParameters(e);
         }
       }
+      if (useAdmin)
+      {
+        copySecureArgsList.adminUidArg.clearValues();
+        copySecureArgsList.adminUidArg.addValue(getAdministratorUID());
+        commandBuilder.addArgument(copySecureArgsList.adminUidArg);
+      }
+      else
+      {
+        copySecureArgsList.bindDnArg.clearValues();
+        copySecureArgsList.bindDnArg.addValue(getBindDN());
+        commandBuilder.addArgument(copySecureArgsList.bindDnArg);
+      }
     }
     else
     {
@@ -655,6 +705,10 @@
             throw ArgumentExceptionFactory.missingBindPassword(bindDN);
           }
         }
+        copySecureArgsList.bindPasswordFileArg.clearValues();
+        copySecureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(
+            secureArgsList.bindPasswordFileArg.getNameToValueMap());
+        commandBuilder.addArgument(secureArgsList.bindPasswordFileArg);
       }
       else if (bindPassword == null || bindPassword.equals("-"))
       {
@@ -695,6 +749,10 @@
               .unableToReadConnectionParameters(e);
         }
       }
+      copySecureArgsList.bindPasswordArg.clearValues();
+      copySecureArgsList.bindPasswordArg.addValue(bindPassword);
+      commandBuilder.addObfuscatedArgument(
+          copySecureArgsList.bindPasswordArg);
     }
   }
 
@@ -707,10 +765,17 @@
   private ApplicationTrustManager getTrustManagerInternal()
   throws ArgumentException
   {
+    // Remove these arguments since this method might be called several times.
+    commandBuilder.removeArgument(copySecureArgsList.trustAllArg);
+    commandBuilder.removeArgument(copySecureArgsList.trustStorePathArg);
+    commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordArg);
+    commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordFileArg);
+
     // If we have the trustALL flag, don't do anything
     // just return null
     if (secureArgsList.trustAllArg.isPresent())
     {
+      commandBuilder.addArgument(copySecureArgsList.trustAllArg);
       return null;
     }
 
@@ -755,6 +820,7 @@
         {
           if (result.getValue().equals(TrustMethod.TRUSTALL.getChoice()))
           {
+            commandBuilder.addArgument(copySecureArgsList.trustAllArg);
             // If we have the trustALL flag, don't do anything
             // just return null
             return null;
@@ -771,6 +837,10 @@
             // The certificate will be displayed to the user
             askForTrustStore = false;
             trustStoreInMemory = true;
+
+            // There is no direct equivalent for this option, so propose the
+            // trust all option as command-line argument.
+            commandBuilder.addArgument(copySecureArgsList.trustAllArg);
           }
           else
           {
@@ -791,9 +861,10 @@
       }
     }
 
-    // If we not trust all server certificates, we have to get info
+    // If we do not trust all server certificates, we have to get info
     // about truststore. First get the truststore path.
     truststorePath = secureArgsList.trustStorePathArg.getValue();
+
     if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent()
         && askForTrustStore)
     {
@@ -841,6 +912,13 @@
       }
     }
 
+    if (truststorePath != null)
+    {
+      copySecureArgsList.trustStorePathArg.clearValues();
+      copySecureArgsList.trustStorePathArg.addValue(truststorePath);
+      commandBuilder.addArgument(copySecureArgsList.trustStorePathArg);
+    }
+
     // Then the truststore password.
     //  As the most common case is to have no password for truststore,
     // we don't ask it in the interactive mode.
@@ -877,6 +955,7 @@
         }
       }
     }
+
     // We've got all the information to get the truststore manager
     try
     {
@@ -898,6 +977,23 @@
       {
         truststore.load(null, null);
       }
+
+      if (secureArgsList.trustStorePasswordFileArg.isPresent())
+      {
+        copySecureArgsList.trustStorePasswordFileArg.clearValues();
+        copySecureArgsList.trustStorePasswordFileArg.getNameToValueMap().putAll(
+            secureArgsList.trustStorePasswordFileArg.getNameToValueMap());
+        commandBuilder.addArgument(
+            copySecureArgsList.trustStorePasswordFileArg);
+      }
+      else
+      {
+        copySecureArgsList.trustStorePasswordArg.clearValues();
+        copySecureArgsList.trustStorePasswordArg.addValue(truststorePassword);
+        commandBuilder.addObfuscatedArgument(
+            copySecureArgsList.trustStorePasswordArg);
+      }
+
       return new ApplicationTrustManager(truststore);
     }
     catch (Exception e)
@@ -915,6 +1011,12 @@
   private KeyManager getKeyManagerInternal()
   throws ArgumentException
   {
+//  Remove these arguments since this method might be called several times.
+    commandBuilder.removeArgument(copySecureArgsList.certNicknameArg);
+    commandBuilder.removeArgument(copySecureArgsList.keyStorePathArg);
+    commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordArg);
+    commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordFileArg);
+
     // Do we need client side authentication ?
     // If one of the client side authentication args is set, we assume
     // that we
@@ -980,6 +1082,14 @@
       }
     }
 
+    if (keystorePath != null)
+    {
+      copySecureArgsList.keyStorePathArg.clearValues();
+      copySecureArgsList.keyStorePathArg.addValue(keystorePath);
+      commandBuilder.addArgument(copySecureArgsList.keyStorePathArg);
+    }
+
+
     // Then the keystore password.
     keystorePassword = secureArgsList.keyStorePasswordArg.getValue();
 
@@ -1094,6 +1204,29 @@
     ApplicationKeyManager akm = new ApplicationKeyManager(keystore,
         keystorePassword.toCharArray());
 
+
+    if (secureArgsList.keyStorePasswordFileArg.isPresent())
+    {
+      copySecureArgsList.keyStorePasswordFileArg.clearValues();
+      copySecureArgsList.keyStorePasswordFileArg.getNameToValueMap().putAll(
+          secureArgsList.keyStorePasswordFileArg.getNameToValueMap());
+      commandBuilder.addArgument(
+          copySecureArgsList.keyStorePasswordFileArg);
+    }
+    else
+    {
+      copySecureArgsList.keyStorePasswordArg.clearValues();
+      copySecureArgsList.keyStorePasswordArg.addValue(keystorePassword);
+      commandBuilder.addObfuscatedArgument(
+          copySecureArgsList.keyStorePasswordArg);
+    }
+
+    if (certifNickname != null)
+    {
+      copySecureArgsList.certNicknameArg.clearValues();
+      copySecureArgsList.certNicknameArg.addValue(certifNickname);
+    }
+
     if (certifNickname != null)
     {
       return new SelectableCertificateKeyManager(akm, certifNickname);
@@ -1499,7 +1632,7 @@
   }
 
  /**
-  * Populates an a set of LDAP options with state from this interaction.
+  * Populates a set of LDAP options with state from this interaction.
   *
   * @param  options existing set of options; may be null in which case this
   *         method will create a new set of <code>LDAPConnectionOptions</code>
@@ -1662,6 +1795,17 @@
  }
 
  /**
+  * Returns the command builder with the equivalent arguments on the
+  * non-interactive mode.
+  * @return the command builder with the equivalent arguments on the
+  * non-interactive mode.
+  */
+ public CommandBuilder getCommandBuilder()
+ {
+   return commandBuilder;
+ }
+
+ /**
   * Displays the heading if it was not displayed before.
   *
   */

--
Gitblit v1.10.0