/*
|
* The contents of this file are subject to the terms of the Common Development and
|
* Distribution License (the License). You may not use this file except in compliance with the
|
* License.
|
*
|
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
|
* specific language governing permission and limitations under the License.
|
*
|
* When distributing Covered Software, include this CDDL Header Notice in each file and include
|
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
|
* Header, with the fields enclosed by brackets [] replaced by your own identifying
|
* information: "Portions Copyright [year] [name of copyright owner]".
|
*
|
* Copyright 2007-2010 Sun Microsystems, Inc.
|
* Portions Copyright 2013-2016 ForgeRock AS.
|
*/
|
package org.forgerock.opendj.config.dsconfig;
|
|
import static com.forgerock.opendj.cli.ArgumentConstants.*;
|
import static com.forgerock.opendj.cli.ReturnCode.*;
|
import static com.forgerock.opendj.dsconfig.DsconfigMessages.*;
|
|
import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.*;
|
import static org.forgerock.opendj.config.dsconfig.DSConfig.*;
|
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.HashMap;
|
import java.util.HashSet;
|
import java.util.Iterator;
|
import java.util.LinkedList;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.SortedMap;
|
import java.util.SortedSet;
|
import java.util.TreeSet;
|
|
import org.forgerock.i18n.LocalizableMessage;
|
import org.forgerock.opendj.config.AbstractManagedObjectDefinition;
|
import org.forgerock.opendj.config.AggregationPropertyDefinition;
|
import org.forgerock.opendj.config.Configuration;
|
import org.forgerock.opendj.config.ConfigurationClient;
|
import org.forgerock.opendj.config.DefinitionDecodingException;
|
import org.forgerock.opendj.config.InstantiableRelationDefinition;
|
import org.forgerock.opendj.config.ManagedObjectAlreadyExistsException;
|
import org.forgerock.opendj.config.ManagedObjectDefinition;
|
import org.forgerock.opendj.config.ManagedObjectNotFoundException;
|
import org.forgerock.opendj.config.ManagedObjectOption;
|
import org.forgerock.opendj.config.ManagedObjectPath;
|
import org.forgerock.opendj.config.OptionalRelationDefinition;
|
import org.forgerock.opendj.config.PropertyDefinition;
|
import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder;
|
import org.forgerock.opendj.config.PropertyException;
|
import org.forgerock.opendj.config.PropertyOption;
|
import org.forgerock.opendj.config.PropertyProvider;
|
import org.forgerock.opendj.config.RelationDefinition;
|
import org.forgerock.opendj.config.SetRelationDefinition;
|
import org.forgerock.opendj.config.client.ConcurrentModificationException;
|
import org.forgerock.opendj.config.client.IllegalManagedObjectNameException;
|
import org.forgerock.opendj.config.client.ManagedObject;
|
import org.forgerock.opendj.config.client.ManagedObjectDecodingException;
|
import org.forgerock.opendj.config.client.ManagementContext;
|
import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException;
|
import org.forgerock.opendj.config.client.OperationRejectedException;
|
import org.forgerock.opendj.config.conditions.Condition;
|
import org.forgerock.opendj.config.conditions.ContainsCondition;
|
import org.forgerock.opendj.ldap.AuthorizationException;
|
import org.forgerock.opendj.ldap.LdapException;
|
|
import com.forgerock.opendj.cli.Argument;
|
import com.forgerock.opendj.cli.ArgumentException;
|
import com.forgerock.opendj.cli.ClientException;
|
import com.forgerock.opendj.cli.CommandBuilder;
|
import com.forgerock.opendj.cli.ConsoleApplication;
|
import com.forgerock.opendj.cli.HelpCallback;
|
import com.forgerock.opendj.cli.MenuBuilder;
|
import com.forgerock.opendj.cli.MenuResult;
|
import com.forgerock.opendj.cli.ReturnCode;
|
import com.forgerock.opendj.cli.StringArgument;
|
import com.forgerock.opendj.cli.SubCommand;
|
import com.forgerock.opendj.cli.SubCommandArgumentParser;
|
import com.forgerock.opendj.cli.TableBuilder;
|
import com.forgerock.opendj.cli.TextTablePrinter;
|
import com.forgerock.opendj.cli.ValidationCallback;
|
|
/**
|
* A sub-command handler which is used to create new managed objects.
|
* <p>
|
* This sub-command implements the various create-xxx sub-commands.
|
*
|
* @param <C>
|
* The type of managed object which can be created.
|
* @param <S>
|
* The type of server managed object which can be created.
|
*/
|
final class CreateSubCommandHandler<C extends ConfigurationClient, S extends Configuration> extends SubCommandHandler {
|
|
/**
|
* A property provider which uses the command-line arguments to provide initial property values.
|
*/
|
private static class MyPropertyProvider implements PropertyProvider {
|
|
/** Decoded set of properties. */
|
private final Map<PropertyDefinition<?>, Collection<?>> properties = new HashMap<>();
|
|
/**
|
* Create a new property provider using the provided set of property value arguments.
|
*
|
* @param d
|
* The managed object definition.
|
* @param namingPropertyDefinition
|
* The naming property definition if there is one.
|
* @param args
|
* The property value arguments.
|
* @throws ArgumentException
|
* If the property value arguments could not be parsed.
|
*/
|
public MyPropertyProvider(ManagedObjectDefinition<?, ?> d, PropertyDefinition<?> namingPropertyDefinition,
|
List<String> args) throws ArgumentException {
|
for (String s : args) {
|
// Parse the property "property:value".
|
int sep = s.indexOf(':');
|
|
if (sep < 0) {
|
throw ArgumentExceptionFactory.missingSeparatorInPropertyArgument(s);
|
}
|
|
if (sep == 0) {
|
throw ArgumentExceptionFactory.missingNameInPropertyArgument(s);
|
}
|
|
String propertyName = s.substring(0, sep);
|
String value = s.substring(sep + 1, s.length());
|
if (value.length() == 0) {
|
throw ArgumentExceptionFactory.missingValueInPropertyArgument(s);
|
}
|
|
// Check the property definition.
|
PropertyDefinition<?> pd;
|
try {
|
pd = d.getPropertyDefinition(propertyName);
|
} catch (IllegalArgumentException e) {
|
throw ArgumentExceptionFactory.unknownProperty(d, propertyName);
|
}
|
|
// Make sure that the user is not attempting to set the naming property.
|
if (pd.equals(namingPropertyDefinition)) {
|
throw ArgumentExceptionFactory.unableToSetNamingProperty(d, pd);
|
}
|
|
// Add the value.
|
addPropertyValue(d, pd, value);
|
}
|
}
|
|
/**
|
* Get the set of parsed property definitions that have values specified.
|
*
|
* @return Returns the set of parsed property definitions that have values specified.
|
*/
|
public Set<PropertyDefinition<?>> getProperties() {
|
return properties.keySet();
|
}
|
|
/** {@inheritDoc} */
|
@Override
|
@SuppressWarnings("unchecked")
|
public <T> Collection<T> getPropertyValues(PropertyDefinition<T> d) {
|
Collection<T> values = (Collection<T>) properties.get(d);
|
if (values != null) {
|
return values;
|
}
|
return Collections.emptySet();
|
}
|
|
/** Add a single property value. */
|
@SuppressWarnings("unchecked")
|
private <T> void addPropertyValue(ManagedObjectDefinition<?, ?> d, PropertyDefinition<T> pd, String s)
|
throws ArgumentException {
|
T value;
|
try {
|
value = pd.decodeValue(s);
|
} catch (PropertyException e) {
|
throw ArgumentExceptionFactory.adaptPropertyException(e, d);
|
}
|
|
Collection<T> values = (Collection<T>) properties.get(pd);
|
if (values == null) {
|
values = new LinkedList<>();
|
}
|
values.add(value);
|
|
if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
|
PropertyException e = PropertyException.propertyIsSingleValuedException(pd);
|
throw ArgumentExceptionFactory.adaptPropertyException(e, d);
|
}
|
|
properties.put(pd, values);
|
}
|
}
|
|
/**
|
* A help call-back which displays help about available component types.
|
*/
|
private static final class TypeHelpCallback<C extends ConfigurationClient, S extends Configuration> implements
|
HelpCallback {
|
|
/** The abstract definition for which to provide help on its sub-types. */
|
private final AbstractManagedObjectDefinition<C, S> d;
|
|
/** Create a new type help call-back. */
|
private TypeHelpCallback(AbstractManagedObjectDefinition<C, S> d) {
|
this.d = d;
|
}
|
|
/** {@inheritDoc} */
|
@Override
|
public void display(ConsoleApplication app) {
|
app.println(INFO_DSCFG_CREATE_TYPE_HELP_HEADING.get(d.getUserFriendlyPluralName()));
|
|
app.println();
|
app.println(d.getSynopsis());
|
|
if (d.getDescription() != null) {
|
app.println();
|
app.println(d.getDescription());
|
}
|
|
app.println();
|
app.println();
|
|
// Create a table containing a description of each component
|
// type.
|
TableBuilder builder = new TableBuilder();
|
|
builder.appendHeading(INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_TYPE.get());
|
|
builder.appendHeading(INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_DESCR.get());
|
|
boolean isFirst = true;
|
for (ManagedObjectDefinition<?, ?> mod : getSubTypes(d).values()) {
|
if (cannotDisplayAdvancedOrCustomTypes(app, mod)) {
|
continue;
|
}
|
|
LocalizableMessage ufn = mod.getUserFriendlyName();
|
LocalizableMessage synopsis = mod.getSynopsis();
|
LocalizableMessage description = mod.getDescription();
|
if (CLIProfile.getInstance().isForCustomization(mod)) {
|
ufn = INFO_DSCFG_CUSTOM_TYPE_OPTION.get(ufn);
|
synopsis = INFO_DSCFG_CUSTOM_TYPE_SYNOPSIS.get(ufn);
|
description = null;
|
} else if (mod == d) {
|
ufn = INFO_DSCFG_GENERIC_TYPE_OPTION.get(ufn);
|
synopsis = INFO_DSCFG_GENERIC_TYPE_SYNOPSIS.get(ufn);
|
description = null;
|
}
|
|
if (!isFirst) {
|
builder.startRow();
|
builder.startRow();
|
} else {
|
isFirst = false;
|
}
|
|
builder.startRow();
|
builder.appendCell(ufn);
|
builder.appendCell(synopsis);
|
if (description != null) {
|
builder.startRow();
|
builder.startRow();
|
builder.appendCell();
|
builder.appendCell(description);
|
}
|
}
|
|
TextTablePrinter printer = new TextTablePrinter(app.getErrorStream());
|
printer.setColumnWidth(1, 0);
|
printer.setColumnSeparator(LIST_TABLE_SEPARATOR);
|
builder.print(printer);
|
app.println();
|
app.pressReturnToContinue();
|
}
|
}
|
|
/** The value for the long option set. */
|
private static final String OPTION_DSCFG_LONG_SET = "set";
|
/** The value for the long option type. */
|
private static final String OPTION_DSCFG_LONG_TYPE = "type";
|
/** The value for the short option property. */
|
private static final Character OPTION_DSCFG_SHORT_SET = null;
|
/** The value for the short option type. */
|
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 relation.
|
*
|
* @param <C>
|
* The type of managed object which can be created.
|
* @param <S>
|
* The type of server managed object which can be created.
|
* @param parser
|
* The sub-command argument parser.
|
* @param p
|
* The parent managed object path.
|
* @param r
|
* The instantiable relation.
|
* @return Returns the new create-xxx sub-command.
|
* @throws ArgumentException
|
* If the sub-command could not be created successfully.
|
*/
|
public static <C extends ConfigurationClient, S extends Configuration> CreateSubCommandHandler<C, S> create(
|
SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> r)
|
throws ArgumentException {
|
return new CreateSubCommandHandler<>(parser, p, r, r.getNamingPropertyDefinition(), p.child(r, "DUMMY"));
|
}
|
|
/**
|
* Creates a new create-xxx sub-command for a sets relation.
|
*
|
* @param <C>
|
* The type of managed object which can be created.
|
* @param <S>
|
* The type of server managed object which can be created.
|
* @param parser
|
* The sub-command argument parser.
|
* @param p
|
* The parent managed object path.
|
* @param r
|
* The set relation.
|
* @return Returns the new create-xxx sub-command.
|
* @throws ArgumentException
|
* If the sub-command could not be created successfully.
|
*/
|
public static <C extends ConfigurationClient, S extends Configuration> CreateSubCommandHandler<C, S> create(
|
SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, SetRelationDefinition<C, S> r)
|
throws ArgumentException {
|
return new CreateSubCommandHandler<>(parser, p, r, null, p.child(r));
|
}
|
|
/**
|
* Creates a new create-xxx sub-command for an optional relation.
|
*
|
* @param <C>
|
* The type of managed object which can be created.
|
* @param <S>
|
* The type of server managed object which can be created.
|
* @param parser
|
* The sub-command argument parser.
|
* @param p
|
* The parent managed object path.
|
* @param r
|
* The optional relation.
|
* @return Returns the new create-xxx sub-command.
|
* @throws ArgumentException
|
* If the sub-command could not be created successfully.
|
*/
|
public static <C extends ConfigurationClient, S extends Configuration> CreateSubCommandHandler<C, S> create(
|
SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p, OptionalRelationDefinition<C, S> r)
|
throws ArgumentException {
|
return new CreateSubCommandHandler<>(parser, p, r, null, p.child(r));
|
}
|
|
/**
|
* 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.
|
* @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.
|
*/
|
public static <C extends ConfigurationClient, S extends Configuration> MenuResult<String> createManagedObject(
|
ConsoleApplication app, ManagementContext context, ManagedObject<?> parent,
|
InstantiableRelationDefinition<C, S> rd) throws ClientException {
|
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 ClientException
|
* 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 {
|
AbstractManagedObjectDefinition<C, S> d = rd.getChildDefinition();
|
|
// First determine what type of component the user wants to create.
|
MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> result;
|
result = getTypeInteractively(app, d, Collections.<String> emptySet());
|
|
ManagedObjectDefinition<? extends C, ? extends S> mod;
|
if (result.isSuccess()) {
|
mod = result.getValue();
|
} else if (result.isCancel()) {
|
return MenuResult.cancel();
|
} else {
|
return MenuResult.quit();
|
}
|
|
// Now create the component.
|
app.println();
|
app.println();
|
// FIXME: handle default value exceptions?
|
List<PropertyException> exceptions = new LinkedList<>();
|
ManagedObject<? extends C> mo = createChildInteractively(app, parent, rd, mod, exceptions);
|
|
// Let the user interactively configure the managed object and commit it.
|
MenuResult<Void> result2 = commitManagedObject(app, context, mo, handler);
|
if (result2.isCancel()) {
|
return MenuResult.cancel();
|
} else if (result2.isQuit()) {
|
return MenuResult.quit();
|
} else {
|
return MenuResult.success(mo.getManagedObjectPath().getName());
|
}
|
}
|
|
/**
|
* Check that any referenced components are enabled if required.
|
*/
|
private static MenuResult<Void> checkReferences(ConsoleApplication app, ManagementContext context,
|
ManagedObject<?> mo, SubCommandHandler handler) throws ClientException {
|
ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
|
LocalizableMessage ufn = d.getUserFriendlyName();
|
|
try {
|
for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
|
if (pd instanceof AggregationPropertyDefinition<?, ?>) {
|
AggregationPropertyDefinition<?, ?> apd = (AggregationPropertyDefinition<?, ?>) pd;
|
|
// Skip this aggregation if the referenced managed objects
|
// do not need to be enabled.
|
if (!apd.getTargetNeedsEnablingCondition().evaluate(context, mo)) {
|
continue;
|
}
|
|
// The referenced component(s) must be enabled.
|
for (String name : mo.getPropertyValues(apd)) {
|
ManagedObjectPath<?, ?> path = apd.getChildPath(name);
|
LocalizableMessage rufn = path.getManagedObjectDefinition().getUserFriendlyName();
|
ManagedObject<?> ref;
|
try {
|
ref = context.getManagedObject(path);
|
} catch (DefinitionDecodingException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_DDE.get(rufn, rufn, rufn);
|
throw new ClientException(ReturnCode.OTHER, msg);
|
} catch (ManagedObjectDecodingException e) {
|
// FIXME: should not abort here. Instead, display the
|
// errors (if verbose) and apply the changes to the
|
// partial managed object.
|
LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MODE.get(rufn);
|
throw new ClientException(ReturnCode.OTHER, msg, e);
|
} catch (ManagedObjectNotFoundException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_GET_CHILD_MONFE.get(rufn);
|
throw new ClientException(ReturnCode.NO_SUCH_OBJECT, msg);
|
}
|
|
Condition condition = apd.getTargetIsEnabledCondition();
|
while (!condition.evaluate(context, ref)) {
|
boolean isBadReference = true;
|
|
if (condition instanceof ContainsCondition) {
|
// Attempt to automatically enable the managed object.
|
ContainsCondition cvc = (ContainsCondition) condition;
|
app.println();
|
if (app.confirmAction(
|
INFO_EDITOR_PROMPT_ENABLED_REFERENCED_COMPONENT.get(rufn, name, ufn), true)) {
|
cvc.setPropertyValue(ref);
|
try {
|
ref.commit();
|
isBadReference = false;
|
} catch (MissingMandatoryPropertiesException e) {
|
// Give the user the chance to fix the problems.
|
app.errPrintln();
|
displayMissingMandatoryPropertyException(app, e);
|
app.errPrintln();
|
if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn), true)) {
|
MenuResult<Void> result = SetPropSubCommandHandler.modifyManagedObject(app,
|
context, ref, handler);
|
if (result.isQuit()) {
|
return result;
|
} else if (result.isSuccess()) {
|
// The referenced component was modified
|
// successfully, but may still be disabled.
|
isBadReference = false;
|
}
|
}
|
} catch (ConcurrentModificationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn);
|
throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg);
|
} catch (OperationRejectedException e) {
|
// Give the user the chance to fix the problems.
|
app.errPrintln();
|
displayOperationRejectedException(app, e);
|
app.errPrintln();
|
if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT.get(rufn), true)) {
|
MenuResult<Void> result = SetPropSubCommandHandler.modifyManagedObject(app,
|
context, ref, handler);
|
if (result.isQuit()) {
|
return result;
|
} else if (result.isSuccess()) {
|
// The referenced component was modified
|
// successfully, but may still be disabled.
|
isBadReference = false;
|
}
|
}
|
} catch (ManagedObjectAlreadyExistsException e) {
|
// Should never happen.
|
throw new IllegalStateException(e);
|
}
|
}
|
} else {
|
app.println();
|
if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_TO_ENABLE.get(rufn, name, ufn), true)) {
|
MenuResult<Void> result = SetPropSubCommandHandler.modifyManagedObject(app,
|
context, ref, handler);
|
if (result.isQuit()) {
|
return result;
|
} else if (result.isSuccess()) {
|
// The referenced component was modified
|
// successfully, but may still be disabled.
|
isBadReference = false;
|
}
|
}
|
}
|
|
// If the referenced component is still disabled because
|
// the user refused to modify it, then give the used the
|
// option of editing the referencing component.
|
if (isBadReference) {
|
app.errPrintln();
|
app.errPrintln(ERR_SET_REFERENCED_COMPONENT_DISABLED.get(ufn, rufn));
|
app.errPrintln();
|
if (app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) {
|
return MenuResult.again();
|
}
|
return MenuResult.cancel();
|
}
|
}
|
}
|
}
|
}
|
} catch (AuthorizationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn);
|
throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
|
} catch (LdapException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage());
|
throw new ClientException(ReturnCode.OTHER, msg);
|
}
|
|
return MenuResult.success();
|
}
|
|
/** Commit a new managed object's configuration. */
|
private static MenuResult<Void> commitManagedObject(ConsoleApplication app, ManagementContext context,
|
ManagedObject<?> mo, SubCommandHandler handler) throws ClientException {
|
ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
|
LocalizableMessage ufn = d.getUserFriendlyName();
|
|
PropertyValueEditor editor = new PropertyValueEditor(app, context);
|
while (true) {
|
// Interactively set properties if applicable.
|
if (app.isInteractive()) {
|
SortedSet<PropertyDefinition<?>> properties = new TreeSet<>();
|
for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
|
if (cannotDisplay(app, pd)) {
|
continue;
|
}
|
properties.add(pd);
|
}
|
|
MenuResult<Void> result = editor.edit(mo, properties, true);
|
|
// Interactively enable/edit referenced components.
|
if (result.isSuccess()) {
|
result = checkReferences(app, context, mo, handler);
|
if (result.isAgain()) {
|
// Edit again.
|
continue;
|
}
|
}
|
|
if (result.isQuit()) {
|
if (!app.isMenuDrivenMode()) {
|
// User chose to cancel any changes.
|
app.println();
|
app.println(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(ufn));
|
}
|
return MenuResult.quit();
|
} else if (result.isCancel()) {
|
return MenuResult.cancel();
|
}
|
}
|
|
try {
|
// Create the managed object.
|
mo.commit();
|
|
// Output success message.
|
if (app.isInteractive() || app.isVerbose()) {
|
app.println();
|
app.println(INFO_DSCFG_CONFIRM_CREATE_SUCCESS.get(ufn));
|
}
|
|
if (handler != null) {
|
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()) {
|
throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e);
|
}
|
// If interactive, give the user the chance to fix the problems.
|
app.errPrintln();
|
displayMissingMandatoryPropertyException(app, e);
|
app.errPrintln();
|
if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) {
|
return MenuResult.cancel();
|
}
|
} catch (AuthorizationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn);
|
throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
|
} catch (ConcurrentModificationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn);
|
throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg);
|
} catch (OperationRejectedException e) {
|
if (!app.isInteractive()) {
|
throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e);
|
}
|
// If interactive, give the user the chance to fix the problems.
|
app.errPrintln();
|
displayOperationRejectedException(app, e);
|
app.errPrintln();
|
if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) {
|
return MenuResult.cancel();
|
}
|
} catch (LdapException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage());
|
return interactivePrintOrThrowError(app, msg, CLIENT_SIDE_SERVER_DOWN);
|
} catch (ManagedObjectAlreadyExistsException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_MOAEE.get(ufn);
|
return interactivePrintOrThrowError(app, msg, ENTRY_ALREADY_EXISTS);
|
}
|
}
|
}
|
|
private static boolean cannotDisplay(ConsoleApplication app, PropertyDefinition<?> pd) {
|
return pd.hasOption(PropertyOption.HIDDEN)
|
|| (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED));
|
}
|
|
/** Interactively create the child by prompting for the name. */
|
private static <C extends ConfigurationClient, S
|
extends Configuration> ManagedObject<? extends C> createChildInteractively(
|
ConsoleApplication app, final ManagedObject<?> parent,
|
final InstantiableRelationDefinition<C, S> irelation,
|
final ManagedObjectDefinition<? extends C, ? extends S> d, final List<PropertyException> exceptions)
|
throws ClientException {
|
|
ValidationCallback<ManagedObject<? extends C>> validator
|
= new ValidationCallback<ManagedObject<? extends C>>() {
|
|
@Override
|
public ManagedObject<? extends C> validate(ConsoleApplication app, String input)
|
throws ClientException {
|
ManagedObject<? extends C> child;
|
|
// First attempt to create the child, this will guarantee that
|
// the name is acceptable.
|
try {
|
child = parent.createChild(irelation, d, input, exceptions);
|
} catch (IllegalManagedObjectNameException e) {
|
app.errPrintln();
|
app.errPrintln(adaptIllegalManagedObjectNameException(e, d).getMessageObject());
|
app.errPrintln();
|
return null;
|
}
|
|
// Make sure that there are not any other children with the
|
// same name.
|
try {
|
// Attempt to retrieve a child using this name.
|
parent.getChild(irelation, input);
|
} catch (AuthorizationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(irelation.getUserFriendlyName());
|
throw new ClientException(ReturnCode.ERROR_USER_DATA, msg);
|
} catch (ConcurrentModificationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(irelation.getUserFriendlyName());
|
throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg);
|
} catch (LdapException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(irelation.getUserFriendlyName(),
|
e.getMessage());
|
throw new ClientException(ReturnCode.APPLICATION_ERROR, msg);
|
} catch (DefinitionDecodingException | ManagedObjectDecodingException e) {
|
// Do nothing.
|
} catch (ManagedObjectNotFoundException e) {
|
// The child does not already exist so this name is ok.
|
return child;
|
}
|
|
// A child with the specified name must already exist.
|
app.errPrintln();
|
app.errPrintln(
|
ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS.get(irelation.getUserFriendlyName(), input));
|
app.errPrintln();
|
return null;
|
}
|
};
|
|
// Display additional help if the name is a naming property.
|
LocalizableMessage ufn = d.getUserFriendlyName();
|
PropertyDefinition<?> pd = irelation.getNamingPropertyDefinition();
|
if (pd != null) {
|
app.println(INFO_DSCFG_CREATE_NAME_PROMPT_NAMING.get(ufn, pd.getName()));
|
|
app.println();
|
app.errPrintln(pd.getSynopsis(), 4);
|
|
if (pd.getDescription() != null) {
|
app.println();
|
app.errPrintln(pd.getDescription(), 4);
|
}
|
|
PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(true);
|
TableBuilder builder = new TableBuilder();
|
builder.startRow();
|
builder.appendCell(INFO_EDITOR_HEADING_SYNTAX.get());
|
builder.appendCell(b.getUsage(pd));
|
|
TextTablePrinter printer = new TextTablePrinter(app.getErrorStream());
|
printer.setDisplayHeadings(false);
|
printer.setIndentWidth(4);
|
printer.setColumnWidth(1, 0);
|
|
app.println();
|
builder.print(printer);
|
app.println();
|
|
return app.readValidatedInput(INFO_DSCFG_CREATE_NAME_PROMPT_NAMING_CONT.get(ufn), validator);
|
} else {
|
return app.readValidatedInput(INFO_DSCFG_CREATE_NAME_PROMPT.get(ufn), validator);
|
}
|
}
|
|
/** Interactively ask the user which type of component they want to create. */
|
private static <C extends ConfigurationClient, S extends Configuration> MenuResult
|
<ManagedObjectDefinition<? extends C, ? extends S>> getTypeInteractively(
|
ConsoleApplication app, AbstractManagedObjectDefinition<C, S> d, Set<String> prohibitedTypes)
|
throws ClientException {
|
// First get the list of available of sub-types.
|
List<ManagedObjectDefinition<? extends C, ? extends S>> filteredTypes
|
= new LinkedList<>(getSubTypes(d).values());
|
boolean isOnlyOneType = filteredTypes.size() == 1;
|
|
Iterator<ManagedObjectDefinition<? extends C, ? extends S>> i;
|
for (i = filteredTypes.iterator(); i.hasNext();) {
|
ManagedObjectDefinition<? extends C, ? extends S> cd = i.next();
|
if (prohibitedTypes.contains(cd.getName())
|
|| cannotDisplayAdvancedOrCustomTypes(app, cd)) {
|
i.remove();
|
}
|
}
|
|
// If there is only one choice then return immediately.
|
if (filteredTypes.size() == 0) {
|
app.errPrintln(ERR_DSCFG_ERROR_NO_AVAILABLE_TYPES.get(d.getUserFriendlyName()));
|
return MenuResult.<ManagedObjectDefinition<? extends C, ? extends S>> cancel();
|
} else if (filteredTypes.size() == 1) {
|
ManagedObjectDefinition<? extends C, ? extends S> type = filteredTypes.iterator().next();
|
if (!isOnlyOneType) {
|
// Only one option available so confirm that the user wishes to use it.
|
LocalizableMessage msg = INFO_DSCFG_TYPE_PROMPT_SINGLE.get(d.getUserFriendlyName(),
|
type.getUserFriendlyName());
|
if (!app.confirmAction(msg, true)) {
|
return MenuResult.cancel();
|
}
|
}
|
return MenuResult.<ManagedObjectDefinition<? extends C, ? extends S>> success(type);
|
} else {
|
MenuBuilder<ManagedObjectDefinition<? extends C, ? extends S>> builder = new MenuBuilder<>(app);
|
LocalizableMessage msg = INFO_DSCFG_CREATE_TYPE_PROMPT.get(d.getUserFriendlyName());
|
builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
|
builder.setPrompt(msg);
|
|
for (ManagedObjectDefinition<? extends C, ? extends S> mod : filteredTypes) {
|
|
LocalizableMessage option = mod.getUserFriendlyName();
|
if (CLIProfile.getInstance().isForCustomization(mod)) {
|
option = INFO_DSCFG_CUSTOM_TYPE_OPTION.get(option);
|
} else if (mod == d) {
|
option = INFO_DSCFG_GENERIC_TYPE_OPTION.get(option);
|
}
|
builder.addNumberedOption(option,
|
MenuResult.<ManagedObjectDefinition<? extends C, ? extends S>> success(mod));
|
}
|
builder.addHelpOption(new TypeHelpCallback<C, S>(d));
|
if (app.isMenuDrivenMode()) {
|
builder.addCancelOption(true);
|
}
|
builder.addQuitOption();
|
return builder.toMenu().run();
|
}
|
}
|
|
/** Only display advanced types and custom types in advanced mode. */
|
private static boolean cannotDisplayAdvancedOrCustomTypes(
|
ConsoleApplication app, ManagedObjectDefinition<?, ?> defn) {
|
return !app.isAdvancedMode()
|
&& (defn.hasOption(ManagedObjectOption.ADVANCED) || CLIProfile.getInstance().isForCustomization(defn));
|
}
|
|
/** The sub-commands naming arguments. */
|
private final List<StringArgument> namingArgs;
|
|
/** The optional naming property definition. */
|
private final PropertyDefinition<?> namingPropertyDefinition;
|
|
/** The path of the parent managed object. */
|
private final ManagedObjectPath<?, ?> path;
|
|
/** The argument which should be used to specify zero or more property values. */
|
private final StringArgument propertySetArgument;
|
|
/** The relation which should be used for creating children. */
|
private final RelationDefinition<C, S> relation;
|
|
/** The sub-command associated with this handler. */
|
private final SubCommand subCommand;
|
|
/** The argument which should be used to specify the type of managed object to be created. */
|
private final StringArgument typeArgument;
|
/** The syntax of the type argument. */
|
private final String typeUsage;
|
|
/** The set of instantiable managed object definitions and their associated type option value. */
|
private final SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> types;
|
|
/** Common constructor. */
|
private CreateSubCommandHandler(SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
|
RelationDefinition<C, S> r, PropertyDefinition<?> pd, ManagedObjectPath<?, ?> c) throws ArgumentException {
|
this.path = p;
|
this.relation = r;
|
this.namingPropertyDefinition = pd;
|
|
// Create the sub-command.
|
String name = "create-" + r.getName();
|
LocalizableMessage description = INFO_DSCFG_DESCRIPTION_SUBCMD_CREATE.get(r.getChildDefinition()
|
.getUserFriendlyPluralName());
|
this.subCommand = new SubCommand(parser, name, false, 0, 0, null, description);
|
|
// Create the -t argument which is used to specify the type of
|
// managed object to be created.
|
this.types = getSubTypes(r.getChildDefinition());
|
|
// Create the naming arguments.
|
this.namingArgs = createNamingArgs(subCommand, c, true);
|
|
// Build the -t option usage.
|
this.typeUsage = getSubTypesUsage(r.getChildDefinition());
|
|
// Create the --property argument which is used to specify property values.
|
this.propertySetArgument =
|
StringArgument.builder(OPTION_DSCFG_LONG_SET)
|
.shortIdentifier(OPTION_DSCFG_SHORT_SET)
|
.description(INFO_DSCFG_DESCRIPTION_PROP_VAL.get())
|
.multiValued()
|
.valuePlaceholder(INFO_VALUE_SET_PLACEHOLDER.get())
|
.buildAndAddToSubCommand(subCommand);
|
|
final StringArgument.Builder typeArgumentBuilder = StringArgument.builder(OPTION_DSCFG_LONG_TYPE)
|
.shortIdentifier(OPTION_DSCFG_SHORT_TYPE)
|
.valuePlaceholder(INFO_TYPE_PLACEHOLDER.get());
|
|
if (!types.containsKey(DSConfig.GENERIC_TYPE)) {
|
// The option is mandatory when non-interactive.
|
typeArgumentBuilder.description(INFO_DSCFG_DESCRIPTION_TYPE.get(
|
r.getChildDefinition().getUserFriendlyName(), typeUsage));
|
} else {
|
// The option has a sensible default "generic".
|
typeArgumentBuilder.description(INFO_DSCFG_DESCRIPTION_TYPE_DEFAULT.get(
|
r.getChildDefinition().getUserFriendlyName(), DSConfig.GENERIC_TYPE, typeUsage))
|
.defaultValue(DSConfig.GENERIC_TYPE);
|
// Hide the option if it defaults to generic and generic is the only possible value.
|
if (types.size() == 1) {
|
typeArgumentBuilder.hidden();
|
}
|
}
|
typeArgument = typeArgumentBuilder.buildAndAddToSubCommand(subCommand);
|
|
// Register the tags associated with the child managed objects.
|
addTags(relation.getChildDefinition().getAllTags());
|
}
|
|
/**
|
* Gets the relation definition associated with the type of component that this sub-command handles.
|
*
|
* @return Returns the relation definition associated with the type of component that this sub-command handles.
|
*/
|
public RelationDefinition<?, ?> getRelationDefinition() {
|
return relation;
|
}
|
|
/** {@inheritDoc} */
|
@Override
|
public SubCommand getSubCommand() {
|
return subCommand;
|
}
|
|
/** {@inheritDoc} */
|
@Override
|
public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory)
|
throws ArgumentException, ClientException {
|
final LocalizableMessage rufn = relation.getUserFriendlyName();
|
|
// Get the naming argument values.
|
List<String> names = getNamingArgValues(app, namingArgs);
|
// Reset the command builder
|
getCommandBuilder().clearArguments();
|
|
setCommandBuilderUseful(false);
|
|
// Update the command builder.
|
updateCommandBuilderWithSubCommand();
|
|
// Add the child managed object.
|
ManagementContext context = factory.getManagementContext(app);
|
MenuResult<ManagedObject<?>> result;
|
try {
|
result = getManagedObject(app, context, path, names);
|
} catch (AuthorizationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(rufn);
|
throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
|
} catch (DefinitionDecodingException e) {
|
LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName();
|
LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(pufn, pufn, pufn);
|
throw new ClientException(ReturnCode.OTHER, msg);
|
} catch (ManagedObjectDecodingException e) {
|
LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName();
|
LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(pufn);
|
throw new ClientException(ReturnCode.OTHER, msg, e);
|
} catch (ConcurrentModificationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(rufn);
|
throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg);
|
} catch (ManagedObjectNotFoundException e) {
|
LocalizableMessage pufn = path.getManagedObjectDefinition().getUserFriendlyName();
|
LocalizableMessage msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(pufn);
|
return interactivePrintOrThrowError(app, msg, NO_SUCH_OBJECT);
|
} catch (LdapException e) {
|
throw new ClientException(ReturnCode.OTHER, LocalizableMessage.raw(e.getLocalizedMessage()));
|
}
|
|
if (result.isQuit()) {
|
if (!app.isMenuDrivenMode()) {
|
// User chose to cancel creation.
|
app.println();
|
app.println(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(rufn));
|
}
|
return MenuResult.quit();
|
} else if (result.isCancel()) {
|
// Must be menu driven, so no need for error message.
|
return MenuResult.cancel();
|
}
|
|
ManagedObject<?> parent = result.getValue();
|
|
// Determine the type of managed object to be created. If we are creating
|
// a managed object beneath a set relation then prevent creation of
|
// duplicates.
|
Set<String> prohibitedTypes;
|
if (relation instanceof SetRelationDefinition) {
|
SetRelationDefinition<C, S> sr = (SetRelationDefinition<C, S>) relation;
|
prohibitedTypes = new HashSet<>();
|
try {
|
for (String child : parent.listChildren(sr)) {
|
prohibitedTypes.add(child);
|
}
|
} catch (AuthorizationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(rufn);
|
throw new ClientException(ReturnCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
|
} catch (ConcurrentModificationException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CME.get(rufn);
|
throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg);
|
} catch (LdapException e) {
|
LocalizableMessage msg = ERR_DSCFG_ERROR_CREATE_CE.get(rufn, e.getMessage());
|
throw new ClientException(ReturnCode.CLIENT_SIDE_SERVER_DOWN, msg);
|
}
|
} else {
|
// No prohibited types.
|
prohibitedTypes = Collections.emptySet();
|
}
|
|
ManagedObjectDefinition<? extends C, ? extends S> d;
|
if (!typeArgument.isPresent()) {
|
if (app.isInteractive()) {
|
// Let the user choose.
|
MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> dresult;
|
app.println();
|
app.println();
|
dresult = getTypeInteractively(app, relation.getChildDefinition(), prohibitedTypes);
|
|
if (dresult.isSuccess()) {
|
d = dresult.getValue();
|
} else if (dresult.isCancel()) {
|
return MenuResult.cancel();
|
} else {
|
// Must be quit.
|
if (!app.isMenuDrivenMode()) {
|
app.println();
|
app.println(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(rufn));
|
}
|
return MenuResult.quit();
|
}
|
} else if (typeArgument.getDefaultValue() != null) {
|
d = types.get(typeArgument.getDefaultValue());
|
} else {
|
throw ArgumentExceptionFactory.missingMandatoryNonInteractiveArgument(typeArgument);
|
}
|
} else {
|
d = types.get(typeArgument.getValue());
|
if (d == null) {
|
throw ArgumentExceptionFactory.unknownSubType(relation, typeArgument.getValue(), typeUsage);
|
}
|
}
|
|
// Encode the provided properties.
|
List<String> propertyArgs = propertySetArgument.getValues();
|
MyPropertyProvider provider = new MyPropertyProvider(d, namingPropertyDefinition, propertyArgs);
|
|
ManagedObject<? extends C> child;
|
List<PropertyException> exceptions = new LinkedList<>();
|
boolean isNameProvidedInteractively = false;
|
String providedNamingArgName = null;
|
if (relation instanceof InstantiableRelationDefinition) {
|
InstantiableRelationDefinition<C, S> irelation = (InstantiableRelationDefinition<C, S>) relation;
|
String name = names.get(names.size() - 1);
|
if (name == null) {
|
if (app.isInteractive()) {
|
app.println();
|
app.println();
|
child = createChildInteractively(app, parent, irelation, d, exceptions);
|
isNameProvidedInteractively = true;
|
providedNamingArgName = CLIProfile.getInstance().getNamingArgument(irelation);
|
} else {
|
throw ArgumentExceptionFactory
|
.missingMandatoryNonInteractiveArgument(namingArgs.get(names.size() - 1));
|
}
|
} else {
|
try {
|
child = parent.createChild(irelation, d, name, exceptions);
|
} catch (IllegalManagedObjectNameException e) {
|
throw ArgumentExceptionFactory.adaptIllegalManagedObjectNameException(e, d);
|
}
|
}
|
} else if (relation instanceof SetRelationDefinition) {
|
SetRelationDefinition<C, S> srelation = (SetRelationDefinition<C, S>) relation;
|
child = parent.createChild(srelation, d, exceptions);
|
} else {
|
OptionalRelationDefinition<C, S> orelation = (OptionalRelationDefinition<C, S>) relation;
|
child = parent.createChild(orelation, d, exceptions);
|
}
|
|
// FIXME: display any default behavior exceptions in verbose mode.
|
|
// Set any properties specified on the command line.
|
for (PropertyDefinition<?> pd : provider.getProperties()) {
|
setProperty(child, provider, pd);
|
}
|
|
// 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, this);
|
if (result2.isCancel()) {
|
return MenuResult.cancel();
|
} else if (result2.isQuit()) {
|
return MenuResult.quit();
|
} else {
|
addArgumentsToCommandBuilder(d, child, isNameProvidedInteractively, providedNamingArgName);
|
return MenuResult.success(0);
|
}
|
}
|
|
private void addArgumentsToCommandBuilder(ManagedObjectDefinition<? extends C, ? extends S> d,
|
ManagedObject<? extends C> child, boolean isNameProvidedInteractively, String providedNamingArgName)
|
throws ArgumentException {
|
CommandBuilder commandBuilder = getCommandBuilder();
|
if (typeArgument.hasValue()) {
|
commandBuilder.addArgument(typeArgument);
|
} else {
|
// Set the type provided by the user
|
StringArgument arg =
|
StringArgument.builder(OPTION_DSCFG_LONG_TYPE)
|
.shortIdentifier(OPTION_DSCFG_SHORT_TYPE)
|
.description(typeArgument.getDescription())
|
.defaultValue(typeArgument.getDefaultValue())
|
.valuePlaceholder(INFO_TYPE_PLACEHOLDER.get())
|
.buildArgument();
|
arg.addValue(getTypeName(d));
|
commandBuilder.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 =
|
StringArgument.builder(OPTION_DSCFG_LONG_SET)
|
.shortIdentifier(OPTION_DSCFG_SHORT_SET)
|
.description(INFO_DSCFG_DESCRIPTION_PROP_VAL.get())
|
.multiValued()
|
.valuePlaceholder(INFO_VALUE_SET_PLACEHOLDER.get())
|
.buildArgument();
|
for (String value : propertySetArgument.getValues()) {
|
if (canAddValue(commandBuilder, value)) {
|
filteredArg.addValue(value);
|
}
|
}
|
if (filteredArg.hasValue()) {
|
commandBuilder.addArgument(filteredArg);
|
}
|
}
|
|
/* Filter the arguments that are used internally */
|
List<Argument> argsCopy = new LinkedList<>(commandBuilder.getArguments());
|
for (Argument arg : argsCopy) {
|
if (arg != null
|
&& (OPTION_DSCFG_LONG_RESET.equals(arg.getLongIdentifier())
|
|| OPTION_DSCFG_LONG_REMOVE.equals(arg.getLongIdentifier()))) {
|
commandBuilder.removeArgument(arg);
|
}
|
}
|
|
if (isNameProvidedInteractively) {
|
StringArgument arg =
|
StringArgument.builder(providedNamingArgName)
|
.description(INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName()))
|
.valuePlaceholder(INFO_NAME_PLACEHOLDER.get())
|
.buildArgument();
|
arg.addValue(child.getManagedObjectPath().getName());
|
commandBuilder.addArgument(arg);
|
} else {
|
for (StringArgument arg : namingArgs) {
|
if (arg.isPresent()) {
|
commandBuilder.addArgument(arg);
|
}
|
}
|
}
|
}
|
|
private boolean canAddValue(CommandBuilder commandBuilder, String value) {
|
final int index = value.indexOf(':');
|
if (index == -1) {
|
return false;
|
}
|
String propName = value.substring(0, index);
|
for (Argument arg : commandBuilder.getArguments()) {
|
for (String value2 : arg.getValues()) {
|
String prop2Name = getPropName(arg.getLongIdentifier(), value2);
|
if (propName.equalsIgnoreCase(prop2Name)) {
|
return false;
|
}
|
}
|
}
|
return true;
|
}
|
|
private String getPropName(String argName, String value) {
|
if (OPTION_DSCFG_LONG_SET.equals(argName)
|
|| OPTION_DSCFG_LONG_REMOVE.equals(argName)) {
|
final int index = value.indexOf(':');
|
if (index != -1) {
|
return value.substring(0, index);
|
}
|
} else if (OPTION_DSCFG_LONG_RESET.equals(argName)) {
|
return value;
|
}
|
return null;
|
}
|
|
/** Set a property's initial values. */
|
private <T> void setProperty(ManagedObject<?> mo, MyPropertyProvider provider, PropertyDefinition<T> pd) {
|
// This cannot fail because the property values have already been validated.
|
mo.setPropertyValues(pd, provider.getPropertyValues(pd));
|
}
|
|
/**
|
* 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 <T> Argument createArgument(PropertyEditorModification<T> mod) throws ArgumentException {
|
StringArgument arg;
|
|
switch (mod.getType()) {
|
case ADD:
|
case SET:
|
arg =
|
StringArgument.builder(OPTION_DSCFG_LONG_SET)
|
.shortIdentifier(OPTION_DSCFG_SHORT_SET)
|
.description(INFO_DSCFG_DESCRIPTION_PROP_VAL.get())
|
.multiValued()
|
.valuePlaceholder(INFO_VALUE_SET_PLACEHOLDER.get())
|
.buildArgument();
|
addValues(mod, arg);
|
return arg;
|
case RESET:
|
arg =
|
StringArgument.builder(OPTION_DSCFG_LONG_RESET)
|
.description(INFO_DSCFG_DESCRIPTION_RESET_PROP.get())
|
.multiValued()
|
.valuePlaceholder(INFO_PROPERTY_PLACEHOLDER.get())
|
.buildArgument();
|
arg.addValue(mod.getPropertyDefinition().getName());
|
return arg;
|
case REMOVE:
|
arg =
|
StringArgument.builder(OPTION_DSCFG_LONG_REMOVE)
|
.description(INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL.get())
|
.multiValued()
|
.valuePlaceholder(INFO_VALUE_SET_PLACEHOLDER.get())
|
.buildArgument();
|
addValues(mod, arg);
|
return arg;
|
default:
|
// Bug
|
throw new IllegalStateException("Unknown modification type: " + mod.getType());
|
}
|
}
|
|
private static <T> void addValues(PropertyEditorModification<T> mod, StringArgument arg) {
|
PropertyDefinition<T> propertyDefinition = mod.getPropertyDefinition();
|
String propName = propertyDefinition.getName();
|
|
for (T value : mod.getModificationValues()) {
|
arg.addValue(propName + ':' + getArgumentValue(propertyDefinition, value));
|
}
|
}
|
|
/**
|
* 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) {
|
for (String key : types.keySet()) {
|
ManagedObjectDefinition<? extends C, ? extends S> current = types.get(key);
|
if (current.equals(d)) {
|
return key;
|
}
|
}
|
return d.getName();
|
}
|
}
|