From 506eee12c5fe5d42d3f90662289426e6cc8aed76 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Fri, 31 Aug 2007 22:06:04 +0000
Subject: [PATCH] Fix issue 1734: Admin framework: refactor client APIs

---
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java                             |  595 ++++++++++
 opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java                   |  696 +++++++++++
 opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Property.java                                |   67 
 opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/PropertySet.java                             |  127 -
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java                  |   38 
 opendj-sdk/opends/src/server/org/opends/server/admin/client/package-info.java                                |    8 
 opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/package-info.java                            |   39 
 opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java                                  |  701 +++++++++++
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java                      | 1162 +-----------------
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java                           |   28 
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java                               |   46 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/spi/PropertySetTest.java |    5 
 12 files changed, 2,244 insertions(+), 1,268 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java
index d34b3e0..e2be911 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java
@@ -216,7 +216,7 @@
 
 
   /**
-   * Retrieve an instantiable child managed object.
+   * Retrieves an instantiable child managed object.
    *
    * @param <C>
    *          The requested type of the child managed object
@@ -262,7 +262,7 @@
 
 
   /**
-   * Retrieve an optional child managed object.
+   * Retrieves an optional child managed object.
    *
    * @param <C>
    *          The requested type of the child managed object
@@ -306,7 +306,7 @@
 
 
   /**
-   * Retrieve a singleton child managed object.
+   * Retrieves a singleton child managed object.
    *
    * @param <C>
    *          The requested type of the child managed object
@@ -362,7 +362,7 @@
 
 
   /**
-   * Get the definition associated with this managed object.
+   * Gets the definition associated with this managed object.
    *
    * @return Returns the definition associated with this managed
    *         object.
@@ -373,7 +373,7 @@
 
 
   /**
-   * Get the path of this managed object.
+   * Gets the path of this managed object.
    *
    * @return Returns the path of this managed object.
    */
@@ -382,30 +382,31 @@
 
 
   /**
-   * Get the default values of the specified property.
+   * Gets a mutable copy of the set of default values for the
+   * specified property.
    *
-   * @param <P>
+   * @param <PD>
    *          The type of the property to be retrieved.
    * @param pd
    *          The property to be retrieved.
-   * @return Returns the property's default values, or an empty set
-   *         if there are no default values defined.
+   * @return Returns the property's default values, or an empty set if
+   *         there are no default values defined.
    * @throws IllegalArgumentException
    *           If the property definition is not associated with this
    *           managed object's definition.
    */
-  <P> SortedSet<P> getPropertyDefaultValues(PropertyDefinition<P> pd)
+  <PD> SortedSet<PD> getPropertyDefaultValues(PropertyDefinition<PD> pd)
       throws IllegalArgumentException;
 
 
 
   /**
-   * Get the effective value of the specified property.
+   * Gets the effective value of the specified property.
    * <p>
    * See the class description for more information about how the
    * effective property value is derived.
    *
-   * @param <P>
+   * @param <PD>
    *          The type of the property to be retrieved.
    * @param pd
    *          The property to be retrieved.
@@ -416,18 +417,19 @@
    *           If the property definition is not associated with this
    *           managed object's definition.
    */
-  <P> P getPropertyValue(PropertyDefinition<P> pd)
+  <PD> PD getPropertyValue(PropertyDefinition<PD> pd)
       throws IllegalArgumentException;
 
 
 
   /**
-   * Get the effective values of the specified property.
+   * Gets a mutable copy of the set of effective values for the
+   * specified property.
    * <p>
    * See the class description for more information about how the
    * effective property values are derived.
    *
-   * @param <P>
+   * @param <PD>
    *          The type of the property to be retrieved.
    * @param pd
    *          The property to be retrieved.
@@ -437,7 +439,7 @@
    *           If the property definition is not associated with this
    *           managed object's definition.
    */
-  <P> SortedSet<P> getPropertyValues(PropertyDefinition<P> pd)
+  <PD> SortedSet<PD> getPropertyValues(PropertyDefinition<PD> pd)
       throws IllegalArgumentException;
 
 
@@ -653,12 +655,12 @@
 
 
   /**
-   * Set a new pending value for the specified property.
+   * Sets a new pending value for the specified property.
    * <p>
    * See the class description for more information regarding pending
    * values.
    *
-   * @param <P>
+   * @param <PD>
    *          The type of the property to be modified.
    * @param pd
    *          The property to be modified.
@@ -678,19 +680,19 @@
    *           If the specified property definition is not associated
    *           with this managed object.
    */
-  <P> void setPropertyValue(PropertyDefinition<P> pd, P value)
+  <PD> void setPropertyValue(PropertyDefinition<PD> pd, PD value)
       throws IllegalPropertyValueException, PropertyIsReadOnlyException,
       PropertyIsMandatoryException, IllegalArgumentException;
 
 
 
   /**
-   * Set a new pending values for the specified property.
+   * Sets a new pending values for the specified property.
    * <p>
    * See the class description for more information regarding pending
    * values.
    *
-   * @param <P>
+   * @param <PD>
    *          The type of the property to be modified.
    * @param pd
    *          The property to be modified.
@@ -714,7 +716,7 @@
    *           If the specified property definition is not associated
    *           with this managed object.
    */
-  <P> void setPropertyValues(PropertyDefinition<P> pd, Collection<P> values)
+  <PD> void setPropertyValues(PropertyDefinition<PD> pd, Collection<PD> values)
       throws IllegalPropertyValueException, PropertyIsSingleValuedException,
       PropertyIsReadOnlyException, PropertyIsMandatoryException,
       IllegalArgumentException;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java
index f454869..818706a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java
@@ -34,7 +34,7 @@
 
 
 /**
- * Management connection context.
+ * Client management connection context.
  */
 public abstract class ManagementContext {
 
@@ -48,21 +48,9 @@
 
 
   /**
-   * Get the root configuration managed object associated with this
+   * Gets the root configuration client associated with this
    * management context.
    *
-   * @return Returns the root configuration managed object associated
-   *         with this management context.
-   */
-  public abstract ManagedObject<RootCfgClient>
-      getRootConfigurationManagedObject();
-
-
-
-  /**
-   * Get the root configuration client associated with this management
-   * context.
-   *
    * @return Returns the root configuration client associated with
    *         this management context.
    */
@@ -70,4 +58,16 @@
     return getRootConfigurationManagedObject().getConfiguration();
   }
 
+
+
+  /**
+   * Gets the root configuration managed object associated with this
+   * management context.
+   *
+   * @return Returns the root configuration managed object associated
+   *         with this management context.
+   */
+  public abstract
+  ManagedObject<RootCfgClient> getRootConfigurationManagedObject();
+
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
new file mode 100644
index 0000000..481e8c0
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
@@ -0,0 +1,595 @@
+/*
+ * 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.client.ldap;
+
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.DefinitionResolver;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+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.OperationRejectedException;
+import org.opends.server.admin.client.spi.Driver;
+import org.opends.server.admin.client.spi.PropertySet;
+
+
+
+/**
+ * The LDAP management context driver implementation.
+ */
+final class LDAPDriver extends Driver {
+
+  // The LDAP connection.
+  private final LDAPConnection connection;
+
+  // The LDAP profile which should be used to construct LDAP
+  // requests and decode LDAP responses.
+  private final LDAPProfile profile;
+
+
+
+  /**
+   * Creates a new LDAP driver using the specified LDAP connection and
+   * profile.
+   *
+   * @param connection
+   *          The LDAP connection.
+   * @param profile
+   *          The LDAP profile.
+   */
+  public LDAPDriver(LDAPConnection connection, LDAPProfile profile) {
+    this.connection = connection;
+    this.profile = profile;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+
+    if (!entryExists(parent)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    return removeManagedObject(parent.child(rd, name));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(parent, rd);
+
+    if (!entryExists(parent)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    return removeManagedObject(parent.child(rd));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getManagedObject(
+      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    if (!entryExists(path)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    try {
+      // Read the entry associated with the managed object.
+      LdapName dn = LDAPNameBuilder.create(path, profile);
+      AbstractManagedObjectDefinition<C, S> d = path
+          .getManagedObjectDefinition();
+      ManagedObjectDefinition<? extends C, ? extends S> mod =
+        getEntryDefinition(d, dn);
+
+      ArrayList<String> attrIds = new ArrayList<String>();
+      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+        String attrId = profile.getAttributeName(mod, pd);
+        attrIds.add(attrId);
+      }
+
+      Attributes attributes = connection.readEntry(dn, attrIds);
+
+      // Build the managed object's properties.
+      List<PropertyException> exceptions = new LinkedList<PropertyException>();
+      PropertySet newProperties = new PropertySet();
+      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+        String attrID = profile.getAttributeName(mod, pd);
+        Attribute attribute = attributes.get(attrID);
+        List<String> values = new LinkedList<String>();
+
+        if (attribute != null && attribute.size() != 0) {
+          NamingEnumeration<?> ldapValues = attribute.getAll();
+          while (ldapValues.hasMore()) {
+            Object obj = ldapValues.next();
+            if (obj != null) {
+              values.add(obj.toString());
+            }
+          }
+        }
+
+        try {
+          decodeProperty(newProperties, path, pd, values);
+        } catch (PropertyException e) {
+          exceptions.add(e);
+        }
+      }
+
+      // If there were no decoding problems then return the object,
+      // otherwise throw an operations exception.
+      ManagedObject<? extends C> mo = createExistingManagedObject(mod, path,
+          newProperties);
+      if (exceptions.isEmpty()) {
+        return mo;
+      } else {
+        throw new ManagedObjectDecodingException(mo, exceptions);
+      }
+    } catch (NameNotFoundException e) {
+      throw new ManagedObjectNotFoundException();
+    } catch (NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <PD> SortedSet<PD> getPropertyValues(ManagedObjectPath<?, ?> path,
+      PropertyDefinition<PD> pd) throws IllegalArgumentException,
+      DefinitionDecodingException, AuthorizationException,
+      ManagedObjectNotFoundException, CommunicationException,
+      PropertyException {
+    if (!entryExists(path)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    try {
+      // Read the entry associated with the managed object.
+      LdapName dn = LDAPNameBuilder.create(path, profile);
+      AbstractManagedObjectDefinition<?, ?> d = path
+          .getManagedObjectDefinition();
+      ManagedObjectDefinition<?, ?> mod = getEntryDefinition(d, dn);
+
+      String attrID = profile.getAttributeName(mod, pd);
+      Attributes attributes = connection.readEntry(dn, Collections
+          .singleton(attrID));
+      Attribute attribute = attributes.get(attrID);
+
+      SortedSet<PD> values = new TreeSet<PD>(pd);
+      if (attribute == null || attribute.size() == 0) {
+        // Use the property's default values.
+        values.addAll(findDefaultValues(path, pd, false));
+      } else {
+        // Decode the values.
+        NamingEnumeration<?> ldapValues = attribute.getAll();
+        while (ldapValues.hasMore()) {
+          Object obj = ldapValues.next();
+          if (obj != null) {
+            PD value = pd.decodeValue(obj.toString());
+            values.add(value);
+          }
+        }
+      }
+
+      // Sanity check the returned values.
+      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+        throw new PropertyIsSingleValuedException(pd);
+      }
+
+      if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+        throw new PropertyIsMandatoryException(pd);
+      }
+
+      return values;
+    } catch (NameNotFoundException e) {
+      throw new ManagedObjectNotFoundException();
+    } catch (NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  boolean hasManagedObject(
+      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+
+    if (!entryExists(parent)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    return entryExists(parent.child(rd));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+
+    if (!entryExists(parent)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    // Get the search base DN.
+    LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
+
+    // Retrieve only those entries which are sub-types of the
+    // specified definition.
+    StringBuilder builder = new StringBuilder();
+    builder.append("(objectclass=");
+    builder.append(profile.getObjectClass(d));
+    builder.append(')');
+    String filter = builder.toString();
+
+    List<String> children = new ArrayList<String>();
+    try {
+      for (LdapName child : connection.listEntries(dn, filter)) {
+        children.add(child.getRdn(child.size() - 1).getValue().toString());
+      }
+    } catch (NameNotFoundException e) {
+      // Ignore this - it means that the base entry does not exist
+      // (which it might not if this managed object has just been
+      // created.
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+
+    return children.toArray(new String[children.size()]);
+  }
+
+
+
+  /**
+   * Adapts a naming exception to an appropriate admin client
+   * exception.
+   *
+   * @param ne
+   *          The naming exception.
+   * @throws CommunicationException
+   *           If the naming exception mapped to a communication
+   *           exception.
+   * @throws AuthorizationException
+   *           If the naming exception mapped to an authorization
+   *           exception.
+   */
+  void adaptNamingException(NamingException ne) throws CommunicationException,
+      AuthorizationException {
+    try {
+      throw ne;
+    } catch (javax.naming.CommunicationException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.ServiceUnavailableException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      // Just treat it as a communication problem.
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
+  /**
+   * Determines whether the named LDAP entry exists.
+   *
+   * @param dn
+   *          The LDAP entry name.
+   * @return Returns <code>true</code> if the named LDAP entry
+   *         exists.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  boolean entryExists(LdapName dn) throws CommunicationException,
+      AuthorizationException {
+    try {
+      return connection.entryExists(dn);
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+    return false;
+  }
+
+
+
+  /**
+   * Gets the LDAP connection used for interacting with the server.
+   *
+   * @return Returns the LDAP connection used for interacting with the
+   *         server.
+   */
+  LDAPConnection getLDAPConnection() {
+    return connection;
+  }
+
+
+
+  /**
+   * Gets the LDAP profile which should be used to construct LDAP
+   * requests and decode LDAP responses.
+   *
+   * @return Returns the LDAP profile which should be used to
+   *         construct LDAP requests and decode LDAP responses.
+   */
+  LDAPProfile getLDAPProfile() {
+    return profile;
+  }
+
+
+
+  // Create a managed object which already exists on the server.
+  private <M extends ConfigurationClient, N extends Configuration>
+  ManagedObject<M> createExistingManagedObject(
+      ManagedObjectDefinition<M, N> d,
+      ManagedObjectPath<? super M, ? super N> p, PropertySet properties) {
+    RelationDefinition<?, ?> rd = p.getRelationDefinition();
+    PropertyDefinition<?> pd = null;
+    if (rd instanceof InstantiableRelationDefinition) {
+      InstantiableRelationDefinition<?, ?> ird =
+        (InstantiableRelationDefinition<?, ?>) rd;
+      pd = ird.getNamingPropertyDefinition();
+    }
+    return new LDAPManagedObject<M>(this, d, p.asSubType(d), properties, true,
+        pd);
+  }
+
+
+
+  // Create a property using the provided string values.
+  private <PD> void decodeProperty(PropertySet newProperties,
+      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd, List<String> values)
+      throws PropertyException {
+    PropertyException exception = null;
+
+    // Get the property's active values.
+    Collection<PD> activeValues = new ArrayList<PD>(values.size());
+    for (String value : values) {
+      try {
+        activeValues.add(pd.decodeValue(value));
+      } catch (IllegalPropertyValueStringException e) {
+        exception = e;
+      }
+    }
+
+    if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+      // This exception takes precedence over previous exceptions.
+      exception = new PropertyIsSingleValuedException(pd);
+      PD value = activeValues.iterator().next();
+      activeValues.clear();
+      activeValues.add(value);
+    }
+
+    if (activeValues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+      // The active values maybe empty because of a previous
+      // exception.
+      if (exception == null) {
+        exception = new PropertyIsMandatoryException(pd);
+      }
+    }
+
+    // Get the property's default values.
+    Collection<PD> defaultValues;
+    try {
+      defaultValues = findDefaultValues(p, pd, false);
+    } catch (DefaultBehaviorException e) {
+      defaultValues = Collections.emptySet();
+      exception = e;
+    }
+
+    newProperties.addProperty(pd, defaultValues, activeValues);
+    if (exception != null) {
+      throw exception;
+    }
+  }
+
+
+
+  // Determines whether the LDAP entry associated with the managed
+  // object path exists.
+  private boolean entryExists(ManagedObjectPath<?, ?> path)
+      throws CommunicationException, AuthorizationException {
+    if (path.isEmpty()) {
+      return true;
+    }
+
+    LdapName dn = LDAPNameBuilder.create(path, profile);
+    return entryExists(dn);
+  }
+
+
+
+  // Determine the type of managed object associated with the named
+  // entry.
+  private <C extends ConfigurationClient, S extends Configuration>
+  ManagedObjectDefinition<? extends C, ? extends S> getEntryDefinition(
+      AbstractManagedObjectDefinition<C, S> d, LdapName dn)
+      throws NamingException, DefinitionDecodingException {
+    Attributes attributes = connection.readEntry(dn, Collections
+        .singleton("objectclass"));
+    Attribute oc = attributes.get("objectclass");
+
+    if (oc == null) {
+      // No object classes.
+      throw new DefinitionDecodingException(Reason.NO_TYPE_INFORMATION);
+    }
+
+    final Set<String> objectClasses = new HashSet<String>();
+    NamingEnumeration<?> values = oc.getAll();
+    while (values.hasMore()) {
+      Object value = values.next();
+      if (value != null) {
+        objectClasses.add(value.toString().toLowerCase().trim());
+      }
+    }
+
+    if (objectClasses.isEmpty()) {
+      // No object classes.
+      throw new DefinitionDecodingException(Reason.NO_TYPE_INFORMATION);
+    }
+
+    // Resolve the appropriate sub-type based on the object classes.
+    DefinitionResolver resolver = new DefinitionResolver() {
+
+      public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
+        String objectClass = profile.getObjectClass(d);
+        return objectClasses.contains(objectClass);
+      }
+
+    };
+
+    return d.resolveManagedObjectDefinition(resolver);
+  }
+
+
+
+  // Remove the named managed object.
+  private boolean removeManagedObject(ManagedObjectPath<?, ?> path)
+      throws CommunicationException, AuthorizationException,
+      OperationRejectedException, ManagedObjectNotFoundException {
+    if (!entryExists(path)) {
+      return false;
+    }
+
+    // Delete the entry and any subordinate entries.
+    LdapName dn = LDAPNameBuilder.create(path, profile);
+    try {
+      connection.deleteSubtree(dn);
+    } catch (OperationNotSupportedException e) {
+      // Unwilling to perform.
+      throw new OperationRejectedException(e);
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+
+    return true;
+  }
+
+
+
+  // Validate that a relation definition belongs to this managed
+  // object.
+  private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
+      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
+    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + d.getName());
+    }
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
index 16120aa..fbdf4df 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -29,18 +29,7 @@
 
 
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-
 import javax.naming.NameAlreadyBoundException;
-import javax.naming.NameNotFoundException;
-import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.NoPermissionException;
 import javax.naming.OperationNotSupportedException;
@@ -51,46 +40,24 @@
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 
-import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
-import org.opends.server.admin.AbstractManagedObjectDefinition;
-import org.opends.server.admin.AliasDefaultBehaviorProvider;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.ConfigurationClient;
-import org.opends.server.admin.DefaultBehaviorException;
-import org.opends.server.admin.DefaultBehaviorProviderVisitor;
-import org.opends.server.admin.DefinedDefaultBehaviorProvider;
-import org.opends.server.admin.DefinitionDecodingException;
-import org.opends.server.admin.DefinitionResolver;
-import org.opends.server.admin.IllegalPropertyValueException;
-import org.opends.server.admin.IllegalPropertyValueStringException;
 import org.opends.server.admin.InstantiableRelationDefinition;
 import org.opends.server.admin.ManagedObjectAlreadyExistsException;
 import org.opends.server.admin.ManagedObjectDefinition;
-import org.opends.server.admin.ManagedObjectNotFoundException;
 import org.opends.server.admin.ManagedObjectPath;
-import org.opends.server.admin.OptionalRelationDefinition;
 import org.opends.server.admin.PropertyDefinition;
-import org.opends.server.admin.PropertyException;
-import org.opends.server.admin.PropertyIsMandatoryException;
-import org.opends.server.admin.PropertyIsReadOnlyException;
-import org.opends.server.admin.PropertyIsSingleValuedException;
-import org.opends.server.admin.PropertyNotFoundException;
 import org.opends.server.admin.PropertyOption;
 import org.opends.server.admin.RelationDefinition;
-import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
-import org.opends.server.admin.SingletonRelationDefinition;
-import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
-import org.opends.server.admin.DefinitionDecodingException.Reason;
 import org.opends.server.admin.client.AuthorizationException;
 import org.opends.server.admin.client.CommunicationException;
 import org.opends.server.admin.client.ConcurrentModificationException;
-import org.opends.server.admin.client.IllegalManagedObjectNameException;
 import org.opends.server.admin.client.ManagedObject;
-import org.opends.server.admin.client.ManagedObjectDecodingException;
-import org.opends.server.admin.client.MissingMandatoryPropertiesException;
 import org.opends.server.admin.client.OperationRejectedException;
-import org.opends.server.admin.client.Property;
-import org.opends.server.admin.client.PropertySet;
+import org.opends.server.admin.client.spi.AbstractManagedObject;
+import org.opends.server.admin.client.spi.Driver;
+import org.opends.server.admin.client.spi.Property;
+import org.opends.server.admin.client.spi.PropertySet;
 import org.opends.server.admin.std.client.RootCfgClient;
 import org.opends.server.admin.std.meta.RootCfgDefn;
 
@@ -103,384 +70,55 @@
  *          The type of client configuration represented by the client
  *          managed object.
  */
-final class LDAPManagedObject<T extends ConfigurationClient> implements
-    ManagedObject<T> {
+final class LDAPManagedObject<T extends ConfigurationClient> extends
+    AbstractManagedObject<T> {
 
   /**
-   * A default behavior visitor used for retrieving the default values
-   * of a property.
+   * Constructs a root LDAP managed object associated with the
+   * provided LDAP driver.
    *
-   * @param <T>
-   *          The type of the property.
-   */
-  private static class DefaultValueFinder<T> implements
-      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
-
-    /**
-     * Get the default values for the specified property.
-     *
-     * @param <T>
-     *          The type of the property.
-     * @param context
-     *          The LDAP management context.
-     * @param p
-     *          The managed object path of the current managed object.
-     * @param pd
-     *          The property definition.
-     * @param isCreate
-     *          Indicates whether the managed object has been created
-     *          yet.
-     * @return Returns the default values for the specified property.
-     * @throws DefaultBehaviorException
-     *           If the default values could not be retrieved or
-     *           decoded properly.
-     */
-    public static <T> Collection<T> getDefaultValues(
-        LDAPManagementContext context, ManagedObjectPath<?, ?> p,
-        PropertyDefinition<T> pd, boolean isCreate)
-        throws DefaultBehaviorException {
-      DefaultValueFinder<T> v = new DefaultValueFinder<T>(context, p, isCreate);
-      return v.find(p, pd);
-    }
-
-    // The LDAP management context.
-    private final LDAPManagementContext context;
-
-    // Any exception that occurred whilst retrieving inherited default
-    // values.
-    private DefaultBehaviorException exception = null;
-
-    // Indicates whether the managed object has been created yet.
-    private final boolean isCreate;
-
-    // The path of the managed object containing the first property.
-    private final ManagedObjectPath<?, ?> firstPath;
-
-    // The path of the managed object containing the next property.
-    private ManagedObjectPath<?, ?> nextPath = null;
-
-    // The next property whose default values were required.
-    private PropertyDefinition<T> nextProperty = null;
-
-
-
-    // Private constructor.
-    private DefaultValueFinder(LDAPManagementContext context,
-        ManagedObjectPath<?, ?> p, boolean isCreate) {
-      this.context = context;
-      this.firstPath = p;
-      this.isCreate = isCreate;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitAbsoluteInherited(
-        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
-      try {
-        return getInheritedProperty(d.getManagedObjectPath(), d
-            .getManagedObjectDefinition(), d.getPropertyName());
-      } catch (DefaultBehaviorException e) {
-        exception = e;
-        return Collections.emptySet();
-      }
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d,
-        Void p) {
-      return Collections.emptySet();
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
-        Void p) {
-      Collection<String> stringValues = d.getDefaultValues();
-      List<T> values = new ArrayList<T>(stringValues.size());
-
-      for (String stringValue : stringValues) {
-        try {
-          values.add(nextProperty.decodeValue(stringValue));
-        } catch (IllegalPropertyValueStringException e) {
-          exception = new DefaultBehaviorException(nextProperty, e);
-          break;
-        }
-      }
-
-      return values;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitRelativeInherited(
-        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
-      try {
-        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
-            .getManagedObjectDefinition(), d.getPropertyName());
-      } catch (DefaultBehaviorException e) {
-        exception = e;
-        return Collections.emptySet();
-      }
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
-        Void p) {
-      return Collections.emptySet();
-    }
-
-
-
-    // Find the default values for the next path/property.
-    private Collection<T> find(ManagedObjectPath<?, ?> p,
-        PropertyDefinition<T> pd)
-        throws DefaultBehaviorException {
-      this.nextPath = p;
-      this.nextProperty = pd;
-
-      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
-          this, null);
-
-      if (exception != null) {
-        throw exception;
-      }
-
-      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
-        throw new DefaultBehaviorException(pd,
-            new PropertyIsSingleValuedException(pd));
-      }
-
-      return values;
-    }
-
-
-
-    // Get an inherited property value.
-    @SuppressWarnings("unchecked")
-    private Collection<T> getInheritedProperty(ManagedObjectPath target,
-        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
-        throws DefaultBehaviorException {
-      // First check that the requested type of managed object
-      // corresponds to the path.
-      AbstractManagedObjectDefinition<?, ?> supr = target
-          .getManagedObjectDefinition();
-      if (!supr.isParentOf(d)) {
-        throw new DefaultBehaviorException(nextProperty,
-            new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION));
-      }
-
-      // Save the current property in case of recursion.
-      PropertyDefinition<T> pd1 = nextProperty;
-
-      try {
-        // If the path relates to the current managed object and the
-        // managed object is in the process of being created it won't
-        // exist, so we should just use the default values of the
-        // referenced property.
-        if (isCreate && firstPath.equals(target)) {
-          PropertyDefinition<T> pd2;
-          try {
-            // FIXME: we use the definition taken from the default
-            // behavior here when we should really use the exact
-            // definition of the component being created.
-            PropertyDefinition<?> pdTmp = d.getPropertyDefinition(propertyName);
-            pd2 = pd1.getClass().cast(pdTmp);
-          } catch (IllegalArgumentException e) {
-            throw new PropertyNotFoundException(propertyName);
-          } catch (ClassCastException e) {
-            // FIXME: would be nice to throw a better exception here.
-            throw new PropertyNotFoundException(propertyName);
-          }
-
-          // Recursively retrieve this property's default values.
-          Collection<T> tmp = find(target, pd2);
-          Collection<T> values = new ArrayList<T>(tmp.size());
-          for (T value : tmp) {
-            pd1.validateValue(value);
-            values.add(value);
-          }
-          return values;
-        } else {
-          // Get the actual managed object definition.
-          LdapName dn = LDAPNameBuilder
-              .create(target, context.getLDAPProfile());
-          ManagedObjectDefinition<?, ?> mod =
-            getEntryDefinition(context, d, dn);
-
-          PropertyDefinition<T> pd2;
-          try {
-            PropertyDefinition<?> pdTmp = mod
-                .getPropertyDefinition(propertyName);
-            pd2 = pd1.getClass().cast(pdTmp);
-          } catch (IllegalArgumentException e) {
-            throw new PropertyNotFoundException(propertyName);
-          } catch (ClassCastException e) {
-            // FIXME: would be nice to throw a better exception here.
-            throw new PropertyNotFoundException(propertyName);
-          }
-
-          String attrID = context.getLDAPProfile().getAttributeName(mod, pd2);
-          Attributes attributes = context.getLDAPConnection().readEntry(dn,
-              Collections.singleton(attrID));
-          Attribute attr = attributes.get(attrID);
-          if (attr == null || attr.size() == 0) {
-            // Recursively retrieve this property's default values.
-            Collection<T> tmp = find(target, pd2);
-            Collection<T> values = new ArrayList<T>(tmp.size());
-            for (T value : tmp) {
-              pd1.validateValue(value);
-              values.add(value);
-            }
-            return values;
-          } else {
-            Collection<T> values = new LinkedList<T>();
-            NamingEnumeration<?> ne = attr.getAll();
-            while (ne.hasMore()) {
-              Object value = ne.next();
-              if (value != null) {
-                values.add(pd1.decodeValue(value.toString()));
-              }
-            }
-            return values;
-          }
-        }
-      } catch (DefaultBehaviorException e) {
-        // Wrap any errors due to recursion.
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (DefinitionDecodingException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (PropertyNotFoundException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (IllegalPropertyValueException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (IllegalPropertyValueStringException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (NameNotFoundException e) {
-        throw new DefaultBehaviorException(pd1,
-            new ManagedObjectNotFoundException());
-      } catch (NoPermissionException e) {
-        throw new DefaultBehaviorException(pd1, new AuthorizationException(e));
-      } catch (NamingException e) {
-        throw new DefaultBehaviorException(pd1, new CommunicationException(e));
-      }
-    }
-  };
-
-
-
-  /**
-   * Construct a root LDAP managed object associated with the provided
-   * LDAP context.
-   *
-   * @param context
-   *          The LDAP management context.
+   * @param driver
+   *          The LDAP management driver.
    * @return Returns a root LDAP managed object associated with the
-   *         provided LDAP context.
+   *         provided LDAP driver.
    */
   static ManagedObject<RootCfgClient> getRootManagedObject(
-      LDAPManagementContext context) {
-    return new LDAPManagedObject<RootCfgClient>(context, RootCfgDefn
+      LDAPDriver driver) {
+    return new LDAPManagedObject<RootCfgClient>(driver, RootCfgDefn
         .getInstance(), ManagedObjectPath.emptyPath(), new PropertySet(), true,
         null);
   }
 
-
-
-  // Determine the type of managed object associated with the named
-  // entry.
-  private static <M extends ConfigurationClient, N extends Configuration>
-      ManagedObjectDefinition<? extends M, ? extends N> getEntryDefinition(
-      final LDAPManagementContext context,
-      AbstractManagedObjectDefinition<M, N> d, LdapName dn)
-      throws NamingException, DefinitionDecodingException {
-    Attributes attributes = context.getLDAPConnection().readEntry(dn,
-        Collections.singleton("objectclass"));
-    Attribute oc = attributes.get("objectclass");
-
-    if (oc == null) {
-      // No object classes.
-      throw new DefinitionDecodingException(Reason.NO_TYPE_INFORMATION);
-    }
-
-    final Set<String> objectClasses = new HashSet<String>();
-    NamingEnumeration<?> values = oc.getAll();
-    while (values.hasMore()) {
-      Object value = values.next();
-      if (value != null) {
-        objectClasses.add(value.toString().toLowerCase().trim());
-      }
-    }
-
-    if (objectClasses.isEmpty()) {
-      // No object classes.
-      throw new DefinitionDecodingException(Reason.NO_TYPE_INFORMATION);
-    }
-
-    // Resolve the appropriate sub-type based on the object classes.
-    DefinitionResolver resolver = new DefinitionResolver() {
-
-      public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
-        String objectClass = context.getLDAPProfile().getObjectClass(d);
-        return objectClasses.contains(objectClass);
-      }
-
-    };
-
-    return d.resolveManagedObjectDefinition(resolver);
-  }
-
-  // The LDAP management context used for the ldap connection.
-  private final LDAPManagementContext context;
-
-  // The managed object definition associated with this managed
-  // object.
-  private final ManagedObjectDefinition<T, ? extends Configuration> definition;
-
-  // Indicates whether or not this managed object exists on the server
-  // (false means the managed object is new and has not been
-  // committed).
-  private boolean existsOnServer;
-
-  // Optional naming property definition.
-  private final PropertyDefinition<?> namingPropertyDefinition;
-
-  // The path associated with this managed object.
-  private ManagedObjectPath<T, ? extends Configuration> path;
-
-  // The managed object's properties.
-  private final PropertySet properties;
+  // The LDAP management driver associated with this managed object.
+  private final LDAPDriver driver;
 
 
 
-  // Create an new LDAP managed object with the provided JNDI context.
-  private LDAPManagedObject(LDAPManagementContext context,
+  /**
+   * Creates a new LDAP managed object instance.
+   *
+   * @param driver
+   *          The underlying LDAP management driver.
+   * @param d
+   *          The managed object's definition.
+   * @param path
+   *          The managed object's path.
+   * @param properties
+   *          The managed object's properties.
+   * @param existsOnServer
+   *          Indicates whether or not the managed object already
+   *          exists.
+   * @param namingPropertyDefinition
+   *          The managed object's naming property definition if there
+   *          is one.
+   */
+  LDAPManagedObject(LDAPDriver driver,
       ManagedObjectDefinition<T, ? extends Configuration> d,
       ManagedObjectPath<T, ? extends Configuration> path,
       PropertySet properties, boolean existsOnServer,
       PropertyDefinition<?> namingPropertyDefinition) {
-    this.definition = d;
-    this.context = context;
-    this.path = path;
-    this.properties = properties;
-    this.existsOnServer = existsOnServer;
-    this.namingPropertyDefinition = namingPropertyDefinition;
+    super(d, path, properties, existsOnServer, namingPropertyDefinition);
+    this.driver = driver;
   }
 
 
@@ -488,411 +126,16 @@
   /**
    * {@inheritDoc}
    */
-  public void commit() throws MissingMandatoryPropertiesException,
-      ConcurrentModificationException, OperationRejectedException,
-      AuthorizationException, CommunicationException,
-      ManagedObjectAlreadyExistsException {
-    // First make sure all mandatory properties are defined.
-    List<PropertyIsMandatoryException> exceptions =
-      new LinkedList<PropertyIsMandatoryException>();
-
-    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
-      Property<?> p = properties.getProperty(pd);
-      if (pd.hasOption(PropertyOption.MANDATORY)
-          && p.getEffectiveValues().isEmpty()) {
-        exceptions.add(new PropertyIsMandatoryException(pd));
-      }
-    }
-
-    if (!exceptions.isEmpty()) {
-      throw new MissingMandatoryPropertiesException(exceptions);
-    }
-
-    // Commit the managed object.
-    if (existsOnServer) {
-      commitExistingManagedObject();
-    } else {
-      commitNewManagedObject();
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration, CC extends C>
-  ManagedObject<CC> createChild(InstantiableRelationDefinition<C, S> r,
-      ManagedObjectDefinition<CC, ? extends S> d, String name,
-      Collection<DefaultBehaviorException> exceptions)
-      throws IllegalManagedObjectNameException, IllegalArgumentException {
-    validateRelationDefinition(r);
-
-    // Empty names are not allowed.
-    if (name.trim().length() == 0) {
-      throw new IllegalManagedObjectNameException(name);
-    }
-
-    // If the relation uses a naming property definition then it must
-    // be a valid value.
-    PropertyDefinition<?> pd = r.getNamingPropertyDefinition();
-    if (pd != null) {
-      try {
-        pd.decodeValue(name);
-      } catch (IllegalPropertyValueStringException e) {
-        throw new IllegalManagedObjectNameException(name, pd);
-      }
-    }
-
-    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d, name);
-    return createNewManagedObject(d, childPath, pd, name, exceptions);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration, CC extends C>
-  ManagedObject<CC> createChild(OptionalRelationDefinition<C, S> r,
-      ManagedObjectDefinition<CC, ? extends S> d,
-      Collection<DefaultBehaviorException> exceptions)
-      throws IllegalArgumentException {
-    validateRelationDefinition(r);
-    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d);
-    return createNewManagedObject(d, childPath, null, null, exceptions);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  ManagedObject<? extends C> getChild(InstantiableRelationDefinition<C, S> r,
-      String name) throws IllegalArgumentException, DefinitionDecodingException,
-      ManagedObjectDecodingException, ManagedObjectNotFoundException,
-      ConcurrentModificationException, AuthorizationException,
-      CommunicationException {
-    validateRelationDefinition(r);
-    ensureThisManagedObjectExists();
-    return readManagedObject(r.getChildDefinition(), path.child(r, name));
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  ManagedObject<? extends C> getChild(OptionalRelationDefinition<C, S> r)
-      throws IllegalArgumentException, DefinitionDecodingException,
-      ManagedObjectDecodingException, ManagedObjectNotFoundException,
-      ConcurrentModificationException, AuthorizationException,
-      CommunicationException {
-    validateRelationDefinition(r);
-    ensureThisManagedObjectExists();
-    return readManagedObject(r.getChildDefinition(), path.child(r));
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  ManagedObject<? extends C> getChild(
-      SingletonRelationDefinition<C, S> r) throws IllegalArgumentException,
-      DefinitionDecodingException, ManagedObjectDecodingException,
-      ManagedObjectNotFoundException, ConcurrentModificationException,
-      AuthorizationException, CommunicationException {
-    validateRelationDefinition(r);
-    ensureThisManagedObjectExists();
-    return readManagedObject(r.getChildDefinition(), path.child(r));
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public T getConfiguration() {
-    return definition.createClientConfiguration(this);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public ManagedObjectDefinition<T, ? extends Configuration>
-  getManagedObjectDefinition() {
-    return definition;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public ManagedObjectPath<T, ? extends Configuration> getManagedObjectPath() {
-    return path;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <P> SortedSet<P> getPropertyDefaultValues(PropertyDefinition<P> pd)
-      throws IllegalArgumentException {
-    Property<P> p = properties.getProperty(pd);
-    return p.getDefaultValues();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <P> P getPropertyValue(PropertyDefinition<P> pd)
-      throws IllegalArgumentException {
-    return properties.getPropertyValue(pd);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <P> SortedSet<P> getPropertyValues(PropertyDefinition<P> pd)
-      throws IllegalArgumentException {
-    return properties.getPropertyValues(pd);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isPropertyPresent(PropertyDefinition<?> pd)
-      throws IllegalArgumentException {
-    Property<?> p = properties.getProperty(pd);
-    return !p.isEmpty();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  boolean hasChild(OptionalRelationDefinition<C, S> r)
-      throws IllegalArgumentException, ConcurrentModificationException,
-      AuthorizationException, CommunicationException {
-    validateRelationDefinition(r);
-    ensureThisManagedObjectExists();
-
-    ManagedObjectPath<C, S> p = path.child(r);
-    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
-    return entryExists(dn);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  String[] listChildren(InstantiableRelationDefinition<C, S> r)
-      throws IllegalArgumentException, ConcurrentModificationException,
-      AuthorizationException, CommunicationException {
-    return listChildren(r, r.getChildDefinition());
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  String[] listChildren(InstantiableRelationDefinition<C, S> r,
-      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
-      throws IllegalArgumentException, ConcurrentModificationException,
-      AuthorizationException, CommunicationException {
-    validateRelationDefinition(r);
-    ensureThisManagedObjectExists();
-
-    // Get the search base DN.
-    LdapName dn = LDAPNameBuilder.create(path, r, context.getLDAPProfile());
-
-    // Retrieve only those entries which are sub-types of the
-    // specified definition.
-    StringBuilder builder = new StringBuilder();
-    builder.append("(objectclass=");
-    builder.append(context.getLDAPProfile().getObjectClass(d));
-    builder.append(')');
-    String filter = builder.toString();
-
-    List<String> children = new ArrayList<String>();
-    try {
-      for (LdapName child :
-          context.getLDAPConnection().listEntries(dn, filter)) {
-        children.add(child.getRdn(child.size() - 1).getValue().toString());
-      }
-    } catch (NameNotFoundException e) {
-      // Ignore this - it means that the base entry does not exist
-      // (which it might not if this managed object has just been
-      // created.
-    } catch (NamingException e) {
-      adaptNamingException(e);
-    }
-    return children.toArray(new String[children.size()]);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  void removeChild(InstantiableRelationDefinition<C, S> r, String name)
-      throws IllegalArgumentException, ManagedObjectNotFoundException,
-      OperationRejectedException, ConcurrentModificationException,
-      AuthorizationException, CommunicationException {
-    validateRelationDefinition(r);
-    ensureThisManagedObjectExists();
-    ManagedObjectPath<C, S> p = path.child(r, name);
-    removeManagedObject(p);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  void removeChild(OptionalRelationDefinition<C, S> r)
-      throws IllegalArgumentException, ManagedObjectNotFoundException,
-        OperationRejectedException, ConcurrentModificationException,
-        AuthorizationException, CommunicationException {
-    validateRelationDefinition(r);
-    ensureThisManagedObjectExists();
-    ManagedObjectPath<C, S> p = path.child(r);
-    removeManagedObject(p);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <P> void setPropertyValue(PropertyDefinition<P> pd, P value)
-      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
-      PropertyIsMandatoryException, IllegalArgumentException {
-    if (value == null) {
-      setPropertyValues(pd, Collections.<P> emptySet());
-    } else {
-      setPropertyValues(pd, Collections.singleton(value));
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public <P> void setPropertyValues(PropertyDefinition<P> pd,
-      Collection<P> values) throws IllegalPropertyValueException,
-      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
-      PropertyIsMandatoryException, IllegalArgumentException {
-    if (pd.hasOption(PropertyOption.MONITORING)) {
-      throw new PropertyIsReadOnlyException(pd);
-    }
-
-    if (existsOnServer && pd.hasOption(PropertyOption.READ_ONLY)) {
-      throw new PropertyIsReadOnlyException(pd);
-    }
-
-    properties.setPropertyValues(pd, values);
-
-    // If this is a naming property then update the name.
-    if (pd.equals(namingPropertyDefinition)) {
-      // The property must be single-valued and mandatory.
-      String newName = pd.encodeValue(values.iterator().next());
-      path = path.rename(newName);
-    }
-  }
-
-  // Adapts a naming exception to an appropriate admin client
-  // exception.
-  private void adaptNamingException(NamingException ne)
-      throws CommunicationException, AuthorizationException {
-    try {
-      throw ne;
-    } catch (javax.naming.CommunicationException e) {
-      throw new CommunicationException(e);
-    } catch (javax.naming.ServiceUnavailableException e) {
-      throw new CommunicationException(e);
-    } catch (javax.naming.NoPermissionException e) {
-      throw new AuthorizationException(e);
-    } catch (NamingException e) {
-      // Just treat it as a communication problem.
-      throw new CommunicationException(e);
-    }
-  }
-
-
-
-  // Commit modifications made to this managed object.
-  private void commitExistingManagedObject()
-      throws ConcurrentModificationException, OperationRejectedException,
-      AuthorizationException, CommunicationException {
-    // Build the list of modified attributes.
-    Attributes mods = new BasicAttributes();
-    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
-      Property<?> p = properties.getProperty(pd);
-      if (p.isModified()) {
-        String attrID = context.getLDAPProfile().getAttributeName(definition,
-            pd);
-        Attribute attribute = new BasicAttribute(attrID);
-        encodeProperty(attribute, pd, properties);
-        mods.put(attribute);
-      }
-    }
-
-    // Perform the LDAP modification if something has changed.
-    if (mods.size() > 0) {
-      try {
-        LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
-        context.getLDAPConnection().modifyEntry(dn, mods);
-      } catch (NoPermissionException e) {
-        throw new AuthorizationException(e);
-      } catch (OperationNotSupportedException e) {
-        // Unwilling to perform.
-        throw new OperationRejectedException(e);
-      } catch (NamingException e) {
-        // Just treat it as a communication problem.
-        throw new CommunicationException(e);
-      }
-    }
-
-    // The changes were committed successfully so update this managed
-    // object's state.
-    properties.commit();
-  }
-
-
-
-  // Commit this new managed object.
-  private void commitNewManagedObject() throws AuthorizationException,
+  @Override
+  protected void addNewManagedObject() throws AuthorizationException,
       CommunicationException, OperationRejectedException,
       ConcurrentModificationException, ManagedObjectAlreadyExistsException {
     // First make sure that the parent managed object still exists.
+    ManagedObjectPath<?, ?> path = getManagedObjectPath();
     ManagedObjectPath<?, ?> parent = path.parent();
     if (!parent.isEmpty()) {
-      LdapName dn = LDAPNameBuilder.create(parent, context.getLDAPProfile());
-      if (!entryExists(dn)) {
+      LdapName dn = LDAPNameBuilder.create(parent, driver.getLDAPProfile());
+      if (!driver.entryExists(dn)) {
         throw new ConcurrentModificationException();
       }
     }
@@ -908,14 +151,14 @@
       // comprise of more than one RDN arc (this will probably never
       // be required anyway).
       LdapName dn = LDAPNameBuilder
-          .create(parent, ir, context.getLDAPProfile());
-      if (!entryExists(dn)) {
+          .create(parent, ir, driver.getLDAPProfile());
+      if (!driver.entryExists(dn)) {
         // We need to create the entry.
         Attributes attributes = new BasicAttributes();
 
         // Create the branch's object class attribute.
         Attribute oc = new BasicAttribute("objectClass");
-        for (String objectClass : context.getLDAPProfile()
+        for (String objectClass : driver.getLDAPProfile()
             .getInstantiableRelationObjectClasses(ir)) {
           oc.add(objectClass);
         }
@@ -927,39 +170,41 @@
 
         // Create the entry.
         try {
-          context.getLDAPConnection().createEntry(dn, attributes);
+          driver.getLDAPConnection().createEntry(dn, attributes);
         } catch (OperationNotSupportedException e) {
           // Unwilling to perform.
           throw new OperationRejectedException(e);
         } catch (NamingException e) {
-          adaptNamingException(e);
+          driver.adaptNamingException(e);
         }
       }
     }
 
     // Now add the entry representing this new managed object.
-    LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
+    LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
     Attributes attributes = new BasicAttributes(true);
 
     // Create the object class attribute.
     Attribute oc = new BasicAttribute("objectclass");
-    for (String objectClass : context.getLDAPProfile().getObjectClasses(
+    ManagedObjectDefinition<?, ?> definition = getManagedObjectDefinition();
+    for (String objectClass : driver.getLDAPProfile().getObjectClasses(
         definition)) {
       oc.add(objectClass);
     }
     attributes.put(oc);
 
     // Create the naming attribute if there is not naming property.
-    if (namingPropertyDefinition == null) {
+    PropertyDefinition<?> npd = getNamingPropertyDefinition();
+    if (npd == null) {
       Rdn rdn = dn.getRdn(dn.size() - 1);
       attributes.put(rdn.getType(), rdn.getValue().toString());
     }
 
     // Create the remaining attributes.
     for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
-      String attrID = context.getLDAPProfile().getAttributeName(definition, pd);
+      String attrID = driver.getLDAPProfile().getAttributeName(definition, pd);
       Attribute attribute = new BasicAttribute(attrID);
-      encodeProperty(attribute, pd, properties);
+      encodeProperty(attribute, pd);
       if (attribute.size() != 0) {
         attributes.put(attribute);
       }
@@ -967,287 +212,100 @@
 
     try {
       // Create the entry.
-      context.getLDAPConnection().createEntry(dn, attributes);
+      driver.getLDAPConnection().createEntry(dn, attributes);
     } catch (NameAlreadyBoundException e) {
       throw new ManagedObjectAlreadyExistsException();
     } catch (OperationNotSupportedException e) {
       // Unwilling to perform.
       throw new OperationRejectedException(e);
     } catch (NamingException e) {
-      adaptNamingException(e);
+      driver.adaptNamingException(e);
     }
-
-    // The entry was created successfully so update this managed
-    // object's state.
-    properties.commit();
-    existsOnServer = true;
   }
 
 
 
-  // Create a managed object which already exists on the server.
-  private <M extends ConfigurationClient, N extends Configuration>
-      ManagedObject<M> createExistingManagedObject(
-      ManagedObjectDefinition<M, N> d,
-      ManagedObjectPath<? super M, ? super N> p, PropertySet properties) {
-    RelationDefinition<?, ?> rd = p.getRelationDefinition();
-    PropertyDefinition<?> pd = null;
-    if (rd instanceof InstantiableRelationDefinition) {
-      InstantiableRelationDefinition<?, ?> ird =
-        (InstantiableRelationDefinition<?, ?>) rd;
-      pd = ird.getNamingPropertyDefinition();
-    }
-    return new LDAPManagedObject<M>(context, d, p.asSubType(d), properties,
-        true, pd);
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected Driver getDriver() {
+    return driver;
   }
 
 
 
-  // Creates a new managed object with no active values, just default
-  // values.
-  private <M extends ConfigurationClient, P>
-      ManagedObject<M> createNewManagedObject(
-      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> p,
-      PropertyDefinition<P> namingPropertyDefinition, String name,
-      Collection<DefaultBehaviorException> exceptions) {
-    PropertySet childProperties = new PropertySet();
-    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void modifyExistingManagedObject()
+      throws ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    // Build the list of modified attributes.
+    Attributes mods = new BasicAttributes();
+    ManagedObjectDefinition<?, ?> definition = getManagedObjectDefinition();
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
+      Property<?> p = getProperty(pd);
+      if (p.isModified()) {
+        String attrID = driver.getLDAPProfile().getAttributeName(definition,
+            pd);
+        Attribute attribute = new BasicAttribute(attrID);
+        encodeProperty(attribute, pd);
+        mods.put(attribute);
+      }
+    }
+
+    // Perform the LDAP modification if something has changed.
+    if (mods.size() > 0) {
       try {
-        createProperty(childProperties, p, pd);
-      } catch (DefaultBehaviorException e) {
-        // Add the exception if requested.
-        if (exceptions != null) {
-          exceptions.add(e);
-        }
+        ManagedObjectPath<?, ?> path = getManagedObjectPath();
+        LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
+        driver.getLDAPConnection().modifyEntry(dn, mods);
+      } catch (NoPermissionException e) {
+        throw new AuthorizationException(e);
+      } catch (OperationNotSupportedException e) {
+        // Unwilling to perform.
+        throw new OperationRejectedException(e);
+      } catch (NamingException e) {
+        // Just treat it as a communication problem.
+        throw new CommunicationException(e);
       }
     }
-
-    // Set the naming property if there is one.
-    if (namingPropertyDefinition != null) {
-      P value = namingPropertyDefinition.decodeValue(name);
-      childProperties.setPropertyValue(namingPropertyDefinition, value);
-    }
-
-    return new LDAPManagedObject<M>(context, d, p, childProperties, false,
-        namingPropertyDefinition);
-  }
-
-
-
-  // Create an empty property.
-  private <P> void createProperty(PropertySet properties,
-      ManagedObjectPath<?, ?> p, PropertyDefinition<P> pd)
-      throws DefaultBehaviorException {
-    try {
-      Collection<P> defaultValues = DefaultValueFinder.getDefaultValues(
-          context, p, pd, true);
-      properties.addProperty(pd, defaultValues, Collections.<P> emptySet());
-    } catch (DefaultBehaviorException e) {
-      // Make sure that we have still created the property.
-      properties.addProperty(pd, Collections.<P> emptySet(), Collections
-          .<P> emptySet());
-      throw e;
-    }
   }
 
 
 
-  // Create a property using the provided string values.
-  private <P> void decodeProperty(PropertySet newProperties,
-      ManagedObjectPath<?, ?> p, PropertyDefinition<P> pd, List<String> values)
-      throws PropertyException {
-    PropertyException exception = null;
-
-    // Get the property's active values.
-    Collection<P> activeValues = new ArrayList<P>(values.size());
-    for (String value : values) {
-      try {
-        activeValues.add(pd.decodeValue(value));
-      } catch (IllegalPropertyValueStringException e) {
-        exception = e;
-      }
-    }
-
-    if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
-      // This exception takes precedence over previous exceptions.
-      exception = new PropertyIsSingleValuedException(pd);
-      P value = activeValues.iterator().next();
-      activeValues.clear();
-      activeValues.add(value);
-    }
-
-    if (activeValues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
-      // The active values maybe empty because of a previous exception.
-      if (exception == null) {
-        exception = new PropertyIsMandatoryException(pd);
-      }
-    }
-
-    // Get the property's default values.
-    Collection<P> defaultValues;
-    try {
-      defaultValues = DefaultValueFinder.getDefaultValues(context, p, pd,
-          false);
-    } catch (DefaultBehaviorException e) {
-      defaultValues = Collections.emptySet();
-      exception = e;
-    }
-
-    newProperties.addProperty(pd, defaultValues, activeValues);
-    if (exception != null) {
-      throw exception;
-    }
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected <M extends ConfigurationClient> ManagedObject<M> newInstance(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> path,
+      PropertySet properties, boolean existsOnServer,
+      PropertyDefinition<?> namingPropertyDefinition) {
+    return new LDAPManagedObject<M>(driver, d, path, properties,
+        existsOnServer, namingPropertyDefinition);
   }
 
 
 
   // Encode a property into LDAP string values.
-  private <P> void encodeProperty(Attribute attribute,
-      PropertyDefinition<P> pd, PropertySet properties) {
-    Property<P> p = properties.getProperty(pd);
+  private <PD> void encodeProperty(Attribute attribute,
+      PropertyDefinition<PD> pd) {
+    Property<PD> p = getProperty(pd);
     if (pd.hasOption(PropertyOption.MANDATORY)) {
       // For mandatory properties we fall-back to the default values
       // if defined which can sometimes be the case e.g when a
       // mandatory property is overridden.
-      for (P value : p.getEffectiveValues()) {
+      for (PD value : p.getEffectiveValues()) {
         attribute.add(pd.encodeValue(value));
       }
     } else {
-      for (P value : p.getPendingValues()) {
+      for (PD value : p.getPendingValues()) {
         attribute.add(pd.encodeValue(value));
       }
     }
   }
 
-
-
-  // Makes sure that the entry corresponding to this managed object
-  // exists.
-  private void ensureThisManagedObjectExists()
-      throws ConcurrentModificationException, CommunicationException,
-      AuthorizationException {
-    if (!path.isEmpty()) {
-      LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
-      if (!entryExists(dn)) {
-        throw new ConcurrentModificationException();
-      }
-    }
-  }
-
-
-
-  // Determine whether the named entry exists or not.
-  private boolean entryExists(LdapName dn) throws CommunicationException,
-      AuthorizationException {
-    try {
-      return context.getLDAPConnection().entryExists(dn);
-    } catch (NamingException e) {
-      adaptNamingException(e);
-    }
-    return false;
-  }
-
-
-
-  // Read the entry identified by the path and which is a sub-type of
-  // the specified definition.
-  private <C extends ConfigurationClient, S extends Configuration>
-      ManagedObject<? extends C> readManagedObject(
-      AbstractManagedObjectDefinition<C, S> d, ManagedObjectPath<C, S> p)
-      throws DefinitionDecodingException, ManagedObjectDecodingException,
-      ManagedObjectNotFoundException, AuthorizationException,
-      CommunicationException {
-    try {
-      // Read the entry associated with the managed object.
-      LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
-      ManagedObjectDefinition<? extends C, ? extends S> mod =
-        getEntryDefinition(context, d, dn);
-
-      ArrayList<String> attrIds = new ArrayList<String>();
-      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
-        String attrId = context.getLDAPProfile().getAttributeName(mod, pd);
-        attrIds.add(attrId);
-      }
-
-      Attributes attributes = context.getLDAPConnection()
-          .readEntry(dn, attrIds);
-
-      // Build the managed object's properties.
-      List<PropertyException> exceptions = new LinkedList<PropertyException>();
-      PropertySet newProperties = new PropertySet();
-      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
-        String attrID = context.getLDAPProfile().getAttributeName(mod, pd);
-        Attribute attribute = attributes.get(attrID);
-        List<String> values = new LinkedList<String>();
-
-        if (attribute != null && attribute.size() != 0) {
-          NamingEnumeration<?> ldapValues = attribute.getAll();
-          while (ldapValues.hasMore()) {
-            Object obj = ldapValues.next();
-            if (obj != null) {
-              values.add(obj.toString());
-            }
-          }
-        }
-
-        try {
-          decodeProperty(newProperties, p, pd, values);
-        } catch (PropertyException e) {
-          exceptions.add(e);
-        }
-      }
-
-      // If there were no decoding problems then return the object,
-      // otherwise throw an operations exception.
-      ManagedObject<? extends C> mo = createExistingManagedObject(mod, p,
-          newProperties);
-      if (exceptions.isEmpty()) {
-        return mo;
-      } else {
-        throw new ManagedObjectDecodingException(mo, exceptions);
-      }
-    } catch (NameNotFoundException e) {
-      throw new ManagedObjectNotFoundException();
-    } catch (NoPermissionException e) {
-      throw new AuthorizationException(e);
-    } catch (NamingException e) {
-      throw new CommunicationException(e);
-    }
-  }
-
-
-
-  // Remove the named managed object.
-  private void removeManagedObject(ManagedObjectPath<?, ?> p)
-      throws CommunicationException, AuthorizationException,
-      OperationRejectedException, ManagedObjectNotFoundException {
-    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
-    if (entryExists(dn)) {
-      // Delete the entry and any subordinate entries.
-      try {
-        context.getLDAPConnection().deleteSubtree(dn);
-      } catch (OperationNotSupportedException e) {
-        // Unwilling to perform.
-        throw new OperationRejectedException(e);
-      } catch (NamingException e) {
-        adaptNamingException(e);
-      }
-    } else {
-      throw new ManagedObjectNotFoundException();
-    }
-  }
-
-
-
-  // Validate that a relation definition belongs to this managed
-  // object.
-  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
-      throws IllegalArgumentException {
-    ManagedObjectDefinition<T, ?> d = getManagedObjectDefinition();
-    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
-    if (tmp != rd) {
-      throw new IllegalArgumentException("The relation " + rd.getName()
-          + " is not associated with a " + d.getName());
-    }
-  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java
index 6828bc9..9e107dc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java
@@ -55,20 +55,15 @@
     return new LDAPManagementContext(connection, LDAPProfile.getInstance());
   }
 
-  // The LDAP connection.
-  private final LDAPConnection connection;
-
-  // The LDAP profile which should be used to construct LDAP requests
-  // and decode LDAP responses.
-  private final LDAPProfile profile;
+  // The LDAP management context driver.
+  private final LDAPDriver driver;
 
 
 
   // Private constructor.
   private LDAPManagementContext(LDAPConnection connection,
       LDAPProfile profile) {
-    this.connection = connection;
-    this.profile = profile;
+    this.driver = new LDAPDriver(connection, profile);
   }
 
 
@@ -77,31 +72,6 @@
    * {@inheritDoc}
    */
   public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
-    return LDAPManagedObject.getRootManagedObject(this);
-  }
-
-
-
-  /**
-   * Gets the LDAP connection used for interacting with the server.
-   *
-   * @return Returns the LDAP connection used for interacting with the
-   *         server.
-   */
-  LDAPConnection getLDAPConnection() {
-    return connection;
-  }
-
-
-
-  /**
-   * Gets the LDAP profile which should be used to construct LDAP
-   * requests and decode LDAP responses.
-   *
-   * @return Returns the LDAP profile which should be used to
-   *         construct LDAP requests and decode LDAP responses.
-   */
-  LDAPProfile getLDAPProfile() {
-    return profile;
+    return LDAPManagedObject.getRootManagedObject(driver);
   }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/package-info.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/package-info.java
index f92dd8b..0d5f99b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/package-info.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/package-info.java
@@ -30,12 +30,8 @@
 /**
  * Common client-side administration classes.
  * <p>
- * This package contains classes which client applications and client-side
- * driver implementations are expected to use.
- * <p>
- * In addition, there are also two example client
- * applications, <code>ExampleClient</code> and
- * <code>ExampleIntrospection</code>.
+ * This package contains classes which client applications are
+ * expected to use.
  */
 @org.opends.server.types.PublicAPI(
      stability=org.opends.server.types.StabilityLevel.PRIVATE)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
new file mode 100644
index 0000000..5e9c9c2
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
@@ -0,0 +1,696 @@
+/*
+ * 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.client.spi;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.IllegalPropertyValueException;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectAlreadyExistsException;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.admin.PropertyIsReadOnlyException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.SingletonRelationDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ConcurrentModificationException;
+import org.opends.server.admin.client.IllegalManagedObjectNameException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.MissingMandatoryPropertiesException;
+import org.opends.server.admin.client.OperationRejectedException;
+
+
+
+/**
+ * An abstract managed object implementation.
+ *
+ * @param <T>
+ *          The type of client configuration represented by the client
+ *          managed object.
+ */
+public abstract class AbstractManagedObject<T extends ConfigurationClient>
+    implements ManagedObject<T> {
+
+  // The managed object definition associated with this managed
+  // object.
+  private final ManagedObjectDefinition<T, ? extends Configuration> definition;
+
+  // Indicates whether or not this managed object exists on the server
+  // (false means the managed object is new and has not been
+  // committed).
+  private boolean existsOnServer;
+
+  // Optional naming property definition.
+  private final PropertyDefinition<?> namingPropertyDefinition;
+
+  // The path associated with this managed object.
+  private ManagedObjectPath<T, ? extends Configuration> path;
+
+  // The managed object's properties.
+  private final PropertySet properties;
+
+
+
+  /**
+   * Creates a new abstract managed object.
+   *
+   * @param d
+   *          The managed object's definition.
+   * @param path
+   *          The managed object's path.
+   * @param properties
+   *          The managed object's properties.
+   * @param existsOnServer
+   *          Indicates whether or not the managed object exists on
+   *          the server (false means the managed object is new and
+   *          has not been committed).
+   * @param namingPropertyDefinition
+   *          Optional naming property definition.
+   */
+  protected AbstractManagedObject(
+      ManagedObjectDefinition<T, ? extends Configuration> d,
+      ManagedObjectPath<T, ? extends Configuration> path,
+      PropertySet properties, boolean existsOnServer,
+      PropertyDefinition<?> namingPropertyDefinition) {
+    this.definition = d;
+    this.path = path;
+    this.properties = properties;
+    this.existsOnServer = existsOnServer;
+    this.namingPropertyDefinition = namingPropertyDefinition;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final void commit() throws ManagedObjectAlreadyExistsException,
+      MissingMandatoryPropertiesException, ConcurrentModificationException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    // First make sure all mandatory properties are defined.
+    List<PropertyIsMandatoryException> exceptions =
+      new LinkedList<PropertyIsMandatoryException>();
+
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
+      Property<?> p = getProperty(pd);
+      if (pd.hasOption(PropertyOption.MANDATORY)
+          && p.getEffectiveValues().isEmpty()) {
+        exceptions.add(new PropertyIsMandatoryException(pd));
+      }
+    }
+
+    if (!exceptions.isEmpty()) {
+      throw new MissingMandatoryPropertiesException(exceptions);
+    }
+
+    // Commit the managed object.
+    if (existsOnServer) {
+      modifyExistingManagedObject();
+    } else {
+      addNewManagedObject();
+      existsOnServer = true;
+    }
+
+    // Make all pending property values active.
+    properties.commit();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration,
+                CC extends C>
+  ManagedObject<CC> createChild(
+      InstantiableRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d, String name,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalManagedObjectNameException, IllegalArgumentException {
+    validateRelationDefinition(r);
+
+    // Empty names are not allowed.
+    if (name.trim().length() == 0) {
+      throw new IllegalManagedObjectNameException(name);
+    }
+
+    // If the relation uses a naming property definition then it must
+    // be a valid value.
+    PropertyDefinition<?> pd = r.getNamingPropertyDefinition();
+    if (pd != null) {
+      try {
+        pd.decodeValue(name);
+      } catch (IllegalPropertyValueStringException e) {
+        throw new IllegalManagedObjectNameException(name, pd);
+      }
+    }
+
+    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d, name);
+    return createNewManagedObject(d, childPath, pd, name, exceptions);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient,
+                S extends Configuration, CC extends C>
+  ManagedObject<CC> createChild(
+      OptionalRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalArgumentException {
+    validateRelationDefinition(r);
+    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d);
+    return createNewManagedObject(d, childPath, null, null, exceptions);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(
+      InstantiableRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(r);
+    ensureThisManagedObjectExists();
+    Driver ctx = getDriver();
+    return ctx.getManagedObject(path.child(r, name));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(
+      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
+      DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    ensureThisManagedObjectExists();
+    Driver ctx = getDriver();
+    return ctx.getManagedObject(path.child(r));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(
+      SingletonRelationDefinition<C, S> r) throws IllegalArgumentException,
+      DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    ensureThisManagedObjectExists();
+    Driver ctx = getDriver();
+    return ctx.getManagedObject(path.child(r));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final T getConfiguration() {
+    return definition.createClientConfiguration(this);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final ManagedObjectDefinition<T, ? extends Configuration>
+  getManagedObjectDefinition() {
+    return definition;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final ManagedObjectPath<T, ? extends Configuration>
+  getManagedObjectPath() {
+    return path;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> SortedSet<PD> getPropertyDefaultValues(
+      PropertyDefinition<PD> pd) throws IllegalArgumentException {
+    return new TreeSet<PD>(getProperty(pd).getDefaultValues());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> PD getPropertyValue(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException {
+    Set<PD> values = getProperty(pd).getEffectiveValues();
+    if (values.isEmpty()) {
+      return null;
+    } else {
+      return values.iterator().next();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> SortedSet<PD> getPropertyValues(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException {
+    return new TreeSet<PD>(getProperty(pd).getEffectiveValues());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean hasChild(
+      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    try {
+      return ctx.hasManagedObject(path, r);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean isPropertyPresent(PropertyDefinition<?> pd)
+      throws IllegalArgumentException {
+    return !getProperty(pd).isEmpty();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(
+      InstantiableRelationDefinition<C, S> r) throws IllegalArgumentException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    return listChildren(r, r.getChildDefinition());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(
+      InstantiableRelationDefinition<C, S> r,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    try {
+      return ctx.listManagedObjects(path, r, d);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(
+      InstantiableRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    boolean found;
+
+    try {
+      found = ctx.deleteManagedObject(path, r, name);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+
+    if (!found) {
+      throw new ManagedObjectNotFoundException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(
+      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    boolean found;
+
+    try {
+      found = ctx.deleteManagedObject(path, r);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+
+    if (!found) {
+      throw new ManagedObjectNotFoundException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> void setPropertyValue(PropertyDefinition<PD> pd, PD value)
+      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
+      PropertyIsMandatoryException, IllegalArgumentException {
+    if (value == null) {
+      setPropertyValues(pd, Collections.<PD> emptySet());
+    } else {
+      setPropertyValues(pd, Collections.singleton(value));
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> void setPropertyValues(PropertyDefinition<PD> pd,
+      Collection<PD> values) throws IllegalPropertyValueException,
+      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
+      PropertyIsMandatoryException, IllegalArgumentException {
+    if (pd.hasOption(PropertyOption.MONITORING)) {
+      throw new PropertyIsReadOnlyException(pd);
+    }
+
+    if (existsOnServer && pd.hasOption(PropertyOption.READ_ONLY)) {
+      throw new PropertyIsReadOnlyException(pd);
+    }
+
+    properties.setPropertyValues(pd, values);
+
+    // If this is a naming property then update the name.
+    if (pd.equals(namingPropertyDefinition)) {
+      // The property must be single-valued and mandatory.
+      String newName = pd.encodeValue(values.iterator().next());
+      path = path.rename(newName);
+    }
+  }
+
+
+
+  /**
+   * Adds this new managed object.
+   *
+   * @throws ManagedObjectAlreadyExistsException
+   *           If the managed object cannot be added to the server
+   *           because it already exists.
+   * @throws ConcurrentModificationException
+   *           If the managed object's parent has been removed by
+   *           another client.
+   * @throws OperationRejectedException
+   *           If the server refuses to add this managed object due to
+   *           some server-side constraint which cannot be satisfied.
+   * @throws AuthorizationException
+   *           If the server refuses to add this managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  protected abstract void addNewManagedObject() throws AuthorizationException,
+      CommunicationException, OperationRejectedException,
+      ConcurrentModificationException, ManagedObjectAlreadyExistsException;
+
+
+
+  /**
+   * Gets the management context driver associated with this managed
+   * object.
+   *
+   * @return Returns the management context driver associated with
+   *         this managed object.
+   */
+  protected abstract Driver getDriver();
+
+
+
+  /**
+   * Gets the naming property definition associated with this managed
+   * object.
+   *
+   * @return Returns the naming property definition associated with
+   *         this managed object, or <code>null</code> if this
+   *         managed object does not have a naming property.
+   */
+  protected final PropertyDefinition<?> getNamingPropertyDefinition() {
+    return namingPropertyDefinition;
+  }
+
+
+
+  /**
+   * Gets the property associated with the specified property
+   * definition.
+   *
+   * @param <PD>
+   *          The underlying type of the property.
+   * @param pd
+   *          The Property definition.
+   * @return Returns the property associated with the specified
+   *         property definition.
+   * @throws IllegalArgumentException
+   *           If this property provider does not recognize the
+   *           requested property definition.
+   */
+  protected final <PD> Property<PD> getProperty(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException {
+    return properties.getProperty(pd);
+  }
+
+
+
+  /**
+   * Applies changes made to this managed object.
+   *
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws OperationRejectedException
+   *           If the server refuses to modify this managed object due
+   *           to some server-side constraint which cannot be
+   *           satisfied.
+   * @throws AuthorizationException
+   *           If the server refuses to modify this managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  protected abstract void modifyExistingManagedObject()
+      throws ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Creates a new managed object.
+   *
+   * @param <M>
+   *          The type of client configuration represented by the
+   *          client managed object.
+   * @param d
+   *          The managed object's definition.
+   * @param path
+   *          The managed object's path.
+   * @param properties
+   *          The managed object's properties.
+   * @param existsOnServer
+   *          Indicates whether or not the managed object exists on
+   *          the server (false means the managed object is new and
+   *          has not been committed).
+   * @param namingPropertyDefinition
+   *          Optional naming property definition.
+   * @return Returns the new managed object.
+   */
+  protected abstract <M extends ConfigurationClient>
+  ManagedObject<M> newInstance(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> path,
+      PropertySet properties, boolean existsOnServer,
+      PropertyDefinition<?> namingPropertyDefinition);
+
+
+
+  // Creates a new managed object with no active values, just default
+  // values.
+  private <M extends ConfigurationClient, PD> ManagedObject<M>
+  createNewManagedObject(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> p,
+      PropertyDefinition<PD> namingPropertyDefinition, String name,
+      Collection<DefaultBehaviorException> exceptions) {
+    PropertySet childProperties = new PropertySet();
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+      try {
+        createProperty(childProperties, p, pd);
+      } catch (DefaultBehaviorException e) {
+        // Add the exception if requested.
+        if (exceptions != null) {
+          exceptions.add(e);
+        }
+      }
+    }
+
+    // Set the naming property if there is one.
+    if (namingPropertyDefinition != null) {
+      PD value = namingPropertyDefinition.decodeValue(name);
+      childProperties.setPropertyValues(namingPropertyDefinition, Collections
+          .singleton(value));
+    }
+
+    return newInstance(d, p, childProperties, false, namingPropertyDefinition);
+  }
+
+
+
+  // Create an empty property.
+  private <PD> void createProperty(PropertySet properties,
+      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd)
+      throws DefaultBehaviorException {
+    try {
+      Driver context = getDriver();
+      Collection<PD> defaultValues = context.findDefaultValues(p, pd, true);
+      properties.addProperty(pd, defaultValues, Collections.<PD> emptySet());
+    } catch (DefaultBehaviorException e) {
+      // Make sure that we have still created the property.
+      properties.addProperty(pd, Collections.<PD> emptySet(), Collections
+          .<PD> emptySet());
+      throw e;
+    }
+  }
+
+
+
+  // Makes sure that this managed object exists.
+  private void ensureThisManagedObjectExists()
+      throws ConcurrentModificationException, CommunicationException,
+      AuthorizationException {
+    if (!path.isEmpty()) {
+      Driver ctx = getDriver();
+
+      try {
+        ctx.getManagedObject(path);
+      } catch (DefinitionDecodingException e) {
+        // Ignore.
+      } catch (ManagedObjectDecodingException e) {
+        // Ignore.
+      } catch (ManagedObjectNotFoundException e) {
+        throw new ConcurrentModificationException();
+      }
+    }
+  }
+
+
+
+  // Validate that a relation definition belongs to this managed
+  // object.
+  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
+      throws IllegalArgumentException {
+    ManagedObjectDefinition<T, ?> d = getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + d.getName());
+    }
+  }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java
new file mode 100644
index 0000000..d26004d
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java
@@ -0,0 +1,701 @@
+/*
+ * 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.client.spi;
+
+
+
+import static org.opends.server.util.StaticUtils.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AliasDefaultBehaviorProvider;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefaultBehaviorProviderVisitor;
+import org.opends.server.admin.DefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyNotFoundException;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+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.OperationRejectedException;
+
+
+
+/**
+ * An abstract management connection context driver which should form
+ * the basis of driver implementations.
+ */
+public abstract class Driver {
+
+  /**
+   * A default behavior visitor used for retrieving the default values
+   * of a property.
+   *
+   * @param <T>
+   *          The type of the property.
+   */
+  private class DefaultValueFinder<T> implements
+      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
+
+    // Any exception that occurred whilst retrieving inherited default
+    // values.
+    private DefaultBehaviorException exception = null;
+
+    // The path of the managed object containing the first property.
+    private final ManagedObjectPath<?, ?> firstPath;
+
+    // Indicates whether the managed object has been created yet.
+    private final boolean isCreate;
+
+    // The path of the managed object containing the next property.
+    private ManagedObjectPath<?, ?> nextPath = null;
+
+    // The next property whose default values were required.
+    private PropertyDefinition<T> nextProperty = null;
+
+
+
+    // Private constructor.
+    private DefaultValueFinder(ManagedObjectPath<?, ?> p, boolean isCreate) {
+      this.firstPath = p;
+      this.isCreate = isCreate;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitAbsoluteInherited(
+        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
+      try {
+        return getInheritedProperty(d.getManagedObjectPath(), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = e;
+        return Collections.emptySet();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
+      return Collections.emptySet();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
+        Void p) {
+      Collection<String> stringValues = d.getDefaultValues();
+      List<T> values = new ArrayList<T>(stringValues.size());
+
+      for (String stringValue : stringValues) {
+        try {
+          values.add(nextProperty.decodeValue(stringValue));
+        } catch (IllegalPropertyValueStringException e) {
+          exception = new DefaultBehaviorException(nextProperty, e);
+          break;
+        }
+      }
+
+      return values;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitRelativeInherited(
+        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
+      try {
+        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = e;
+        return Collections.emptySet();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
+        Void p) {
+      return Collections.emptySet();
+    }
+
+
+
+    // Find the default values for the next path/property.
+    private Collection<T> find(ManagedObjectPath<?, ?> p,
+        PropertyDefinition<T> pd) throws DefaultBehaviorException {
+      this.nextPath = p;
+      this.nextProperty = pd;
+
+      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
+          this, null);
+
+      if (exception != null) {
+        throw exception;
+      }
+
+      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+        throw new DefaultBehaviorException(pd,
+            new PropertyIsSingleValuedException(pd));
+      }
+
+      return values;
+    }
+
+
+
+    // Get an inherited property value.
+    @SuppressWarnings("unchecked")
+    private Collection<T> getInheritedProperty(ManagedObjectPath target,
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
+        throws DefaultBehaviorException {
+      // First check that the requested type of managed object
+      // corresponds to the path.
+      AbstractManagedObjectDefinition<?, ?> supr = target
+          .getManagedObjectDefinition();
+      if (!supr.isParentOf(d)) {
+        throw new DefaultBehaviorException(nextProperty,
+            new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION));
+      }
+
+      // Save the current property in case of recursion.
+      PropertyDefinition<T> pd1 = nextProperty;
+
+      try {
+        // Determine the requested property definition.
+        PropertyDefinition<T> pd2;
+        try {
+          // FIXME: we use the definition taken from the default
+          // behavior here when we should really use the exact
+          // definition of the component being created.
+          PropertyDefinition<?> pdTmp = d.getPropertyDefinition(propertyName);
+          pd2 = pd1.getClass().cast(pdTmp);
+        } catch (IllegalArgumentException e) {
+          throw new PropertyNotFoundException(propertyName);
+        } catch (ClassCastException e) {
+          // FIXME: would be nice to throw a better exception here.
+          throw new PropertyNotFoundException(propertyName);
+        }
+
+        // If the path relates to the current managed object and the
+        // managed object is in the process of being created it won't
+        // exist, so we should just use the default values of the
+        // referenced property.
+        if (isCreate && firstPath.equals(target)) {
+          // Recursively retrieve this property's default values.
+          Collection<T> tmp = find(target, pd2);
+          Collection<T> values = new ArrayList<T>(tmp.size());
+          for (T value : tmp) {
+            pd1.validateValue(value);
+            values.add(value);
+          }
+          return values;
+        } else {
+          return getPropertyValues(target, pd2);
+        }
+      } catch (DefaultBehaviorException e) {
+        // Wrap any errors due to recursion.
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (DefinitionDecodingException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (PropertyNotFoundException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (AuthorizationException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (ManagedObjectNotFoundException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (CommunicationException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (PropertyException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      }
+    }
+  };
+
+
+
+  /**
+   * Creates a new abstract management context.
+   */
+  protected Driver() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Deletes the named instantiable child managed object from the
+   * named parent managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @return Returns <code>true</code> if the named instantiable
+   *         child managed object was found, or <code>false</code>
+   *         if it was not found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the server refuses to remove the child managed
+   *           object due to some server-side constraint which cannot
+   *           be satisfied (for example, if it is referenced by
+   *           another managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to make the list the managed
+   *           objects because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Deletes the optional child managed object from the named parent
+   * managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The optional relation definition.
+   * @return Returns <code>true</code> if the optional child managed
+   *         object was found, or <code>false</code> if it was not
+   *         found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the server refuses to remove the child managed
+   *           object due to some server-side constraint which cannot
+   *           be satisfied (for example, if it is referenced by
+   *           another managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to make the list the managed
+   *           objects because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException;
+
+
+
+  /**
+   * Gets the named managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param path
+   *          The path of the managed object.
+   * @return Returns the named managed object.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getManagedObject(
+      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Gets the effective value of a property in the named managed
+   * object.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective value, or
+   *         <code>null</code> if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
+      PropertyDefinition<PD> pd) throws IllegalArgumentException,
+      DefinitionDecodingException, AuthorizationException,
+      ManagedObjectNotFoundException, CommunicationException,
+      PropertyException {
+    Set<PD> values = getPropertyValues(path, pd);
+    if (values.isEmpty()) {
+      return null;
+    } else {
+      return values.iterator().next();
+    }
+  }
+
+
+
+  /**
+   * Gets the effective values of a property in the named managed
+   * object.
+   * <p>
+   * Implementations MUST NOT not use
+   * {@link #getManagedObject(ManagedObjectPath)} to read the
+   * referenced managed object in its entirety. Specifically,
+   * implementations MUST only attempt to resolve the default values
+   * for the requested property and its dependencies (if it uses
+   * inherited defaults). This is to avoid infinite recursion where a
+   * managed object contains a property which inherits default values
+   * from another property in the same managed object.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective values, or an empty set
+   *         if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <PD> SortedSet<PD> getPropertyValues(
+      ManagedObjectPath<?, ?> path, PropertyDefinition<PD> pd)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      AuthorizationException, ManagedObjectNotFoundException,
+      CommunicationException, PropertyException;
+
+
+
+  /**
+   * Determines whether or not the named parent managed object has the
+   * named instantiable child managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object.
+   * @return Returns <code>true</code> if the named instantiable
+   *         child managed object exists, <code>false</code>
+   *         otherwise.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean hasManagedObject(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, AuthorizationException,
+      CommunicationException {
+    // FIXME: use naming properties for comparison where available.
+    String[] children = listManagedObjects(parent, rd);
+    String nname = toLowerCase(name.trim().replaceAll(" +", " "));
+    for (String child : children) {
+      if (nname.equals(toLowerCase(child.trim().replaceAll(" +", " ")))) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+
+  /**
+   * Determines whether or not the named parent managed object has an
+   * optional child managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The optional relation definition.
+   * @return Returns <code>true</code> if the optional child managed
+   *         object exists, <code>false</code> otherwise.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  boolean hasManagedObject(
+      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    return listManagedObjects(parent, rd, rd.getChildDefinition());
+  }
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object which are a sub-type of the specified managed object
+   * definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param d
+   *          The managed object definition.
+   * @return Returns the names of the child managed objects which are
+   *         a sub-type of the specified managed object definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Gets the default values for the specified property.
+   *
+   * @param <PD>
+   *          The type of the property.
+   * @param p
+   *          The managed object path of the current managed object.
+   * @param pd
+   *          The property definition.
+   * @param isCreate
+   *          Indicates whether the managed object has been created
+   *          yet.
+   * @return Returns the default values for the specified property.
+   * @throws DefaultBehaviorException
+   *           If the default values could not be retrieved or decoded
+   *           properly.
+   */
+  protected final <PD> Collection<PD> findDefaultValues(
+      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd, boolean isCreate)
+      throws DefaultBehaviorException {
+    DefaultValueFinder<PD> v = new DefaultValueFinder<PD>(p, isCreate);
+    return v.find(p, pd);
+  }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/Property.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Property.java
similarity index 72%
rename from opendj-sdk/opends/src/server/org/opends/server/admin/client/Property.java
rename to opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Property.java
index b694cb7..33cd229 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/Property.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Property.java
@@ -25,7 +25,7 @@
  *      Portions Copyright 2007 Sun Microsystems, Inc.
  */
 
-package org.opends.server.admin.client;
+package org.opends.server.admin.client.spi;
 
 
 
@@ -36,28 +36,26 @@
 
 
 /**
- * A managed object property comprising of the property's definition and its set
- * of values.
+ * A managed object property comprising of the property's definition
+ * and its set of values.
  * <p>
- * The property stores the values in a sorted set in which values are compared
- * using the comparator defined by the property definition.
+ * The property stores the values in a sorted set in which values are
+ * compared using the comparator defined by the property definition.
  * <p>
- * The property keeps track of whether or not its pending set of values differs
- * from its active values.
+ * The property keeps track of whether or not its pending set of
+ * values differs from its active values.
  *
  * @param <T>
  *          The type of the property.
- * @see ManagedObject The <code>ManagedObject</code> documentation describes
- *      the different types of property values in more detail.
  */
 public interface Property<T> {
 
   /**
    * Get an immutable set view of this property's active values.
    *
-   * @return Returns an immutable set view of this property's active values. An
-   *         empty set indicates that there are no active values, and any
-   *         default values are applicable.
+   * @return Returns an immutable set view of this property's active
+   *         values. An empty set indicates that there are no active
+   *         values, and any default values are applicable.
    */
   SortedSet<T> getActiveValues();
 
@@ -66,8 +64,9 @@
   /**
    * Get an immutable set view of this property's default values.
    *
-   * @return Returns an immutable set view of this property's default values. An
-   *         empty set indicates that there are no default values.
+   * @return Returns an immutable set view of this property's default
+   *         values. An empty set indicates that there are no default
+   *         values.
    */
   SortedSet<T> getDefaultValues();
 
@@ -76,7 +75,8 @@
   /**
    * Get an immutable set view of this property's effective values.
    *
-   * @return Returns an immutable set view of this property's effective values.
+   * @return Returns an immutable set view of this property's
+   *         effective values.
    */
   SortedSet<T> getEffectiveValues();
 
@@ -85,12 +85,12 @@
   /**
    * Get an immutable set view of this property's pending values.
    * <p>
-   * Immediately after construction, the pending values matches the active
-   * values.
+   * Immediately after construction, the pending values matches the
+   * active values.
    *
-   * @return Returns an immutable set view of this property's pending values. An
-   *         empty set indicates that there are no pending values, and any
-   *         default values are applicable.
+   * @return Returns an immutable set view of this property's pending
+   *         values. An empty set indicates that there are no pending
+   *         values, and any default values are applicable.
    */
   SortedSet<T> getPendingValues();
 
@@ -99,39 +99,42 @@
   /**
    * Get the property definition associated with this property.
    *
-   * @return Returns the property definition associated with this property.
+   * @return Returns the property definition associated with this
+   *         property.
    */
   PropertyDefinition<T> getPropertyDefinition();
 
 
 
   /**
-   * Determines whether or not this property contains any pending values.
+   * Determines whether or not this property contains any pending
+   * values.
    *
-   * @return Returns <code>true</code> if this property does not contain any
-   *         pending values.
+   * @return Returns <code>true</code> if this property does not
+   *         contain any pending values.
    */
   boolean isEmpty();
 
 
 
   /**
-   * Determines whether or not this property has been modified since it was
-   * constructed. In other words, whether or not the set of pending values
-   * differs from the set of active values.
+   * Determines whether or not this property has been modified since
+   * it was constructed. In other words, whether or not the set of
+   * pending values differs from the set of active values.
    *
-   * @return Returns <code>true</code> if this property has been modified
-   *         since it was constructed.
+   * @return Returns <code>true</code> if this property has been
+   *         modified since it was constructed.
    */
   boolean isModified();
 
 
 
   /**
-   * Determines whether or not this property contains any active values.
+   * Determines whether or not this property contains any active
+   * values.
    *
-   * @return Returns <code>true</code> if this property does not contain any
-   *         active values.
+   * @return Returns <code>true</code> if this property does not
+   *         contain any active values.
    */
   boolean wasEmpty();
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/PropertySet.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/PropertySet.java
similarity index 74%
rename from opendj-sdk/opends/src/server/org/opends/server/admin/client/PropertySet.java
rename to opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/PropertySet.java
index 5bddcbd..8b73974 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/PropertySet.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/PropertySet.java
@@ -25,7 +25,7 @@
  *      Portions Copyright 2007 Sun Microsystems, Inc.
  */
 
-package org.opends.server.admin.client;
+package org.opends.server.admin.client.spi;
 
 
 
@@ -33,7 +33,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -42,7 +41,6 @@
 import org.opends.server.admin.PropertyIsMandatoryException;
 import org.opends.server.admin.PropertyIsSingleValuedException;
 import org.opends.server.admin.PropertyOption;
-import org.opends.server.admin.PropertyProvider;
 
 
 
@@ -50,7 +48,7 @@
  * A set of properties. Instances of this class can be used as the
  * core of a managed object implementation.
  */
-public final class PropertySet implements PropertyProvider {
+public final class PropertySet {
 
   /**
    * Internal property implementation.
@@ -255,17 +253,6 @@
 
 
   /**
-   * Makes all pending values active.
-   */
-  public void commit() {
-    for (MyProperty<?> p : properties.values()) {
-      p.commit();
-    }
-  }
-
-
-
-  /**
    * Get the property associated with the specified property
    * definition.
    *
@@ -292,88 +279,34 @@
 
 
   /**
-   * Get the effective value of the specified property.
-   * <p>
-   * See the class description for more information about how the
-   * effective property value is derived.
-   *
-   * @param <T>
-   *          The type of the property to be retrieved.
-   * @param d
-   *          The property to be retrieved.
-   * @return Returns the property's effective value, or
-   *         <code>null</code> if there is no effective value
-   *         defined.
-   * @throws IllegalArgumentException
-   *           If the property definition is not associated with this
-   *           managed object's definition.
+   * {@inheritDoc}
    */
-  public <T> T getPropertyValue(PropertyDefinition<T> d)
-      throws IllegalArgumentException {
-    Set<T> values = getPropertyValues(d);
-    if (values.isEmpty()) {
-      return null;
-    } else {
-      return values.iterator().next();
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append('{');
+    for (Map.Entry<PropertyDefinition<?>, MyProperty<?>> entry : properties
+        .entrySet()) {
+      builder.append(entry.getKey().getName());
+      builder.append('=');
+      builder.append(entry.getValue().toString());
+      builder.append(' ');
     }
+    builder.append('}');
+    return builder.toString();
   }
 
 
 
-  /**
-   * Get the effective values of the specified property.
-   * <p>
-   * See the class description for more information about how the
-   * effective property values are derived.
-   *
-   * @param <T>
-   *          The type of the property to be retrieved.
-   * @param d
-   *          The property to be retrieved.
-   * @return Returns the property's effective values, or an empty set
-   *         if there are no effective values defined.
-   * @throws IllegalArgumentException
-   *           If the property definition is not associated with this
-   *           managed object's definition.
-   */
-  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
-      throws IllegalArgumentException {
-    Property<T> property = getProperty(d);
-    return new TreeSet<T>(property.getEffectiveValues());
-  }
 
 
 
   /**
-   * Set a new pending value for the specified property.
-   * <p>
-   * See the class description for more information regarding pending
-   * values.
-   *
-   * @param <T>
-   *          The type of the property to be modified.
-   * @param d
-   *          The property to be modified.
-   * @param value
-   *          The new pending value for the property, or
-   *          <code>null</code> if the property should be reset to
-   *          its default behavior.
-   * @throws IllegalPropertyValueException
-   *           If the new pending value is deemed to be invalid
-   *           according to the property definition.
-   * @throws PropertyIsMandatoryException
-   *           If an attempt was made to remove a mandatory property.
-   * @throws IllegalArgumentException
-   *           If the specified property definition is not associated
-   *           with this managed object.
+   * Makes all pending values active.
    */
-  public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
-      throws IllegalPropertyValueException, PropertyIsMandatoryException,
-      IllegalArgumentException {
-    if (value == null) {
-      setPropertyValues(d, Collections.<T> emptySet());
-    } else {
-      setPropertyValues(d, Collections.singleton(value));
+  void commit() {
+    for (MyProperty<?> p : properties.values()) {
+      p.commit();
     }
   }
 
@@ -406,7 +339,7 @@
    *           If the specified property definition is not associated
    *           with this managed object.
    */
-  public <T> void setPropertyValues(PropertyDefinition<T> d,
+  <T> void setPropertyValues(PropertyDefinition<T> d,
       Collection<T> values) throws IllegalPropertyValueException,
       PropertyIsSingleValuedException, PropertyIsMandatoryException,
       IllegalArgumentException {
@@ -435,24 +368,4 @@
     // Update the property.
     property.setPendingValues(values);
   }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public String toString() {
-    StringBuilder builder = new StringBuilder();
-    builder.append('{');
-    for (Map.Entry<PropertyDefinition<?>, MyProperty<?>> entry : properties
-        .entrySet()) {
-      builder.append(entry.getKey().getName());
-      builder.append('=');
-      builder.append(entry.getValue().toString());
-      builder.append(' ');
-    }
-    builder.append('}');
-    return builder.toString();
-  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/package-info.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/package-info.java
new file mode 100644
index 0000000..287ecb6
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+
+
+/**
+ * Client side driver implementation interfaces.
+ * <p>
+ * This package contains classes which client-side driver
+ * implementations are expected to use.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin.client.spi;
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/PropertySetTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/spi/PropertySetTest.java
similarity index 98%
rename from opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/PropertySetTest.java
rename to opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/spi/PropertySetTest.java
index 5633837..800af80 100755
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/PropertySetTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/spi/PropertySetTest.java
@@ -25,12 +25,15 @@
  *      Portions Copyright 2007 Sun Microsystems, Inc.
  */
 
-package org.opends.server.admin.client;
+package org.opends.server.admin.client.spi;
 
 import static org.testng.Assert.*;
 import org.testng.annotations.*;
 import org.opends.server.admin.*;
 import org.opends.server.admin.Configuration;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.spi.Property;
+import org.opends.server.admin.client.spi.PropertySet;
 import org.opends.server.admin.server.ServerManagedObject;
 import org.opends.server.admin.std.meta.RootCfgDefn;
 

--
Gitblit v1.10.0