From e538344449d345daa6e5ecb9b05ceba5427408e9 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Wed, 04 Dec 2013 14:13:59 +0000
Subject: [PATCH] OpenDJ 3 : config framework

---
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java | 2894 ++++++++++++++++++++++++++--------------------------------
 1 files changed, 1,307 insertions(+), 1,587 deletions(-)

diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java
index 431ac69..e31fb2c 100644
--- a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java
@@ -27,7 +27,8 @@
 
 package org.opends.server.admin.server;
 
-
+import static com.forgerock.opendj.ldap.AdminMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.*;
 
 import java.util.Collections;
 import java.util.LinkedList;
@@ -36,6 +37,7 @@
 import java.util.Set;
 import java.util.SortedSet;
 
+import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.opendj.ldap.DN;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.Constraint;
@@ -52,1641 +54,1359 @@
 import org.opends.server.api.ConfigChangeListener;
 import org.opends.server.api.ConfigDeleteListener;
 import org.opends.server.config.ConfigException;
-
-
+import org.opends.server.util.DynamicConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * A server-side managed object.
  *
  * @param <S>
- *          The type of server configuration represented by the server
- *          managed object.
+ *            The type of server configuration represented by the server managed
+ *            object.
  */
-public final class ServerManagedObject<S extends Configuration> implements
-    PropertyProvider {
+public final class ServerManagedObject<S extends Configuration> implements PropertyProvider {
 
-  /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = getTracer();
+    private static final Logger logger = LoggerFactory.getLogger(ServerManagedObject.class);
 
-  // The configuration entry associated with this server managed
-  // object (null if root).
-  private ConfigEntry configEntry;
+    // The configuration entry associated with this server managed
+    // object (null if root).
+    private ConfigEntry configEntry;
 
-  // The management context.
-  private final ServerManagementContext context = ServerManagementContext
-      .getInstance();
+    // The management context.
+    private final ServerManagementContext context = ServerManagementContext.getInstance();
 
-  // The managed object's definition.
-  private final ManagedObjectDefinition<?, S> definition;
+    // The managed object's definition.
+    private final ManagedObjectDefinition<?, S> definition;
 
-  // The managed object path identifying this managed object's
-  // location.
-  private final ManagedObjectPath<?, S> path;
+    // The managed object path identifying this managed object's
+    // location.
+    private final ManagedObjectPath<?, S> path;
 
-  // The managed object's properties.
-  private final Map<PropertyDefinition<?>, SortedSet<?>> properties;
+    // The managed object's properties.
+    private final Map<PropertyDefinition<?>, SortedSet<?>> properties;
 
-
-
-  /**
-   * Creates an new server side managed object.
-   *
-   * @param path
-   *          The managed object path.
-   * @param d
-   *          The managed object definition.
-   * @param properties
-   *          The managed object's properties.
-   * @param configEntry
-   *          The configuration entry associated with the managed
-   *          object.
-   */
-  ServerManagedObject(ManagedObjectPath<?, S> path,
-      ManagedObjectDefinition<?, S> d,
-      Map<PropertyDefinition<?>, SortedSet<?>> properties,
-      ConfigEntry configEntry) {
-    this.definition = d;
-    this.path = path;
-    this.properties = properties;
-    this.configEntry = configEntry;
-  }
-
-
-
-  /**
-   * Deregisters an existing configuration add listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The configuration add listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterAddListener(
-      InstantiableRelationDefinition<?, M> d,
-      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterAddListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing server managed object add listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The server managed object add listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterAddListener(
-      InstantiableRelationDefinition<?, M> d,
-      ServerManagedObjectAddListener<M> listener)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterAddListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing configuration add listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The configuration add listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterAddListener(
-      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d).getParent();
-    deregisterAddListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing server managed object add listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The server managed object add listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterAddListener(
-      OptionalRelationDefinition<?, M> d,
-      ServerManagedObjectAddListener<M> listener)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d).getParent();
-    deregisterAddListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing configuration add listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The configuration add listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterAddListener(
-      SetRelationDefinition<?, M> d,
-      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterAddListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing server managed object add listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The server managed object add listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterAddListener(
-      SetRelationDefinition<?, M> d,
-      ServerManagedObjectAddListener<M> listener)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterAddListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing configuration change listener.
-   *
-   * @param listener
-   *          The configuration change listener.
-   */
-  public void deregisterChangeListener(
-      ConfigurationChangeListener<? super S> listener) {
-    for (ConfigChangeListener l : configEntry.getChangeListeners()) {
-      if (l instanceof ConfigChangeListenerAdaptor) {
-        ConfigChangeListenerAdaptor<?> adaptor =
-          (ConfigChangeListenerAdaptor<?>) l;
-        ServerManagedObjectChangeListener<?> l2 = adaptor
-            .getServerManagedObjectChangeListener();
-        if (l2 instanceof ServerManagedObjectChangeListenerAdaptor<?>) {
-          ServerManagedObjectChangeListenerAdaptor<?> adaptor2 =
-            (ServerManagedObjectChangeListenerAdaptor<?>) l2;
-          if (adaptor2.getConfigurationChangeListener() == listener) {
-            adaptor.finalizeChangeListener();
-            configEntry.deregisterChangeListener(adaptor);
-          }
-        }
-      }
-    }
-  }
-
-
-
-  /**
-   * Deregisters an existing server managed object change listener.
-   *
-   * @param listener
-   *          The server managed object change listener.
-   */
-  public void deregisterChangeListener(
-      ServerManagedObjectChangeListener<? super S> listener) {
-    for (ConfigChangeListener l : configEntry.getChangeListeners()) {
-      if (l instanceof ConfigChangeListenerAdaptor) {
-        ConfigChangeListenerAdaptor<?> adaptor =
-          (ConfigChangeListenerAdaptor<?>) l;
-        if (adaptor.getServerManagedObjectChangeListener() == listener) {
-          adaptor.finalizeChangeListener();
-          configEntry.deregisterChangeListener(adaptor);
-        }
-      }
-    }
-  }
-
-
-
-  /**
-   * Deregisters an existing configuration delete listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The configuration delete listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterDeleteListener(
-      InstantiableRelationDefinition<?, M> d,
-      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterDeleteListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing server managed object delete listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The server managed object delete listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterDeleteListener(
-      InstantiableRelationDefinition<?, M> d,
-      ServerManagedObjectDeleteListener<M> listener)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterDeleteListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing configuration delete listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The configuration delete listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterDeleteListener(
-      OptionalRelationDefinition<?, M> d,
-      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d).getParent();
-    deregisterDeleteListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing server managed object delete listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The server managed object delete listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterDeleteListener(
-      OptionalRelationDefinition<?, M> d,
-      ServerManagedObjectDeleteListener<M> listener)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d).getParent();
-    deregisterDeleteListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing configuration delete listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The configuration delete listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterDeleteListener(
-      SetRelationDefinition<?, M> d,
-      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterDeleteListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Deregisters an existing server managed object delete listener.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The server managed object delete listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   */
-  public <M extends Configuration> void deregisterDeleteListener(
-      SetRelationDefinition<?, M> d,
-      ServerManagedObjectDeleteListener<M> listener)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-
-    DN baseDN = DNBuilder.create(path, d);
-    deregisterDeleteListener(baseDN, listener);
-  }
-
-
-
-  /**
-   * Retrieve an instantiable child managed object.
-   *
-   * @param <M>
-   *          The requested type of the child server managed object
-   *          configuration.
-   * @param d
-   *          The instantiable relation definition.
-   * @param name
-   *          The name of the child managed object.
-   * @return Returns the instantiable child managed object.
-   * @throws IllegalArgumentException
-   *           If the relation definition is not associated with this
-   *           managed object's definition.
-   * @throws ConfigException
-   *           If the child managed object could not be found or if it
-   *           could not be decoded.
-   */
-  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
-      InstantiableRelationDefinition<?, M> d, String name)
-      throws IllegalArgumentException, ConfigException {
-    validateRelationDefinition(d);
-    return context.getManagedObject(path.child(d, name));
-  }
-
-
-
-  /**
-   * Retrieve an optional child managed object.
-   *
-   * @param <M>
-   *          The requested type of the child server managed object
-   *          configuration.
-   * @param d
-   *          The optional relation definition.
-   * @return Returns the optional child managed object.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   * @throws ConfigException
-   *           If the child managed object could not be found or if it
-   *           could not be decoded.
-   */
-  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
-      OptionalRelationDefinition<?, M> d) throws IllegalArgumentException,
-      ConfigException {
-    validateRelationDefinition(d);
-    return context.getManagedObject(path.child(d));
-  }
-
-
-
-  /**
-   * Retrieve a set child managed object.
-   *
-   * @param <M>
-   *          The requested type of the child server managed object
-   *          configuration.
-   * @param d
-   *          The set relation definition.
-   * @param name
-   *          The name of the child managed object.
-   * @return Returns the set child managed object.
-   * @throws IllegalArgumentException
-   *           If the relation definition is not associated with this
-   *           managed object's definition or if {@code name} specifies
-   *           a managed object definition which is not a sub-type of
-   *           the relation's child definition.
-   * @throws ConfigException
-   *           If the child managed object could not be found or if it
-   *           could not be decoded.
-   */
-  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
-      SetRelationDefinition<?, M> d, String name)
-      throws IllegalArgumentException, ConfigException
-  {
-    validateRelationDefinition(d);
-
-    return context.getManagedObject(path.child(d, name));
-  }
-
-
-
-  /**
-   * Retrieve a singleton child managed object.
-   *
-   * @param <M>
-   *          The requested type of the child server managed object
-   *          configuration.
-   * @param d
-   *          The singleton relation definition.
-   * @return Returns the singleton child managed object.
-   * @throws IllegalArgumentException
-   *           If the relation definition is not associated with this
-   *           managed object's definition.
-   * @throws ConfigException
-   *           If the child managed object could not be found or if it
-   *           could not be decoded.
-   */
-  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
-      SingletonRelationDefinition<?, M> d) throws IllegalArgumentException,
-      ConfigException {
-    validateRelationDefinition(d);
-    return context.getManagedObject(path.child(d));
-  }
-
-
-
-  /**
-   * Creates a server configuration view of this managed object.
-   *
-   * @return Returns the server configuration view of this managed
-   *         object.
-   */
-  public S getConfiguration() {
-    return definition.createServerConfiguration(this);
-  }
-
-
-
-  /**
-   * Get the DN of the LDAP entry associated with this server managed
-   * object.
-   *
-   * @return Returns the DN of the LDAP entry associated with this
-   *         server managed object, or an null DN if this is the root
-   *         managed object.
-   */
-  public DN getDN() {
-    if (configEntry != null) {
-      return configEntry.getDN();
-    } else {
-      return DN.nullDN();
-    }
-  }
-
-
-
-  /**
-   * Get the definition associated with this server managed object.
-   *
-   * @return Returns the definition associated with this server
-   *         managed object.
-   */
-  public ManagedObjectDefinition<?, S> getManagedObjectDefinition() {
-    return definition;
-  }
-
-
-
-  /**
-   * Get the path of this server managed object.
-   *
-   * @return Returns the path of this server managed object.
-   */
-  public ManagedObjectPath<?, S> getManagedObjectPath() {
-    return path;
-  }
-
-
-
-  /**
-   * Get the effective value of the specified property. If the
-   * property is multi-valued then just the first value is returned.
-   * If the property does not have a value then its default value is
-   * returned if it has one, or <code>null</code> indicating that
-   * any default behavior is applicable.
-   *
-   * @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> indicating that any default behavior
-   *         is applicable.
-   * @throws IllegalArgumentException
-   *           If the property definition is not associated with this
-   *           managed object's definition.
-   */
-  public <T> T getPropertyValue(PropertyDefinition<T> d)
-      throws IllegalArgumentException {
-    Set<T> values = getPropertyValues(d);
-    if (values.isEmpty()) {
-      return null;
-    } else {
-      return values.iterator().next();
-    }
-  }
-
-
-
-  /**
-   * Get the effective values of the specified property. If the
-   * property does not have any values then its default values are
-   * returned if it has any, or an empty set indicating that any
-   * default behavior is applicable.
-   *
-   * @param <T>
-   *          The type of the property to be retrieved.
-   * @param d
-   *          The property to be retrieved.
-   * @return Returns an unmodifiable set containing the property's
-   *         effective values. An empty set indicates that the
-   *         property has no default values defined and any default
-   *         behavior is applicable.
-   * @throws IllegalArgumentException
-   *           If the property definition is not associated with this
-   *           managed object's definition.
-   */
-  @SuppressWarnings("unchecked")
-  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
-      throws IllegalArgumentException {
-    if (!properties.containsKey(d)) {
-      throw new IllegalArgumentException("Unknown property " + d.getName());
-    }
-    return Collections.unmodifiableSortedSet((SortedSet<T>) properties.get(d));
-  }
-
-
-
-  /**
-   * Determines whether or not the optional managed object associated
-   * with the specified optional relations exists.
-   *
-   * @param d
-   *          The optional relation definition.
-   * @return Returns <code>true</code> if the optional managed
-   *         object exists, <code>false</code> otherwise.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   */
-  public boolean hasChild(OptionalRelationDefinition<?, ?> d)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-    return context.managedObjectExists(path.child(d));
-  }
-
-
-
-  /**
-   * Lists the child managed objects associated with the specified
-   * instantiable relation.
-   *
-   * @param d
-   *          The instantiable relation definition.
-   * @return Returns the names of the child managed objects.
-   * @throws IllegalArgumentException
-   *           If the relation definition is not associated with this
-   *           managed object's definition.
-   */
-  public String[] listChildren(InstantiableRelationDefinition<?, ?> d)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-    return context.listManagedObjects(path, d);
-  }
-
-
-
-  /**
-   * Lists the child managed objects associated with the specified
-   * set relation.
-   *
-   * @param d
-   *          The set relation definition.
-   * @return Returns the names of the child managed objects.
-   * @throws IllegalArgumentException
-   *           If the relation definition is not associated with this
-   *           managed object's definition.
-   */
-  public String[] listChildren(SetRelationDefinition<?, ?> d)
-      throws IllegalArgumentException {
-    validateRelationDefinition(d);
-    return context.listManagedObjects(path, d);
-  }
-
-
-
-  /**
-   * Register to be notified when new child configurations are added
-   * beneath an instantiable relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The configuration add listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           instantiable relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerAddListener(
-      InstantiableRelationDefinition<?, M> d,
-      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
-      ConfigException {
-    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
-        listener));
-  }
-
-
-
-  /**
-   * Register to be notified when new child server managed object are
-   * added beneath an instantiable relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The server managed object add listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           instantiable relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerAddListener(
-      InstantiableRelationDefinition<?, M> d,
-      ServerManagedObjectAddListener<M> listener)
-      throws IllegalArgumentException, ConfigException {
-    validateRelationDefinition(d);
-    DN baseDN = DNBuilder.create(path, d);
-    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
-        listener);
-    registerAddListener(baseDN, adaptor);
-  }
-
-
-
-  /**
-   * Register to be notified when a new child configurations is added
-   * beneath an optional relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The configuration add listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the optional
-   *           relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerAddListener(
-      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
-      throws IllegalArgumentException, ConfigException {
-    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
-        listener));
-  }
-
-
-
-  /**
-   * Register to be notified when a new child server managed object is
-   * added beneath an optional relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The server managed object add listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the optional
-   *           relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerAddListener(
-      OptionalRelationDefinition<?, M> d,
-      ServerManagedObjectAddListener<M> listener)
-      throws IllegalArgumentException, ConfigException {
-    validateRelationDefinition(d);
-    DN baseDN = DNBuilder.create(path, d).getParent();
-    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
-        listener);
-    registerAddListener(baseDN, adaptor);
-  }
-
-
-
-  /**
-   * Register to be notified when new child configurations are added
-   * beneath a set relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The configuration add listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           set relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerAddListener(
-      SetRelationDefinition<?, M> d,
-      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
-      ConfigException {
-    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
-        listener));
-  }
-
-
-
-  /**
-   * Register to be notified when new child server managed object are
-   * added beneath a set relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The server managed object add listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           set relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerAddListener(
-      SetRelationDefinition<?, M> d,
-      ServerManagedObjectAddListener<M> listener)
-      throws IllegalArgumentException, ConfigException {
-    validateRelationDefinition(d);
-    DN baseDN = DNBuilder.create(path, d);
-    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
-        listener);
-    registerAddListener(baseDN, adaptor);
-  }
-
-
-
-  /**
-   * Register to be notified when this server managed object is
-   * changed.
-   *
-   * @param listener
-   *          The configuration change listener.
-   */
-  public void registerChangeListener(
-      ConfigurationChangeListener<? super S> listener) {
-    registerChangeListener(new ServerManagedObjectChangeListenerAdaptor<S>(
-        listener));
-  }
-
-
-
-  /**
-   * Register to be notified when this server managed object is
-   * changed.
-   *
-   * @param listener
-   *          The server managed object change listener.
-   */
-  public void registerChangeListener(
-      ServerManagedObjectChangeListener<? super S> listener) {
-    ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path,
-        listener);
-    configEntry.registerChangeListener(adaptor);
-
-    // Change listener registration usually signifies that a managed
-    // object has been accepted and added to the server configuration
-    // during initialization post-add.
-
-    // FIXME: we should prevent multiple invocations in the case where
-    // multiple change listeners are registered for the same object.
-    for (Constraint constraint : definition.getAllConstraints()) {
-      for (ServerConstraintHandler handler : constraint
-          .getServerConstraintHandlers()) {
-        try {
-          handler.performPostAdd(this);
-        } catch (ConfigException e) {
-          if (debugEnabled()) {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
-          }
-        }
-      }
-    }
-  }
-
-
-
-  /**
-   * Register to be notified when existing child configurations are
-   * deleted beneath an instantiable relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The configuration delete listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           instantiable relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerDeleteListener(
-      InstantiableRelationDefinition<?, M> d,
-      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
-      ConfigException {
-    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
-        listener));
-  }
-
-
-
-  /**
-   * Register to be notified when existing child server managed
-   * objects are deleted beneath an instantiable relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The instantiable relation definition.
-   * @param listener
-   *          The server managed objects delete listener.
-   * @throws IllegalArgumentException
-   *           If the instantiable relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           instantiable relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerDeleteListener(
-      InstantiableRelationDefinition<?, M> d,
-      ServerManagedObjectDeleteListener<M> listener)
-      throws IllegalArgumentException, ConfigException {
-    validateRelationDefinition(d);
-    DN baseDN = DNBuilder.create(path, d);
-    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
-        listener);
-    registerDeleteListener(baseDN, adaptor);
-  }
-
-
-
-  /**
-   * Register to be notified when an existing child configuration is
-   * deleted beneath an optional relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The configuration delete listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the optional
-   *           relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerDeleteListener(
-      OptionalRelationDefinition<?, M> d,
-      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
-      ConfigException {
-    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
-        listener));
-  }
-
-
-
-  /**
-   * Register to be notified when an existing child server managed
-   * object is deleted beneath an optional relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The optional relation definition.
-   * @param listener
-   *          The server managed object delete listener.
-   * @throws IllegalArgumentException
-   *           If the optional relation definition is not associated
-   *           with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the optional
-   *           relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerDeleteListener(
-      OptionalRelationDefinition<?, M> d,
-      ServerManagedObjectDeleteListener<M> listener)
-      throws IllegalArgumentException, ConfigException {
-    validateRelationDefinition(d);
-    DN baseDN = DNBuilder.create(path, d).getParent();
-    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
-        listener);
-    registerDeleteListener(baseDN, adaptor);
-  }
-
-
-
-  /**
-   * Register to be notified when existing child configurations are
-   * deleted beneath a set relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The configuration delete listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           set relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerDeleteListener(
-      SetRelationDefinition<?, M> d,
-      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
-      ConfigException {
-    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
-        listener));
-  }
-
-
-
-  /**
-   * Register to be notified when existing child server managed
-   * objects are deleted beneath a set relation.
-   *
-   * @param <M>
-   *          The type of the child server configuration object.
-   * @param d
-   *          The set relation definition.
-   * @param listener
-   *          The server managed objects delete listener.
-   * @throws IllegalArgumentException
-   *           If the set relation definition is not
-   *           associated with this managed object's definition.
-   * @throws ConfigException
-   *           If the configuration entry associated with the
-   *           set relation could not be retrieved.
-   */
-  public <M extends Configuration> void registerDeleteListener(
-      SetRelationDefinition<?, M> d,
-      ServerManagedObjectDeleteListener<M> listener)
-      throws IllegalArgumentException, ConfigException {
-    validateRelationDefinition(d);
-    DN baseDN = DNBuilder.create(path, d);
-    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
-        listener);
-    registerDeleteListener(baseDN, adaptor);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public String toString() {
-    StringBuilder builder = new StringBuilder();
-
-    builder.append("{ TYPE=");
-    builder.append(definition.getName());
-    builder.append(", DN=\"");
-    builder.append(getDN());
-    builder.append('\"');
-    for (Map.Entry<PropertyDefinition<?>, SortedSet<?>> value : properties
-        .entrySet()) {
-      builder.append(", ");
-      builder.append(value.getKey().getName());
-      builder.append('=');
-      builder.append(value.getValue());
-    }
-    builder.append(" }");
-
-    return builder.toString();
-  }
-
-
-
-  /**
-   * Determines whether or not this managed object can be used by the
-   * server.
-   *
-   * @throws ConstraintViolationException
-   *           If one or more constraints determined that this managed
-   *           object cannot be used by the server.
-   */
-  void ensureIsUsable() throws ConstraintViolationException {
-    // Enforce any constraints.
-    boolean isUsable = true;
-    List<LocalizableMessage> reasons = new LinkedList<Message>();
-    for (Constraint constraint : definition.getAllConstraints()) {
-      for (ServerConstraintHandler handler : constraint
-          .getServerConstraintHandlers()) {
-        try {
-          if (!handler.isUsable(this, reasons)) {
-            isUsable = false;
-          }
-        } catch (ConfigException e) {
-          LocalizableMessage message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
-              .getMessageObject());
-          reasons.add(message);
-          isUsable = false;
-        }
-      }
+    /**
+     * Creates an new server side managed object.
+     *
+     * @param path
+     *            The managed object path.
+     * @param d
+     *            The managed object definition.
+     * @param properties
+     *            The managed object's properties.
+     * @param configEntry
+     *            The configuration entry associated with the managed object.
+     */
+    ServerManagedObject(ManagedObjectPath<?, S> path, ManagedObjectDefinition<?, S> d,
+            Map<PropertyDefinition<?>, SortedSet<?>> properties, ConfigEntry configEntry) {
+        this.definition = d;
+        this.path = path;
+        this.properties = properties;
+        this.configEntry = configEntry;
     }
 
-    if (!isUsable) {
-      throw new ConstraintViolationException(this, reasons);
-    }
-  }
+    /**
+     * Deregisters an existing configuration add listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The configuration add listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterAddListener(InstantiableRelationDefinition<?, M> d,
+            ConfigurationAddListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
 
-
-
-  /**
-   * Update the config entry associated with this server managed
-   * object. This is only intended to be used by change listener call
-   * backs in order to update the managed object with the correct
-   * config entry.
-   *
-   * @param configEntry
-   *          The configuration entry.
-   */
-  void setConfigEntry(ConfigEntry configEntry) {
-    this.configEntry = configEntry;
-  }
-
-
-
-  // Deregister an add listener.
-  private <M extends Configuration> void deregisterAddListener(DN baseDN,
-      ConfigurationAddListener<M> listener) {
-    try {
-      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
-      if (configEntry != null) {
-        for (ConfigAddListener l : configEntry.getAddListeners()) {
-          if (l instanceof ConfigAddListenerAdaptor) {
-            ConfigAddListenerAdaptor<?> adaptor =
-              (ConfigAddListenerAdaptor<?>) l;
-            ServerManagedObjectAddListener<?> l2 = adaptor
-                .getServerManagedObjectAddListener();
-            if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
-              ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
-                (ServerManagedObjectAddListenerAdaptor<?>) l2;
-              if (adaptor2.getConfigurationAddListener() == listener) {
-                configEntry.deregisterAddListener(adaptor);
-              }
-            }
-          }
-        }
-      }
-      else
-      {
-        // The relation entry does not exist so check for and deregister
-        // delayed add listener.
-        deregisterDelayedAddListener(baseDN, listener);
-      }
-    } catch (ConfigException e) {
-      // Ignore the exception since this implies deregistration.
-      if (debugEnabled()) {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
-  }
-
-
-
-  // Deregister an add listener.
-  private <M extends Configuration> void deregisterAddListener(DN baseDN,
-      ServerManagedObjectAddListener<M> listener) {
-    try {
-      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
-      if (configEntry != null) {
-        for (ConfigAddListener l : configEntry.getAddListeners()) {
-          if (l instanceof ConfigAddListenerAdaptor) {
-            ConfigAddListenerAdaptor<?> adaptor =
-              (ConfigAddListenerAdaptor<?>) l;
-            if (adaptor.getServerManagedObjectAddListener() == listener) {
-              configEntry.deregisterAddListener(adaptor);
-            }
-          }
-        }
-      }
-      else
-      {
-        // The relation entry does not exist so check for and deregister
-        // delayed add listener.
-        deregisterDelayedAddListener(baseDN, listener);
-      }
-    } catch (ConfigException e) {
-      // Ignore the exception since this implies deregistration.
-      if (debugEnabled()) {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
-  }
-
-
-
-  // Deregister a delete listener.
-  private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
-      ConfigurationDeleteListener<M> listener) {
-    try {
-      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
-      if (configEntry != null) {
-        for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
-          if (l instanceof ConfigDeleteListenerAdaptor) {
-            ConfigDeleteListenerAdaptor<?> adaptor =
-              (ConfigDeleteListenerAdaptor<?>) l;
-            ServerManagedObjectDeleteListener<?> l2 = adaptor
-                .getServerManagedObjectDeleteListener();
-            if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
-              ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
-                (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
-              if (adaptor2.getConfigurationDeleteListener() == listener) {
-                configEntry.deregisterDeleteListener(adaptor);
-              }
-            }
-          }
-        }
-      }
-      else
-      {
-        // The relation entry does not exist so check for and deregister
-        // delayed add listener.
-        deregisterDelayedDeleteListener(baseDN, listener);
-      }
-    } catch (ConfigException e) {
-      // Ignore the exception since this implies deregistration.
-      if (debugEnabled()) {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
-  }
-
-
-
-  // Deregister a delete listener.
-  private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
-      ServerManagedObjectDeleteListener<M> listener) {
-    try {
-      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
-      if (configEntry != null) {
-        for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
-          if (l instanceof ConfigDeleteListenerAdaptor) {
-            ConfigDeleteListenerAdaptor<?> adaptor =
-              (ConfigDeleteListenerAdaptor<?>) l;
-            if (adaptor.getServerManagedObjectDeleteListener() == listener) {
-              configEntry.deregisterDeleteListener(adaptor);
-            }
-          }
-        }
-      }
-      else
-      {
-        // The relation entry does not exist so check for and deregister
-        // delayed add listener.
-        deregisterDelayedDeleteListener(baseDN, listener);
-      }
-    } catch (ConfigException e) {
-      // Ignore the exception since this implies deregistration.
-      if (debugEnabled()) {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
-  }
-
-
-
-  // Gets a config entry required for a listener and throws a config
-  // exception on failure or returns null if the entry does not exist.
-  private ConfigEntry getListenerConfigEntry(DN dn) throws ConfigException {
-    // Attempt to retrieve the listener base entry.
-    ConfigEntry configEntry;
-    try {
-      configEntry = DirectoryServer.getConfigEntry(dn);
-    } catch (ConfigException e) {
-      if (debugEnabled()) {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      LocalizableMessage message = ERR_ADMIN_CANNOT_GET_LISTENER_BASE.get(
-          String.valueOf(dn), stackTraceToSingleLineString(e));
-      throw new ConfigException(message, e);
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterAddListener(baseDN, listener);
     }
 
-    return configEntry;
-  }
+    /**
+     * Deregisters an existing server managed object add listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The server managed object add listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterAddListener(InstantiableRelationDefinition<?, M> d,
+            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
 
-
-
-  // Register an instantiable or optional relation add listener.
-  private void registerAddListener(DN baseDN, ConfigAddListener adaptor)
-      throws IllegalArgumentException, ConfigException {
-    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
-
-    if (relationEntry != null) {
-      relationEntry.registerAddListener(adaptor);
-    } else {
-      // The relation entry does not exist yet so register a delayed
-      // add listener.
-      ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
-          adaptor);
-      registerDelayedListener(baseDN, delayedListener);
-    }
-  }
-
-
-
-  // Register a delayed listener with the nearest existing parent
-  // entry to the provided base DN.
-  private void registerDelayedListener(DN baseDN,
-      ConfigAddListener delayedListener) throws ConfigException {
-    DN parentDN = baseDN.getParent();
-    while (parentDN != null) {
-      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
-      if (relationEntry == null) {
-        delayedListener = new DelayedConfigAddListener(parentDN,
-            delayedListener);
-        parentDN = parentDN.getParent();
-      } else {
-        relationEntry.registerAddListener(delayedListener);
-        return;
-      }
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterAddListener(baseDN, listener);
     }
 
-    // No parent entry could be found.
-    LocalizableMessage message = ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER
-        .get(String.valueOf(baseDN));
-    throw new ConfigException(message);
-  }
+    /**
+     * Deregisters an existing configuration add listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The configuration add listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterAddListener(OptionalRelationDefinition<?, M> d,
+            ConfigurationAddListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
 
-  // Deregister a delayed listener with the nearest existing parent
-  // entry to the provided base DN.
-  private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
-      ConfigurationAddListener<M> listener) throws ConfigException {
-    DN parentDN = baseDN.getParent();
-    int delayWrappers = 0;
-    while (parentDN != null) {
-      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
-      if (relationEntry == null) {
-        parentDN = parentDN.getParent();
-        delayWrappers++;
-      } else {
-        for (ConfigAddListener l : relationEntry.getAddListeners()) {
-          if(l instanceof DelayedConfigAddListener)
-          {
-            DelayedConfigAddListener delayListener =
-                (DelayedConfigAddListener) l;
-            ConfigAddListener wrappedListener;
+        DN baseDN = DNBuilder.create(path, d).parent();
+        deregisterAddListener(baseDN, listener);
+    }
 
-            int i = delayWrappers;
-            for(; i > 0; i--)
-            {
-              wrappedListener = delayListener.getDelayedAddListener();
-              if(wrappedListener != null &&
-                  wrappedListener instanceof DelayedConfigAddListener)
-              {
-                delayListener = (DelayedConfigAddListener) l;
-              }
-              else
-              {
-                break;
-              }
-            }
+    /**
+     * Deregisters an existing server managed object add listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The server managed object add listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterAddListener(OptionalRelationDefinition<?, M> d,
+            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
 
-            if(i > 0)
-            {
-              // There are not enough level of wrapping so this can't be
-              // the listener we are looking for.
-              continue;
-            }
+        DN baseDN = DNBuilder.create(path, d).parent();
+        deregisterAddListener(baseDN, listener);
+    }
 
-            ConfigAddListener delayedListener =
-                delayListener.getDelayedAddListener();
+    /**
+     * Deregisters an existing configuration add listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The configuration add listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     */
+    public <M extends Configuration> void deregisterAddListener(SetRelationDefinition<?, M> d,
+            ConfigurationAddListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
 
-            if (delayedListener != null &&
-                 delayedListener instanceof ConfigAddListenerAdaptor) {
-              ConfigAddListenerAdaptor<?> adaptor =
-                  (ConfigAddListenerAdaptor<?>) delayedListener;
-              ServerManagedObjectAddListener<?> l2 = adaptor
-                  .getServerManagedObjectAddListener();
-              if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
-                ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
-                    (ServerManagedObjectAddListenerAdaptor<?>) l2;
-                if (adaptor2.getConfigurationAddListener() == listener) {
-                  relationEntry.deregisterAddListener(l);
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterAddListener(baseDN, listener);
+    }
+
+    /**
+     * Deregisters an existing server managed object add listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The server managed object add listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     */
+    public <M extends Configuration> void deregisterAddListener(SetRelationDefinition<?, M> d,
+            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterAddListener(baseDN, listener);
+    }
+
+    /**
+     * Deregisters an existing configuration change listener.
+     *
+     * @param listener
+     *            The configuration change listener.
+     */
+    public void deregisterChangeListener(ConfigurationChangeListener<? super S> listener) {
+        for (ConfigChangeListener l : configEntry.getChangeListeners()) {
+            if (l instanceof ConfigChangeListenerAdaptor) {
+                ConfigChangeListenerAdaptor<?> adaptor = (ConfigChangeListenerAdaptor<?>) l;
+                ServerManagedObjectChangeListener<?> l2 = adaptor.getServerManagedObjectChangeListener();
+                if (l2 instanceof ServerManagedObjectChangeListenerAdaptor<?>) {
+                    ServerManagedObjectChangeListenerAdaptor<?> adaptor2 =
+                            (ServerManagedObjectChangeListenerAdaptor<?>) l2;
+                    if (adaptor2.getConfigurationChangeListener() == listener) {
+                        adaptor.finalizeChangeListener();
+                        configEntry.deregisterChangeListener(adaptor);
+                    }
                 }
-              }
             }
-          }
         }
-        return;
-      }
     }
-  }
 
-
-  // Deregister a delayed listener with the nearest existing parent
-  // entry to the provided base DN.
-  private <M extends Configuration> void deregisterDelayedDeleteListener(
-      DN baseDN, ConfigurationDeleteListener<M> listener)
-      throws ConfigException {
-    DN parentDN = baseDN.getParent();
-    int delayWrappers = 0;
-    while (parentDN != null) {
-      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
-      if (relationEntry == null) {
-        parentDN = parentDN.getParent();
-        delayWrappers++;
-      } else {
-        for (ConfigAddListener l : relationEntry.getAddListeners()) {
-          if(l instanceof DelayedConfigAddListener)
-          {
-            DelayedConfigAddListener delayListener =
-                (DelayedConfigAddListener) l;
-            ConfigAddListener wrappedListener;
-
-            int i = delayWrappers;
-            for(; i > 0; i--)
-            {
-              wrappedListener = delayListener.getDelayedAddListener();
-              if(wrappedListener != null &&
-                  wrappedListener instanceof DelayedConfigAddListener)
-              {
-                delayListener = (DelayedConfigAddListener) l;
-              }
-              else
-              {
-                break;
-              }
-            }
-
-            if(i > 0)
-            {
-              // There are not enough level of wrapping so this can't be
-              // the listener we are looking for.
-              continue;
-            }
-
-            ConfigDeleteListener delayedListener =
-                delayListener.getDelayedDeleteListener();
-
-            if (delayedListener != null &&
-                delayedListener instanceof ConfigDeleteListenerAdaptor) {
-              ConfigDeleteListenerAdaptor<?> adaptor =
-                  (ConfigDeleteListenerAdaptor<?>) delayedListener;
-              ServerManagedObjectDeleteListener<?> l2 = adaptor
-                  .getServerManagedObjectDeleteListener();
-              if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
-                ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
-                    (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
-                if (adaptor2.getConfigurationDeleteListener() == listener) {
-                  relationEntry.deregisterAddListener(l);
+    /**
+     * Deregisters an existing server managed object change listener.
+     *
+     * @param listener
+     *            The server managed object change listener.
+     */
+    public void deregisterChangeListener(ServerManagedObjectChangeListener<? super S> listener) {
+        for (ConfigChangeListener l : configEntry.getChangeListeners()) {
+            if (l instanceof ConfigChangeListenerAdaptor) {
+                ConfigChangeListenerAdaptor<?> adaptor = (ConfigChangeListenerAdaptor<?>) l;
+                if (adaptor.getServerManagedObjectChangeListener() == listener) {
+                    adaptor.finalizeChangeListener();
+                    configEntry.deregisterChangeListener(adaptor);
                 }
-              }
             }
-          }
         }
-        return;
-      }
     }
-  }
 
-  // Deregister a delayed listener with the nearest existing parent
-  // entry to the provided base DN.
-  private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
-      ServerManagedObjectAddListener<M> listener) throws ConfigException {
-    DN parentDN = baseDN.getParent();
-    int delayWrappers = 0;
-    while (parentDN != null) {
-      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
-      if (relationEntry == null) {
-        parentDN = parentDN.getParent();
-        delayWrappers++;
-      } else {
-        for (ConfigAddListener l : relationEntry.getAddListeners()) {
-          if(l instanceof DelayedConfigAddListener)
-          {
-            DelayedConfigAddListener delayListener =
-                (DelayedConfigAddListener) l;
-            ConfigAddListener wrappedListener;
+    /**
+     * Deregisters an existing configuration delete listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The configuration delete listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterDeleteListener(InstantiableRelationDefinition<?, M> d,
+            ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
 
-            int i = delayWrappers;
-            for(; i > 0; i--)
-            {
-              wrappedListener = delayListener.getDelayedAddListener();
-              if(wrappedListener != null &&
-                  wrappedListener instanceof DelayedConfigAddListener)
-              {
-                delayListener = (DelayedConfigAddListener) l;
-              }
-              else
-              {
-                break;
-              }
-            }
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterDeleteListener(baseDN, listener);
+    }
 
-            if(i > 0)
-            {
-              // There are not enough level of wrapping so this can't be
-              // the listener we are looking for.
-              continue;
-            }
+    /**
+     * Deregisters an existing server managed object delete listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The server managed object delete listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterDeleteListener(InstantiableRelationDefinition<?, M> d,
+            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
 
-            ConfigAddListener delayedListener =
-                delayListener.getDelayedAddListener();
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterDeleteListener(baseDN, listener);
+    }
 
-            if (delayedListener != null &&
-                 delayedListener instanceof ConfigAddListenerAdaptor) {
-              ConfigAddListenerAdaptor<?> adaptor =
-                  (ConfigAddListenerAdaptor<?>) delayedListener;
-              if (adaptor.getServerManagedObjectAddListener() == listener) {
-                relationEntry.deregisterAddListener(l);
-              }
-            }
-          }
+    /**
+     * Deregisters an existing configuration delete listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The configuration delete listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterDeleteListener(OptionalRelationDefinition<?, M> d,
+            ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+
+        DN baseDN = DNBuilder.create(path, d).parent();
+        deregisterDeleteListener(baseDN, listener);
+    }
+
+    /**
+     * Deregisters an existing server managed object delete listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The server managed object delete listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     */
+    public <M extends Configuration> void deregisterDeleteListener(OptionalRelationDefinition<?, M> d,
+            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+
+        DN baseDN = DNBuilder.create(path, d).parent();
+        deregisterDeleteListener(baseDN, listener);
+    }
+
+    /**
+     * Deregisters an existing configuration delete listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The configuration delete listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     */
+    public <M extends Configuration> void deregisterDeleteListener(SetRelationDefinition<?, M> d,
+            ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterDeleteListener(baseDN, listener);
+    }
+
+    /**
+     * Deregisters an existing server managed object delete listener.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The server managed object delete listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     */
+    public <M extends Configuration> void deregisterDeleteListener(SetRelationDefinition<?, M> d,
+            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+
+        DN baseDN = DNBuilder.create(path, d);
+        deregisterDeleteListener(baseDN, listener);
+    }
+
+    /**
+     * Retrieve an instantiable child managed object.
+     *
+     * @param <M>
+     *            The requested type of the child server managed object
+     *            configuration.
+     * @param d
+     *            The instantiable relation definition.
+     * @param name
+     *            The name of the child managed object.
+     * @return Returns the instantiable child managed object.
+     * @throws IllegalArgumentException
+     *             If the relation definition is not associated with this
+     *             managed object's definition.
+     * @throws ConfigException
+     *             If the child managed object could not be found or if it could
+     *             not be decoded.
+     */
+    public <M extends Configuration> ServerManagedObject<? extends M> getChild(InstantiableRelationDefinition<?, M> d,
+            String name) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        return context.getManagedObject(path.child(d, name));
+    }
+
+    /**
+     * Retrieve an optional child managed object.
+     *
+     * @param <M>
+     *            The requested type of the child server managed object
+     *            configuration.
+     * @param d
+     *            The optional relation definition.
+     * @return Returns the optional child managed object.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     * @throws ConfigException
+     *             If the child managed object could not be found or if it could
+     *             not be decoded.
+     */
+    public <M extends Configuration> ServerManagedObject<? extends M> getChild(OptionalRelationDefinition<?, M> d)
+            throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        return context.getManagedObject(path.child(d));
+    }
+
+    /**
+     * Retrieve a set child managed object.
+     *
+     * @param <M>
+     *            The requested type of the child server managed object
+     *            configuration.
+     * @param d
+     *            The set relation definition.
+     * @param name
+     *            The name of the child managed object.
+     * @return Returns the set child managed object.
+     * @throws IllegalArgumentException
+     *             If the relation definition is not associated with this
+     *             managed object's definition or if {@code name} specifies a
+     *             managed object definition which is not a sub-type of the
+     *             relation's child definition.
+     * @throws ConfigException
+     *             If the child managed object could not be found or if it could
+     *             not be decoded.
+     */
+    public <M extends Configuration> ServerManagedObject<? extends M> getChild(SetRelationDefinition<?, M> d,
+            String name) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+
+        return context.getManagedObject(path.child(d, name));
+    }
+
+    /**
+     * Retrieve a singleton child managed object.
+     *
+     * @param <M>
+     *            The requested type of the child server managed object
+     *            configuration.
+     * @param d
+     *            The singleton relation definition.
+     * @return Returns the singleton child managed object.
+     * @throws IllegalArgumentException
+     *             If the relation definition is not associated with this
+     *             managed object's definition.
+     * @throws ConfigException
+     *             If the child managed object could not be found or if it could
+     *             not be decoded.
+     */
+    public <M extends Configuration> ServerManagedObject<? extends M> getChild(SingletonRelationDefinition<?, M> d)
+            throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        return context.getManagedObject(path.child(d));
+    }
+
+    /**
+     * Creates a server configuration view of this managed object.
+     *
+     * @return Returns the server configuration view of this managed object.
+     */
+    public S getConfiguration() {
+        return definition.createServerConfiguration(this);
+    }
+
+    /**
+     * Get the DN of the LDAP entry associated with this server managed object.
+     *
+     * @return Returns the DN of the LDAP entry associated with this server
+     *         managed object, or an null DN if this is the root managed object.
+     */
+    public DN getDN() {
+        if (configEntry != null) {
+            return configEntry.getDN();
+        } else {
+            return DN.rootDN();
         }
-        return;
-      }
     }
-  }
 
+    /**
+     * Get the definition associated with this server managed object.
+     *
+     * @return Returns the definition associated with this server managed
+     *         object.
+     */
+    public ManagedObjectDefinition<?, S> getManagedObjectDefinition() {
+        return definition;
+    }
 
-  // Deregister a delayed listener with the nearest existing parent
-  // entry to the provided base DN.
-  private <M extends Configuration> void deregisterDelayedDeleteListener(
-      DN baseDN, ServerManagedObjectDeleteListener<M> listener)
-      throws ConfigException {
-    DN parentDN = baseDN.getParent();
-    int delayWrappers = 0;
-    while (parentDN != null) {
-      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
-      if (relationEntry == null) {
-        parentDN = parentDN.getParent();
-        delayWrappers++;
-      } else {
-        for (ConfigAddListener l : relationEntry.getAddListeners()) {
-          if(l instanceof DelayedConfigAddListener)
-          {
-            DelayedConfigAddListener delayListener =
-                (DelayedConfigAddListener) l;
-            ConfigAddListener wrappedListener;
+    /**
+     * Get the path of this server managed object.
+     *
+     * @return Returns the path of this server managed object.
+     */
+    public ManagedObjectPath<?, S> getManagedObjectPath() {
+        return path;
+    }
 
-            int i = delayWrappers;
-            for(; i > 0; i--)
-            {
-              wrappedListener = delayListener.getDelayedAddListener();
-              if(wrappedListener != null &&
-                  wrappedListener instanceof DelayedConfigAddListener)
-              {
-                delayListener = (DelayedConfigAddListener) l;
-              }
-              else
-              {
-                break;
-              }
-            }
-
-            if(i > 0)
-            {
-              // There are not enough level of wrapping so this can't be
-              // the listener we are looking for.
-              continue;
-            }
-
-            ConfigDeleteListener delayedListener =
-                delayListener.getDelayedDeleteListener();
-
-            if (delayedListener != null &&
-                 delayedListener instanceof ConfigDeleteListenerAdaptor) {
-              ConfigDeleteListenerAdaptor<?> adaptor =
-                  (ConfigDeleteListenerAdaptor<?>) delayedListener;
-              if (adaptor.getServerManagedObjectDeleteListener() == listener) {
-                relationEntry.deregisterAddListener(l);
-              }
-            }
-          }
+    /**
+     * Get the effective value of the specified property. If the property is
+     * multi-valued then just the first value is returned. If the property does
+     * not have a value then its default value is returned if it has one, or
+     * <code>null</code> indicating that any default behavior is applicable.
+     *
+     * @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>
+     *         indicating that any default behavior is applicable.
+     * @throws IllegalArgumentException
+     *             If the property definition is not associated with this
+     *             managed object's definition.
+     */
+    public <T> T getPropertyValue(PropertyDefinition<T> d) throws IllegalArgumentException {
+        Set<T> values = getPropertyValues(d);
+        if (values.isEmpty()) {
+            return null;
+        } else {
+            return values.iterator().next();
         }
-        return;
-      }
     }
-  }
 
-
-  // Register an instantiable or optional relation delete listener.
-  private void registerDeleteListener(DN baseDN, ConfigDeleteListener adaptor)
-      throws ConfigException {
-    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
-
-    if (relationEntry != null) {
-      relationEntry.registerDeleteListener(adaptor);
-    } else {
-      // The relation entry does not exist yet so register a delayed
-      // add listener.
-      ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
-          adaptor);
-      registerDelayedListener(baseDN, delayedListener);
+    /**
+     * Get the effective values of the specified property. If the property does
+     * not have any values then its default values are returned if it has any,
+     * or an empty set indicating that any default behavior is applicable.
+     *
+     * @param <T>
+     *            The type of the property to be retrieved.
+     * @param d
+     *            The property to be retrieved.
+     * @return Returns an unmodifiable set containing the property's effective
+     *         values. An empty set indicates that the property has no default
+     *         values defined and any default behavior is applicable.
+     * @throws IllegalArgumentException
+     *             If the property definition is not associated with this
+     *             managed object's definition.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d) throws IllegalArgumentException {
+        if (!properties.containsKey(d)) {
+            throw new IllegalArgumentException("Unknown property " + d.getName());
+        }
+        return Collections.unmodifiableSortedSet((SortedSet<T>) properties.get(d));
     }
-  }
 
-
-
-  // Validate that a relation definition belongs to this managed
-  // object.
-  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
-      throws IllegalArgumentException {
-    RelationDefinition<?, ?> tmp = definition.getRelationDefinition(rd
-        .getName());
-    if (tmp != rd) {
-      throw new IllegalArgumentException("The relation " + rd.getName()
-          + " is not associated with a " + definition.getName());
+    /**
+     * Determines whether or not the optional managed object associated with the
+     * specified optional relations exists.
+     *
+     * @param d
+     *            The optional relation definition.
+     * @return Returns <code>true</code> if the optional managed object exists,
+     *         <code>false</code> otherwise.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     */
+    public boolean hasChild(OptionalRelationDefinition<?, ?> d) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+        return context.managedObjectExists(path.child(d));
     }
-  }
+
+    /**
+     * Lists the child managed objects associated with the specified
+     * instantiable relation.
+     *
+     * @param d
+     *            The instantiable relation definition.
+     * @return Returns the names of the child managed objects.
+     * @throws IllegalArgumentException
+     *             If the relation definition is not associated with this
+     *             managed object's definition.
+     */
+    public String[] listChildren(InstantiableRelationDefinition<?, ?> d) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+        return context.listManagedObjects(path, d);
+    }
+
+    /**
+     * Lists the child managed objects associated with the specified set
+     * relation.
+     *
+     * @param d
+     *            The set relation definition.
+     * @return Returns the names of the child managed objects.
+     * @throws IllegalArgumentException
+     *             If the relation definition is not associated with this
+     *             managed object's definition.
+     */
+    public String[] listChildren(SetRelationDefinition<?, ?> d) throws IllegalArgumentException {
+        validateRelationDefinition(d);
+        return context.listManagedObjects(path, d);
+    }
+
+    /**
+     * Register to be notified when new child configurations are added beneath
+     * an instantiable relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The configuration add listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the instantiable
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerAddListener(InstantiableRelationDefinition<?, M> d,
+            ConfigurationAddListener<M> listener) throws IllegalArgumentException, ConfigException {
+        registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(listener));
+    }
+
+    /**
+     * Register to be notified when new child server managed object are added
+     * beneath an instantiable relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The server managed object add listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the instantiable
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerAddListener(InstantiableRelationDefinition<?, M> d,
+            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        DN baseDN = DNBuilder.create(path, d);
+        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d, listener);
+        registerAddListener(baseDN, adaptor);
+    }
+
+    /**
+     * Register to be notified when a new child configurations is added beneath
+     * an optional relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The configuration add listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the optional
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerAddListener(OptionalRelationDefinition<?, M> d,
+            ConfigurationAddListener<M> listener) throws IllegalArgumentException, ConfigException {
+        registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(listener));
+    }
+
+    /**
+     * Register to be notified when a new child server managed object is added
+     * beneath an optional relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The server managed object add listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the optional
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerAddListener(OptionalRelationDefinition<?, M> d,
+            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        DN baseDN = DNBuilder.create(path, d).parent();
+        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d, listener);
+        registerAddListener(baseDN, adaptor);
+    }
+
+    /**
+     * Register to be notified when new child configurations are added beneath a
+     * set relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The configuration add listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the set relation
+     *             could not be retrieved.
+     */
+    public <M extends Configuration> void registerAddListener(SetRelationDefinition<?, M> d,
+            ConfigurationAddListener<M> listener) throws IllegalArgumentException, ConfigException {
+        registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(listener));
+    }
+
+    /**
+     * Register to be notified when new child server managed object are added
+     * beneath a set relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The server managed object add listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the set relation
+     *             could not be retrieved.
+     */
+    public <M extends Configuration> void registerAddListener(SetRelationDefinition<?, M> d,
+            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        DN baseDN = DNBuilder.create(path, d);
+        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d, listener);
+        registerAddListener(baseDN, adaptor);
+    }
+
+    /**
+     * Register to be notified when this server managed object is changed.
+     *
+     * @param listener
+     *            The configuration change listener.
+     */
+    public void registerChangeListener(ConfigurationChangeListener<? super S> listener) {
+        registerChangeListener(new ServerManagedObjectChangeListenerAdaptor<S>(listener));
+    }
+
+    /**
+     * Register to be notified when this server managed object is changed.
+     *
+     * @param listener
+     *            The server managed object change listener.
+     */
+    public void registerChangeListener(ServerManagedObjectChangeListener<? super S> listener) {
+        ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path, listener);
+        configEntry.registerChangeListener(adaptor);
+
+        // Change listener registration usually signifies that a managed
+        // object has been accepted and added to the server configuration
+        // during initialization post-add.
+
+        // FIXME: we should prevent multiple invocations in the case where
+        // multiple change listeners are registered for the same object.
+        for (Constraint constraint : definition.getAllConstraints()) {
+            for (ServerConstraintHandler handler : constraint.getServerConstraintHandlers()) {
+                try {
+                    handler.performPostAdd(this);
+                } catch (ConfigException e) {
+                    logger.trace("Unable to perform post add", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Register to be notified when existing child configurations are deleted
+     * beneath an instantiable relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The configuration delete listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the instantiable
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerDeleteListener(InstantiableRelationDefinition<?, M> d,
+            ConfigurationDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
+        registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(listener));
+    }
+
+    /**
+     * Register to be notified when existing child server managed objects are
+     * deleted beneath an instantiable relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The instantiable relation definition.
+     * @param listener
+     *            The server managed objects delete listener.
+     * @throws IllegalArgumentException
+     *             If the instantiable relation definition is not associated
+     *             with this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the instantiable
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerDeleteListener(InstantiableRelationDefinition<?, M> d,
+            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        DN baseDN = DNBuilder.create(path, d);
+        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d, listener);
+        registerDeleteListener(baseDN, adaptor);
+    }
+
+    /**
+     * Register to be notified when an existing child configuration is deleted
+     * beneath an optional relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The configuration delete listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the optional
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerDeleteListener(OptionalRelationDefinition<?, M> d,
+            ConfigurationDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
+        registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(listener));
+    }
+
+    /**
+     * Register to be notified when an existing child server managed object is
+     * deleted beneath an optional relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The optional relation definition.
+     * @param listener
+     *            The server managed object delete listener.
+     * @throws IllegalArgumentException
+     *             If the optional relation definition is not associated with
+     *             this managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the optional
+     *             relation could not be retrieved.
+     */
+    public <M extends Configuration> void registerDeleteListener(OptionalRelationDefinition<?, M> d,
+            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        DN baseDN = DNBuilder.create(path, d).parent();
+        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d, listener);
+        registerDeleteListener(baseDN, adaptor);
+    }
+
+    /**
+     * Register to be notified when existing child configurations are deleted
+     * beneath a set relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The configuration delete listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the set relation
+     *             could not be retrieved.
+     */
+    public <M extends Configuration> void registerDeleteListener(SetRelationDefinition<?, M> d,
+            ConfigurationDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
+        registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(listener));
+    }
+
+    /**
+     * Register to be notified when existing child server managed objects are
+     * deleted beneath a set relation.
+     *
+     * @param <M>
+     *            The type of the child server configuration object.
+     * @param d
+     *            The set relation definition.
+     * @param listener
+     *            The server managed objects delete listener.
+     * @throws IllegalArgumentException
+     *             If the set relation definition is not associated with this
+     *             managed object's definition.
+     * @throws ConfigException
+     *             If the configuration entry associated with the set relation
+     *             could not be retrieved.
+     */
+    public <M extends Configuration> void registerDeleteListener(SetRelationDefinition<?, M> d,
+            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
+        validateRelationDefinition(d);
+        DN baseDN = DNBuilder.create(path, d);
+        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d, listener);
+        registerDeleteListener(baseDN, adaptor);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append("{ TYPE=");
+        builder.append(definition.getName());
+        builder.append(", DN=\"");
+        builder.append(getDN());
+        builder.append('\"');
+        for (Map.Entry<PropertyDefinition<?>, SortedSet<?>> value : properties.entrySet()) {
+            builder.append(", ");
+            builder.append(value.getKey().getName());
+            builder.append('=');
+            builder.append(value.getValue());
+        }
+        builder.append(" }");
+
+        return builder.toString();
+    }
+
+    /**
+     * Determines whether or not this managed object can be used by the server.
+     *
+     * @throws ConstraintViolationException
+     *             If one or more constraints determined that this managed
+     *             object cannot be used by the server.
+     */
+    void ensureIsUsable() throws ConstraintViolationException {
+        // Enforce any constraints.
+        boolean isUsable = true;
+        List<LocalizableMessage> reasons = new LinkedList<LocalizableMessage>();
+        for (Constraint constraint : definition.getAllConstraints()) {
+            for (ServerConstraintHandler handler : constraint.getServerConstraintHandlers()) {
+                try {
+                    if (!handler.isUsable(this, reasons)) {
+                        isUsable = false;
+                    }
+                } catch (ConfigException e) {
+                    LocalizableMessage message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e.getMessageObject());
+                    reasons.add(message);
+                    isUsable = false;
+                }
+            }
+        }
+
+        if (!isUsable) {
+            throw new ConstraintViolationException(this, reasons);
+        }
+    }
+
+    /**
+     * Update the config entry associated with this server managed object. This
+     * is only intended to be used by change listener call backs in order to
+     * update the managed object with the correct config entry.
+     *
+     * @param configEntry
+     *            The configuration entry.
+     */
+    void setConfigEntry(ConfigEntry configEntry) {
+        this.configEntry = configEntry;
+    }
+
+    // Deregister an add listener.
+    private <M extends Configuration> void deregisterAddListener(DN baseDN, ConfigurationAddListener<M> listener) {
+        try {
+            ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+            if (configEntry != null) {
+                for (ConfigAddListener l : configEntry.getAddListeners()) {
+                    if (l instanceof ConfigAddListenerAdaptor) {
+                        ConfigAddListenerAdaptor<?> adaptor = (ConfigAddListenerAdaptor<?>) l;
+                        ServerManagedObjectAddListener<?> l2 = adaptor.getServerManagedObjectAddListener();
+                        if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
+                            ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
+                                    (ServerManagedObjectAddListenerAdaptor<?>) l2;
+                            if (adaptor2.getConfigurationAddListener() == listener) {
+                                configEntry.deregisterAddListener(adaptor);
+                            }
+                        }
+                    }
+                }
+            } else {
+                // The relation entry does not exist so check for and deregister
+                // delayed add listener.
+                deregisterDelayedAddListener(baseDN, listener);
+            }
+        } catch (ConfigException e) {
+            // Ignore the exception since this implies deregistration.
+            logger.trace("Unable to deregister add listener", e);
+        }
+    }
+
+    // Deregister an add listener.
+    private <M extends Configuration> void deregisterAddListener(DN baseDN,
+            ServerManagedObjectAddListener<M> listener) {
+        try {
+            ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+            if (configEntry != null) {
+                for (ConfigAddListener l : configEntry.getAddListeners()) {
+                    if (l instanceof ConfigAddListenerAdaptor) {
+                        ConfigAddListenerAdaptor<?> adaptor = (ConfigAddListenerAdaptor<?>) l;
+                        if (adaptor.getServerManagedObjectAddListener() == listener) {
+                            configEntry.deregisterAddListener(adaptor);
+                        }
+                    }
+                }
+            } else {
+                // The relation entry does not exist so check for and deregister
+                // delayed add listener.
+                deregisterDelayedAddListener(baseDN, listener);
+            }
+        } catch (ConfigException e) {
+            // Ignore the exception since this implies deregistration.
+            logger.trace("Unable to deregister add listener", e);
+        }
+    }
+
+    // Deregister a delete listener.
+    private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
+            ConfigurationDeleteListener<M> listener) {
+        try {
+            ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+            if (configEntry != null) {
+                for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
+                    if (l instanceof ConfigDeleteListenerAdaptor) {
+                        ConfigDeleteListenerAdaptor<?> adaptor = (ConfigDeleteListenerAdaptor<?>) l;
+                        ServerManagedObjectDeleteListener<?> l2 = adaptor.getServerManagedObjectDeleteListener();
+                        if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
+                            ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
+                                    (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
+                            if (adaptor2.getConfigurationDeleteListener() == listener) {
+                                configEntry.deregisterDeleteListener(adaptor);
+                            }
+                        }
+                    }
+                }
+            } else {
+                // The relation entry does not exist so check for and deregister
+                // delayed add listener.
+                deregisterDelayedDeleteListener(baseDN, listener);
+            }
+        } catch (ConfigException e) {
+            // Ignore the exception since this implies deregistration.
+            logger.trace("Unable to deregister delete listener", e);
+        }
+    }
+
+    // Deregister a delete listener.
+    private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
+            ServerManagedObjectDeleteListener<M> listener) {
+        try {
+            ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+            if (configEntry != null) {
+                for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
+                    if (l instanceof ConfigDeleteListenerAdaptor) {
+                        ConfigDeleteListenerAdaptor<?> adaptor = (ConfigDeleteListenerAdaptor<?>) l;
+                        if (adaptor.getServerManagedObjectDeleteListener() == listener) {
+                            configEntry.deregisterDeleteListener(adaptor);
+                        }
+                    }
+                }
+            } else {
+                // The relation entry does not exist so check for and deregister
+                // delayed add listener.
+                deregisterDelayedDeleteListener(baseDN, listener);
+            }
+        } catch (ConfigException e) {
+            // Ignore the exception since this implies deregistration.
+            logger.trace("Unable to deregister delete listener", e);
+        }
+    }
+
+    // Gets a config entry required for a listener and throws a config
+    // exception on failure or returns null if the entry does not exist.
+    private ConfigEntry getListenerConfigEntry(DN dn) throws ConfigException {
+        // Attempt to retrieve the listener base entry.
+        ConfigEntry configEntry;
+        try {
+            configEntry = DirectoryServer.getConfigEntry(dn);
+        } catch (ConfigException e) {
+            logger.trace("Unable to get listener base entry", e);
+            LocalizableMessage message = ERR_ADMIN_CANNOT_GET_LISTENER_BASE.get(String.valueOf(dn),
+                    stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD));
+            throw new ConfigException(message, e);
+        }
+
+        return configEntry;
+    }
+
+    // Register an instantiable or optional relation add listener.
+    private void registerAddListener(DN baseDN, ConfigAddListener adaptor) throws IllegalArgumentException,
+            ConfigException {
+        ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
+
+        if (relationEntry != null) {
+            relationEntry.registerAddListener(adaptor);
+        } else {
+            // The relation entry does not exist yet so register a delayed
+            // add listener.
+            ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor);
+            registerDelayedListener(baseDN, delayedListener);
+        }
+    }
+
+    // Register a delayed listener with the nearest existing parent
+    // entry to the provided base DN.
+    private void registerDelayedListener(DN baseDN, ConfigAddListener delayedListener) throws ConfigException {
+        DN parentDN = baseDN.parent();
+        while (parentDN != null) {
+            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+            if (relationEntry == null) {
+                delayedListener = new DelayedConfigAddListener(parentDN, delayedListener);
+                parentDN = parentDN.parent();
+            } else {
+                relationEntry.registerAddListener(delayedListener);
+                return;
+            }
+        }
+
+        // No parent entry could be found.
+        LocalizableMessage message = ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER.get(String.valueOf(baseDN));
+        throw new ConfigException(message);
+    }
+
+    // Deregister a delayed listener with the nearest existing parent
+    // entry to the provided base DN.
+    private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
+            ConfigurationAddListener<M> listener)
+            throws ConfigException {
+        DN parentDN = baseDN.parent();
+        int delayWrappers = 0;
+        while (parentDN != null) {
+            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+            if (relationEntry == null) {
+                parentDN = parentDN.parent();
+                delayWrappers++;
+            } else {
+                for (ConfigAddListener l : relationEntry.getAddListeners()) {
+                    if (l instanceof DelayedConfigAddListener) {
+                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
+                        ConfigAddListener wrappedListener;
+
+                        int i = delayWrappers;
+                        for (; i > 0; i--) {
+                            wrappedListener = delayListener.getDelayedAddListener();
+                            if (wrappedListener != null && wrappedListener instanceof DelayedConfigAddListener) {
+                                delayListener = (DelayedConfigAddListener) l;
+                            } else {
+                                break;
+                            }
+                        }
+
+                        if (i > 0) {
+                            // There are not enough level of wrapping so this
+                            // can't be
+                            // the listener we are looking for.
+                            continue;
+                        }
+
+                        ConfigAddListener delayedListener = delayListener.getDelayedAddListener();
+
+                        if (delayedListener != null && delayedListener instanceof ConfigAddListenerAdaptor) {
+                            ConfigAddListenerAdaptor<?> adaptor = (ConfigAddListenerAdaptor<?>) delayedListener;
+                            ServerManagedObjectAddListener<?> l2 = adaptor.getServerManagedObjectAddListener();
+                            if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
+                                ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
+                                        (ServerManagedObjectAddListenerAdaptor<?>) l2;
+                                if (adaptor2.getConfigurationAddListener() == listener) {
+                                    relationEntry.deregisterAddListener(l);
+                                }
+                            }
+                        }
+                    }
+                }
+                return;
+            }
+        }
+    }
+
+    // Deregister a delayed listener with the nearest existing parent
+    // entry to the provided base DN.
+    private <M extends Configuration> void deregisterDelayedDeleteListener(DN baseDN,
+            ConfigurationDeleteListener<M> listener) throws ConfigException {
+        DN parentDN = baseDN.parent();
+        int delayWrappers = 0;
+        while (parentDN != null) {
+            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+            if (relationEntry == null) {
+                parentDN = parentDN.parent();
+                delayWrappers++;
+            } else {
+                for (ConfigAddListener l : relationEntry.getAddListeners()) {
+                    if (l instanceof DelayedConfigAddListener) {
+                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
+                        ConfigAddListener wrappedListener;
+
+                        int i = delayWrappers;
+                        for (; i > 0; i--) {
+                            wrappedListener = delayListener.getDelayedAddListener();
+                            if (wrappedListener != null && wrappedListener instanceof DelayedConfigAddListener) {
+                                delayListener = (DelayedConfigAddListener) l;
+                            } else {
+                                break;
+                            }
+                        }
+
+                        if (i > 0) {
+                            // There are not enough level of wrapping so this
+                            // can't be
+                            // the listener we are looking for.
+                            continue;
+                        }
+
+                        ConfigDeleteListener delayedListener = delayListener.getDelayedDeleteListener();
+
+                        if (delayedListener != null && delayedListener instanceof ConfigDeleteListenerAdaptor) {
+                            ConfigDeleteListenerAdaptor<?> adaptor = (ConfigDeleteListenerAdaptor<?>) delayedListener;
+                            ServerManagedObjectDeleteListener<?> l2 = adaptor.getServerManagedObjectDeleteListener();
+                            if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
+                                ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
+                                        (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
+                                if (adaptor2.getConfigurationDeleteListener() == listener) {
+                                    relationEntry.deregisterAddListener(l);
+                                }
+                            }
+                        }
+                    }
+                }
+                return;
+            }
+        }
+    }
+
+    // Deregister a delayed listener with the nearest existing parent
+    // entry to the provided base DN.
+    private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
+            ServerManagedObjectAddListener<M> listener) throws ConfigException {
+        DN parentDN = baseDN.parent();
+        int delayWrappers = 0;
+        while (parentDN != null) {
+            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+            if (relationEntry == null) {
+                parentDN = parentDN.parent();
+                delayWrappers++;
+            } else {
+                for (ConfigAddListener l : relationEntry.getAddListeners()) {
+                    if (l instanceof DelayedConfigAddListener) {
+                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
+                        ConfigAddListener wrappedListener;
+
+                        int i = delayWrappers;
+                        for (; i > 0; i--) {
+                            wrappedListener = delayListener.getDelayedAddListener();
+                            if (wrappedListener != null && wrappedListener instanceof DelayedConfigAddListener) {
+                                delayListener = (DelayedConfigAddListener) l;
+                            } else {
+                                break;
+                            }
+                        }
+
+                        if (i > 0) {
+                            // There are not enough level of wrapping so this
+                            // can't be
+                            // the listener we are looking for.
+                            continue;
+                        }
+
+                        ConfigAddListener delayedListener = delayListener.getDelayedAddListener();
+
+                        if (delayedListener != null && delayedListener instanceof ConfigAddListenerAdaptor) {
+                            ConfigAddListenerAdaptor<?> adaptor = (ConfigAddListenerAdaptor<?>) delayedListener;
+                            if (adaptor.getServerManagedObjectAddListener() == listener) {
+                                relationEntry.deregisterAddListener(l);
+                            }
+                        }
+                    }
+                }
+                return;
+            }
+        }
+    }
+
+    // Deregister a delayed listener with the nearest existing parent
+    // entry to the provided base DN.
+    private <M extends Configuration> void deregisterDelayedDeleteListener(DN baseDN,
+            ServerManagedObjectDeleteListener<M> listener) throws ConfigException {
+        DN parentDN = baseDN.parent();
+        int delayWrappers = 0;
+        while (parentDN != null) {
+            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+            if (relationEntry == null) {
+                parentDN = parentDN.parent();
+                delayWrappers++;
+            } else {
+                for (ConfigAddListener l : relationEntry.getAddListeners()) {
+                    if (l instanceof DelayedConfigAddListener) {
+                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
+                        ConfigAddListener wrappedListener;
+
+                        int i = delayWrappers;
+                        for (; i > 0; i--) {
+                            wrappedListener = delayListener.getDelayedAddListener();
+                            if (wrappedListener != null && wrappedListener instanceof DelayedConfigAddListener) {
+                                delayListener = (DelayedConfigAddListener) l;
+                            } else {
+                                break;
+                            }
+                        }
+
+                        if (i > 0) {
+                            // There are not enough level of wrapping so this
+                            // can't be
+                            // the listener we are looking for.
+                            continue;
+                        }
+
+                        ConfigDeleteListener delayedListener = delayListener.getDelayedDeleteListener();
+
+                        if (delayedListener != null && delayedListener instanceof ConfigDeleteListenerAdaptor) {
+                            ConfigDeleteListenerAdaptor<?> adaptor = (ConfigDeleteListenerAdaptor<?>) delayedListener;
+                            if (adaptor.getServerManagedObjectDeleteListener() == listener) {
+                                relationEntry.deregisterAddListener(l);
+                            }
+                        }
+                    }
+                }
+                return;
+            }
+        }
+    }
+
+    // Register an instantiable or optional relation delete listener.
+    private void registerDeleteListener(DN baseDN, ConfigDeleteListener adaptor) throws ConfigException {
+        ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
+
+        if (relationEntry != null) {
+            relationEntry.registerDeleteListener(adaptor);
+        } else {
+            // The relation entry does not exist yet so register a delayed
+            // add listener.
+            ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor);
+            registerDelayedListener(baseDN, delayedListener);
+        }
+    }
+
+    // Validate that a relation definition belongs to this managed
+    // object.
+    private void validateRelationDefinition(RelationDefinition<?, ?> rd) throws IllegalArgumentException {
+        RelationDefinition<?, ?> tmp = definition.getRelationDefinition(rd.getName());
+        if (tmp != rd) {
+            throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a "
+                    + definition.getName());
+        }
+    }
 }

--
Gitblit v1.10.0