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