From 02d3bf611bd9d4c33d8ae74d320790cfb0495d8e Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Thu, 03 May 2007 09:37:28 +0000
Subject: [PATCH] Three introspection related changes required in order to support the CLI:

---
 opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java                   |   67 ++
 opends/src/server/org/opends/server/admin/server/ServerManagedObject.java                    |    7 
 opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java               |  231 +++++++---
 opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ManagedObjectPathTest.java |  240 +++++++++++
 opends/src/server/org/opends/server/admin/server/DNBuilder.java                              |   21 
 opends/src/server/org/opends/server/admin/client/PropertySet.java                            |    8 
 opends/src/server/org/opends/server/admin/client/ManagedObject.java                          |    6 
 opends/src/server/org/opends/server/admin/ManagedObjectPath.java                             |  618 +++++++++++++++++++++++-----
 opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java                   |   21 
 opends/src/server/org/opends/server/admin/client/ExampleIntrospection.java                   |   24 -
 opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java                 |   11 
 opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java               |    2 
 opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java            |    2 
 13 files changed, 1,005 insertions(+), 253 deletions(-)

diff --git a/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java b/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
index 608e7d9..b1853a6 100644
--- a/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
+++ b/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
@@ -29,9 +29,11 @@
 
 
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
@@ -90,52 +92,15 @@
       AbstractManagedObjectDefinition<? super C, ? super S> parent) {
     this.name = name;
     this.parent = parent;
+    this.propertyDefinitions = new HashMap<String, PropertyDefinition<?>>();
+    this.relationDefinitions = new HashMap<String, RelationDefinition<?,?>>();
+    this.children = new HashMap<String,
+      AbstractManagedObjectDefinition<? extends C, ? extends S>>();
 
     // If we have a parent definition then inherit its features.
     if (parent != null) {
-      this.propertyDefinitions = new HashMap<String, PropertyDefinition<?>>(
-          parent.propertyDefinitions);
-      this.relationDefinitions = new HashMap<String, RelationDefinition<?,?>>(
-          parent.relationDefinitions);
       parent.children.put(name, this);
-    } else {
-      this.propertyDefinitions = new HashMap<String, PropertyDefinition<?>>();
-      this.relationDefinitions = new HashMap<String, RelationDefinition<?,?>>();
     }
-
-    this.children = new HashMap<String,
-      AbstractManagedObjectDefinition<? extends C, ? extends S>>();
-  }
-
-
-
-  /**
-   * Get the named child managed object definition which inherits from this
-   * managed object definition.
-   *
-   * @param name
-   *          The name of the managed object definition sub-type.
-   * @return Returns the named child managed object definition which inherits
-   *         from this managed object definition.
-   * @throws IllegalArgumentException
-   *           If the specified managed object definition name was null or empty
-   *           or if the requested subordinate managed object definition was not
-   *           found.
-   */
-  public final AbstractManagedObjectDefinition<? extends C, ? extends S>
-      getChild(String name) throws IllegalArgumentException {
-    if ((name == null) || (name.length() == 0)) {
-      throw new IllegalArgumentException("null or empty managed object name");
-    }
-
-    AbstractManagedObjectDefinition<? extends C, ? extends S> d = children
-        .get(name);
-    if (d == null) {
-      throw new IllegalArgumentException("managed object definition \"" + name
-          + "\" not found");
-    }
-
-    return d;
   }
 
 
@@ -149,6 +114,121 @@
    *         this managed object definition.
    */
   public final Collection<AbstractManagedObjectDefinition
+      <? extends C, ? extends S>> getAllChildren() {
+    List<AbstractManagedObjectDefinition<? extends C, ? extends S>> list =
+      new ArrayList<AbstractManagedObjectDefinition<? extends C, ? extends S>>(
+        children.values());
+
+    for (AbstractManagedObjectDefinition<? extends C, ? extends S> child :
+        children.values()) {
+      list.addAll(child.getAllChildren());
+    }
+
+    return Collections.unmodifiableCollection(list);
+  }
+
+
+
+  /**
+   * Get all the property definitions associated with this type of
+   * managed object. The returned collection will contain inherited
+   * property definitions.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         property definitions associated with this type of managed
+   *         object.
+   */
+  public final Collection<PropertyDefinition<?>> getAllPropertyDefinitions() {
+    if (parent == null) {
+      return getPropertyDefinitions();
+    } else {
+      List<PropertyDefinition<?>> list = new ArrayList<PropertyDefinition<?>>(
+          propertyDefinitions.values());
+      list.addAll(parent.getAllPropertyDefinitions());
+      return Collections.unmodifiableCollection(list);
+    }
+  }
+
+
+
+  /**
+   * Get all the relation definitions associated with this type of
+   * managed object. The returned collection will contain inherited
+   * relation definitions.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         relation definitions associated with this type of managed
+   *         object.
+   */
+  public final Collection<RelationDefinition<?, ?>>
+      getAllRelationDefinitions() {
+    if (parent == null) {
+      return getRelationDefinitions();
+    } else {
+      List<RelationDefinition<?, ?>> list =
+        new ArrayList<RelationDefinition<?, ?>>(relationDefinitions.values());
+      list.addAll(parent.getAllRelationDefinitions());
+      return Collections.unmodifiableCollection(list);
+    }
+  }
+
+
+
+  /**
+   * Get the named child managed object definition which inherits from
+   * this managed object definition. This method will recursively
+   * search down through the inheritance hierarchy.
+   *
+   * @param name
+   *          The name of the managed object definition sub-type.
+   * @return Returns the named child managed object definition which
+   *         inherits from this managed object definition.
+   * @throws IllegalArgumentException
+   *           If the specified managed object definition name was
+   *           null or empty or if the requested subordinate managed
+   *           object definition was not found.
+   */
+  public final AbstractManagedObjectDefinition<? extends C, ? extends S>
+      getChild(String name) throws IllegalArgumentException {
+    if ((name == null) || (name.length() == 0)) {
+      throw new IllegalArgumentException("null or empty managed object name");
+    }
+
+    AbstractManagedObjectDefinition<? extends C, ? extends S> d = children
+        .get(name);
+
+    if (d == null) {
+      // Recursively search.
+      for (AbstractManagedObjectDefinition<? extends C, ? extends S> child :
+          children.values()) {
+        try {
+          d = child.getChild(name);
+          break;
+        } catch (IllegalArgumentException e) {
+          // Try the next child.
+        }
+      }
+    }
+
+    if (d == null) {
+      throw new IllegalArgumentException("child managed object definition \""
+          + name + "\" not found");
+    }
+
+    return d;
+  }
+
+
+
+  /**
+   * Get the child managed object definitions which inherit directly
+   * from this managed object definition.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         subordinate managed object definitions which inherit
+   *         directly from this managed object definition.
+   */
+  public final Collection<AbstractManagedObjectDefinition
       <? extends C, ? extends S>> getChildren() {
     return Collections.unmodifiableCollection(children.values());
   }
@@ -216,16 +296,17 @@
 
 
   /**
-   * Get the specified property definition associated with this type of managed
-   * object.
+   * Get the specified property definition associated with this type
+   * of managed object. The search will include any inherited property
+   * definitions.
    *
    * @param name
    *          The name of the property definition to be retrieved.
-   * @return Returns the specified property definition associated with this type
-   *         of managed object.
+   * @return Returns the specified property definition associated with
+   *         this type of managed object.
    * @throws IllegalArgumentException
-   *           If the specified property name was null or empty or if the
-   *           requested property definition was not found.
+   *           If the specified property name was null or empty or if
+   *           the requested property definition was not found.
    */
   public final PropertyDefinition getPropertyDefinition(String name)
       throws IllegalArgumentException {
@@ -234,9 +315,14 @@
     }
 
     PropertyDefinition d = propertyDefinitions.get(name);
+
     if (d == null) {
-      throw new IllegalArgumentException("property definition \"" + name
-          + "\" not found");
+      if (parent != null) {
+        return parent.getPropertyDefinition(name);
+      } else {
+        throw new IllegalArgumentException("property definition \"" + name
+            + "\" not found");
+      }
     }
 
     return d;
@@ -245,12 +331,13 @@
 
 
   /**
-   * Get all the property definitions associated with this type of
-   * managed object.
+   * Get the property definitions defined by this managed object
+   * definition. The returned collection will not contain inherited
+   * property definitions.
    *
-   * @return Returns an unmodifiable collection containing all the
-   *         property definitions associated with this type of managed
-   *         object.
+   * @return Returns an unmodifiable collection containing the
+   *         property definitions defined by this managed object
+   *         definition.
    */
   public final Collection<PropertyDefinition<?>> getPropertyDefinitions() {
     return Collections.unmodifiableCollection(propertyDefinitions
@@ -260,16 +347,17 @@
 
 
   /**
-   * Get the specified relation definition associated with this type of managed
-   * object.
+   * Get the specified relation definition associated with this type
+   * of managed object.The search will include any inherited relation
+   * definitions.
    *
    * @param name
    *          The name of the relation definition to be retrieved.
-   * @return Returns the specified relation definition associated with this type
-   *         of managed object.
+   * @return Returns the specified relation definition associated with
+   *         this type of managed object.
    * @throws IllegalArgumentException
-   *           If the specified relation name was null or empty or if the
-   *           requested relation definition was not found.
+   *           If the specified relation name was null or empty or if
+   *           the requested relation definition was not found.
    */
   public final RelationDefinition getRelationDefinition(String name)
       throws IllegalArgumentException {
@@ -278,9 +366,14 @@
     }
 
     RelationDefinition d = relationDefinitions.get(name);
+
     if (d == null) {
-      throw new IllegalArgumentException("relation definition \"" + name
-          + "\" not found");
+      if (parent != null) {
+        return parent.getRelationDefinition(name);
+      } else {
+        throw new IllegalArgumentException("relation definition \"" + name
+            + "\" not found");
+      }
     }
 
     return d;
@@ -289,16 +382,16 @@
 
 
   /**
-   * Get all the relation definitions associated with this type of
-   * managed object.
+   * Get the relation definitions defined by this managed object
+   * definition. The returned collection will not contain inherited
+   * relation definitions.
    *
-   * @return Returns an unmodifiable collection containing all the
-   *         relation definitions associated with this type of managed
-   *         object.
+   * @return Returns an unmodifiable collection containing the
+   *         relation definitions defined by this managed object
+   *         definition.
    */
   public final Collection<RelationDefinition<?,?>> getRelationDefinitions() {
-    return Collections.unmodifiableCollection(relationDefinitions
-        .values());
+    return Collections.unmodifiableCollection(relationDefinitions.values());
   }
 
 
diff --git a/opends/src/server/org/opends/server/admin/ManagedObjectPath.java b/opends/src/server/org/opends/server/admin/ManagedObjectPath.java
index 5e3639a..1571cbb 100644
--- a/opends/src/server/org/opends/server/admin/ManagedObjectPath.java
+++ b/opends/src/server/org/opends/server/admin/ManagedObjectPath.java
@@ -32,38 +32,62 @@
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
+import org.opends.server.admin.std.client.RootCfgClient;
 import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.admin.std.server.RootCfg;
 
 
 
 /**
  * A path which can be used to determine the location of a managed
  * object instance.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          path references.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          path references.
  */
-public final class ManagedObjectPath {
+public final class ManagedObjectPath
+    <C extends ConfigurationClient, S extends Configuration> {
 
   /**
    * Abstract path element.
    */
-  private static abstract class Element {
+  private static abstract class Element
+      <C extends ConfigurationClient, S extends Configuration> {
+
+    // The type of managed object referenced by this element.
+    private final AbstractManagedObjectDefinition<C, S> definition;
+
+
 
     /**
      * Protected constructor.
+     *
+     * @param definition
+     *          The type of managed object referenced by this element.
      */
-    protected Element() {
-      // No implementation required.
+    protected Element(AbstractManagedObjectDefinition<C, S> definition) {
+      this.definition = definition;
     }
 
 
 
     /**
-     * Get the relation definition associated with this element.
+     * Get the managed object definition associated with this element.
      *
-     * @return Returns the relation definition associated with this
-     *         element.
+     * @return Returns the managed object definition associated with
+     *         this element.
      */
-    public abstract RelationDefinition<?, ?> getRelation();
+    public final AbstractManagedObjectDefinition<C, S>
+        getManagedObjectDefinition() {
+      return definition;
+    }
 
 
 
@@ -74,8 +98,7 @@
      * @param serializer
      *          The managed object path serialization strategy.
      */
-    public abstract void serialize(
-        ManagedObjectPathSerializer serializer);
+    public abstract void serialize(ManagedObjectPathSerializer serializer);
   }
 
 
@@ -83,19 +106,32 @@
   /**
    * A path element representing an instantiable managed object.
    */
-  private static final class InstantiableElement extends Element {
+  private static final class InstantiableElement
+      <C extends ConfigurationClient, S extends Configuration>
+      extends Element<C, S> {
 
-    // The instantiable relation.
-    private final InstantiableRelationDefinition<?, ?> r;
+    // Factory method.
+    private static final
+        <C extends ConfigurationClient, S extends Configuration>
+        InstantiableElement<C, S> create(
+        InstantiableRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d, String name) {
+      return new InstantiableElement<C, S>(r, d, name);
+    }
 
     // The name of the managed object.
     private final String name;
 
+    // The instantiable relation.
+    private final InstantiableRelationDefinition<? super C, ? super S> r;
+
 
 
     // Private constructor.
     private InstantiableElement(
-        InstantiableRelationDefinition<?, ?> r, String name) {
+        InstantiableRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d, String name) {
+      super(d);
       this.r = r;
       this.name = name;
     }
@@ -106,20 +142,10 @@
      * {@inheritDoc}
      */
     @Override
-    public RelationDefinition<?, ?> getRelation() {
-      return r;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void serialize(ManagedObjectPathSerializer serializer) {
-      serializer.appendManagedObjectPathElement(r, name);
+      serializer.appendManagedObjectPathElement(r,
+          getManagedObjectDefinition(), name);
     }
-
   }
 
 
@@ -127,15 +153,28 @@
   /**
    * A path element representing an optional managed object.
    */
-  private static final class OptionalElement extends Element {
+  private static final class OptionalElement
+      <C extends ConfigurationClient, S extends Configuration>
+      extends Element<C, S> {
+
+    // Factory method.
+    private static final
+        <C extends ConfigurationClient, S extends Configuration>
+        OptionalElement<C, S> create(
+        OptionalRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      return new OptionalElement<C, S>(r, d);
+    }
 
     // The optional relation.
-    private final OptionalRelationDefinition<?, ?> r;
+    private final OptionalRelationDefinition<? super C, ? super S> r;
 
 
 
     // Private constructor.
-    private OptionalElement(OptionalRelationDefinition<?, ?> r) {
+    private OptionalElement(OptionalRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      super(d);
       this.r = r;
     }
 
@@ -145,18 +184,9 @@
      * {@inheritDoc}
      */
     @Override
-    public RelationDefinition<?, ?> getRelation() {
-      return r;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void serialize(ManagedObjectPathSerializer serializer) {
-      serializer.appendManagedObjectPathElement(r);
+      serializer
+          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
     }
   }
 
@@ -165,15 +195,29 @@
   /**
    * A path element representing a singleton managed object.
    */
-  private static final class SingletonElement extends Element {
+  private static final class SingletonElement
+      <C extends ConfigurationClient, S extends Configuration>
+      extends Element<C, S> {
+
+    // Factory method.
+    private static final
+        <C extends ConfigurationClient, S extends Configuration>
+        SingletonElement<C, S> create(
+        SingletonRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      return new SingletonElement<C, S>(r, d);
+    }
 
     // The singleton relation.
-    private final SingletonRelationDefinition<?, ?> r;
+    private final SingletonRelationDefinition<? super C, ? super S> r;
 
 
 
     // Private constructor.
-    private SingletonElement(SingletonRelationDefinition<?, ?> r) {
+    private SingletonElement(
+        SingletonRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      super(d);
       this.r = r;
     }
 
@@ -183,8 +227,29 @@
      * {@inheritDoc}
      */
     @Override
-    public RelationDefinition<?, ?> getRelation() {
-      return r;
+    public void serialize(ManagedObjectPathSerializer serializer) {
+      serializer
+          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
+    }
+  }
+
+
+
+  /**
+   * A serialize which is used to generate the toString
+   * representation.
+   */
+  private static final class StringSerializer implements
+      ManagedObjectPathSerializer {
+
+    // Serialize to this string builder.
+    private final StringBuilder builder;
+
+
+
+    // Private constructor.
+    private StringSerializer(StringBuilder builder) {
+      this.builder = builder;
     }
 
 
@@ -192,15 +257,69 @@
     /**
      * {@inheritDoc}
      */
-    @Override
-    public void serialize(ManagedObjectPathSerializer serializer) {
-      serializer.appendManagedObjectPathElement(r);
+    public <M extends ConfigurationClient, N extends Configuration>
+        void appendManagedObjectPathElement(
+        InstantiableRelationDefinition<? super M, ? super N> r,
+        AbstractManagedObjectDefinition<M, N> d, String name) {
+      serializeElement(r, d);
+
+      // Be careful to escape any forward slashes in the name.
+      builder.append("+name=");
+      builder.append(name.replace("/", "//"));
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <M extends ConfigurationClient, N extends Configuration>
+        void appendManagedObjectPathElement(
+        OptionalRelationDefinition<? super M, ? super N> r,
+        AbstractManagedObjectDefinition<M, N> d) {
+      serializeElement(r, d);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <M extends ConfigurationClient, N extends Configuration>
+        void appendManagedObjectPathElement(
+        SingletonRelationDefinition<? super M, ? super N> r,
+        AbstractManagedObjectDefinition<M, N> d) {
+      serializeElement(r, d);
+    }
+
+
+
+    // Common element serialization.
+    private <M, N> void serializeElement(RelationDefinition r,
+        AbstractManagedObjectDefinition d) {
+      // Always specify the relation name.
+      builder.append("/relation=");
+      builder.append(r.getName());
+
+      // Only specify the type if it is a sub-type of the relation's
+      // type.
+      if (r.getChildDefinition() != d) {
+        builder.append("+type=");
+        builder.append(d.getName());
+      }
     }
   }
 
   // Single instance of a root path.
-  private static final ManagedObjectPath EMPTY_PATH = new ManagedObjectPath(
-      new LinkedList<Element>());
+  private static final ManagedObjectPath<RootCfgClient, RootCfg> EMPTY_PATH =
+    new ManagedObjectPath<RootCfgClient, RootCfg>(
+      new LinkedList<Element<?, ?>>(), RootCfgDefn.getInstance());
+
+  // A regular expression used to parse path elements.
+  private static final Pattern PE_REGEXP = Pattern
+      .compile("^\\s*relation=\\s*([^+]+)\\s*"
+          + "(\\+\\s*type=\\s*([^+]+)\\s*)?"
+          + "(\\+\\s*name=\\s*([^+]+)\\s*)?$");
 
 
 
@@ -211,11 +330,12 @@
    * @return Returns a new managed object path representing the
    *         configuration root.
    */
-  public static ManagedObjectPath emptyPath() {
+  public static ManagedObjectPath<RootCfgClient, RootCfg> emptyPath() {
     return EMPTY_PATH;
   }
 
 
+
   /**
    * Returns a managed object path holding the value of the specified
    * string.
@@ -227,29 +347,226 @@
    * @throws IllegalArgumentException
    *           If the string could not be parsed.
    */
-  public static ManagedObjectPath valueOf(String s)
+  public static ManagedObjectPath<?, ?> valueOf(String s)
       throws IllegalArgumentException {
-    return null;
+    String ns = s.trim();
+
+    // Check for root special case.
+    if (ns.equals("/")) {
+      return EMPTY_PATH;
+    }
+
+    // Parse the elements.
+    LinkedList<Element<?, ?>> elements = new LinkedList<Element<?, ?>>();
+    AbstractManagedObjectDefinition<?, ?> definition = RootCfgDefn
+        .getInstance();
+
+    if (!ns.startsWith("/")) {
+      throw new IllegalArgumentException("Invalid path \"" + ns
+          + "\": must begin with a \"/\"");
+    }
+
+    int start = 1;
+    while (true) {
+      // Get the next path element.
+      int end;
+      for (end = start; end < ns.length(); end++) {
+        char c = ns.charAt(end);
+        if (c == '/') {
+          if (end == (ns.length() - 1)) {
+            throw new IllegalArgumentException("Invalid path \"" + ns
+                + "\": must not end with a trailing \"/\"");
+          }
+
+          if (ns.charAt(end + 1) == '/') {
+            // Found an escaped forward slash.
+            end++;
+          } else {
+            // Found the end of this path element.
+            break;
+          }
+        }
+      }
+
+      // Get the next element.
+      String es = ns.substring(start, end);
+
+      Matcher m = PE_REGEXP.matcher(es);
+      if (!m.matches()) {
+        throw new IllegalArgumentException("Invalid path element \"" + es
+            + "\" in path \"" + ns + "\"");
+      }
+
+      String relation = m.group(1); // Mandatory.
+      String type = m.group(3); // Optional.
+      String name = m.group(5); // Mandatory if relation is
+      // instantiable.
+
+      // Get the relation definition.
+      RelationDefinition<?, ?> r;
+      try {
+        r = definition.getRelationDefinition(relation);
+      } catch (IllegalArgumentException e) {
+        throw new IllegalArgumentException("Invalid path element \"" + es
+            + "\" in path \"" + ns + "\": unknown relation \"" + relation
+            + "\"");
+      }
+
+      // Append the next element.
+      Element<?, ?> e = createElement(r, ns, es, type, name);
+      elements.add(e);
+      definition = e.getManagedObjectDefinition();
+
+      // Update start to point to the beginning of the next element.
+      if (end < ns.length()) {
+        // Skip to the beginning of the next element
+        start = end + 1;
+      } else {
+        // We reached the end of the string.
+        break;
+      }
+    }
+
+    // Construct the new path.
+    return create(elements, definition);
   }
 
 
 
+  // Factory method required in order to allow generic wild-card
+  // construction of new paths.
+  private static <C extends ConfigurationClient, S extends Configuration>
+      ManagedObjectPath<C, S> create(
+      LinkedList<Element<?, ?>> elements,
+      AbstractManagedObjectDefinition<C, S> definition) {
+    return new ManagedObjectPath<C, S>(elements, definition);
+  }
+
+
+
+  // Decode an element.
+  private static <C extends ConfigurationClient, S extends Configuration>
+      Element<? extends C, ? extends S> createElement(
+      RelationDefinition<C, S> r, String path, String element, String type,
+      String name) {
+    // First determine the managed object definition.
+    AbstractManagedObjectDefinition<? extends C, ? extends S> d = null;
+
+    if (type != null) {
+      for (AbstractManagedObjectDefinition<? extends C, ? extends S> child : r
+          .getChildDefinition().getAllChildren()) {
+        if (child.getName().equals(type)) {
+          d = child;
+          break;
+        }
+      }
+
+      if (d == null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path + "\": unknown sub-type \"" + type + "\"");
+      }
+    } else {
+      d = r.getChildDefinition();
+    }
+
+    if (r instanceof InstantiableRelationDefinition) {
+      InstantiableRelationDefinition<C, S> ir =
+        (InstantiableRelationDefinition<C, S>) r;
+
+      if (name == null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path
+            + "\": no instance name for instantiable relation");
+      }
+
+      return InstantiableElement.create(ir, d, name);
+    } else if (r instanceof OptionalRelationDefinition) {
+      OptionalRelationDefinition<C, S> or =
+        (OptionalRelationDefinition<C, S>) r;
+
+      if (name != null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path
+            + "\": instance name specified for optional relation");
+      }
+
+      return OptionalElement.create(or, d);
+    } else if (r instanceof SingletonRelationDefinition) {
+      SingletonRelationDefinition<C, S> sr =
+        (SingletonRelationDefinition<C, S>) r;
+
+      if (name != null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path
+            + "\": instance name specified for singleton relation");
+      }
+
+      return SingletonElement.create(sr, d);
+    } else {
+      throw new IllegalArgumentException("Invalid path element \"" + element
+          + "\" in path \"" + path + "\": unsupported relation type");
+    }
+  }
+
+  // The last element in this path.
+  private final AbstractManagedObjectDefinition<C, S> definition;
+
   // The list of path elements in this path.
-  private final List<Element> elements;
+  private final List<Element<?, ?>> elements;
 
 
 
   // Private constructor.
-  private ManagedObjectPath(LinkedList<Element> elements) {
+  private ManagedObjectPath(LinkedList<Element<?, ?>> elements,
+      AbstractManagedObjectDefinition<C, S> definition) {
     this.elements = Collections.unmodifiableList(elements);
+    this.definition = definition;
   }
 
 
 
   /**
    * Creates a new child managed object path beneath the provided
-   * parent path.
+   * parent path having the specified managed object definition.
    *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The instantiable relation referencing the child.
+   * @param d
+   *          The managed object definition associated with the child
+   *          (must be a sub-type of the relation).
+   * @param name
+   *          The relative name of the child managed object.
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      InstantiableRelationDefinition<? super M, ? super N> r,
+      AbstractManagedObjectDefinition<M, N> d, String name) {
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements);
+    celements.add(new InstantiableElement<M, N>(r, d, name));
+    return new ManagedObjectPath<M, N>(celements, d);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path using the relation's child managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
    * @param r
    *          The instantiable relation referencing the child.
    * @param name
@@ -257,45 +574,116 @@
    * @return Returns a new child managed object path beneath the
    *         provided parent path.
    */
-  public ManagedObjectPath child(
-      InstantiableRelationDefinition<?, ?> r, String name) {
-    LinkedList<Element> celements = new LinkedList<Element>(elements);
-    celements.add(new InstantiableElement(r, name));
-    return new ManagedObjectPath(celements);
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      InstantiableRelationDefinition<M, N> r, String name) {
+    return child(r, r.getChildDefinition(), name);
   }
 
 
 
   /**
    * Creates a new child managed object path beneath the provided
-   * parent path.
+   * parent path having the specified managed object definition.
    *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The optional relation referencing the child.
+   * @param d
+   *          The managed object definition associated with the child
+   *          (must be a sub-type of the relation).
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      OptionalRelationDefinition<? super M, ? super N> r,
+      AbstractManagedObjectDefinition<M, N> d) {
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements);
+    celements.add(new OptionalElement<M, N>(r, d));
+    return new ManagedObjectPath<M, N>(celements, d);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path using the relation's child managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
    * @param r
    *          The optional relation referencing the child.
    * @return Returns a new child managed object path beneath the
    *         provided parent path.
    */
-  public ManagedObjectPath child(OptionalRelationDefinition<?, ?> r) {
-    LinkedList<Element> celements = new LinkedList<Element>(elements);
-    celements.add(new OptionalElement(r));
-    return new ManagedObjectPath(celements);
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      OptionalRelationDefinition<M, N> r) {
+    return child(r, r.getChildDefinition());
   }
 
 
 
   /**
    * Creates a new child managed object path beneath the provided
-   * parent path.
+   * parent path having the specified managed object definition.
    *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The singleton relation referencing the child.
+   * @param d
+   *          The managed object definition associated with the child
+   *          (must be a sub-type of the relation).
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      SingletonRelationDefinition<? super M, ? super N> r,
+      AbstractManagedObjectDefinition<M, N> d) {
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements);
+    celements.add(new SingletonElement<M, N>(r, d));
+    return new ManagedObjectPath<M, N>(celements, d);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path using the relation's child managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
    * @param r
    *          The singleton relation referencing the child.
    * @return Returns a new child managed object path beneath the
    *         provided parent path.
    */
-  public ManagedObjectPath child(SingletonRelationDefinition<?, ?> r) {
-    LinkedList<Element> celements = new LinkedList<Element>(elements);
-    celements.add(new SingletonElement(r));
-    return new ManagedObjectPath(celements);
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      SingletonRelationDefinition<M, N> r) {
+    return child(r, r.getChildDefinition());
   }
 
 
@@ -321,20 +709,14 @@
    * Get the definition of the managed object referred to by this
    * path.
    * <p>
-   * When the path is empty, the {@link RootCfgDefn}
-   * is returned.
+   * When the path is empty, the {@link RootCfgDefn} is returned.
    *
    * @return Returns the definition of the managed object referred to
-   *         by this path, or the {@link RootCfgDefn}
-   *         if the path is empty.
+   *         by this path, or the {@link RootCfgDefn} if the path is
+   *         empty.
    */
-  public AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition() {
-    if (elements.isEmpty()) {
-      return RootCfgDefn.getInstance();
-    } else {
-      Element e = elements.get(elements.size() - 1);
-      return e.getRelation().getChildDefinition();
-    }
+  public AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
+    return definition;
   }
 
 
@@ -362,6 +744,23 @@
 
 
   /**
+   * Creates a new parent managed object path representing the
+   * immediate parent of this path. This method is a short-hand
+   * for <code>parent(1)</code>.
+   *
+   * @return Returns a new parent managed object path representing the
+   *         immediate parent of this path.
+   * @throws IllegalArgumentException
+   *           If this path does not have a parent (i.e. it is the
+   *           empty path).
+   */
+  public ManagedObjectPath<?, ?> parent() throws IllegalArgumentException {
+    return parent(1);
+  }
+
+
+
+  /**
    * Creates a new parent managed object path the specified number of
    * path elements above this path.
    *
@@ -374,7 +773,7 @@
    *           If the offset is less than 0, or greater than the
    *           number of path elements in this path.
    */
-  public ManagedObjectPath parent(int offset)
+  public ManagedObjectPath<?, ?> parent(int offset)
       throws IllegalArgumentException {
     if (offset < 0) {
       throw new IllegalArgumentException("Negative offset");
@@ -390,9 +789,16 @@
       return this;
     }
 
-    LinkedList<Element> celements = new LinkedList<Element>(elements
-        .subList(0, elements.size() - offset));
-    return new ManagedObjectPath(celements);
+    // Return the empty path if the parent has zero elements.
+    if (elements.size() == offset) {
+      return emptyPath();
+    }
+
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements.subList(0, elements.size() - offset));
+    AbstractManagedObjectDefinition<?, ?> definition = celements.getLast()
+        .getManagedObjectDefinition();
+    return create(celements, definition);
   }
 
 
@@ -409,7 +815,7 @@
    *          The managed object path serialization strategy.
    */
   public void serialize(ManagedObjectPathSerializer serializer) {
-    for (Element element : elements) {
+    for (Element<?, ?> element : elements) {
       element.serialize(serializer);
     }
   }
@@ -449,36 +855,14 @@
    * @see #toString()
    */
   public void toString(final StringBuilder builder) {
-    // Use a simple serializer to create the contents.
-    ManagedObjectPathSerializer serializer = new ManagedObjectPathSerializer() {
-
-      public void appendManagedObjectPathElement(
-          InstantiableRelationDefinition<?, ?> r, String name) {
-        builder.append('/');
-        builder.append(r.getName());
-        builder.append('/');
-        builder.append(name);
-      }
-
-
-
-      public void appendManagedObjectPathElement(
-          OptionalRelationDefinition<?, ?> r) {
-        builder.append('/');
-        builder.append(r.getName());
-      }
-
-
-
-      public void appendManagedObjectPathElement(
-          SingletonRelationDefinition<?, ?> r) {
-        builder.append('/');
-        builder.append(r.getName());
-      }
-
-    };
-
-    serialize(serializer);
+    if (isEmpty()) {
+      // Special treatment of root configuration paths.
+      builder.append('/');
+    } else {
+      // Use a simple serializer to create the contents.
+      ManagedObjectPathSerializer serializer = new StringSerializer(builder);
+      serialize(serializer);
+    }
   }
 
 }
diff --git a/opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java b/opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java
index 3c4cd55..df164c1 100644
--- a/opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java
+++ b/opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java
@@ -31,48 +31,83 @@
 /**
  * A strategy for serializing managed object paths.
  * <p>
- * This interface provides a generic means for serializing managed object paths
- * into application specific forms. For example, a JNDI client would use this
- * interface to construct <code>LdapName</code> objects from a path.
- * Similarly, on the server side, a serialization strategy is used to construct
- * <code>DN</code> instances from a path.
+ * This interface provides a generic means for serializing managed
+ * object paths into application specific forms. For example, a JNDI
+ * client would use this interface to construct <code>LdapName</code>
+ * objects from a path. Similarly, on the server side, a serialization
+ * strategy is used to construct <code>DN</code> instances from a
+ * path.
  * <p>
- * During serialization the serializer is invoked for each element in the
- * managed object path in big-endian order, starting from the root and
- * proceeding down to the leaf element.
+ * During serialization the serializer is invoked for each element in
+ * the managed object path in big-endian order, starting from the root
+ * and proceeding down to the leaf element.
  */
 public interface ManagedObjectPathSerializer {
 
   /**
-   * Append a managed object path element identified by an instantiable relation
-   * and an instance name.
+   * Append a managed object path element identified by an
+   * instantiable relation and an instance name.
    *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this path element references.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this path element references.
    * @param r
    *          The instantiable relation.
+   * @param d
+   *          The managed object definition.
    * @param name
    *          The instance name.
    */
-  void appendManagedObjectPathElement(InstantiableRelationDefinition<?, ?> r,
-      String name);
+  <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+      InstantiableRelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d, String name);
 
 
 
   /**
-   * Append a managed object path element identified by a optional relation.
+   * Append a managed object path element identified by an optional
+   * relation.
    *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this path element references.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this path element references.
    * @param r
    *          The optional relation.
+   * @param d
+   *          The managed object definition.
    */
-  void appendManagedObjectPathElement(OptionalRelationDefinition<?, ?> r);
+  <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+      OptionalRelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d);
 
 
 
   /**
-   * Append a managed object path element identified by a singleton relation.
+   * Append a managed object path element identified by a singleton
+   * relation.
    *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this path element references.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this path element references.
    * @param r
    *          The singleton relation.
+   * @param d
+   *          The managed object definition.
    */
-  void appendManagedObjectPathElement(SingletonRelationDefinition<?, ?> r);
+  <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+      SingletonRelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d);
 
 }
diff --git a/opends/src/server/org/opends/server/admin/client/ExampleIntrospection.java b/opends/src/server/org/opends/server/admin/client/ExampleIntrospection.java
index a5f2276..cf22f7c 100644
--- a/opends/src/server/org/opends/server/admin/client/ExampleIntrospection.java
+++ b/opends/src/server/org/opends/server/admin/client/ExampleIntrospection.java
@@ -32,17 +32,14 @@
 import static org.opends.server.util.ServerConstants.PROPERTY_SERVER_ROOT;
 
 import java.io.File;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
 
 import org.opends.server.admin.AbstractManagedObjectDefinition;
-import org.opends.server.admin.ClassLoaderProvider;
 import org.opends.server.admin.AggregationRelationDefinition;
 import org.opends.server.admin.AttributeTypePropertyDefinition;
+import org.opends.server.admin.ClassLoaderProvider;
 import org.opends.server.admin.ClassPropertyDefinition;
 import org.opends.server.admin.InstantiableRelationDefinition;
 import org.opends.server.admin.ManagedObjectDefinition;
@@ -136,7 +133,7 @@
     RootCfgDefn d = RootCfgDefn.getInstance();
 
     String operands = "";
-    for (RelationDefinition<?, ?> r : d.getRelationDefinitions()) {
+    for (RelationDefinition<?, ?> r : d.getAllRelationDefinitions()) {
       r.accept(this, operands);
     }
   }
@@ -316,22 +313,7 @@
   private void processManagedObjectDefinition(
       AbstractManagedObjectDefinition<?, ?> parent,
       AbstractManagedObjectDefinition<?, ?> child, String operands) {
-    Set<RelationDefinition<?, ?>> parentRelations;
-    Set<RelationDefinition<?, ?>> childRelations;
-
-    if (parent != null) {
-      parentRelations = new HashSet<RelationDefinition<?, ?>>(parent
-          .getRelationDefinitions());
-    } else {
-      parentRelations = Collections.emptySet();
-    }
-
-    // Only process relations not defined in parent.
-    childRelations = new HashSet<RelationDefinition<?, ?>>(child
-        .getRelationDefinitions());
-    childRelations.removeAll(parentRelations);
-
-    for (RelationDefinition<?, ?> r : childRelations) {
+    for (RelationDefinition<?, ?> r : child.getRelationDefinitions()) {
       r.accept(this, operands);
     }
 
diff --git a/opends/src/server/org/opends/server/admin/client/ManagedObject.java b/opends/src/server/org/opends/server/admin/client/ManagedObject.java
index c0ebde7..2fc4148 100644
--- a/opends/src/server/org/opends/server/admin/client/ManagedObject.java
+++ b/opends/src/server/org/opends/server/admin/client/ManagedObject.java
@@ -154,8 +154,7 @@
    *           If an attempt was made to remove a mandatory property.
    * @throws IllegalArgumentException
    *           If the specified property definition is not associated
-   *           with this managed object, or if the property is
-   *           read-only.
+   *           with this managed object.
    */
   public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
       throws IllegalPropertyValueException,
@@ -191,8 +190,7 @@
    *           If an attempt was made to remove a mandatory property.
    * @throws IllegalArgumentException
    *           If the specified property definition is not associated
-   *           with this managed object, or if the property is
-   *           read-only.
+   *           with this managed object.
    */
   public <T> void setPropertyValues(PropertyDefinition<T> d,
       Collection<T> values) throws IllegalPropertyValueException,
diff --git a/opends/src/server/org/opends/server/admin/client/PropertySet.java b/opends/src/server/org/opends/server/admin/client/PropertySet.java
index 83272d4..155ad0b 100644
--- a/opends/src/server/org/opends/server/admin/client/PropertySet.java
+++ b/opends/src/server/org/opends/server/admin/client/PropertySet.java
@@ -434,7 +434,7 @@
       new HashMap<PropertyDefinition, MyProperty>();
 
     // Copy the properties from the provider.
-    for (PropertyDefinition<?> pd : d.getPropertyDefinitions()) {
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
       createProperty(pd, p, i, properties, exceptions);
     }
 
@@ -472,7 +472,7 @@
       new HashMap<PropertyDefinition, MyProperty>();
 
     // Copy the properties from the provider.
-    for (PropertyDefinition<?> pd : d.getPropertyDefinitions()) {
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
       createProperty(pd, p, i, properties, exceptions);
     }
 
@@ -692,7 +692,7 @@
    *           If an attempt was made to remove a mandatory property.
    * @throws IllegalArgumentException
    *           If the specified property definition is not associated with this
-   *           managed object, or if the property is read-only.
+   *           managed object.
    */
   public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
       throws IllegalPropertyValueException, PropertyIsReadOnlyException,
@@ -732,7 +732,7 @@
    *           If an attempt was made to remove a mandatory property.
    * @throws IllegalArgumentException
    *           If the specified property definition is not associated with this
-   *           managed object, or if the property is read-only.
+   *           managed object.
    */
   public <T> void setPropertyValues(PropertyDefinition<T> d,
       Collection<T> values) throws IllegalPropertyValueException,
diff --git a/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java b/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
index 8f8a8a3..d95a896 100644
--- a/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
+++ b/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -128,7 +128,8 @@
     public Collection<?> getDefaultPropertyValues(
         ManagedObjectPath path, String propertyName)
         throws OperationsException, PropertyNotFoundException {
-      ManagedObject<?> mo = readEntry(dirContext, path, path
+      ManagedObjectPath<?, ?> tmp = path;
+      ManagedObject<?> mo = readEntry(dirContext, tmp, tmp
           .getManagedObjectDefinition());
       ManagedObjectDefinition<?, ?> mod = mo
           .getManagedObjectDefinition();
@@ -202,7 +203,7 @@
       ManagedObjectDefinition<?, ?> d) {
     ArrayList<String> attrIds = new ArrayList<String>();
 
-    for (PropertyDefinition<?> pd : d.getPropertyDefinitions()) {
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
       String attrId = LDAPProfile.getInstance().getAttributeName(d,
           pd);
       attrIds.add(attrId);
@@ -348,7 +349,7 @@
   private final DirContext dirContext;
 
   // The path associated with this managed object.
-  private final ManagedObjectPath path;
+  private final ManagedObjectPath<?, ?> path;
 
   // LDAP profile associated with this connection.
   private final LDAPProfile profile;
@@ -382,7 +383,7 @@
     ManagedObjectDefinition<C, ?> d = getManagedObjectDefinition();
     LDAPChangeBuilder builder = new LDAPChangeBuilder(dirContext,
         path, d);
-    for (PropertyDefinition<?> pd : d.getPropertyDefinitions()) {
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
       // FIXME: should throw an error when there are missing mandatory
       // properties.
       Property<?> p = properties.getProperty(pd);
@@ -696,7 +697,7 @@
     attributes.put(rdn.getType(), rdn.getValue().toString());
 
     // Create the remaining attributes.
-    for (PropertyDefinition<?> pd : d.getPropertyDefinitions()) {
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
       String attrID = profile.getAttributeName(d, pd);
       Attribute attribute = new BasicAttribute(attrID);
       encodeProperty(attribute, pd, properties);
diff --git a/opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java b/opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
index 53c2f80..021b1ce 100644
--- a/opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
+++ b/opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
@@ -35,6 +35,9 @@
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
 import org.opends.server.admin.InstantiableRelationDefinition;
 import org.opends.server.admin.LDAPProfile;
 import org.opends.server.admin.ManagedObjectPath;
@@ -106,8 +109,10 @@
   /**
    * {@inheritDoc}
    */
-  public void appendManagedObjectPathElement(
-      InstantiableRelationDefinition<?, ?> r, String name) {
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          InstantiableRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d, String name) {
     // Add the RDN sequence representing the relation.
     appendManagedObjectPathElement((RelationDefinition) r);
 
@@ -146,8 +151,10 @@
   /**
    * {@inheritDoc}
    */
-  public void appendManagedObjectPathElement(
-      OptionalRelationDefinition<?, ?> r) {
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          OptionalRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d) {
     // Add the RDN sequence representing the relation.
     appendManagedObjectPathElement((RelationDefinition) r);
   }
@@ -157,8 +164,10 @@
   /**
    * {@inheritDoc}
    */
-  public void appendManagedObjectPathElement(
-      SingletonRelationDefinition<?, ?> r) {
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          SingletonRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d) {
     // Add the RDN sequence representing the relation.
     appendManagedObjectPathElement((RelationDefinition) r);
   }
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
index d921be2..8d55359 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -58,7 +58,7 @@
     AbstractConfigListenerAdaptor implements ConfigAddListener {
 
   // The managed object path of the parent.
-  private final ManagedObjectPath path;
+  private final ManagedObjectPath<?, ?> path;
 
   // The instantiable relation.
   private final InstantiableRelationDefinition<?, S> instantiableRelation;
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
index 8184d8e..8abd474 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
@@ -60,7 +60,7 @@
     ConfigDeleteListener {
 
   // The managed object path of the parent.
-  private final ManagedObjectPath path;
+  private final ManagedObjectPath<?, ?> path;
 
   // The instantiable relation.
   private final InstantiableRelationDefinition<?, S> instantiableRelation;
diff --git a/opends/src/server/org/opends/server/admin/server/DNBuilder.java b/opends/src/server/org/opends/server/admin/server/DNBuilder.java
index 4219b2e..3d89202 100644
--- a/opends/src/server/org/opends/server/admin/server/DNBuilder.java
+++ b/opends/src/server/org/opends/server/admin/server/DNBuilder.java
@@ -29,6 +29,9 @@
 
 
 
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
 import org.opends.server.admin.InstantiableRelationDefinition;
 import org.opends.server.admin.LDAPProfile;
 import org.opends.server.admin.ManagedObjectPath;
@@ -120,8 +123,10 @@
   /**
    * {@inheritDoc}
    */
-  public void appendManagedObjectPathElement(
-      InstantiableRelationDefinition<?, ?> r, String name) {
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          InstantiableRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d, String name) {
     // Add the RDN sequence representing the relation.
     appendManagedObjectPathElement((RelationDefinition) r);
 
@@ -155,8 +160,10 @@
   /**
    * {@inheritDoc}
    */
-  public void appendManagedObjectPathElement(
-      OptionalRelationDefinition<?, ?> r) {
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          OptionalRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d) {
     // Add the RDN sequence representing the relation.
     appendManagedObjectPathElement((RelationDefinition) r);
   }
@@ -166,8 +173,10 @@
   /**
    * {@inheritDoc}
    */
-  public void appendManagedObjectPathElement(
-      SingletonRelationDefinition<?, ?> r) {
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          SingletonRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d) {
     // Add the RDN sequence representing the relation.
     appendManagedObjectPathElement((RelationDefinition) r);
   }
diff --git a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java b/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
index 2347042..ddf788d 100644
--- a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
+++ b/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -96,7 +96,7 @@
       InheritedDefaultValueProvider {
 
     // The base path.
-    private final ManagedObjectPath path;
+    private final ManagedObjectPath<?, ?> path;
 
 
 
@@ -131,7 +131,8 @@
         throw new ManagedObjectNotFoundException();
       }
 
-      ServerManagedObject<?> mo = decode(path, path
+      ManagedObjectPath<?, ?> tmp = path;
+      ServerManagedObject<?> mo = decode(tmp, tmp
           .getManagedObjectDefinition(), configEntry);
       ManagedObjectDefinition<?, ?> mod = mo
           .getManagedObjectDefinition();
@@ -295,7 +296,7 @@
 
   // The managed object path identifying this managed object's
   // location.
-  private final ManagedObjectPath path;
+  private final ManagedObjectPath<?, ?> path;
 
   // The managed object's properties.
   private final PropertySet properties;
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ManagedObjectPathTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ManagedObjectPathTest.java
new file mode 100644
index 0000000..6b65d80
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ManagedObjectPathTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.client.ConnectionHandlerCfgClient;
+import org.opends.server.admin.std.client.GlobalCfgClient;
+import org.opends.server.admin.std.client.LDAPConnectionHandlerCfgClient;
+import org.opends.server.admin.std.meta.ConnectionHandlerCfgDefn;
+import org.opends.server.admin.std.meta.GlobalCfgDefn;
+import org.opends.server.admin.std.meta.LDAPConnectionHandlerCfgDefn;
+import org.opends.server.admin.std.meta.MultimasterDomainCfgDefn;
+import org.opends.server.admin.std.meta.MultimasterSynchronizationProviderCfgDefn;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.admin.std.server.ConnectionHandlerCfg;
+import org.opends.server.admin.std.server.GlobalCfg;
+import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+
+
+/**
+ * ManagedObjectPath test cases.
+ */
+public class ManagedObjectPathTest {
+
+  /**
+   * Sets up tests
+   *
+   * @throws Exception
+   *           If the server could not be initialized.
+   */
+  @BeforeClass
+  public void setUp() throws Exception {
+    // This test suite depends on having the schema available, so
+    // we'll start the server.
+    TestCaseUtils.startServer();
+  }
+
+
+
+  /**
+   * Tests that the empty path is empty.
+   */
+  @Test
+  public void testEmptyPathIsEmpty() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    assertTrue(path.isEmpty());
+  }
+
+
+
+  /**
+   * Tests that the empty path has a size of zero.
+   */
+  @Test
+  public void testEmptyPathHasZeroElements() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    assertEquals(path.size(), 0);
+  }
+
+
+
+  /**
+   * Tests that the empty path has no parent.
+   */
+  @Test(expectedExceptions = IllegalArgumentException.class)
+  public void testEmptyPathHasNoParent() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    path.parent();
+  }
+
+
+
+  /**
+   * Tests that the empty path represents the root configuration.
+   */
+  @Test
+  public void testEmptyPathIsRootConfiguration() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    assertEquals(path.getManagedObjectDefinition(), RootCfgDefn.getInstance());
+  }
+
+
+
+  /**
+   * Tests that the empty path has a string representation of "/".
+   */
+  @Test
+  public void testEmptyPathString() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    assertEquals(path.toString(), "/");
+  }
+
+
+
+  /**
+   * Tests that the empty path can be decoded.
+   */
+  @Test
+  public void testEmptyPathDecode() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.valueOf("/");
+    assertEquals(path, ManagedObjectPath.emptyPath());
+  }
+
+
+
+  /**
+   * Tests singleton child creation.
+   */
+  @Test
+  public void testSingletonChild() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    ManagedObjectPath<GlobalCfgClient, GlobalCfg> child = path
+        .child(RootCfgDefn.getInstance()
+            .getGlobalConfigurationRelationDefinition());
+
+    assertFalse(child.isEmpty());
+    assertEquals(child.size(), 1);
+    assertEquals(child.parent(), path);
+    assertEquals(child.getManagedObjectDefinition(), GlobalCfgDefn
+        .getInstance());
+    assertEquals(child.toString(), "/relation=global-configuration");
+    assertEquals(child, ManagedObjectPath
+        .valueOf("/relation=global-configuration"));
+  }
+
+
+
+  /**
+   * Tests instantiable child creation.
+   */
+  @Test
+  public void testInstantiableChild() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    ManagedObjectPath<ConnectionHandlerCfgClient, ConnectionHandlerCfg> child = path
+        .child(RootCfgDefn.getInstance()
+            .getConnectionHandlersRelationDefinition(),
+            "LDAP connection handler");
+
+    assertFalse(child.isEmpty());
+    assertEquals(child.size(), 1);
+    assertEquals(child.parent(), path);
+    assertEquals(child.getManagedObjectDefinition(), ConnectionHandlerCfgDefn
+        .getInstance());
+    assertEquals(child.toString(),
+        "/relation=connection-handler+name=LDAP connection handler");
+    assertEquals(child, ManagedObjectPath
+        .valueOf("/relation=connection-handler+name=LDAP connection handler"));
+  }
+
+
+
+  /**
+   * Tests instantiable child creation with specific sub-type.
+   */
+  @Test
+  public void testInstantiableChildWithSubtype() {
+    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
+    ManagedObjectPath<LDAPConnectionHandlerCfgClient, LDAPConnectionHandlerCfg> child = path
+        .child(RootCfgDefn.getInstance()
+            .getConnectionHandlersRelationDefinition(),
+            LDAPConnectionHandlerCfgDefn.getInstance(),
+            "LDAP connection handler");
+
+    assertFalse(child.isEmpty());
+    assertEquals(child.size(), 1);
+    assertEquals(child.parent(), path);
+    assertEquals(child.getManagedObjectDefinition(),
+        LDAPConnectionHandlerCfgDefn.getInstance());
+    assertEquals(
+        child.toString(),
+        "/relation=connection-handler+type=ldap-connection-handler+name=LDAP connection handler");
+    assertEquals(
+        child,
+        ManagedObjectPath
+            .valueOf("/relation=connection-handler+type=ldap-connection-handler+name=LDAP connection handler"));
+  }
+
+
+
+  /**
+   * Tests instantiable child creation with multiple levels.
+   */
+  @Test
+  public void testInstantiableMultipleLevels() {
+    ManagedObjectPath<?, ?> root = ManagedObjectPath.emptyPath();
+    ManagedObjectPath<?, ?> mmr = root.child(RootCfgDefn.getInstance()
+        .getSynchronizationProvidersRelationDefinition(),
+        MultimasterSynchronizationProviderCfgDefn.getInstance(), "MMR");
+    ManagedObjectPath<?, ?> domain = mmr.child(
+        MultimasterSynchronizationProviderCfgDefn.getInstance()
+            .getMultimasterDomainsRelationDefinition(), "Domain");
+    assertFalse(domain.isEmpty());
+    assertEquals(domain.size(), 2);
+    assertEquals(domain.parent(), mmr);
+    assertEquals(domain.parent(2), root);
+    assertEquals(domain.getManagedObjectDefinition(), MultimasterDomainCfgDefn
+        .getInstance());
+    assertEquals(
+        domain.toString(),
+        "/relation=synchronization-provider+type=multimaster-synchronization-provider+name=MMR/relation=multimaster-domain+name=Domain");
+    assertEquals(
+        domain,
+        ManagedObjectPath
+            .valueOf("/relation=synchronization-provider+type=multimaster-synchronization-provider+name=MMR/relation=multimaster-domain+name=Domain"));
+  }
+}

--
Gitblit v1.10.0