From 00e13f972c166b2d849c8f7c72ee5c60330ef7e5 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Wed, 19 Sep 2007 14:16:12 +0000
Subject: [PATCH] Partial fix for issue 1449: add support in dsconfig for dynamically creating new components when editing aggregation properties.

---
 opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java     |  101 +++++++
 opends/src/messages/messages/dsconfig.properties                                |    1 
 opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java |  667 ++++++++++++++++++++++++++++----------------------
 3 files changed, 471 insertions(+), 298 deletions(-)

diff --git a/opends/src/messages/messages/dsconfig.properties b/opends/src/messages/messages/dsconfig.properties
index b6055d3..5dce163 100644
--- a/opends/src/messages/messages/dsconfig.properties
+++ b/opends/src/messages/messages/dsconfig.properties
@@ -439,3 +439,4 @@
 INFO_DSCFG_PROMPT_EDIT_142=Would you like to edit the properties of the %s in order to resolve this problem?
 SEVERE_ERR_GET_HEADING_MODE_SINGLE_143=The %s could not be decoded due to the following reason:
 SEVERE_ERR_GET_HEADING_MODE_PLURAL_144=The %s could not be decoded due to the following reasons:
+INFO_EDITOR_OPTION_CREATE_A_NEW_COMPONENT_145=Create a new %s
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java b/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
index 21de5a4..fb0a0d7 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
+++ b/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
@@ -83,7 +83,6 @@
 import org.opends.server.util.cli.CLIException;
 import org.opends.server.util.cli.ConsoleApplication;
 import org.opends.server.util.cli.HelpCallback;
-import org.opends.server.util.cli.Menu;
 import org.opends.server.util.cli.MenuBuilder;
 import org.opends.server.util.cli.MenuResult;
 import org.opends.server.util.cli.ValidationCallback;
@@ -233,7 +232,21 @@
    * A help call-back which displays help about available component
    * types.
    */
-  private final class TypeHelpCallback implements HelpCallback {
+  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}
@@ -250,7 +263,7 @@
           .get());
 
       boolean isFirst = true;
-      for (ManagedObjectDefinition<?, ?> d : types.values()) {
+      for (ManagedObjectDefinition<?, ?> mod : getSubTypes(d).values()) {
         if (!isFirst) {
           builder.startRow();
           builder.startRow();
@@ -259,13 +272,13 @@
         }
 
         builder.startRow();
-        builder.appendCell(d.getUserFriendlyName());
-        builder.appendCell(d.getSynopsis());
-        if (d.getDescription() != null) {
+        builder.appendCell(mod.getUserFriendlyName());
+        builder.appendCell(mod.getSynopsis());
+        if (mod.getDescription() != null) {
           builder.startRow();
           builder.startRow();
           builder.appendCell();
-          builder.appendCell(d.getDescription());
+          builder.appendCell(mod.getDescription());
         }
       }
 
@@ -278,6 +291,8 @@
     }
   }
 
+
+
   /**
    * The value for the -t argument which will be used for the most
    * generic managed object when it is instantiable.
@@ -361,18 +376,27 @@
 
 
   /**
-   * Create the provided managed object.
+   * 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 mo
-   *          The managed object to be created.
-   * @return Returns a MenuResult.success() if the managed object was
-   *         created successfully, or MenuResult.quit(), or
-   *         MenuResult.cancel(), if the managed object was edited
-   *         interactively and the user chose to quit or cancel.
+   * @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.
@@ -380,103 +404,43 @@
    *           If an error occurred whilst interacting with the
    *           console.
    */
-  public static MenuResult<Void> createManagedObject(ConsoleApplication app,
-      ManagementContext context, ManagedObject<?> mo) throws ClientException,
-      CLIException {
-    ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
-    Message ufn = d.getUserFriendlyName();
+  public static <C extends ConfigurationClient, S extends Configuration>
+      MenuResult<String> createManagedObject(
+      ConsoleApplication app, ManagementContext context,
+      ManagedObject<?> parent, InstantiableRelationDefinition<C, S> rd)
+      throws ClientException, CLIException {
+    AbstractManagedObjectDefinition<C, S> d = rd.getChildDefinition();
 
-    while (true) {
-      // Interactively set properties if applicable.
-      if (app.isInteractive()) {
-        SortedSet<PropertyDefinition<?>> properties =
-          new TreeSet<PropertyDefinition<?>>();
-        for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
-          if (pd.hasOption(PropertyOption.HIDDEN)) {
-            continue;
-          }
-          if (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
-            continue;
-          }
-          properties.add(pd);
-        }
+    // First determine what type of component the user wants to create.
+    MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> result;
+    result = getTypeInteractively(app, d);
 
-        PropertyValueEditor editor = new PropertyValueEditor(app, context);
-        MenuResult<Void> result = editor.edit(mo, properties, false);
+    ManagedObjectDefinition<? extends C, ? extends S> mod;
+    if (result.isSuccess()) {
+      mod = result.getValue();
+    } else if (result.isCancel()) {
+      return MenuResult.cancel();
+    } else {
+      return MenuResult.quit();
+    }
 
-        // Interactively enable/edit referenced components.
-        if (result.isSuccess()) {
-          result = checkReferences(app, context, mo);
-          if (result.isAgain()) {
-            // Edit again.
-            continue;
-          }
-        }
+    // Now create the component.
+    // FIXME: handle default value exceptions?
+    List<DefaultBehaviorException> exceptions =
+      new LinkedList<DefaultBehaviorException>();
+    app.println();
+    app.println();
+    ManagedObject<? extends C> mo =
+      createChildInteractively(app, parent, rd, mod, exceptions);
 
-        if (result.isQuit()) {
-          if (!app.isMenuDrivenMode()) {
-            // User chose to cancel any changes.
-            Message msg = INFO_DSCFG_CONFIRM_CREATE_FAIL.get(ufn);
-            app.printVerboseMessage(msg);
-          }
-          return MenuResult.quit();
-        } else if (result.isCancel()) {
-          return MenuResult.cancel();
-        }
-      }
-
-      try {
-        // Create the managed object.
-        mo.commit();
-
-        // Output success message.
-        app.println();
-        Message msg = INFO_DSCFG_CONFIRM_CREATE_SUCCESS.get(ufn);
-        app.printVerboseMessage(msg);
-
-        return MenuResult.success();
-      } catch (MissingMandatoryPropertiesException e) {
-        if (app.isInteractive()) {
-          // If interactive, give the user the chance to fix the
-          // problems.
-          app.println();
-          displayMissingMandatoryPropertyException(app, e);
-          app.println();
-          if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) {
-            return MenuResult.cancel();
-          }
-        } else {
-          throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, e
-              .getMessageObject(), e);
-        }
-      } catch (AuthorizationException e) {
-        Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn);
-        throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
-            msg);
-      } catch (ConcurrentModificationException e) {
-        Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn);
-        throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
-      } catch (OperationRejectedException e) {
-        if (app.isInteractive()) {
-          // If interactive, give the user the chance to fix the
-          // problems.
-          app.println();
-          displayOperationRejectedException(app, e);
-          app.println();
-          if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) {
-            return MenuResult.cancel();
-          }
-        } else {
-          throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, e
-              .getMessageObject(), e);
-        }
-      } catch (CommunicationException e) {
-        Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage());
-        throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
-      } catch (ManagedObjectAlreadyExistsException e) {
-        Message msg = ERR_DSCFG_ERROR_CREATE_MOAEE.get(ufn);
-        throw new ClientException(LDAPResultCode.ENTRY_ALREADY_EXISTS, msg);
-      }
+    // Let the user interactively configure the managed object and commit it.
+    MenuResult<Void> result2 = commitManagedObject(app, context, mo);
+    if (result2.isCancel()) {
+      return MenuResult.cancel();
+    } else if (result2.isQuit()) {
+      return MenuResult.quit();
+    } else {
+      return MenuResult.success(mo.getManagedObjectPath().getName());
     }
   }
 
@@ -637,6 +601,286 @@
 
 
 
+  // Commit a new managed object's configuration.
+  private static MenuResult<Void> commitManagedObject(ConsoleApplication app,
+      ManagementContext context, ManagedObject<?> mo) throws ClientException,
+      CLIException {
+    ManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition();
+    Message ufn = d.getUserFriendlyName();
+
+    while (true) {
+      // Interactively set properties if applicable.
+      if (app.isInteractive()) {
+        SortedSet<PropertyDefinition<?>> properties =
+          new TreeSet<PropertyDefinition<?>>();
+        for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+          if (pd.hasOption(PropertyOption.HIDDEN)) {
+            continue;
+          }
+          if (!app.isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
+            continue;
+          }
+          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);
+          if (result.isAgain()) {
+            // Edit again.
+            continue;
+          }
+        }
+
+        if (result.isQuit()) {
+          if (!app.isMenuDrivenMode()) {
+            // User chose to cancel any changes.
+            Message msg = INFO_DSCFG_CONFIRM_CREATE_FAIL.get(ufn);
+            app.printVerboseMessage(msg);
+          }
+          return MenuResult.quit();
+        } else if (result.isCancel()) {
+          return MenuResult.cancel();
+        }
+      }
+
+      try {
+        // Create the managed object.
+        mo.commit();
+
+        // Output success message.
+        app.println();
+        Message msg = INFO_DSCFG_CONFIRM_CREATE_SUCCESS.get(ufn);
+        app.printVerboseMessage(msg);
+
+        return MenuResult.success();
+      } catch (MissingMandatoryPropertiesException e) {
+        if (app.isInteractive()) {
+          // If interactive, give the user the chance to fix the
+          // problems.
+          app.println();
+          displayMissingMandatoryPropertyException(app, e);
+          app.println();
+          if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) {
+            return MenuResult.cancel();
+          }
+        } else {
+          throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, e
+              .getMessageObject(), e);
+        }
+      } catch (AuthorizationException e) {
+        Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn);
+        throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+            msg);
+      } catch (ConcurrentModificationException e) {
+        Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn);
+        throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
+      } catch (OperationRejectedException e) {
+        if (app.isInteractive()) {
+          // If interactive, give the user the chance to fix the
+          // problems.
+          app.println();
+          displayOperationRejectedException(app, e);
+          app.println();
+          if (!app.confirmAction(INFO_DSCFG_PROMPT_EDIT_AGAIN.get(ufn), true)) {
+            return MenuResult.cancel();
+          }
+        } else {
+          throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, e
+              .getMessageObject(), e);
+        }
+      } catch (CommunicationException e) {
+        Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage());
+        throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
+      } catch (ManagedObjectAlreadyExistsException e) {
+        Message msg = ERR_DSCFG_ERROR_CREATE_MOAEE.get(ufn);
+        throw new ClientException(LDAPResultCode.ENTRY_ALREADY_EXISTS, msg);
+      }
+    }
+  }
+
+
+
+  // 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<DefaultBehaviorException> exceptions) throws CLIException {
+    ValidationCallback<ManagedObject<? extends C>> validator =
+      new ValidationCallback<ManagedObject<? extends C>>() {
+
+      public ManagedObject<? extends C> validate(ConsoleApplication app,
+          String input) throws CLIException {
+        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) {
+          CLIException ae = ArgumentExceptionFactory
+              .adaptIllegalManagedObjectNameException(e, d);
+          app.println();
+          app.println(ae.getMessageObject());
+          app.println();
+          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) {
+          Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(irelation
+              .getUserFriendlyName());
+          throw new CLIException(msg);
+        } catch (ConcurrentModificationException e) {
+          Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(irelation
+              .getUserFriendlyName());
+          throw new CLIException(msg);
+        } catch (CommunicationException e) {
+          Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(irelation
+              .getUserFriendlyName(), e.getMessage());
+          throw new CLIException(msg);
+        } catch (DefinitionDecodingException e) {
+          // Do nothing.
+        } catch (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.
+        Message msg = ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS.get(irelation
+            .getUserFriendlyName(), input);
+        app.println();
+        app.println(msg);
+        app.println();
+        return null;
+      }
+
+    };
+
+    // Display additional help if the name is a naming property.
+    Message 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.println(pd.getSynopsis(), 4);
+
+      if (pd.getDescription() != null) {
+        app.println();
+        app.println(pd.getDescription(), 4);
+      }
+
+      PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(
+          true);
+      app.println();
+      app.println(INFO_EDITOR_HEADING_SYNTAX.get(b.getUsage(pd)), 4);
+      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);
+    }
+  }
+
+
+
+  // Generate the type name - definition mapping table.
+  @SuppressWarnings("unchecked")
+  private static <C extends ConfigurationClient, S extends Configuration>
+  SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>>
+      getSubTypes(AbstractManagedObjectDefinition<C, S> d) {
+    SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> map;
+    map =
+      new TreeMap<String, ManagedObjectDefinition<? extends C, ? extends S>>();
+
+    // If the top-level definition is instantiable, we use the value
+    // "generic".
+    if (d instanceof ManagedObjectDefinition) {
+      ManagedObjectDefinition<? extends C, ? extends S> mod =
+        (ManagedObjectDefinition<? extends C, ? extends S>) d;
+      map.put(GENERIC_TYPE, mod);
+    }
+
+    // Process its sub-definitions.
+    String suffix = "-" + d.getName();
+    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
+        .getAllChildren()) {
+      if (c instanceof ManagedObjectDefinition) {
+        ManagedObjectDefinition<? extends C, ? extends S> mod =
+          (ManagedObjectDefinition<? extends C, ? extends S>) c;
+
+        // For the type name we shorten it, if possible, by stripping
+        // off the trailing part of the name which matches the
+        // base-type.
+        String name = mod.getName();
+        if (name.endsWith(suffix)) {
+          name = name.substring(0, name.length() - suffix.length());
+        }
+
+        map.put(name, mod);
+      }
+    }
+
+    return map;
+  }
+
+
+
+  // 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) throws CLIException {
+    Collection<ManagedObjectDefinition<? extends C, ? extends S>> types;
+    types = getSubTypes(d).values();
+
+    // If there is only one choice then return immediately.
+    if (types.size() == 1) {
+      ManagedObjectDefinition<? extends C, ? extends S> type =
+        types.iterator().next();
+      return MenuResult.<ManagedObjectDefinition<? extends C,
+          ? extends S>>success(type);
+    } else {
+      MenuBuilder<ManagedObjectDefinition<? extends C, ? extends S>> builder =
+        new MenuBuilder<ManagedObjectDefinition<? extends C, ? extends S>>(app);
+      Message msg = INFO_DSCFG_CREATE_TYPE_PROMPT.get(d.getUserFriendlyName());
+      builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
+      builder.setPrompt(msg);
+
+      for (ManagedObjectDefinition<? extends C, ? extends S> mod : types) {
+        Message option = mod.getUserFriendlyName();
+        if ((mod == d) && (mod instanceof ManagedObjectDefinition)) {
+          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();
+    }
+  }
+
+
+
   // The sub-commands naming arguments.
   private final List<StringArgument> namingArgs;
 
@@ -770,69 +1014,41 @@
       ManagementContextFactory factory) throws ArgumentException,
       ClientException, CLIException {
     // Determine the type of managed object to be created.
-    String typeName;
-
+    ManagedObjectDefinition<? extends C, ? extends S> d;
     if (!typeArgument.isPresent()) {
       if (app.isInteractive()) {
         // Let the user choose.
+        MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> result;
+        app.println();
+        app.println();
+        result = getTypeInteractively(app, relation.getChildDefinition());
 
-        // If there is only one choice then return immediately.
-        if (types.size() == 1) {
-          typeName = types.keySet().iterator().next();
+        if (result.isSuccess()) {
+          d = result.getValue();
+        } else if (result.isCancel()) {
+          return MenuResult.cancel();
         } else {
-          MenuBuilder<String> builder = new MenuBuilder<String>(app);
-          Message msg = INFO_DSCFG_CREATE_TYPE_PROMPT.get(relation
-              .getChildDefinition().getUserFriendlyName());
-          builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
-          builder.setPrompt(msg);
-
-          for (String type : types.keySet()) {
-            ManagedObjectDefinition<?, ?> d = types.get(type);
-            Message option = d.getUserFriendlyName();
-            if (type.equals(GENERIC_TYPE)) {
-              option = INFO_DSCFG_GENERIC_TYPE_OPTION.get(option);
-            }
-            builder.addNumberedOption(option, MenuResult.success(type));
+          // Must be quit.
+          if (!app.isMenuDrivenMode()) {
+            app.printVerboseMessage(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(relation
+                .getUserFriendlyName()));
           }
-          builder.addHelpOption(new TypeHelpCallback());
-          if (app.isMenuDrivenMode()) {
-            builder.addCancelOption(true);
-          }
-          builder.addQuitOption();
-
-          Menu<String> menu = builder.toMenu();
-          app.println();
-          app.println();
-          MenuResult<String> result = menu.run();
-          if (result.isSuccess()) {
-            typeName = result.getValue();
-          } else if (result.isCancel()) {
-            return MenuResult.cancel();
-          } else {
-            // Must be quit.
-            if (!app.isMenuDrivenMode()) {
-              msg = INFO_DSCFG_CONFIRM_CREATE_FAIL.get(relation
-                  .getUserFriendlyName());
-              app.printVerboseMessage(msg);
-            }
-            return MenuResult.quit();
-          }
+          return MenuResult.quit();
         }
       } else if (typeArgument.getDefaultValue() != null) {
-        typeName = typeArgument.getDefaultValue();
+        d = types.get(typeArgument.getDefaultValue());
       } else {
         throw ArgumentExceptionFactory
             .missingMandatoryNonInteractiveArgument(typeArgument);
       }
     } else {
-      typeName = typeArgument.getValue();
+      d = types.get(typeArgument.getValue());
+      if (d == null) {
+        throw ArgumentExceptionFactory.unknownSubType(relation, typeArgument
+            .getValue(), typeUsage);
+      }
     }
 
-    ManagedObjectDefinition<? extends C, ? extends S> d = types.get(typeName);
-    if (d == null) {
-      throw ArgumentExceptionFactory.unknownSubType(relation, typeName,
-          typeUsage);
-    }
     Message ufn = d.getUserFriendlyName();
 
     // Get the naming argument values.
@@ -894,6 +1110,8 @@
       String name = names.get(names.size() - 1);
       if (name == null) {
         if (app.isInteractive()) {
+          app.println();
+          app.println();
           child = createChildInteractively(app, parent, irelation,
               d, exceptions);
         } else {
@@ -927,7 +1145,7 @@
     // 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 = createManagedObject(app, context, child);
+    MenuResult<Void> result2 = commitManagedObject(app, context, child);
     if (result2.isCancel()) {
       return MenuResult.cancel();
     } else if (result2.isQuit()) {
@@ -939,143 +1157,6 @@
 
 
 
-  // Interactively create the child by prompting for the name.
-  private ManagedObject<? extends C> createChildInteractively(
-      ConsoleApplication app, final ManagedObject<?> parent,
-      final InstantiableRelationDefinition<C, S> irelation,
-      final ManagedObjectDefinition<? extends C, ? extends S> d,
-      final List<DefaultBehaviorException> exceptions) throws CLIException {
-    ValidationCallback<ManagedObject<? extends C>> validator =
-      new ValidationCallback<ManagedObject<? extends C>>() {
-
-      public ManagedObject<? extends C> validate(ConsoleApplication app,
-          String input) throws CLIException {
-        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) {
-          CLIException ae = ArgumentExceptionFactory
-              .adaptIllegalManagedObjectNameException(e, d);
-          app.println();
-          app.println(ae.getMessageObject());
-          app.println();
-          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) {
-          Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(irelation
-              .getUserFriendlyName());
-          throw new CLIException(msg);
-        } catch (ConcurrentModificationException e) {
-          Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(irelation
-              .getUserFriendlyName());
-          throw new CLIException(msg);
-        } catch (CommunicationException e) {
-          Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(irelation
-              .getUserFriendlyName(), e.getMessage());
-          throw new CLIException(msg);
-        } catch (DefinitionDecodingException e) {
-          // Do nothing.
-        } catch (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.
-        Message msg = ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS.get(relation
-            .getUserFriendlyName(), input);
-        app.println();
-        app.println(msg);
-        app.println();
-        return null;
-      }
-
-    };
-
-    app.println();
-    app.println();
-
-    // Display additional help if the name is a naming property.
-    Message 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.println(pd.getSynopsis(), 4);
-
-      if (pd.getDescription() != null) {
-        app.println();
-        app.println(pd.getDescription(), 4);
-      }
-
-      PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(
-          true);
-      app.println();
-      app.println(INFO_EDITOR_HEADING_SYNTAX.get(b.getUsage(pd)), 4);
-      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);
-    }
-  }
-
-
-
-  // Generate the type name - definition mapping table.
-  @SuppressWarnings("unchecked")
-  private SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>>
-      getSubTypes(AbstractManagedObjectDefinition<C, S> d) {
-    SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> map;
-    map =
-      new TreeMap<String, ManagedObjectDefinition<? extends C, ? extends S>>();
-
-    // If the top-level definition is instantiable, we use the value
-    // "generic".
-    if (d instanceof ManagedObjectDefinition) {
-      ManagedObjectDefinition<? extends C, ? extends S> mod =
-        (ManagedObjectDefinition<? extends C, ? extends S>) d;
-      map.put(GENERIC_TYPE, mod);
-    }
-
-    // Process its sub-definitions.
-    String suffix = "-" + d.getName();
-    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
-        .getAllChildren()) {
-      if (c instanceof ManagedObjectDefinition) {
-        ManagedObjectDefinition<? extends C, ? extends S> mod =
-          (ManagedObjectDefinition<? extends C, ? extends S>) c;
-
-        // For the type name we shorten it, if possible, by stripping
-        // off the trailing part of the name which matches the
-        // base-type.
-        String name = mod.getName();
-        if (name.endsWith(suffix)) {
-          name = name.substring(0, name.length() - suffix.length());
-        }
-
-        map.put(name, mod);
-      }
-    }
-
-    return map;
-  }
-
-
-
   // Set a property's initial values.
   private <T> void setProperty(ManagedObject<?> mo,
       MyPropertyProvider provider, PropertyDefinition<T> pd) {
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java b/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java
index f051165..0919530 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java
+++ b/opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java
@@ -51,6 +51,7 @@
 import org.opends.server.admin.ConfigurationClient;
 import org.opends.server.admin.DefaultBehaviorProviderVisitor;
 import org.opends.server.admin.DefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DefinitionDecodingException;
 import org.opends.server.admin.EnumPropertyDefinition;
 import org.opends.server.admin.IllegalPropertyValueException;
 import org.opends.server.admin.IllegalPropertyValueStringException;
@@ -71,7 +72,10 @@
 import org.opends.server.admin.client.AuthorizationException;
 import org.opends.server.admin.client.CommunicationException;
 import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagedObjectDecodingException;
 import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.tools.ClientException;
 import org.opends.server.util.Validator;
 import org.opends.server.util.cli.CLIException;
 import org.opends.server.util.cli.ConsoleApplication;
@@ -91,6 +95,86 @@
 final class PropertyValueEditor {
 
   /**
+   * A menu call-back which can be used to dynamically create new
+   * components when configuring aggregation based properties.
+   */
+  private final class CreateComponentCallback
+      <C extends ConfigurationClient, S extends Configuration>
+      implements MenuCallback<String> {
+
+    // The aggregation property definition.
+    private final AggregationPropertyDefinition<C, S> pd;
+
+
+
+    // Creates a new component create call-back for the provided
+    // aggregation property definition.
+    private CreateComponentCallback(AggregationPropertyDefinition<C, S> pd) {
+      this.pd = pd;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public MenuResult<String> invoke(ConsoleApplication app)
+        throws CLIException {
+      try {
+        // First get the parent managed object.
+        InstantiableRelationDefinition<?, ?> rd = pd.getRelationDefinition();
+        ManagedObjectPath<?, ?> path = pd.getParentPath();
+        Message ufn = rd.getUserFriendlyName();
+
+        ManagedObject<?> parent;
+        try {
+          parent = context.getManagedObject(path);
+        } catch (AuthorizationException e) {
+          Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn);
+          throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+              msg);
+        } catch (DefinitionDecodingException e) {
+          Message pufn = path.getManagedObjectDefinition()
+              .getUserFriendlyName();
+          Message msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(pufn, pufn, pufn);
+          throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
+        } catch (ManagedObjectDecodingException e) {
+          Message pufn = path.getManagedObjectDefinition()
+              .getUserFriendlyName();
+          Message msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(pufn);
+          throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg, e);
+        } catch (CommunicationException e) {
+          Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e.getMessage());
+          throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN,
+              msg);
+        } catch (ManagedObjectNotFoundException e) {
+          Message pufn = path.getManagedObjectDefinition()
+              .getUserFriendlyName();
+          Message msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(pufn);
+          throw new ClientException(LDAPResultCode.NO_SUCH_OBJECT, msg);
+        }
+
+        // Now let the user create the child component.
+        app.println();
+        app.println();
+        return CreateSubCommandHandler.createManagedObject(app, context,
+            parent, rd);
+      } catch (ClientException e) {
+        // FIXME: should really do something better with the exception
+        // handling here. For example, if a authz or communications
+        // exception occurs then the application should exit.
+        app.println();
+        app.println(e.getMessageObject());
+        app.println();
+        app.pressReturnToContinue();
+        return MenuResult.cancel();
+      }
+    }
+  }
+
+
+
+  /**
    * A help call-back which displays a description and summary of a
    * component and its properties.
    */
@@ -409,11 +493,13 @@
         return MenuResult.quit();
       }
 
-      // FIXME: give the user the option to enable/create a component.
       for (String value : values) {
         Message option = getPropertyValues(d, Collections.singleton(value));
         builder.addNumberedOption(option, MenuResult.success(value));
       }
+      MenuCallback<String> callback = new CreateComponentCallback<C, S>(d);
+      builder.addNumberedOption(INFO_EDITOR_OPTION_CREATE_A_NEW_COMPONENT
+          .get(rd.getUserFriendlyName()), callback);
 
       builder.addHelpOption(new PropertyHelpCallback(mo
           .getManagedObjectDefinition(), d));
@@ -618,11 +704,10 @@
     public <C extends ConfigurationClient, S extends Configuration>
         MenuResult<Boolean> visitAggregation(
         final AggregationPropertyDefinition<C, S> d, Void p) {
-      // FIXME: give the user the option to enable/create a component.
       final SortedSet<String> defaultValues = mo.getPropertyDefaultValues(d);
       final SortedSet<String> oldValues = mo.getPropertyValues(d);
       final SortedSet<String> currentValues = mo.getPropertyValues(d);
-      InstantiableRelationDefinition<C, S> rd = d.getRelationDefinition();
+      final InstantiableRelationDefinition<C, S> rd = d.getRelationDefinition();
       final Message ufpn = rd.getUserFriendlyPluralName();
 
       boolean isFirst = true;
@@ -679,6 +764,11 @@
                     .singleton(value));
                 builder.addNumberedOption(svalue, MenuResult.success(value));
               }
+              MenuCallback<String> callback = new CreateComponentCallback<C, S>(
+                  d);
+              builder.addNumberedOption(
+                  INFO_EDITOR_OPTION_CREATE_A_NEW_COMPONENT.get(rd
+                      .getUserFriendlyName()), callback);
 
               if (values.size() > 1) {
                 // No point in having this option if there's only one
@@ -1550,6 +1640,9 @@
 
         builder.addNumberedOption(option, MenuResult.success(value));
       }
+      MenuCallback<String> callback = new CreateComponentCallback<C, S>(d);
+      builder.addNumberedOption(INFO_EDITOR_OPTION_CREATE_A_NEW_COMPONENT
+          .get(ufn), callback);
 
       // Third option is to reset the value back to its default.
       if (mo.isPropertyPresent(d) && !query.isDefined()) {
@@ -1559,8 +1652,6 @@
         }
       }
 
-      // FIXME: give the user the option to enable/create a component.
-
       return runMenu(d, builder);
     }
 

--
Gitblit v1.10.0