mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Nicolas Capponi
04.13.2013 e538344449d345daa6e5ecb9b05ceba5427408e9
OpenDJ 3 : config framework

Reducing compilation errors in org.opends.server.admin packages

* Changes in org.opends.server.admin.server package
** Replace use of custom logging classes and methods by slf4j logger and i18n framework
** Replace use of DN server class and methods by SDK DN class and methods
** Other minor changes

* Add i18n-slf4j dependency to pom.xml
15 files modified
6938 ■■■■■ changed files
opendj-admin/pom.xml 58 ●●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/AdministratorAction.java 2 ●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigAddListenerAdaptor.java 21 ●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java 712 ●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java 410 ●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigExceptionFactory.java 180 ●●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationChangeListener.java 63 ●●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationDeleteListener.java 62 ●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ConstraintViolationException.java 213 ●●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/DNBuilder.java 79 ●●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/DelayedConfigAddListener.java 243 ●●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ServerConstraintHandler.java 268 ●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java 2894 ●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDecodingException.java 157 ●●●●● patch | view | raw | blame | history
opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java 1576 ●●●● patch | view | raw | blame | history
opendj-admin/pom.xml
@@ -32,6 +32,10 @@
      <artifactId>i18n-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.forgerock.commons</groupId>
      <artifactId>i18n-slf4j</artifactId>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
    </dependency>
@@ -423,60 +427,6 @@
      </plugin>
    </plugins>
    <pluginManagement>
      <plugins>
        <!--This plugin's configuration is used to store Eclipse m2e settings
          only. It has no influence on the Maven build itself. -->
        <plugin>
          <groupId>org.eclipse.m2e</groupId>
          <artifactId>lifecycle-mapping</artifactId>
          <version>1.0.0</version>
          <configuration>
            <lifecycleMappingMetadata>
              <pluginExecutions>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <versionRange>[1.8,)</versionRange>
                    <goals>
                      <goal>parse-version</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore></ignore>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>templating-maven-plugin</artifactId>
                    <versionRange>[1.0-alpha-3,)</versionRange>
                    <goals>
                      <goal>filter-sources</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore></ignore>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>xml-maven-plugin</artifactId>
                    <versionRange>[1.0,)</versionRange>
                    <goals>
                      <goal>transform</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore></ignore>
                  </action>
                </pluginExecution>
              </pluginExecutions>
            </lifecycleMappingMetadata>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
  <reporting>
opendj-admin/src/main/java/org/opends/server/admin/AdministratorAction.java
@@ -150,7 +150,7 @@
        ManagedObjectDefinitionI18NResource resource = ManagedObjectDefinitionI18NResource.getInstance();
        String property = "property." + propertyName + ".requires-admin-action.synopsis";
        try {
            return resource.getLocalizableMessage(definition, property, locale);
            return resource.getMessage(definition, property, locale);
        } catch (MissingResourceException e) {
            return null;
        }
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -28,8 +28,6 @@
import static org.opends.server.loggers.debug.DebugLogger.*;
import java.util.LinkedList;
import java.util.List;
@@ -46,14 +44,12 @@
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ConfigChangeResult;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ResultCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.ResultCode;
@@ -68,10 +64,7 @@
final class ConfigAddListenerAdaptor<S extends Configuration> extends
    AbstractConfigListenerAdaptor implements ConfigAddListener {
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  private static final Logger debugLogger = LoggerFactory.getLogger(ConfigAddListenerAdaptor.class);
  // Cached managed object between accept/apply callbacks.
  private ServerManagedObject<? extends S> cachedManagedObject;
@@ -196,9 +189,7 @@
          try {
            handler.performPostAdd(cachedManagedObject);
          } catch (ConfigException e) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
              debugLogger.trace("Unable to perform post add", e);
          }
        }
      }
@@ -257,7 +248,7 @@
    }
    // Let the add listener decide.
    List<LocalizableMessage> reasons = new LinkedList<Message>();
    List<LocalizableMessage> reasons = new LinkedList<LocalizableMessage>();
    if (listener.isConfigurationAddAcceptable(cachedManagedObject, reasons)) {
      return true;
    } else {
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
@@ -26,9 +26,7 @@
 */
package org.opends.server.admin.server;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static com.forgerock.opendj.ldap.AdminMessages.*;
import java.util.Collection;
import java.util.HashSet;
@@ -36,9 +34,12 @@
import java.util.List;
import java.util.Set;
import org.opends.messages.AdminMessages;
import com.forgerock.opendj.ldap.AdminMessages;
import com.forgerock.opendj.util.StaticUtils;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
@@ -55,438 +56,367 @@
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.ErrorLogger;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ConfigChangeResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ResultCode;
import org.opends.server.util.StaticUtils;
import org.forgerock.opendj.ldap.ResultCode;
/**
 * An adaptor class which converts {@link ConfigChangeListener}
 * call-backs to {@link ServerManagedObjectChangeListener}
 * call-backs.
 * An adaptor class which converts {@link ConfigChangeListener} call-backs to
 * {@link ServerManagedObjectChangeListener} call-backs.
 *
 * @param <S>
 *          The type of server configuration handled by the change
 *          listener.
 *            The type of server configuration handled by the change listener.
 */
final class ConfigChangeListenerAdaptor<S extends Configuration> extends
    AbstractConfigListenerAdaptor implements ConfigChangeListener {
final class ConfigChangeListenerAdaptor<S extends Configuration> extends AbstractConfigListenerAdaptor implements
        ConfigChangeListener {
  /**
   * A default behavior visitor used for determining the set of
   * dependencies.
   *
   * @param <T>
   *          The type of property.
   */
  private static final class Visitor<T> implements
      DefaultBehaviorProviderVisitor<T, Void, ManagedObjectPath<?, ?>> {
    private static final Logger debugLogger = LoggerFactory.getLogger(ConfigChangeListenerAdaptor.class);
    private static final LocalizedLogger adminLogger = LocalizedLogger.getLocalizedLogger(
            ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST.get("").resourceName());
    /**
     * Finds the dependencies associated with the provided property
     * definition.
     * A default behavior visitor used for determining the set of dependencies.
     *
     * @param <T>
     * @param path
     *          The current base path used for relative name
     *          resolution.
     * @param pd
     *          The property definition.
     * @param dependencies
     *          Add dependencies names to this collection.
     *            The type of property.
     */
    public static <T> void find(ManagedObjectPath<?, ?> path,
        PropertyDefinition<T> pd, Collection<DN> dependencies) {
      Visitor<T> v = new Visitor<T>(dependencies);
      DefaultBehaviorProvider<T> db = pd.getDefaultBehaviorProvider();
      db.accept(v, path);
    private static final class Visitor<T> implements DefaultBehaviorProviderVisitor<T, Void, ManagedObjectPath<?, ?>> {
        /**
         * Finds the dependencies associated with the provided property
         * definition.
         *
         * @param <T>
         * @param path
         *            The current base path used for relative name resolution.
         * @param pd
         *            The property definition.
         * @param dependencies
         *            Add dependencies names to this collection.
         */
        public static <T> void find(ManagedObjectPath<?, ?> path, PropertyDefinition<T> pd, Collection<DN> dependencies) {
            Visitor<T> v = new Visitor<T>(dependencies);
            DefaultBehaviorProvider<T> db = pd.getDefaultBehaviorProvider();
            db.accept(v, path);
        }
        // The names of entries that this change listener depends on.
        private final Collection<DN> dependencies;
        // Prevent instantiation.
        private Visitor(Collection<DN> dependencies) {
            this.dependencies = dependencies;
        }
        /**
         * {@inheritDoc}
         */
        public Void visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath<?, ?> p) {
            ManagedObjectPath<?, ?> next = d.getManagedObjectPath();
            dependencies.add(DNBuilder.create(next));
            // If the dependent property uses inherited defaults then
            // recursively get those as well.
            String propertyName = d.getPropertyName();
            AbstractManagedObjectDefinition<?, ?> mod = d.getManagedObjectDefinition();
            PropertyDefinition<?> pd = mod.getPropertyDefinition(propertyName);
            find(next, pd, dependencies);
            return null;
        }
        /**
         * {@inheritDoc}
         */
        public Void visitAlias(AliasDefaultBehaviorProvider<T> d, ManagedObjectPath<?, ?> p) {
            return null;
        }
        /**
         * {@inheritDoc}
         */
        public Void visitDefined(DefinedDefaultBehaviorProvider<T> d, ManagedObjectPath<?, ?> p) {
            return null;
        }
        /**
         * {@inheritDoc}
         */
        public Void visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath<?, ?> p) {
            ManagedObjectPath<?, ?> next = d.getManagedObjectPath(p);
            dependencies.add(DNBuilder.create(next));
            // If the dependent property uses inherited defaults then
            // recursively get those as well.
            String propertyName = d.getPropertyName();
            AbstractManagedObjectDefinition<?, ?> mod = d.getManagedObjectDefinition();
            PropertyDefinition<?> pd = mod.getPropertyDefinition(propertyName);
            find(next, pd, dependencies);
            return null;
        }
        /**
         * {@inheritDoc}
         */
        public Void visitUndefined(UndefinedDefaultBehaviorProvider<T> d, ManagedObjectPath<?, ?> p) {
            return null;
        }
    }
    // Cached managed object between accept/apply call-backs.
    private ServerManagedObject<? extends S> cachedManagedObject;
    // The delete listener which is used to remove this listener and any
    // dependencies.
    private final ConfigDeleteListener cleanerListener;
    // The names of entries that this change listener depends on.
    private final Collection<DN> dependencies;
    private final Set<DN> dependencies;
    // The listener used to notify this listener when dependency entries
    // are modified.
    private final ConfigChangeListener dependencyListener;
    // The DN associated with this listener.
    private final DN dn;
    // Prevent instantiation.
    private Visitor(Collection<DN> dependencies) {
      this.dependencies = dependencies;
    }
    // The underlying change listener.
    private final ServerManagedObjectChangeListener<? super S> listener;
    // The managed object path.
    private final ManagedObjectPath<?, S> path;
    /**
     * {@inheritDoc}
     * Create a new configuration change listener adaptor.
     *
     * @param path
     *            The managed object path.
     * @param listener
     *            The underlying change listener.
     */
    public Void visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d,
        ManagedObjectPath<?, ?> p) {
      ManagedObjectPath<?, ?> next = d.getManagedObjectPath();
      dependencies.add(DNBuilder.create(next));
    public ConfigChangeListenerAdaptor(ManagedObjectPath<?, S> path,
            ServerManagedObjectChangeListener<? super S> listener) {
        this.path = path;
        this.dn = DNBuilder.create(path);
        this.listener = listener;
        this.cachedManagedObject = null;
      // If the dependent property uses inherited defaults then
      // recursively get those as well.
      String propertyName = d.getPropertyName();
      AbstractManagedObjectDefinition<?, ?> mod = d
          .getManagedObjectDefinition();
      PropertyDefinition<?> pd = mod.getPropertyDefinition(propertyName);
      find(next, pd, dependencies);
        // This change listener should be notified when dependent entries
        // are modified. Determine the dependencies and register change
        // listeners against them.
        this.dependencies = new HashSet<DN>();
        this.dependencyListener = new ConfigChangeListener() {
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public Void visitAlias(AliasDefaultBehaviorProvider<T> d,
        ManagedObjectPath<?, ?> p) {
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public Void visitDefined(DefinedDefaultBehaviorProvider<T> d,
        ManagedObjectPath<?, ?> p) {
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public Void visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d,
        ManagedObjectPath<?, ?> p) {
      ManagedObjectPath<?, ?> next = d.getManagedObjectPath(p);
      dependencies.add(DNBuilder.create(next));
      // If the dependent property uses inherited defaults then
      // recursively get those as well.
      String propertyName = d.getPropertyName();
      AbstractManagedObjectDefinition<?, ?> mod = d
          .getManagedObjectDefinition();
      PropertyDefinition<?> pd = mod.getPropertyDefinition(propertyName);
      find(next, pd, dependencies);
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public Void visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        ManagedObjectPath<?, ?> p) {
      return null;
    }
  }
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  // Cached managed object between accept/apply call-backs.
  private ServerManagedObject<? extends S> cachedManagedObject;
  // The delete listener which is used to remove this listener and any
  // dependencies.
  private final ConfigDeleteListener cleanerListener;
  // The names of entries that this change listener depends on.
  private final Set<DN> dependencies;
  // The listener used to notify this listener when dependency entries
  // are modified.
  private final ConfigChangeListener dependencyListener;
  // The DN associated with this listener.
  private final DN dn;
  // The underlying change listener.
  private final ServerManagedObjectChangeListener<? super S> listener;
  // The managed object path.
  private final ManagedObjectPath<?, S> path;
  /**
   * Create a new configuration change listener adaptor.
   *
   * @param path
   *          The managed object path.
   * @param listener
   *          The underlying change listener.
   */
  public ConfigChangeListenerAdaptor(ManagedObjectPath<?, S> path,
      ServerManagedObjectChangeListener<? super S> listener) {
    this.path = path;
    this.dn = DNBuilder.create(path);
    this.listener = listener;
    this.cachedManagedObject = null;
    // This change listener should be notified when dependent entries
    // are modified. Determine the dependencies and register change
    // listeners against them.
    this.dependencies = new HashSet<DN>();
    this.dependencyListener = new ConfigChangeListener() {
      public ConfigChangeResult applyConfigurationChange(
          ConfigEntry configEntry) {
        ConfigEntry dependentConfigEntry = getConfigEntry(dn);
        if (dependentConfigEntry != null) {
          return ConfigChangeListenerAdaptor.this
              .applyConfigurationChange(dependentConfigEntry);
        } else {
          // The dependent entry was not found.
          configEntry.deregisterChangeListener(this);
          return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
      }
      public boolean configChangeIsAcceptable(ConfigEntry configEntry,
          LocalizableMessageBuilder unacceptableReason) {
        ConfigEntry dependentConfigEntry = getConfigEntry(dn);
        if (dependentConfigEntry != null) {
          return ConfigChangeListenerAdaptor.this.configChangeIsAcceptable(
              dependentConfigEntry, unacceptableReason, configEntry);
        } else {
          // The dependent entry was not found.
          configEntry.deregisterChangeListener(this);
          return true;
        }
      }
    };
    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
      Visitor.find(path, pd, dependencies);
    }
    for (DN entryDN : dependencies) {
      // Be careful not to register listeners against the dependent
      // entry itself.
      if (!entryDN.equals(dn)) {
        ConfigEntry configEntry = getConfigEntry(entryDN);
        if (configEntry != null) {
          configEntry.registerChangeListener(dependencyListener);
        }
      }
    }
    // Register a delete listener against the parent which will
    // finalize this change listener when the monitored configuration
    // entry is removed.
    this.cleanerListener = new ConfigDeleteListener() {
      public ConfigChangeResult applyConfigurationDelete(
          ConfigEntry configEntry) {
        // Perform finalization if the deleted entry is the monitored
        // entry.
        if (configEntry.getDN().equals(dn)) {
          finalizeChangeListener();
        }
        return new ConfigChangeResult(ResultCode.SUCCESS, false);
      }
      public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
          LocalizableMessageBuilder unacceptableReason) {
        // Always acceptable.
        return true;
      }
    };
    DN parent = dn.getParent();
    if (parent != null) {
      ConfigEntry configEntry = getConfigEntry(dn.getParent());
      if (configEntry != null) {
        configEntry.registerDeleteListener(cleanerListener);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
    // Looking at the ConfigFileHandler implementation reveals
    // that this ConfigEntry will actually be a different object to
    // the one passed in the previous call-back (it will have the same
    // content though). This configuration entry has the correct
    // listener lists.
    cachedManagedObject.setConfigEntry(configEntry);
    ConfigChangeResult result = listener
        .applyConfigurationChange(cachedManagedObject);
    // Now apply post constraint call-backs.
    if (result.getResultCode() == ResultCode.SUCCESS) {
      ManagedObjectDefinition<?, ?> d = cachedManagedObject
          .getManagedObjectDefinition();
      for (Constraint constraint : d.getAllConstraints()) {
        for (ServerConstraintHandler handler : constraint
            .getServerConstraintHandlers()) {
          try {
            handler.performPostModify(cachedManagedObject);
          } catch (ConfigException e) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
                ConfigEntry dependentConfigEntry = getConfigEntry(dn);
                if (dependentConfigEntry != null) {
                    return ConfigChangeListenerAdaptor.this.applyConfigurationChange(dependentConfigEntry);
                } else {
                    // The dependent entry was not found.
                    configEntry.deregisterChangeListener(this);
                    return new ConfigChangeResult(ResultCode.SUCCESS, false);
                }
            }
          }
            public boolean configChangeIsAcceptable(ConfigEntry configEntry,
                    LocalizableMessageBuilder unacceptableReason) {
                ConfigEntry dependentConfigEntry = getConfigEntry(dn);
                if (dependentConfigEntry != null) {
                    return ConfigChangeListenerAdaptor.this.configChangeIsAcceptable(dependentConfigEntry,
                            unacceptableReason, configEntry);
                } else {
                    // The dependent entry was not found.
                    configEntry.deregisterChangeListener(this);
                    return true;
                }
            }
        };
        AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
        for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
            Visitor.find(path, pd, dependencies);
        }
      }
        for (DN entryDN : dependencies) {
            // Be careful not to register listeners against the dependent
            // entry itself.
            if (!entryDN.equals(dn)) {
                ConfigEntry configEntry = getConfigEntry(entryDN);
                if (configEntry != null) {
                    configEntry.registerChangeListener(dependencyListener);
                }
            }
        }
        // Register a delete listener against the parent which will
        // finalize this change listener when the monitored configuration
        // entry is removed.
        this.cleanerListener = new ConfigDeleteListener() {
            public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
                // Perform finalization if the deleted entry is the monitored
                // entry.
                if (configEntry.getDN().equals(dn)) {
                    finalizeChangeListener();
                }
                return new ConfigChangeResult(ResultCode.SUCCESS, false);
            }
            public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                    LocalizableMessageBuilder unacceptableReason) {
                // Always acceptable.
                return true;
            }
        };
        DN parent = dn.parent();
        if (parent != null) {
            ConfigEntry configEntry = getConfigEntry(dn.parent();
            if (configEntry != null) {
                configEntry.registerDeleteListener(cleanerListener);
            }
        }
    }
    return result;
  }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
        // Looking at the ConfigFileHandler implementation reveals
        // that this ConfigEntry will actually be a different object to
        // the one passed in the previous call-back (it will have the same
        // content though). This configuration entry has the correct
        // listener lists.
        cachedManagedObject.setConfigEntry(configEntry);
        ConfigChangeResult result = listener.applyConfigurationChange(cachedManagedObject);
        // Now apply post constraint call-backs.
        if (result.getResultCode() == ResultCode.SUCCESS) {
            ManagedObjectDefinition<?, ?> d = cachedManagedObject.getManagedObjectDefinition();
            for (Constraint constraint : d.getAllConstraints()) {
                for (ServerConstraintHandler handler : constraint.getServerConstraintHandlers()) {
                    try {
                        handler.performPostModify(cachedManagedObject);
                    } catch (ConfigException e) {
                        debugLogger.trace("Unable to perform post modify", e);
                    }
                }
            }
        }
  /**
   * {@inheritDoc}
   */
  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
      LocalizableMessageBuilder unacceptableReason) {
    return configChangeIsAcceptable(configEntry, unacceptableReason,
        configEntry);
  }
  /**
   * Indicates whether the configuration entry that will result from a
   * proposed modification is acceptable to this change listener.
   *
   * @param configEntry
   *          The configuration entry that will result from the
   *          requested update.
   * @param unacceptableReason
   *          A buffer to which this method can append a
   *          human-readable message explaining why the proposed
   *          change is not acceptable.
   * @param newConfigEntry
   *          The configuration entry that caused the notification
   *          (will be different from <code>configEntry</code> if a
   *          dependency was modified).
   * @return <CODE>true</CODE> if the proposed entry contains an
   *         acceptable configuration, or <CODE>false</CODE> if it
   *         does not.
   */
  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
      LocalizableMessageBuilder unacceptableReason, ConfigEntry newConfigEntry) {
    try {
      ServerManagementContext context = ServerManagementContext.getInstance();
      cachedManagedObject = context.decode(path, configEntry, newConfigEntry);
    } catch (DecodingException e) {
      unacceptableReason.append(e.getMessageObject());
      return false;
        return result;
    }
    // Give up immediately if a constraint violation occurs.
    try {
      cachedManagedObject.ensureIsUsable();
    } catch (ConstraintViolationException e) {
      generateUnacceptableReason(e.getMessages(), unacceptableReason);
      return false;
    /**
     * {@inheritDoc}
     */
    public boolean configChangeIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason) {
        return configChangeIsAcceptable(configEntry, unacceptableReason, configEntry);
    }
    // Let the change listener decide.
    List<LocalizableMessage> reasons = new LinkedList<Message>();
    if (listener.isConfigurationChangeAcceptable(cachedManagedObject,reasons)) {
      return true;
    } else {
      generateUnacceptableReason(reasons, unacceptableReason);
      return false;
    }
  }
    /**
     * Indicates whether the configuration entry that will result from a
     * proposed modification is acceptable to this change listener.
     *
     * @param configEntry
     *            The configuration entry that will result from the requested
     *            update.
     * @param unacceptableReason
     *            A buffer to which this method can append a human-readable
     *            message explaining why the proposed change is not acceptable.
     * @param newConfigEntry
     *            The configuration entry that caused the notification (will be
     *            different from <code>configEntry</code> if a dependency was
     *            modified).
     * @return <CODE>true</CODE> if the proposed entry contains an acceptable
     *         configuration, or <CODE>false</CODE> if it does not.
     */
    public boolean configChangeIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason,
            ConfigEntry newConfigEntry) {
        try {
            ServerManagementContext context = ServerManagementContext.getInstance();
            cachedManagedObject = context.decode(path, configEntry, newConfigEntry);
        } catch (DecodingException e) {
            unacceptableReason.append(e.getMessageObject());
            return false;
        }
        // Give up immediately if a constraint violation occurs.
        try {
            cachedManagedObject.ensureIsUsable();
        } catch (ConstraintViolationException e) {
            generateUnacceptableReason(e.getMessages(), unacceptableReason);
            return false;
        }
  /**
   * Finalizes this configuration change listener adaptor. This method
   * must be called before this change listener is removed.
   */
  public void finalizeChangeListener() {
    // Remove the dependency listeners.
    for (DN dependency : dependencies) {
      ConfigEntry listenerConfigEntry = getConfigEntry(dependency);
      if (listenerConfigEntry != null) {
        listenerConfigEntry.deregisterChangeListener(dependencyListener);
      }
        // Let the change listener decide.
        List<LocalizableMessage> reasons = new LinkedList<LocalizableMessage>();
        if (listener.isConfigurationChangeAcceptable(cachedManagedObject, reasons)) {
            return true;
        } else {
            generateUnacceptableReason(reasons, unacceptableReason);
            return false;
        }
    }
    // Now remove the cleaner listener as it will no longer be
    // needed.
    ConfigEntry parentConfigEntry = getConfigEntry(dn.getParent());
    if (parentConfigEntry != null) {
      parentConfigEntry.deregisterDeleteListener(cleanerListener);
    /**
     * Finalizes this configuration change listener adaptor. This method must be
     * called before this change listener is removed.
     */
    public void finalizeChangeListener() {
        // Remove the dependency listeners.
        for (DN dependency : dependencies) {
            ConfigEntry listenerConfigEntry = getConfigEntry(dependency);
            if (listenerConfigEntry != null) {
                listenerConfigEntry.deregisterChangeListener(dependencyListener);
            }
        }
        // Now remove the cleaner listener as it will no longer be
        // needed.
        ConfigEntry parentConfigEntry = getConfigEntry(dn.parent();
        if (parentConfigEntry != null) {
            parentConfigEntry.deregisterDeleteListener(cleanerListener);
        }
    }
  }
  /**
   * Get the server managed object change listener associated with
   * this adaptor.
   *
   * @return Returns the server managed object change listener
   *         associated with this adaptor.
   */
  ServerManagedObjectChangeListener<? super S>
  getServerManagedObjectChangeListener() {
    return listener;
  }
  // Returns the named configuration entry or null if it could not be
  // retrieved.
  private ConfigEntry getConfigEntry(DN dn) {
    try {
      ConfigEntry configEntry = DirectoryServer.getConfigEntry(dn);
      if (configEntry != null) {
        return configEntry;
      } else {
        LocalizableMessage message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
            .get(String.valueOf(dn));
        ErrorLogger.logError(message);
      }
    } catch (ConfigException e) {
      // The dependent entry could not be retrieved.
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      LocalizableMessage message = AdminMessages.ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
          String.valueOf(dn), StaticUtils.getExceptionMessage(e));
      ErrorLogger.logError(message);
    /**
     * Get the server managed object change listener associated with this
     * adaptor.
     *
     * @return Returns the server managed object change listener associated with
     *         this adaptor.
     */
    ServerManagedObjectChangeListener<? super S> getServerManagedObjectChangeListener() {
        return listener;
    }
    return null;
  }
    // Returns the named configuration entry or null if it could not be
    // retrieved.
    private ConfigEntry getConfigEntry(DN dn) {
        try {
            ConfigEntry configEntry = DirectoryServer.getConfigEntry(dn);
            if (configEntry != null) {
                return configEntry;
            } else {
                adminLogger.error(ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST, String.valueOf(dn));
            }
        } catch (ConfigException e) {
            debugLogger.trace("The dependent entry could not be retrieved", e);
            adminLogger.error(ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT, String.valueOf(dn),
                    StaticUtils.getExceptionMessage(e));
        }
        return null;
    }
}
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
@@ -26,16 +26,14 @@
 */
package org.opends.server.admin.server;
import static com.forgerock.opendj.ldap.AdminMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import java.util.LinkedList;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.Constraint;
import org.opends.server.admin.DecodingException;
@@ -47,256 +45,222 @@
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ConfigChangeResult;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ResultCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.ResultCode;
/**
 * An adaptor class which converts {@link ConfigDeleteListener}
 * callbacks to {@link ServerManagedObjectDeleteListener} callbacks.
 * An adaptor class which converts {@link ConfigDeleteListener} callbacks to
 * {@link ServerManagedObjectDeleteListener} callbacks.
 *
 * @param <S>
 *          The type of server configuration handled by the delete
 *          listener.
 *            The type of server configuration handled by the delete listener.
 */
final class ConfigDeleteListenerAdaptor<S extends Configuration> extends
    AbstractConfigListenerAdaptor implements ConfigDeleteListener {
final class ConfigDeleteListenerAdaptor<S extends Configuration> extends AbstractConfigListenerAdaptor implements
        ConfigDeleteListener {
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
    private static final Logger debugLogger = LoggerFactory.getLogger(ConfigDeleteListenerAdaptor.class);
  // Cached managed object between accept/apply callbacks.
  private ServerManagedObject<? extends S> cachedManagedObject;
    // Cached managed object between accept/apply callbacks.
    private ServerManagedObject<? extends S> cachedManagedObject;
  // The instantiable relation.
  private final InstantiableRelationDefinition<?, S> instantiableRelation;
    // The instantiable relation.
    private final InstantiableRelationDefinition<?, S> instantiableRelation;
  // The set relation.
  private final SetRelationDefinition<?, S> setRelation;
    // The set relation.
    private final SetRelationDefinition<?, S> setRelation;
  // The underlying delete listener.
  private final ServerManagedObjectDeleteListener<S> listener;
    // The underlying delete listener.
    private final ServerManagedObjectDeleteListener<S> listener;
  // The optional relation.
  private final OptionalRelationDefinition<?, S> optionalRelation;
    // The optional relation.
    private final OptionalRelationDefinition<?, S> optionalRelation;
  // The managed object path of the parent.
  private final ManagedObjectPath<?, ?> path;
    // The managed object path of the parent.
    private final ManagedObjectPath<?, ?> path;
  /**
   * Create a new configuration delete listener adaptor for an
   * instantiable relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The instantiable relation.
   * @param listener
   *          The underlying delete listener.
   */
  public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path,
      InstantiableRelationDefinition<?, S> relation,
      ServerManagedObjectDeleteListener<S> listener) {
    this.path = path;
    this.optionalRelation = null;
    this.instantiableRelation = relation;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * Create a new configuration delete listener adaptor for an
   * optional relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The optional relation.
   * @param listener
   *          The underlying delete listener.
   */
  public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path,
      OptionalRelationDefinition<?, S> relation,
      ServerManagedObjectDeleteListener<S> listener) {
    this.path = path;
    this.optionalRelation = relation;
    this.instantiableRelation = null;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * Create a new configuration delete listener adaptor for an
   * set relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The set relation.
   * @param listener
   *          The underlying delete listener.
   */
  public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path,
      SetRelationDefinition<?, S> relation,
      ServerManagedObjectDeleteListener<S> listener) {
    this.path = path;
    this.optionalRelation = null;
    this.instantiableRelation = null;
    this.setRelation = relation;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
    if (optionalRelation != null) {
      // Optional managed objects are located directly beneath the
      // parent and have a well-defined name. We need to make sure
      // that we are handling the correct entry.
      ManagedObjectPath<?, ?> childPath = path.child(optionalRelation);
      DN expectedDN = DNBuilder.create(childPath);
      if (!configEntry.getDN().equals(expectedDN)) {
        // Doesn't apply to us.
        return new ConfigChangeResult(ResultCode.SUCCESS, false);
      }
    /**
     * Create a new configuration delete listener adaptor for an instantiable
     * relation.
     *
     * @param path
     *            The managed object path of the parent.
     * @param relation
     *            The instantiable relation.
     * @param listener
     *            The underlying delete listener.
     */
    public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path, InstantiableRelationDefinition<?, S> relation,
            ServerManagedObjectDeleteListener<S> listener) {
        this.path = path;
        this.optionalRelation = null;
        this.instantiableRelation = relation;
        this.setRelation = null;
        this.listener = listener;
        this.cachedManagedObject = null;
    }
    // Cached objects are guaranteed to be from previous acceptable
    // callback.
    ConfigChangeResult result = listener
        .applyConfigurationDelete(cachedManagedObject);
    /**
     * Create a new configuration delete listener adaptor for an optional
     * relation.
     *
     * @param path
     *            The managed object path of the parent.
     * @param relation
     *            The optional relation.
     * @param listener
     *            The underlying delete listener.
     */
    public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path, OptionalRelationDefinition<?, S> relation,
            ServerManagedObjectDeleteListener<S> listener) {
        this.path = path;
        this.optionalRelation = relation;
        this.instantiableRelation = null;
        this.setRelation = null;
        this.listener = listener;
        this.cachedManagedObject = null;
    }
    // Now apply post constraint call-backs.
    if (result.getResultCode() == ResultCode.SUCCESS) {
      ManagedObjectDefinition<?, ?> d = cachedManagedObject
          .getManagedObjectDefinition();
      for (Constraint constraint : d.getAllConstraints()) {
        for (ServerConstraintHandler handler : constraint
            .getServerConstraintHandlers()) {
          try {
            handler.performPostDelete(cachedManagedObject);
          } catch (ConfigException e) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
    /**
     * Create a new configuration delete listener adaptor for an set relation.
     *
     * @param path
     *            The managed object path of the parent.
     * @param relation
     *            The set relation.
     * @param listener
     *            The underlying delete listener.
     */
    public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path, SetRelationDefinition<?, S> relation,
            ServerManagedObjectDeleteListener<S> listener) {
        this.path = path;
        this.optionalRelation = null;
        this.instantiableRelation = null;
        this.setRelation = relation;
        this.listener = listener;
        this.cachedManagedObject = null;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
        if (optionalRelation != null) {
            // Optional managed objects are located directly beneath the
            // parent and have a well-defined name. We need to make sure
            // that we are handling the correct entry.
            ManagedObjectPath<?, ?> childPath = path.child(optionalRelation);
            DN expectedDN = DNBuilder.create(childPath);
            if (!configEntry.getDN().equals(expectedDN)) {
                // Doesn't apply to us.
                return new ConfigChangeResult(ResultCode.SUCCESS, false);
            }
          }
        }
      }
        // Cached objects are guaranteed to be from previous acceptable
        // callback.
        ConfigChangeResult result = listener.applyConfigurationDelete(cachedManagedObject);
        // Now apply post constraint call-backs.
        if (result.getResultCode() == ResultCode.SUCCESS) {
            ManagedObjectDefinition<?, ?> d = cachedManagedObject.getManagedObjectDefinition();
            for (Constraint constraint : d.getAllConstraints()) {
                for (ServerConstraintHandler handler : constraint.getServerConstraintHandlers()) {
                    try {
                        handler.performPostDelete(cachedManagedObject);
                    } catch (ConfigException e) {
                        debugLogger.trace("Unable to perform post delete", e);
                    }
                }
            }
        }
        return result;
    }
    return result;
  }
    /**
     * {@inheritDoc}
     */
    public boolean configDeleteIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason) {
        DN dn = configEntry.getDN();
        AttributeValue av = dn.rdn().getAttributeValue(0);
        String name = av.getValue().toString().trim();
  /**
   * {@inheritDoc}
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
      LocalizableMessageBuilder unacceptableReason) {
    DN dn = configEntry.getDN();
    AttributeValue av = dn.getRDN().getAttributeValue(0);
    String name = av.getValue().toString().trim();
    try {
      ManagedObjectPath<?, ? extends S> childPath;
      if (instantiableRelation != null) {
        childPath = path.child(instantiableRelation, name);
      } else if (setRelation != null) {
        try {
          childPath = path.child(setRelation, name);
        } catch (IllegalArgumentException e) {
          throw new DefinitionDecodingException(setRelation
              .getChildDefinition(), Reason.WRONG_TYPE_INFORMATION);
            ManagedObjectPath<?, ? extends S> childPath;
            if (instantiableRelation != null) {
                childPath = path.child(instantiableRelation, name);
            } else if (setRelation != null) {
                try {
                    childPath = path.child(setRelation, name);
                } catch (IllegalArgumentException e) {
                    throw new DefinitionDecodingException(setRelation.getChildDefinition(),
                            Reason.WRONG_TYPE_INFORMATION);
                }
            } else {
                // Optional managed objects are located directly beneath the
                // parent and have a well-defined name. We need to make sure
                // that we are handling the correct entry.
                childPath = path.child(optionalRelation);
                DN expectedDN = DNBuilder.create(childPath);
                if (!dn.equals(expectedDN)) {
                    // Doesn't apply to us.
                    return true;
                }
            }
            ServerManagementContext context = ServerManagementContext.getInstance();
            cachedManagedObject = context.decode(childPath, configEntry);
        } catch (DecodingException e) {
            unacceptableReason.append(e.getMessageObject());
            return false;
        }
      } else {
        // Optional managed objects are located directly beneath the
        // parent and have a well-defined name. We need to make sure
        // that we are handling the correct entry.
        childPath = path.child(optionalRelation);
        DN expectedDN = DNBuilder.create(childPath);
        if (!dn.equals(expectedDN)) {
          // Doesn't apply to us.
          return true;
        List<LocalizableMessage> reasons = new LinkedList<LocalizableMessage>();
        // Enforce any constraints.
        boolean isDeleteAllowed = true;
        ManagedObjectDefinition<?, ?> d = cachedManagedObject.getManagedObjectDefinition();
        for (Constraint constraint : d.getAllConstraints()) {
            for (ServerConstraintHandler handler : constraint.getServerConstraintHandlers()) {
                try {
                    if (!handler.isDeleteAllowed(cachedManagedObject, reasons)) {
                        isDeleteAllowed = false;
                    }
                } catch (ConfigException e) {
                    LocalizableMessage message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e.getMessageObject());
                    reasons.add(message);
                    isDeleteAllowed = false;
                }
            }
        }
      }
      ServerManagementContext context = ServerManagementContext.getInstance();
      cachedManagedObject = context.decode(childPath, configEntry);
    } catch (DecodingException e) {
      unacceptableReason.append(e.getMessageObject());
      return false;
    }
    List<LocalizableMessage> reasons = new LinkedList<Message>();
    // Enforce any constraints.
    boolean isDeleteAllowed = true;
    ManagedObjectDefinition<?, ?> d = cachedManagedObject
        .getManagedObjectDefinition();
    for (Constraint constraint : d.getAllConstraints()) {
      for (ServerConstraintHandler handler : constraint
          .getServerConstraintHandlers()) {
        try {
          if (!handler.isDeleteAllowed(cachedManagedObject, reasons)) {
            isDeleteAllowed = false;
          }
        } catch (ConfigException e) {
          LocalizableMessage message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
              .getMessageObject());
          reasons.add(message);
          isDeleteAllowed = false;
        // Give up immediately if a constraint violation occurs.
        if (!isDeleteAllowed) {
            generateUnacceptableReason(reasons, unacceptableReason);
            return false;
        }
      }
        // Let the delete listener decide.
        if (listener.isConfigurationDeleteAcceptable(cachedManagedObject, reasons)) {
            return true;
        } else {
            generateUnacceptableReason(reasons, unacceptableReason);
            return false;
        }
    }
    // Give up immediately if a constraint violation occurs.
    if (!isDeleteAllowed) {
      generateUnacceptableReason(reasons, unacceptableReason);
      return false;
    /**
     * Get the server managed object delete listener associated with this
     * adaptor.
     *
     * @return Returns the server managed object delete listener associated with
     *         this adaptor.
     */
    ServerManagedObjectDeleteListener<S> getServerManagedObjectDeleteListener() {
        return listener;
    }
    // Let the delete listener decide.
    if (listener.isConfigurationDeleteAcceptable(cachedManagedObject,
        reasons)) {
      return true;
    } else {
      generateUnacceptableReason(reasons, unacceptableReason);
      return false;
    }
  }
  /**
   * Get the server managed object delete listener associated with
   * this adaptor.
   *
   * @return Returns the server managed object delete listener
   *         associated with this adaptor.
   */
  ServerManagedObjectDeleteListener<S> getServerManagedObjectDeleteListener() {
    return listener;
  }
}
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigExceptionFactory.java
@@ -25,124 +25,104 @@
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import org.forgerock.i18n.LocalizableMessage;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import static com.forgerock.opendj.util.StaticUtils.*;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.config.ConfigException;
import org.opends.messages.AdminMessages;
import org.opends.server.util.DynamicConstants;
import com.forgerock.opendj.ldap.AdminMessages;
import org.forgerock.opendj.ldap.DN;
/**
 * A utility class for converting admin exceptions to config exceptions.
 */
final class ConfigExceptionFactory {
  // The singleton instance.
  private static final ConfigExceptionFactory INSTANCE =
    new ConfigExceptionFactory();
    // The singleton instance.
    private static final ConfigExceptionFactory INSTANCE = new ConfigExceptionFactory();
    // Prevent instantiation.
    private ConfigExceptionFactory() {
        // Do nothing.
    }
    /**
     * Get the configuration exception factory instance.
     *
     * @return Returns the configuration exception factory instance.
     */
    public static ConfigExceptionFactory getInstance() {
        return INSTANCE;
    }
  // Prevent instantiation.
  private ConfigExceptionFactory() {
    // Do nothing.
  }
    /**
     * Create a configuration exception from a definition decoding exception.
     *
     * @param dn
     *            The dn of the configuration entry that could not be decoded.
     * @param e
     *            The definition decoding exception
     * @return Returns the configuration exception.
     */
    public ConfigException createDecodingExceptionAdaptor(DN dn, DefinitionDecodingException e) {
        LocalizableMessage message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.get(String.valueOf(dn),
                stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD));
        return new ConfigException(message, e);
    }
    /**
     * Create a configuration exception from a server managed object decoding
     * exception.
     *
     * @param e
     *            The server managed object decoding exception.
     * @return Returns the configuration exception.
     */
    public ConfigException createDecodingExceptionAdaptor(ServerManagedObjectDecodingException e) {
        DN dn = e.getPartialManagedObject().getDN();
        LocalizableMessage message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.get(String.valueOf(dn),
                stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD));
        return new ConfigException(message, e);
    }
  /**
   * Get the configuration exception factory instance.
   *
   * @return Returns the configuration exception factory instance.
   */
  public static ConfigExceptionFactory getInstance() {
    return INSTANCE;
  }
    /**
     * Create a configuration exception from a constraints violation decoding
     * exception.
     *
     * @param e
     *            The constraints violation decoding exception.
     * @return Returns the configuration exception.
     */
    public ConfigException createDecodingExceptionAdaptor(ConstraintViolationException e) {
        DN dn = e.getManagedObject().getDN();
        LocalizableMessage message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.get(String.valueOf(dn),
                stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD));
        return new ConfigException(message, e);
    }
    /**
     * Create an exception that describes a problem that occurred when
     * attempting to load and instantiate a class.
     *
     * @param dn
     *            The dn of the configuration entry was being processed.
     * @param className
     *            The name of the class that could not be loaded or
     *            instantiated.
     * @param e
     *            The exception that occurred.
     * @return Returns the configuration exception.
     */
  /**
   * Create a configuration exception from a definition decoding exception.
   *
   * @param dn
   *          The dn of the configuration entry that could not be decoded.
   * @param e
   *          The definition decoding exception
   * @return Returns the configuration exception.
   */
  public ConfigException createDecodingExceptionAdaptor(DN dn,
      DefinitionDecodingException e) {
    LocalizableMessage message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.
        get(String.valueOf(dn), stackTraceToSingleLineString(e));
    return new ConfigException(message, e);
  }
  /**
   * Create a configuration exception from a server managed object decoding
   * exception.
   *
   * @param e
   *          The server managed object decoding exception.
   * @return Returns the configuration exception.
   */
  public ConfigException createDecodingExceptionAdaptor(
      ServerManagedObjectDecodingException e) {
    DN dn = e.getPartialManagedObject().getDN();
    LocalizableMessage message =
            AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.get(
                    String.valueOf(dn),
        stackTraceToSingleLineString(e));
    return new ConfigException(message, e);
  }
  /**
   * Create a configuration exception from a constraints violation
   * decoding exception.
   *
   * @param e
   *          The constraints violation decoding exception.
   * @return Returns the configuration exception.
   */
  public ConfigException createDecodingExceptionAdaptor(
      ConstraintViolationException e) {
    DN dn = e.getManagedObject().getDN();
    LocalizableMessage message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM
        .get(String.valueOf(dn), stackTraceToSingleLineString(e));
    return new ConfigException(message, e);
  }
  /**
   * Create an exception that describes a problem that occurred when
   * attempting to load and instantiate a class.
   *
   * @param dn
   *          The dn of the configuration entry was being processed.
   * @param className
   *          The name of the class that could not be loaded or
   *          instantiated.
   * @param e
   *          The exception that occurred.
   * @return Returns the configuration exception.
   */
  public ConfigException createClassLoadingExceptionAdaptor(DN dn,
      String className, Exception e) {
    LocalizableMessage message = AdminMessages.ERR_ADMIN_CANNOT_INSTANTIATE_CLASS.
        get(String.valueOf(className), String.valueOf(dn),
            stackTraceToSingleLineString(e));
    return new ConfigException(message, e);
  }
    public ConfigException createClassLoadingExceptionAdaptor(DN dn, String className, Exception e) {
        LocalizableMessage message = AdminMessages.ERR_ADMIN_CANNOT_INSTANTIATE_CLASS.get(String.valueOf(className),
                String.valueOf(dn), stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD));
        return new ConfigException(message, e);
    }
}
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationChangeListener.java
@@ -25,53 +25,46 @@
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import org.forgerock.i18n.LocalizableMessage;
import java.util.List;
import org.opends.server.admin.Configuration;
import org.opends.server.types.ConfigChangeResult;
/**
 * This interface defines the methods that a Directory Server
 * configurable component should implement if it wishes to be able to
 * receive notifications when a its associated configuration is
 * changed.
 * This interface defines the methods that a Directory Server configurable
 * component should implement if it wishes to be able to receive notifications
 * when a its associated configuration is changed.
 *
 * @param <T>
 *          The type of configuration that this listener should be
 *          notified about.
 *            The type of configuration that this listener should be notified
 *            about.
 */
public interface ConfigurationChangeListener<T extends Configuration> {
  /**
   * Indicates whether the proposed change to the configuration is
   * acceptable to this change listener.
   *
   * @param configuration
   *          The new configuration containing the changes.
   * @param unacceptableReasons
   *          A list that can be used to hold messages about why the
   *          provided configuration is not acceptable.
   * @return Returns <code>true</code> if the proposed change is
   *         acceptable, or <code>false</code> if it is not.
   */
  public boolean isConfigurationChangeAcceptable(T configuration,
      List<LocalizableMessage> unacceptableReasons);
    /**
     * Indicates whether the proposed change to the configuration is acceptable
     * to this change listener.
     *
     * @param configuration
     *            The new configuration containing the changes.
     * @param unacceptableReasons
     *            A list that can be used to hold messages about why the
     *            provided configuration is not acceptable.
     * @return Returns <code>true</code> if the proposed change is acceptable,
     *         or <code>false</code> if it is not.
     */
    public boolean isConfigurationChangeAcceptable(T configuration, List<LocalizableMessage> unacceptableReasons);
  /**
   * Applies the configuration changes to this change listener.
   *
   * @param configuration
   *          The new configuration containing the changes.
   * @return Returns information about the result of changing the
   *         configuration.
   */
  public ConfigChangeResult applyConfigurationChange(T configuration);
    /**
     * Applies the configuration changes to this change listener.
     *
     * @param configuration
     *            The new configuration containing the changes.
     * @return Returns information about the result of changing the
     *         configuration.
     */
    public ConfigChangeResult applyConfigurationChange(T configuration);
}
opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationDeleteListener.java
@@ -25,52 +25,46 @@
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import org.forgerock.i18n.LocalizableMessage;
import java.util.List;
import org.opends.server.admin.Configuration;
import org.opends.server.types.ConfigChangeResult;
/**
 * This interface defines the methods that a Directory Server
 * configurable component should implement if it wishes to be able to
 * receive notifications when an existing configuration is deleted.
 * This interface defines the methods that a Directory Server configurable
 * component should implement if it wishes to be able to receive notifications
 * when an existing configuration is deleted.
 *
 * @param <T>
 *          The type of configuration that this listener should be
 *          notified about.
 *            The type of configuration that this listener should be notified
 *            about.
 */
public interface ConfigurationDeleteListener<T extends Configuration> {
  /**
   * Indicates whether the proposed deletion of an existing
   * configuration is acceptable to this delete listener.
   *
   * @param configuration
   *          The configuration that will be deleted.
   * @param unacceptableReasons
   *          A list that can be used to hold messages about why the
   *          provided configuration is not acceptable.
   * @return Returns <code>true</code> if the proposed deletion is
   *         acceptable, or <code>false</code> if it is not.
   */
  public boolean isConfigurationDeleteAcceptable(T configuration,
      List<LocalizableMessage> unacceptableReasons);
    /**
     * Indicates whether the proposed deletion of an existing configuration is
     * acceptable to this delete listener.
     *
     * @param configuration
     *            The configuration that will be deleted.
     * @param unacceptableReasons
     *            A list that can be used to hold messages about why the
     *            provided configuration is not acceptable.
     * @return Returns <code>true</code> if the proposed deletion is acceptable,
     *         or <code>false</code> if it is not.
     */
    public boolean isConfigurationDeleteAcceptable(T configuration, List<LocalizableMessage> unacceptableReasons);
  /**
   * Deletes an existing configuration from this delete listener.
   *
   * @param configuration
   *          The existing configuration that will be deleted.
   * @return Returns information about the result of deleting the
   *         configuration.
   */
  public ConfigChangeResult applyConfigurationDelete(T configuration);
    /**
     * Deletes an existing configuration from this delete listener.
     *
     * @param configuration
     *            The existing configuration that will be deleted.
     * @return Returns information about the result of deleting the
     *         configuration.
     */
    public ConfigChangeResult applyConfigurationDelete(T configuration);
}
opendj-admin/src/main/java/org/opends/server/admin/server/ConstraintViolationException.java
@@ -24,12 +24,10 @@
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import static com.forgerock.opendj.ldap.AdminMessages.*;
import static com.forgerock.opendj.util.Validator.*;
import java.util.ArrayList;
import java.util.Collection;
@@ -38,142 +36,115 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.opends.server.admin.DecodingException;
import static com.forgerock.opendj.util.Validator.*;
/**
 * This exception is thrown when the server refuses to use or delete a
 * managed object due to one or more constraints that cannot be
 * satisfied.
 * This exception is thrown when the server refuses to use or delete a managed
 * object due to one or more constraints that cannot be satisfied.
 */
public class ConstraintViolationException extends DecodingException {
  /**
   * Serialization ID.
   */
  private static final long serialVersionUID = -4902443848460011875L;
    /**
     * Serialization ID.
     */
    private static final long serialVersionUID = -4902443848460011875L;
  // The server managed object.
  private final ServerManagedObject<?> managedObject;
    // The server managed object.
    private final ServerManagedObject<?> managedObject;
    // Gets the default message.
    private static LocalizableMessage getDefaultMessage(Collection<LocalizableMessage> messages) {
        ensureNotNull(messages);
        ensureTrue(!messages.isEmpty(), "messages should not be empty");
  // Gets the default message.
  private static LocalizableMessage getDefaultMessage(Collection<Message> messages) {
    Validator.ensureNotNull(messages);
    Validator.ensureTrue(!messages.isEmpty());
    if (messages.size() == 1) {
      return ERR_CONSTRAINT_VIOLATION_EXCEPTION_SINGLE.get(messages.iterator()
          .next());
    } else {
      return ERR_CONSTRAINT_VIOLATION_EXCEPTION_PLURAL
          .get(getSingleMessage(messages));
    }
  }
  // Merge the messages into a single message.
  private static LocalizableMessage getSingleMessage(Collection<Message> messages) {
    if (messages.size() == 1) {
      return messages.iterator().next();
    } else {
      LocalizableMessageBuilder builder = new MessageBuilder();
      boolean isFirst = true;
      for (LocalizableMessage m : messages) {
        if (!isFirst) {
          builder.append(";  ");
        if (messages.size() == 1) {
            return ERR_CONSTRAINT_VIOLATION_EXCEPTION_SINGLE.get(messages.iterator().next());
        } else {
            return ERR_CONSTRAINT_VIOLATION_EXCEPTION_PLURAL.get(getSingleMessage(messages));
        }
        builder.append(m);
        isFirst = false;
      }
      return builder.toMessage();
    }
  }
  // The messages describing the constraint violations that occurred.
  private final Collection<LocalizableMessage> messages;
    // Merge the messages into a single message.
    private static LocalizableMessage getSingleMessage(Collection<LocalizableMessage> messages) {
        if (messages.size() == 1) {
            return messages.iterator().next();
        } else {
            LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
            boolean isFirst = true;
            for (LocalizableMessage m : messages) {
                if (!isFirst) {
                    builder.append(";  ");
                }
                builder.append(m);
                isFirst = false;
            }
            return builder.toMessage();
        }
    }
  /**
   * Creates a new constraint violation exception with the provided
   * messages.
   *
   * @param managedObject
   *          The server managed object which caused the constraint
   *          violations.
   * @param messages
   *          The messages describing the constraint violations that
   *          occurred (must be non-<code>null</code> and
   *          non-empty).
   */
  public ConstraintViolationException(ServerManagedObject<?> managedObject,
      Collection<LocalizableMessage> messages) {
    super(getDefaultMessage(messages));
    // The messages describing the constraint violations that occurred.
    private final Collection<LocalizableMessage> messages;
    this.managedObject = managedObject;
    this.messages = new ArrayList<LocalizableMessage>(messages);
  }
    /**
     * Creates a new constraint violation exception with the provided messages.
     *
     * @param managedObject
     *            The server managed object which caused the constraint
     *            violations.
     * @param messages
     *            The messages describing the constraint violations that
     *            occurred (must be non-<code>null</code> and non-empty).
     */
    public ConstraintViolationException(ServerManagedObject<?> managedObject, Collection<LocalizableMessage> messages) {
        super(getDefaultMessage(messages));
        this.managedObject = managedObject;
        this.messages = new ArrayList<LocalizableMessage>(messages);
    }
    /**
     * Creates a new constraint violation exception with the provided message.
     *
     * @param managedObject
     *            The server managed object which caused the constraint
     *            violations.
     * @param message
     *            The message describing the constraint violation that occurred.
     */
    public ConstraintViolationException(ServerManagedObject<?> managedObject, LocalizableMessage message) {
        this(managedObject, Collections.singleton(message));
    }
  /**
   * Creates a new constraint violation exception with the provided
   * message.
   *
   * @param managedObject
   *          The server managed object which caused the constraint
   *          violations.
   * @param message
   *          The message describing the constraint violation that
   *          occurred.
   */
  public ConstraintViolationException(ServerManagedObject<?> managedObject,
      LocalizableMessage message) {
    this(managedObject, Collections.singleton(message));
  }
    /**
     * Gets an unmodifiable collection view of the messages describing the
     * constraint violations that occurred.
     *
     * @return Returns an unmodifiable collection view of the messages
     *         describing the constraint violations that occurred.
     */
    public Collection<LocalizableMessage> getMessages() {
        return Collections.unmodifiableCollection(messages);
    }
    /**
     * Creates a single message listing all the messages combined into a single
     * list separated by semi-colons.
     *
     * @return Returns a single message listing all the messages combined into a
     *         single list separated by semi-colons.
     */
    public LocalizableMessage getMessagesAsSingleMessage() {
        return getSingleMessage(messages);
    }
  /**
   * Gets an unmodifiable collection view of the messages describing
   * the constraint violations that occurred.
   *
   * @return Returns an unmodifiable collection view of the messages
   *         describing the constraint violations that occurred.
   */
  public Collection<LocalizableMessage> getMessages() {
    return Collections.unmodifiableCollection(messages);
  }
  /**
   * Creates a single message listing all the messages combined into a
   * single list separated by semi-colons.
   *
   * @return Returns a single message listing all the messages
   *         combined into a single list separated by semi-colons.
   */
  public LocalizableMessage getMessagesAsSingleMessage() {
    return getSingleMessage(messages);
  }
  /**
   * Gets the server managed object which caused the constraint
   * violations.
   *
   * @return Returns the server managed object which caused the
   *         constraint violations.
   */
  public ServerManagedObject<?> getManagedObject() {
    return managedObject;
  }
    /**
     * Gets the server managed object which caused the constraint violations.
     *
     * @return Returns the server managed object which caused the constraint
     *         violations.
     */
    public ServerManagedObject<?> getManagedObject() {
        return managedObject;
    }
}
opendj-admin/src/main/java/org/opends/server/admin/server/DNBuilder.java
@@ -27,64 +27,47 @@
package org.opends.server.admin.server;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.types.DirectoryException;
/**
 * A factory class for creating <code>DN</code>s from managed
 * object paths.
 * A factory class for creating <code>DN</code>s from managed object paths.
 */
final class DNBuilder {
  /**
   * Creates a new DN representing the specified managed object path.
   *
   * @param path
   *          The managed object path.
   * @return Returns a new DN representing the specified managed
   *         object path.
   */
  public static DN create(ManagedObjectPath<?, ?> path) {
    return path.toDN();
  }
  /**
   * Creates a new DN representing the specified managed object path
   * and relation.
   *
   * @param path
   *          The managed object path.
   * @param relation
   *          The child relation.
   * @return Returns a new DN representing the specified managed
   *         object path and relation.
   */
  public static DN create(ManagedObjectPath<?, ?> path,
      RelationDefinition<?, ?> relation) {
    DN dn = path.toDN();
    try {
      LDAPProfile profile = LDAPProfile.getInstance();
      DN localName = DN.decode(profile.getRelationRDNSequence(relation));
      return dn.concat(localName);
    } catch (DirectoryException e) {
      throw new RuntimeException(e);
    /**
     * Creates a new DN representing the specified managed object path.
     *
     * @param path
     *            The managed object path.
     * @return Returns a new DN representing the specified managed object path.
     */
    public static DN create(ManagedObjectPath<?, ?> path) {
        return path.toDN();
    }
  }
    /**
     * Creates a new DN representing the specified managed object path and
     * relation.
     *
     * @param path
     *            The managed object path.
     * @param relation
     *            The child relation.
     * @return Returns a new DN representing the specified managed object path
     *         and relation.
     */
    public static DN create(ManagedObjectPath<?, ?> path, RelationDefinition<?, ?> relation) {
        DN dn = path.toDN();
        LDAPProfile profile = LDAPProfile.getInstance();
        DN localName = DN.valueOf(profile.getRelationRDNSequence(relation));
        return dn.child(localName);
    }
  // Prevent instantiation.
  private DNBuilder() {
    // No implementation required.
  }
    // Prevent instantiation.
    private DNBuilder() {
        // No implementation required.
    }
}
opendj-admin/src/main/java/org/opends/server/admin/server/DelayedConfigAddListener.java
@@ -26,166 +26,139 @@
 */
package org.opends.server.admin.server;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ConfigChangeResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ResultCode;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * A configuration add listener which will monitor a parent entry to
 * see when a specified child entry has been added. When the child
 * entry is added the add listener will automatically register its
 * "delayed" add or delete listener.
 * A configuration add listener which will monitor a parent entry to see when a
 * specified child entry has been added. When the child entry is added the add
 * listener will automatically register its "delayed" add or delete listener.
 */
final class DelayedConfigAddListener implements ConfigAddListener {
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
    private static final Logger debugLogger = LoggerFactory.getLogger(DelayedConfigAddListener.class);
  // The name of the parent entry.
  private final DN parent;
    // The name of the parent entry.
    private final DN parent;
  // The name of the subordinate entry which should have an add or
  // delete listener registered with it when it is created.
  private final DN child;
    // The name of the subordinate entry which should have an add or
    // delete listener registered with it when it is created.
    private final DN child;
  // The add listener to be registered with the subordinate entry when
  // it is added (or null if a delete listener should be registered).
  private final ConfigAddListener delayedAddListener;
    // The add listener to be registered with the subordinate entry when
    // it is added (or null if a delete listener should be registered).
    private final ConfigAddListener delayedAddListener;
  // The delete listener to be registered with the subordinate entry
  // when it is added (or null if an add listener should be
  // registered).
  private final ConfigDeleteListener delayedDeleteListener;
    // The delete listener to be registered with the subordinate entry
    // when it is added (or null if an add listener should be
    // registered).
    private final ConfigDeleteListener delayedDeleteListener;
  /**
   * Create a new delayed add listener which will register an add
   * listener with the specified entry when it is added.
   *
   * @param child
   *          The name of the subordinate entry which should have an
   *          add listener registered with it when it is created.
   * @param addListener
   *          The add listener to be added to the subordinate entry
   *          when it is added.
   */
  public DelayedConfigAddListener(DN child, ConfigAddListener addListener) {
    this.parent = child.getParent();
    this.child = child;
    this.delayedAddListener = addListener;
    this.delayedDeleteListener = null;
  }
  /**
   * Create a new delayed add listener which will register a delete
   * listener with the specified entry when it is added.
   *
   * @param child
   *          The name of the subordinate entry which should have a
   *          delete listener registered with it when it is created.
   * @param deleteListener
   *          The delete listener to be added to the subordinate entry
   *          when it is added.
   */
  public DelayedConfigAddListener(DN child,
      ConfigDeleteListener deleteListener) {
    this.parent = child.getParent();
    this.child = child;
    this.delayedAddListener = null;
    this.delayedDeleteListener = deleteListener;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry) {
    if (configEntry.getDN().equals(child)) {
      // The subordinate entry matched our criteria so register the
      // listener(s).
      if (delayedAddListener != null) {
        configEntry.registerAddListener(delayedAddListener);
      }
      if (delayedDeleteListener != null) {
        configEntry.registerDeleteListener(delayedDeleteListener);
      }
      // We are no longer needed.
      try {
        ConfigEntry myEntry = DirectoryServer.getConfigEntry(parent);
        if (myEntry != null) {
          myEntry.deregisterAddListener(this);
        }
      } catch (ConfigException e) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        // Ignore this error as it implies that this listener has
        // already been deregistered.
      }
    /**
     * Create a new delayed add listener which will register an add listener
     * with the specified entry when it is added.
     *
     * @param child
     *            The name of the subordinate entry which should have an add
     *            listener registered with it when it is created.
     * @param addListener
     *            The add listener to be added to the subordinate entry when it
     *            is added.
     */
    public DelayedConfigAddListener(DN child, ConfigAddListener addListener) {
        this.parent = child.parent();
        this.child = child;
        this.delayedAddListener = addListener;
        this.delayedDeleteListener = null;
    }
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
    /**
     * Create a new delayed add listener which will register a delete listener
     * with the specified entry when it is added.
     *
     * @param child
     *            The name of the subordinate entry which should have a delete
     *            listener registered with it when it is created.
     * @param deleteListener
     *            The delete listener to be added to the subordinate entry when
     *            it is added.
     */
    public DelayedConfigAddListener(DN child, ConfigDeleteListener deleteListener) {
        this.parent = child.parent();
        this.child = child;
        this.delayedAddListener = null;
        this.delayedDeleteListener = deleteListener;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry) {
        if (configEntry.getDN().equals(child)) {
            // The subordinate entry matched our criteria so register the
            // listener(s).
            if (delayedAddListener != null) {
                configEntry.registerAddListener(delayedAddListener);
            }
            if (delayedDeleteListener != null) {
                configEntry.registerDeleteListener(delayedDeleteListener);
            }
  /**
   * {@inheritDoc}
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
      LocalizableMessageBuilder unacceptableReason) {
    // Always acceptable.
    return true;
  }
            // We are no longer needed.
            try {
                ConfigEntry myEntry = DirectoryServer.getConfigEntry(parent);
                if (myEntry != null) {
                    myEntry.deregisterAddListener(this);
                }
            } catch (ConfigException e) {
                debugLogger.trace("Unable to deregister add listener", e);
                // Ignore this error as it implies that this listener has
                // already been deregistered.
            }
        }
        return new ConfigChangeResult(ResultCode.SUCCESS, false);
    }
    /**
     * {@inheritDoc}
     */
    public boolean configAddIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason) {
        // Always acceptable.
        return true;
    }
  /**
   * Gets the delayed add listener.
   * <p>
   * This method is provided for unit-testing.
   *
   * @return Returns the delayed add listener, or <code>null</code>
   *         if this listener is delaying a delete listener.
   */
  ConfigAddListener getDelayedAddListener() {
    return delayedAddListener;
  }
    /**
     * Gets the delayed add listener.
     * <p>
     * This method is provided for unit-testing.
     *
     * @return Returns the delayed add listener, or <code>null</code> if this
     *         listener is delaying a delete listener.
     */
    ConfigAddListener getDelayedAddListener() {
        return delayedAddListener;
    }
  /**
   * Gets the delayed delete listener.
   * <p>
   * This method is provided for unit-testing.
   *
   * @return Returns the delayed delete listener, or <code>null</code>
   *         if this listener is delaying a add listener.
   */
  ConfigDeleteListener getDelayedDeleteListener() {
    return delayedDeleteListener;
  }
    /**
     * Gets the delayed delete listener.
     * <p>
     * This method is provided for unit-testing.
     *
     * @return Returns the delayed delete listener, or <code>null</code> if this
     *         listener is delaying a add listener.
     */
    ConfigDeleteListener getDelayedDeleteListener() {
        return delayedDeleteListener;
    }
}
opendj-admin/src/main/java/org/opends/server/admin/server/ServerConstraintHandler.java
@@ -26,168 +26,146 @@
 */
package org.opends.server.admin.server;
import java.util.Collection;
import org.forgerock.i18n.LocalizableMessage;
import org.opends.server.config.ConfigException;
/**
 * An interface for performing server-side constraint validation.
 * <p>
 * Constraints are evaluated immediately before and after write
 * operations are performed. Server-side constraints are evaluated in
 * two phases: the first phase determines if the proposed add, delete,
 * or modification is acceptable according to the constraint. If one
 * or more constraints fails, the write write operation is refused,
 * and the client will receive an
 * <code>OperationRejectedException</code> exception. The second
 * phase is invoked once the add, delete, or modification request has
 * been allowed and any changes applied. The second phase gives the
 * constraint handler a chance to register listener call-backs if
 * required.
 * Constraints are evaluated immediately before and after write operations are
 * performed. Server-side constraints are evaluated in two phases: the first
 * phase determines if the proposed add, delete, or modification is acceptable
 * according to the constraint. If one or more constraints fails, the write
 * write operation is refused, and the client will receive an
 * <code>OperationRejectedException</code> exception. The second phase is
 * invoked once the add, delete, or modification request has been allowed and
 * any changes applied. The second phase gives the constraint handler a chance
 * to register listener call-backs if required.
 * <p>
 * A server constraint handler must override at least one of the
 * provided methods.
 * A server constraint handler must override at least one of the provided
 * methods.
 *
 * @see org.opends.server.admin.Constraint
 */
public abstract class ServerConstraintHandler {
  /**
   * Creates a new server constraint handler.
   */
  protected ServerConstraintHandler() {
    // No implementation required.
  }
    /**
     * Creates a new server constraint handler.
     */
    protected ServerConstraintHandler() {
        // No implementation required.
    }
    /**
     * Determines whether or not the existing managed object can be deleted from
     * the server's configuration. For example, an implementation might enforce
     * referential integrity by preventing referenced managed objects from being
     * deleted.
     * <p>
     * If the constraint is not satisfied, the implementation must return
     * <code>false</code> and add a message describing why the managed object
     * cannot be deleted.
     * <p>
     * The default implementation is to return <code>true</code>.
     *
     * @param managedObject
     *            The managed object which is about to be deleted.
     * @param unacceptableReasons
     *            A list of messages to which error messages should be added.
     * @return Returns <code>true</code> if this constraint is satisfied, or
     *         <code>false</code> if it is not and the managed object cannot be
     *         deleted.
     * @throws ConfigException
     *             If an configuration exception prevented this constraint from
     *             being evaluated.
     */
    public boolean isDeleteAllowed(ServerManagedObject<?> managedObject,
            Collection<LocalizableMessage> unacceptableReasons) throws ConfigException {
        return true;
    }
    /**
     * Determines whether or not the provided managed object can be used by the
     * server. This method is invoked each time a managed object is decoded by
     * the administration framework: when an attempt is made to add a new
     * configuration, modify an existing configuration, or during server
     * initialization. If the constraint is not satisfied the managed object
     * will be rejected.
     * <p>
     * If the constraint is not satisfied, the implementation must return
     * <code>false</code> and add a message describing why the managed object is
     * not usable.
     * <p>
     * The default implementation is to return <code>true</code>.
     *
     * @param managedObject
     *            The new managed object.
     * @param unacceptableReasons
     *            A list of messages to which error messages should be added.
     * @return Returns <code>true</code> if this constraint is satisfied, or
     *         <code>false</code> if it is not and the managed object cannot be
     *         used.
     * @throws ConfigException
     *             If an configuration exception prevented this constraint from
     *             being evaluated.
     */
    public boolean isUsable(ServerManagedObject<?> managedObject, Collection<LocalizableMessage> unacceptableReasons)
            throws ConfigException {
        return true;
    }
  /**
   * Determines whether or not the existing managed object can be
   * deleted from the server's configuration. For example, an
   * implementation might enforce referential integrity by preventing
   * referenced managed objects from being deleted.
   * <p>
   * If the constraint is not satisfied, the implementation must
   * return <code>false</code> and add a message describing why the
   * managed object cannot be deleted.
   * <p>
   * The default implementation is to return <code>true</code>.
   *
   * @param managedObject
   *          The managed object which is about to be deleted.
   * @param unacceptableReasons
   *          A list of messages to which error messages should be
   *          added.
   * @return Returns <code>true</code> if this constraint is
   *         satisfied, or <code>false</code> if it is not and the
   *         managed object cannot be deleted.
   * @throws ConfigException
   *           If an configuration exception prevented this constraint
   *           from being evaluated.
   */
  public boolean isDeleteAllowed(ServerManagedObject<?> managedObject,
      Collection<LocalizableMessage> unacceptableReasons) throws ConfigException {
    return true;
  }
    /**
     * Performs any post-add processing required by this constraint. This method
     * is invoked after a new managed object has been accepted for use by the
     * administration framework. This might occur during initialization or when
     * a managed object is added at run-time.
     * <p>
     * The default implementation is to do nothing.
     *
     * @param managedObject
     *            The managed object which has just been added to the server's
     *            configuration.
     * @throws ConfigException
     *             If the post-add processing fails due to a configuration
     *             exception.
     */
    public void performPostAdd(ServerManagedObject<?> managedObject) throws ConfigException {
        // Do nothing.
    }
    /**
     * Performs any post-delete processing required by this constraint. This
     * method is invoked after a managed object has been accepted for deletion
     * from the server's configuration.
     * <p>
     * The default implementation is to do nothing.
     *
     * @param managedObject
     *            The managed object which was deleted.
     * @throws ConfigException
     *             If the post-delete processing fails due to a configuration
     *             exception.
     */
    public void performPostDelete(ServerManagedObject<?> managedObject) throws ConfigException {
        // Do nothing.
    }
  /**
   * Determines whether or not the provided managed object can be used
   * by the server. This method is invoked each time a managed object
   * is decoded by the administration framework: when an attempt is
   * made to add a new configuration, modify an existing
   * configuration, or during server initialization. If the constraint
   * is not satisfied the managed object will be rejected.
   * <p>
   * If the constraint is not satisfied, the implementation must
   * return <code>false</code> and add a message describing why the
   * managed object is not usable.
   * <p>
   * The default implementation is to return <code>true</code>.
   *
   * @param managedObject
   *          The new managed object.
   * @param unacceptableReasons
   *          A list of messages to which error messages should be
   *          added.
   * @return Returns <code>true</code> if this constraint is
   *         satisfied, or <code>false</code> if it is not and the
   *         managed object cannot be used.
   * @throws ConfigException
   *           If an configuration exception prevented this constraint
   *           from being evaluated.
   */
  public boolean isUsable(ServerManagedObject<?> managedObject,
      Collection<LocalizableMessage> unacceptableReasons) throws ConfigException {
    return true;
  }
  /**
   * Performs any post-add processing required by this constraint.
   * This method is invoked after a new managed object has been
   * accepted for use by the administration framework. This might
   * occur during initialization or when a managed object is added at
   * run-time.
   * <p>
   * The default implementation is to do nothing.
   *
   * @param managedObject
   *          The managed object which has just been added to the
   *          server's configuration.
   * @throws ConfigException
   *           If the post-add processing fails due to a configuration
   *           exception.
   */
  public void performPostAdd(ServerManagedObject<?> managedObject)
      throws ConfigException {
    // Do nothing.
  }
  /**
   * Performs any post-delete processing required by this constraint.
   * This method is invoked after a managed object has been accepted
   * for deletion from the server's configuration.
   * <p>
   * The default implementation is to do nothing.
   *
   * @param managedObject
   *          The managed object which was deleted.
   * @throws ConfigException
   *           If the post-delete processing fails due to a
   *           configuration exception.
   */
  public void performPostDelete(ServerManagedObject<?> managedObject)
      throws ConfigException {
    // Do nothing.
  }
  /**
   * Performs any post-modify processing required by this constraint.
   * This method is invoked after changes to an existing managed
   * object have been accepted.
   * <p>
   * The default implementation is to do nothing.
   *
   * @param managedObject
   *          The managed object which was modified.
   * @throws ConfigException
   *           If the post-modify processing fails due to a
   *           configuration exception.
   */
  public void performPostModify(ServerManagedObject<?> managedObject)
      throws ConfigException {
    // Do nothing.
  }
    /**
     * Performs any post-modify processing required by this constraint. This
     * method is invoked after changes to an existing managed object have been
     * accepted.
     * <p>
     * The default implementation is to do nothing.
     *
     * @param managedObject
     *            The managed object which was modified.
     * @throws ConfigException
     *             If the post-modify processing fails due to a configuration
     *             exception.
     */
    public void performPostModify(ServerManagedObject<?> managedObject) throws ConfigException {
        // Do nothing.
    }
}
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());
        }
    }
}
opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDecodingException.java
@@ -27,8 +27,6 @@
package org.opends.server.admin.server;
import static com.forgerock.opendj.ldap.AdminMessages.*;
import java.util.Collection;
@@ -42,107 +40,90 @@
import org.opends.server.admin.PropertyException;
import static com.forgerock.opendj.util.Validator.*;
/**
 * The requested server managed object was found but one or more of
 * its properties could not be decoded successfully.
 * The requested server managed object was found but one or more of its
 * properties could not be decoded successfully.
 */
public class ServerManagedObjectDecodingException extends DecodingException {
  /**
   * Version ID required by serializable classes.
   */
  private static final long serialVersionUID = 1598401431084729853L;
    /**
     * Version ID required by serializable classes.
     */
    private static final long serialVersionUID = 1598401431084729853L;
    // Create the message.
    private static LocalizableMessage createMessage(ServerManagedObject<?> partialManagedObject,
            Collection<PropertyException> causes) {
        ensureNotNull(causes);
        ensureTrue(!causes.isEmpty(), "causes should nnot be empty");
        ManagedObjectDefinition<?, ?> d = partialManagedObject.getManagedObjectDefinition();
        if (causes.size() == 1) {
            return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_SINGLE.get(d.getUserFriendlyName(), causes.iterator().next()
                    .getLocalizableMessageObject());
        } else {
            LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
  // Create the message.
  private static LocalizableMessage createMessage(
      ServerManagedObject<?> partialManagedObject,
      Collection<PropertyException> causes) {
    ensureNotNull(causes);
    ensureTrue(!causes.isEmpty(), "causes should nnot be empty");
            boolean isFirst = true;
            for (PropertyException cause : causes) {
                if (!isFirst) {
                    builder.append("; ");
                }
                builder.append(cause.getLocalizableMessageObject());
                isFirst = false;
            }
    ManagedObjectDefinition<?, ?> d = partialManagedObject
        .getManagedObjectDefinition();
    if (causes.size() == 1) {
      return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_SINGLE.get(d
          .getUserFriendlyName(), causes.iterator().next().getMessageObject());
    } else {
      LocalizableMessageBuilder builder = new MessageBuilder();
      boolean isFirst = true;
      for (PropertyException cause : causes) {
        if (!isFirst) {
          builder.append("; ");
            return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_PLURAL.get(d.getUserFriendlyName(), builder.toMessage());
        }
        builder.append(cause.getMessageObject());
        isFirst = false;
      }
      return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_PLURAL.get(d
          .getUserFriendlyName(), builder.toMessage());
    }
  }
  // The exception(s) that caused this decoding exception.
  private final Collection<PropertyException> causes;
    // The exception(s) that caused this decoding exception.
    private final Collection<PropertyException> causes;
  // The partially created server managed object.
  private final ServerManagedObject<?> partialManagedObject;
    // The partially created server managed object.
    private final ServerManagedObject<?> partialManagedObject;
    /**
     * Create a new property decoding exception.
     *
     * @param partialManagedObject
     *            The partially created server managed object containing
     *            properties which were successfully decoded and empty
     *            properties for those which were not (this may include empty
     *            mandatory properties).
     * @param causes
     *            The exception(s) that caused this decoding exception.
     */
    public ServerManagedObjectDecodingException(ServerManagedObject<?> partialManagedObject,
            Collection<PropertyException> causes) {
        super(createMessage(partialManagedObject, causes));
        this.partialManagedObject = partialManagedObject;
        this.causes = Collections.unmodifiableList(new LinkedList<PropertyException>(causes));
    }
  /**
   * Create a new property decoding exception.
   *
   * @param partialManagedObject
   *          The partially created server managed object containing
   *          properties which were successfully decoded and empty
   *          properties for those which were not (this may include
   *          empty mandatory properties).
   * @param causes
   *          The exception(s) that caused this decoding exception.
   */
  public ServerManagedObjectDecodingException(
      ServerManagedObject<?> partialManagedObject,
      Collection<PropertyException> causes) {
    super(createMessage(partialManagedObject, causes));
    /**
     * Get an unmodifiable collection view of the causes of this exception.
     *
     * @return Returns an unmodifiable collection view of the causes of this
     *         exception.
     */
    public Collection<PropertyException> getCauses() {
        return causes;
    }
    this.partialManagedObject = partialManagedObject;
    this.causes = Collections
        .unmodifiableList(new LinkedList<PropertyException>(causes));
  }
  /**
   * Get an unmodifiable collection view of the causes of this
   * exception.
   *
   * @return Returns an unmodifiable collection view of the causes of
   *         this exception.
   */
  public Collection<PropertyException> getCauses() {
    return causes;
  }
  /**
   * Get the partially created server managed object containing
   * properties which were successfully decoded and empty properties
   * for those which were not (this may include empty mandatory
   * properties).
   *
   * @return Returns the partially created server managed object
   *         containing properties which were successfully decoded and
   *         empty properties for those which were not (this may
   *         include empty mandatory properties).
   */
  public ServerManagedObject<?> getPartialManagedObject() {
    return partialManagedObject;
  }
    /**
     * Get the partially created server managed object containing properties
     * which were successfully decoded and empty properties for those which were
     * not (this may include empty mandatory properties).
     *
     * @return Returns the partially created server managed object containing
     *         properties which were successfully decoded and empty properties
     *         for those which were not (this may include empty mandatory
     *         properties).
     */
    public ServerManagedObject<?> getPartialManagedObject() {
        return partialManagedObject;
    }
}
opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java
@@ -27,11 +27,7 @@
package org.opends.server.admin.server;
import static com.forgerock.opendj.ldap.AdminMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.Collection;
@@ -76,911 +72,783 @@
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.std.meta.RootCfgDefn;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.api.AttributeValueDecoder;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.forgerock.opendj.admin.meta.RootCfgDefn;
import org.forgerock.opendj.admin.server.RootCfg;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
/**
 * Server management connection context.
 */
public final class ServerManagementContext {
  /**
   * A default behavior visitor used for retrieving the default values
   * of a property.
   *
   * @param <T>
   *          The type of the property.
   */
  private class DefaultValueFinder<T> implements
      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
    /**
     * A default behavior visitor used for retrieving the default values of a
     * property.
     *
     * @param <T>
     *            The type of the property.
     */
    private class DefaultValueFinder<T> implements DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
    // Any exception that occurred whilst retrieving inherited default
    // values.
    private DefaultBehaviorException exception = null;
        // Any exception that occurred whilst retrieving inherited default
        // values.
        private DefaultBehaviorException exception = null;
    // Optional new configuration entry which does not yet exist in
    // the configuration back-end.
    private final ConfigEntry newConfigEntry;
        // Optional new configuration entry which does not yet exist in
        // the configuration back-end.
        private final ConfigEntry newConfigEntry;
    // The path of the managed object containing the next property.
    private ManagedObjectPath<?, ?> nextPath = null;
        // The path of the managed object containing the next property.
        private ManagedObjectPath<?, ?> nextPath = null;
    // The next property whose default values were required.
    private PropertyDefinition<T> nextProperty = null;
        // The next property whose default values were required.
        private PropertyDefinition<T> nextProperty = null;
        // Private constructor.
        private DefaultValueFinder(ConfigEntry newConfigEntry) {
            this.newConfigEntry = newConfigEntry;
        }
        /**
         * {@inheritDoc}
         */
        public Collection<T> visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
            try {
                return getInheritedProperty(d.getManagedObjectPath(), d.getManagedObjectDefinition(),
                        d.getPropertyName());
            } catch (DefaultBehaviorException e) {
                exception = e;
                return Collections.emptySet();
            }
        }
        /**
         * {@inheritDoc}
         */
        public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
            return Collections.emptySet();
        }
        /**
         * {@inheritDoc}
         */
        public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d, Void p) {
            Collection<String> stringValues = d.getDefaultValues();
            List<T> values = new ArrayList<T>(stringValues.size());
            for (String stringValue : stringValues) {
                try {
                    values.add(nextProperty.decodeValue(stringValue));
                } catch (IllegalPropertyValueStringException e) {
                    exception = new DefaultBehaviorException(nextProperty, e);
                    break;
                }
            }
            return values;
        }
        /**
         * {@inheritDoc}
         */
        public Collection<T> visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
            try {
                return getInheritedProperty(d.getManagedObjectPath(nextPath), d.getManagedObjectDefinition(),
                        d.getPropertyName());
            } catch (DefaultBehaviorException e) {
                exception = e;
                return Collections.emptySet();
            }
        }
        /**
         * {@inheritDoc}
         */
        public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d, Void p) {
            return Collections.emptySet();
        }
        // Find the default values for the next path/property.
        private Collection<T> find(ManagedObjectPath<?, ?> p, PropertyDefinition<T> pd) throws DefaultBehaviorException {
            nextPath = p;
            nextProperty = pd;
            Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(this, null);
            if (exception != null) {
                throw exception;
            }
            if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
                throw new DefaultBehaviorException(pd, new PropertyIsSingleValuedException(pd));
            }
            return values;
        }
        // Get an inherited property value.
        @SuppressWarnings("unchecked")
        private Collection<T> getInheritedProperty(ManagedObjectPath target, AbstractManagedObjectDefinition<?, ?> d,
                String propertyName) throws DefaultBehaviorException {
            // First check that the requested type of managed object
            // corresponds to the path.
            AbstractManagedObjectDefinition<?, ?> supr = target.getManagedObjectDefinition();
            if (!supr.isParentOf(d)) {
                throw new DefaultBehaviorException(nextProperty, new DefinitionDecodingException(supr,
                        Reason.WRONG_TYPE_INFORMATION));
            }
            // Save the current property in case of recursion.
            PropertyDefinition<T> pd1 = nextProperty;
            try {
                // Get the actual managed object definition.
                DN dn = DNBuilder.create(target);
                ConfigEntry configEntry;
                if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
                    configEntry = newConfigEntry;
                } else {
                    configEntry = getManagedObjectConfigEntry(dn);
                }
                DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
                ManagedObjectDefinition<?, ?> mod = d.resolveManagedObjectDefinition(resolver);
                PropertyDefinition<T> pd2;
                try {
                    PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
                    pd2 = pd1.getClass().cast(pdTmp);
                } catch (IllegalArgumentException e) {
                    throw new PropertyNotFoundException(propertyName);
                } catch (ClassCastException e) {
                    // FIXME: would be nice to throw a better exception here.
                    throw new PropertyNotFoundException(propertyName);
                }
                List<AttributeValue> values = getAttribute(mod, pd2, configEntry);
                if (values.isEmpty()) {
                    // Recursively retrieve this property's default values.
                    Collection<T> tmp = find(target, pd2);
                    Collection<T> pvalues = new ArrayList<T>(tmp.size());
                    for (T value : tmp) {
                        pd1.validateValue(value);
                        pvalues.add(value);
                    }
                    return pvalues;
                } else {
                    Collection<T> pvalues = new ArrayList<T>(values.size());
                    for (AttributeValue value : values) {
                        pvalues.add(ValueDecoder.decode(pd1, value));
                    }
                    return pvalues;
                }
            } catch (DefinitionDecodingException e) {
                throw new DefaultBehaviorException(pd1, e);
            } catch (PropertyNotFoundException e) {
                throw new DefaultBehaviorException(pd1, e);
            } catch (IllegalPropertyValueException e) {
                throw new DefaultBehaviorException(pd1, e);
            } catch (IllegalPropertyValueStringException e) {
                throw new DefaultBehaviorException(pd1, e);
            } catch (ConfigException e) {
                throw new DefaultBehaviorException(pd1, e);
            }
        }
    }
    /**
     * A definition resolver that determines the managed object definition from
     * the object classes of a ConfigEntry.
     */
    private class MyDefinitionResolver implements DefinitionResolver {
        // The config entry.
        private final ConfigEntry entry;
        // Private constructor.
        private MyDefinitionResolver(ConfigEntry entry) {
            this.entry = entry;
        }
        /**
         * {@inheritDoc}
         */
        public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
            String oc = LDAPProfile.getInstance().getObjectClass(d);
            return entry.hasObjectClass(oc);
        }
    }
    /**
     * A visitor which is used to decode property LDAP values.
     */
    private static final class ValueDecoder extends PropertyDefinitionVisitor<Object, String> {
        /**
         * Decodes the provided property LDAP value.
         *
         * @param <PD>
         *            The type of the property.
         * @param pd
         *            The property definition.
         * @param value
         *            The LDAP string representation.
         * @return Returns the decoded LDAP value.
         * @throws IllegalPropertyValueStringException
         *             If the property value could not be decoded because it was
         *             invalid.
         */
        public static <PD> PD decode(PropertyDefinition<PD> pd, AttributeValue value)
                throws IllegalPropertyValueStringException {
            String s = value.getValue().toString();
            return pd.castValue(pd.accept(new ValueDecoder(), s));
        }
        // Prevent instantiation.
        private ValueDecoder() {
            // No implementation required.
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public <C extends ConfigurationClient, S extends Configuration> Object visitAggregation(
                AggregationPropertyDefinition<C, S> d, String p) {
            // Aggregations values are stored as full DNs in LDAP, but
            // just their common name is exposed in the admin framework.
            try {
                Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d.getRelationDefinition(), p);
                return reference.getName();
            } catch (IllegalArgumentException e) {
                throw new IllegalPropertyValueStringException(d, p);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public <T> Object visitUnknown(PropertyDefinition<T> d, String p) throws UnknownPropertyDefinitionException {
            // By default the property definition's decoder will do.
            return d.decodeValue(p);
        }
    }
    // Singleton instance.
    private final static ServerManagementContext INSTANCE = new ServerManagementContext();
    /**
     * The root server managed object.
     */
    private static final ServerManagedObject<RootCfg> ROOT = new ServerManagedObject<RootCfg>(
            ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(),
            Collections.<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
    /**
     * Get the single server-side management context.
     *
     * @return Returns the single server-side management context.
     */
    public static ServerManagementContext getInstance() {
        return INSTANCE;
    }
    // Private constructor.
    private DefaultValueFinder(ConfigEntry newConfigEntry) {
      this.newConfigEntry = newConfigEntry;
    private ServerManagementContext() {
        // No implementation required.
    }
    /**
     * {@inheritDoc}
     * Gets the named managed object.
     *
     * @param <C>
     *            The type of client managed object configuration that the path
     *            definition refers to.
     * @param <S>
     *            The type of server managed object configuration that the path
     *            definition refers to.
     * @param path
     *            The path of the managed object.
     * @return Returns the named managed object.
     * @throws ConfigException
     *             If the named managed object could not be found or if it could
     *             not be decoded.
     */
    public Collection<T> visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = e;
        return Collections.emptySet();
      }
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
      return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
        Void p) {
      Collection<String> stringValues = d.getDefaultValues();
      List<T> values = new ArrayList<T>(stringValues.size());
      for (String stringValue : stringValues) {
        try {
          values.add(nextProperty.decodeValue(stringValue));
        } catch (IllegalPropertyValueStringException e) {
          exception = new DefaultBehaviorException(nextProperty, e);
          break;
        }
      }
      return values;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = e;
        return Collections.emptySet();
      }
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        Void p) {
      return Collections.emptySet();
    }
    // Find the default values for the next path/property.
    private Collection<T> find(ManagedObjectPath<?, ?> p,
        PropertyDefinition<T> pd) throws DefaultBehaviorException {
      nextPath = p;
      nextProperty = pd;
      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
          this, null);
      if (exception != null) {
        throw exception;
      }
      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
        throw new DefaultBehaviorException(pd,
            new PropertyIsSingleValuedException(pd));
      }
      return values;
    }
    // Get an inherited property value.
    @SuppressWarnings("unchecked")
    private Collection<T> getInheritedProperty(ManagedObjectPath target,
        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
        throws DefaultBehaviorException {
      // First check that the requested type of managed object
      // corresponds to the path.
      AbstractManagedObjectDefinition<?, ?> supr = target
          .getManagedObjectDefinition();
      if (!supr.isParentOf(d)) {
        throw new DefaultBehaviorException(
            nextProperty, new DefinitionDecodingException(supr,
                Reason.WRONG_TYPE_INFORMATION));
      }
      // Save the current property in case of recursion.
      PropertyDefinition<T> pd1 = nextProperty;
      try {
        // Get the actual managed object definition.
        DN dn = DNBuilder.create(target);
        ConfigEntry configEntry;
        if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
          configEntry = newConfigEntry;
        } else {
          configEntry = getManagedObjectConfigEntry(dn);
    public <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> getManagedObject(
            ManagedObjectPath<C, S> path) throws ConfigException {
        // Be careful to handle the root configuration.
        if (path.isEmpty()) {
            return (ServerManagedObject<S>) getRootConfigurationManagedObject();
        }
        // Get the configuration entry.
        DN targetDN = DNBuilder.create(path);
        ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
        try {
            ServerManagedObject<? extends S> managedObject;
            managedObject = decode(path, configEntry);
            // Enforce any constraints.
            managedObject.ensureIsUsable();
            return managedObject;
        } catch (DefinitionDecodingException e) {
            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(targetDN, e);
        } catch (ServerManagedObjectDecodingException e) {
            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e);
        } catch (ConstraintViolationException e) {
            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e);
        }
    }
    /**
     * Gets the effective value of a property in the named managed object.
     *
     * @param <C>
     *            The type of client managed object configuration that the path
     *            definition refers to.
     * @param <S>
     *            The type of server managed object configuration that the path
     *            definition refers to.
     * @param <PD>
     *            The type of the property to be retrieved.
     * @param path
     *            The path of the managed object containing the property.
     * @param pd
     *            The property to be retrieved.
     * @return Returns the property's effective value, or <code>null</code> if
     *         there are no values defined.
     * @throws IllegalArgumentException
     *             If the property definition is not associated with the
     *             referenced managed object's definition.
     * @throws PropertyException
     *             If the managed object was found but the requested property
     *             could not be decoded.
     * @throws ConfigException
     *             If the named managed object could not be found or if it could
     *             not be decoded.
     */
    public <C extends ConfigurationClient, S extends Configuration, PD> PD getPropertyValue(
            ManagedObjectPath<C, S> path, PropertyDefinition<PD> pd) throws IllegalArgumentException, ConfigException,
            PropertyException {
        SortedSet<PD> values = getPropertyValues(path, pd);
        if (values.isEmpty()) {
            return null;
        } else {
            return values.first();
        }
    }
    /**
     * Gets the effective values of a property in the named managed object.
     *
     * @param <C>
     *            The type of client managed object configuration that the path
     *            definition refers to.
     * @param <S>
     *            The type of server managed object configuration that the path
     *            definition refers to.
     * @param <PD>
     *            The type of the property to be retrieved.
     * @param path
     *            The path of the managed object containing the property.
     * @param pd
     *            The property to be retrieved.
     * @return Returns the property's effective values, or an empty set if there
     *         are no values defined.
     * @throws IllegalArgumentException
     *             If the property definition is not associated with the
     *             referenced managed object's definition.
     * @throws PropertyException
     *             If the managed object was found but the requested property
     *             could not be decoded.
     * @throws ConfigException
     *             If the named managed object could not be found or if it could
     *             not be decoded.
     */
    @SuppressWarnings("unchecked")
    public <C extends ConfigurationClient, S extends Configuration, PD> SortedSet<PD> getPropertyValues(
            ManagedObjectPath<C, S> path, PropertyDefinition<PD> pd) throws IllegalArgumentException, ConfigException,
            PropertyException {
        // Check that the requested property is from the definition
        // associated with the path.
        AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
        PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
        if (tmp != pd) {
            throw new IllegalArgumentException("The property " + pd.getName() + " is not associated with a "
                    + d.getName());
        }
        // Determine the exact type of managed object referenced by the
        // path.
        DN dn = DNBuilder.create(path);
        ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
        DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
        ManagedObjectDefinition<?, ?> mod = d
            .resolveManagedObjectDefinition(resolver);
        ManagedObjectDefinition<? extends C, ? extends S> mod;
        PropertyDefinition<T> pd2;
        try {
          PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
          pd2 = pd1.getClass().cast(pdTmp);
        } catch (IllegalArgumentException e) {
          throw new PropertyNotFoundException(propertyName);
        } catch (ClassCastException e) {
          // FIXME: would be nice to throw a better exception here.
          throw new PropertyNotFoundException(propertyName);
            mod = d.resolveManagedObjectDefinition(resolver);
        } catch (DefinitionDecodingException e) {
            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(dn, e);
        }
        List<AttributeValue> values = getAttribute(mod, pd2, configEntry);
        if (values.isEmpty()) {
          // Recursively retrieve this property's default values.
          Collection<T> tmp = find(target, pd2);
          Collection<T> pvalues = new ArrayList<T>(tmp.size());
          for (T value : tmp) {
            pd1.validateValue(value);
            pvalues.add(value);
          }
          return pvalues;
        } else {
          Collection<T> pvalues = new ArrayList<T>(values.size());
          for (AttributeValue value : values) {
            pvalues.add(ValueDecoder.decode(pd1, value));
          }
          return pvalues;
        }
      } catch (DefinitionDecodingException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (PropertyNotFoundException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (IllegalPropertyValueException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (IllegalPropertyValueStringException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (ConfigException e) {
        throw new DefaultBehaviorException(pd1, e);
      }
        // Make sure we use the correct property definition, the
        // provided one might have been overridden in the resolved
        // definition.
        pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
        List<AttributeValue> values = getAttribute(mod, pd, configEntry);
        return decodeProperty(path.asSubType(mod), pd, values, null);
    }
  }
  /**
   * A definition resolver that determines the managed object
   * definition from the object classes of a ConfigEntry.
   */
  private class MyDefinitionResolver implements DefinitionResolver {
    // The config entry.
    private final ConfigEntry entry;
    // Private constructor.
    private MyDefinitionResolver(ConfigEntry entry) {
      this.entry = entry;
    }
    /**
     * {@inheritDoc}
     */
    public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
      String oc = LDAPProfile.getInstance().getObjectClass(d);
      return entry.hasObjectClass(oc);
    }
  }
  /**
   * A visitor which is used to decode property LDAP values.
   */
  private static final class ValueDecoder extends
      PropertyDefinitionVisitor<Object, String> {
    /**
     * Decodes the provided property LDAP value.
     * Get the root configuration manager associated with this management
     * context.
     *
     * @param <PD>
     *          The type of the property.
     * @param pd
     *          The property definition.
     * @param value
     *          The LDAP string representation.
     * @return Returns the decoded LDAP value.
     * @throws IllegalPropertyValueStringException
     *           If the property value could not be decoded because it
     *           was invalid.
     * @return Returns the root configuration manager associated with this
     *         management context.
     */
    public static <PD> PD decode(PropertyDefinition<PD> pd,
        AttributeValue value) throws IllegalPropertyValueStringException {
      String s = value.getValue().toString();
      return pd.castValue(pd.accept(new ValueDecoder(), s));
    public RootCfg getRootConfiguration() {
        return getRootConfigurationManagedObject().getConfiguration();
    }
    // Prevent instantiation.
    private ValueDecoder() {
      // No implementation required.
    }
    /**
     * {@inheritDoc}
     * Get the root configuration server managed object associated with this
     * management context.
     *
     * @return Returns the root configuration server managed object associated
     *         with this management context.
     */
    @Override
    public <C extends ConfigurationClient, S extends Configuration>
    Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) {
      // Aggregations values are stored as full DNs in LDAP, but
      // just their common name is exposed in the admin framework.
      try {
        Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d
            .getRelationDefinition(), p);
        return reference.getName();
      } catch (IllegalArgumentException e) {
        throw new IllegalPropertyValueStringException(d, p);
      }
    public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
        return ROOT;
    }
    /**
     * {@inheritDoc}
     * Lists the child managed objects of the named parent managed object.
     *
     * @param <C>
     *            The type of client managed object configuration that the
     *            relation definition refers to.
     * @param <S>
     *            The type of server managed object configuration that the
     *            relation definition refers to.
     * @param parent
     *            The path of the parent managed object.
     * @param rd
     *            The instantiable relation definition.
     * @return Returns the names of the child managed objects.
     * @throws IllegalArgumentException
     *             If the relation definition is not associated with the parent
     *             managed object's definition.
     */
    @Override
    public <T> Object visitUnknown(PropertyDefinition<T> d, String p)
        throws UnknownPropertyDefinitionException {
      // By default the property definition's decoder will do.
      return d.decodeValue(p);
    }
  }
    public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
            ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd) throws IllegalArgumentException {
        validateRelationDefinition(parent, rd);
  // Singleton instance.
  private final static ServerManagementContext INSTANCE =
    new ServerManagementContext();
  /**
   * The root server managed object.
   */
  private static final ServerManagedObject<RootCfg> ROOT =
    new ServerManagedObject<RootCfg>(
      ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections
          .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  /**
   * Get the single server-side management context.
   *
   * @return Returns the single server-side management context.
   */
  public static ServerManagementContext getInstance() {
    return INSTANCE;
  }
  // Private constructor.
  private ServerManagementContext() {
    // No implementation required.
  }
  /**
   * Gets the named managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          path definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          path definition refers to.
   * @param path
   *          The path of the managed object.
   * @return Returns the named managed object.
   * @throws ConfigException
   *           If the named managed object could not be found or if it
   *           could not be decoded.
   */
  @SuppressWarnings("unchecked")
  public <C extends ConfigurationClient, S extends Configuration>
  ServerManagedObject<? extends S> getManagedObject(
      ManagedObjectPath<C, S> path) throws ConfigException {
    // Be careful to handle the root configuration.
    if (path.isEmpty()) {
      return (ServerManagedObject<S>) getRootConfigurationManagedObject();
    }
    // Get the configuration entry.
    DN targetDN = DNBuilder.create(path);
    ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
    try {
      ServerManagedObject<? extends S> managedObject;
      managedObject = decode(path, configEntry);
      // Enforce any constraints.
      managedObject.ensureIsUsable();
      return managedObject;
    } catch (DefinitionDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(targetDN, e);
    } catch (ServerManagedObjectDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(e);
    } catch (ConstraintViolationException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(e);
    }
  }
  /**
   * Gets the effective value of a property in the named managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          path definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          path definition refers to.
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param path
   *          The path of the managed object containing the property.
   * @param pd
   *          The property to be retrieved.
   * @return Returns the property's effective value, or
   *         <code>null</code> if there are no values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with the
   *           referenced managed object's definition.
   * @throws PropertyException
   *           If the managed object was found but the requested
   *           property could not be decoded.
   * @throws ConfigException
   *           If the named managed object could not be found or if it
   *           could not be decoded.
   */
  public <C extends ConfigurationClient, S extends Configuration, PD>
  PD getPropertyValue(ManagedObjectPath<C, S> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      ConfigException, PropertyException {
    SortedSet<PD> values = getPropertyValues(path, pd);
    if (values.isEmpty()) {
      return null;
    } else {
      return values.first();
    }
  }
  /**
   * Gets the effective values of a property in the named managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          path definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          path definition refers to.
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param path
   *          The path of the managed object containing the property.
   * @param pd
   *          The property to be retrieved.
   * @return Returns the property's effective values, or an empty set
   *         if there are no values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with the
   *           referenced managed object's definition.
   * @throws PropertyException
   *           If the managed object was found but the requested
   *           property could not be decoded.
   * @throws ConfigException
   *           If the named managed object could not be found or if it
   *           could not be decoded.
   */
  @SuppressWarnings("unchecked")
  public <C extends ConfigurationClient, S extends Configuration, PD>
  SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      ConfigException, PropertyException {
    // Check that the requested property is from the definition
    // associated with the path.
    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
    PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
    if (tmp != pd) {
      throw new IllegalArgumentException("The property " + pd.getName()
          + " is not associated with a " + d.getName());
    }
    // Determine the exact type of managed object referenced by the
    // path.
    DN dn = DNBuilder.create(path);
    ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
    ManagedObjectDefinition<? extends C, ? extends S> mod;
    try {
      mod = d.resolveManagedObjectDefinition(resolver);
    } catch (DefinitionDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(dn, e);
    }
    // Make sure we use the correct property definition, the
    // provided one might have been overridden in the resolved
    // definition.
    pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
    List<AttributeValue> values = getAttribute(mod, pd, configEntry);
    return decodeProperty(path.asSubType(mod), pd, values, null);
  }
  /**
   * Get the root configuration manager associated with this
   * management context.
   *
   * @return Returns the root configuration manager associated with
   *         this management context.
   */
  public RootCfg getRootConfiguration() {
    return getRootConfigurationManagedObject().getConfiguration();
  }
  /**
   * Get the root configuration server managed object associated with
   * this management context.
   *
   * @return Returns the root configuration server managed object
   *         associated with this management context.
   */
  public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
    return ROOT;
  }
  /**
   * Lists the child managed objects of the named parent managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The instantiable relation definition.
   * @return Returns the names of the child managed objects.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   */
  public <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
      throws IllegalArgumentException {
    validateRelationDefinition(parent, rd);
    // Get the target entry.
    DN targetDN = DNBuilder.create(parent, rd);
    ConfigEntry configEntry;
    try {
      configEntry = DirectoryServer.getConfigEntry(targetDN);
    } catch (ConfigException e) {
      return new String[0];
    }
    if (configEntry == null) {
      return new String[0];
    }
    // Retrieve the children.
    Set<DN> children = configEntry.getChildren().keySet();
    ArrayList<String> names = new ArrayList<String>(children.size());
    for (DN child : children) {
      // Assume that RDNs are single-valued and can be trimmed.
      AttributeValue av = child.getRDN().getAttributeValue(0);
      names.add(av.getValue().toString().trim());
    }
    return names.toArray(new String[names.size()]);
  }
  /**
   * Lists the child managed objects of the named parent managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The set relation definition.
   * @return Returns the names of the child managed objects.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   */
  public <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd)
      throws IllegalArgumentException {
    validateRelationDefinition(parent, rd);
    // Get the target entry.
    DN targetDN = DNBuilder.create(parent, rd);
    ConfigEntry configEntry;
    try {
      configEntry = DirectoryServer.getConfigEntry(targetDN);
    } catch (ConfigException e) {
      return new String[0];
    }
    if (configEntry == null) {
      return new String[0];
    }
    // Retrieve the children.
    Set<DN> children = configEntry.getChildren().keySet();
    ArrayList<String> names = new ArrayList<String>(children.size());
    for (DN child : children) {
      // Assume that RDNs are single-valued and can be trimmed.
      AttributeValue av = child.getRDN().getAttributeValue(0);
      names.add(av.toString().trim());
    }
    return names.toArray(new String[names.size()]);
  }
  /**
   * Determines whether or not the named managed object exists.
   *
   * @param path
   *          The path of the named managed object.
   * @return Returns <code>true</code> if the named managed object
   *         exists, <code>false</code> otherwise.
   */
  public boolean managedObjectExists(ManagedObjectPath<?, ?> path) {
    // Get the configuration entry.
    DN targetDN = DNBuilder.create(path);
    try {
      return (getManagedObjectConfigEntry(targetDN) != null);
    } catch (ConfigException e) {
      // Assume it doesn't exist.
      return false;
    }
  }
  /**
   * Decodes a configuration entry into the required type of server
   * managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          path definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          path definition refers to.
   * @param path
   *          The location of the server managed object.
   * @param configEntry
   *          The configuration entry that should be decoded.
   * @return Returns the new server-side managed object from the
   *         provided definition and configuration entry.
   * @throws DefinitionDecodingException
   *           If the managed object's type could not be determined.
   * @throws ServerManagedObjectDecodingException
   *           If one or more of the managed object's properties could
   *           not be decoded.
   */
  <C extends ConfigurationClient, S extends Configuration>
  ServerManagedObject<? extends S> decode(
      ManagedObjectPath<C, S> path, ConfigEntry configEntry)
      throws DefinitionDecodingException, ServerManagedObjectDecodingException {
    return decode(path, configEntry, null);
  }
  /**
   * Decodes a configuration entry into the required type of server
   * managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          path definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          path definition refers to.
   * @param path
   *          The location of the server managed object.
   * @param configEntry
   *          The configuration entry that should be decoded.
   * @param newConfigEntry
   *          Optional new configuration that does not exist yet in
   *          the configuration back-end. This will be used for
   *          resolving inherited default values.
   * @return Returns the new server-side managed object from the
   *         provided definition and configuration entry.
   * @throws DefinitionDecodingException
   *           If the managed object's type could not be determined.
   * @throws ServerManagedObjectDecodingException
   *           If one or more of the managed object's properties could
   *           not be decoded.
   */
  <C extends ConfigurationClient, S extends Configuration>
  ServerManagedObject<? extends S> decode(
      ManagedObjectPath<C, S> path, ConfigEntry configEntry,
      ConfigEntry newConfigEntry) throws DefinitionDecodingException,
      ServerManagedObjectDecodingException {
    // First determine the correct definition to use for the entry.
    // This could either be the provided definition, or one of its
    // sub-definitions.
    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
    ManagedObjectDefinition<? extends C, ? extends S> mod = d
        .resolveManagedObjectDefinition(resolver);
    // Build the managed object's properties.
    List<PropertyException> exceptions = new LinkedList<PropertyException>();
    Map<PropertyDefinition<?>, SortedSet<?>> properties =
      new HashMap<PropertyDefinition<?>, SortedSet<?>>();
    for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
      List<AttributeValue> values = getAttribute(mod, pd, configEntry);
      try {
        SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
        properties.put(pd, pvalues);
      } catch (PropertyException e) {
        exceptions.add(e);
      }
    }
    // If there were no decoding problems then return the managed
    // object, otherwise throw an operations exception.
    ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties,
        configEntry);
    if (exceptions.isEmpty()) {
      return mo;
    } else {
      throw new ServerManagedObjectDecodingException(mo, exceptions);
    }
  }
  // Decode helper method required to avoid generics warning.
  private <C extends ConfigurationClient, S extends Configuration>
  ServerManagedObject<S> decodeAux(
      ManagedObjectPath<? super C, ? super S> path,
      ManagedObjectDefinition<C, S> d,
      Map<PropertyDefinition<?>, SortedSet<?>> properties,
      ConfigEntry configEntry) {
    ManagedObjectPath<C, S> newPath = path.asSubType(d);
    return new ServerManagedObject<S>(newPath, d, properties, configEntry);
  }
  // Create a property using the provided string values.
  private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path,
      PropertyDefinition<T> pd, List<AttributeValue> values,
      ConfigEntry newConfigEntry) throws PropertyException {
    PropertyException exception = null;
    SortedSet<T> pvalues = new TreeSet<T>(pd);
    if (!values.isEmpty()) {
      // The property has values defined for it.
      for (AttributeValue value : values) {
        // Get the target entry.
        DN targetDN = DNBuilder.create(parent, rd);
        ConfigEntry configEntry;
        try {
          pvalues.add(ValueDecoder.decode(pd, value));
        } catch (IllegalPropertyValueStringException e) {
          exception = e;
            configEntry = DirectoryServer.getConfigEntry(targetDN);
        } catch (ConfigException e) {
            return new String[0];
        }
      }
    } else {
      // No values defined so get the defaults.
      try {
        pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
      } catch (DefaultBehaviorException e) {
        exception = e;
      }
        if (configEntry == null) {
            return new String[0];
        }
        // Retrieve the children.
        Set<DN> children = configEntry.getChildren().keySet();
        ArrayList<String> names = new ArrayList<String>(children.size());
        for (DN child : children) {
            // Assume that RDNs are single-valued and can be trimmed.
            AttributeValue av = child.rdn().getAttributeValue(0);
            names.add(av.getValue().toString().trim());
        }
        return names.toArray(new String[names.size()]);
    }
    if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
      // This exception takes precedence over previous exceptions.
      exception = new PropertyIsSingleValuedException(pd);
      T value = pvalues.first();
      pvalues.clear();
      pvalues.add(value);
    /**
     * Lists the child managed objects of the named parent managed object.
     *
     * @param <C>
     *            The type of client managed object configuration that the
     *            relation definition refers to.
     * @param <S>
     *            The type of server managed object configuration that the
     *            relation definition refers to.
     * @param parent
     *            The path of the parent managed object.
     * @param rd
     *            The set relation definition.
     * @return Returns the names of the child managed objects.
     * @throws IllegalArgumentException
     *             If the relation definition is not associated with the parent
     *             managed object's definition.
     */
    public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
            ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd) throws IllegalArgumentException {
        validateRelationDefinition(parent, rd);
        // Get the target entry.
        DN targetDN = DNBuilder.create(parent, rd);
        ConfigEntry configEntry;
        try {
            configEntry = DirectoryServer.getConfigEntry(targetDN);
        } catch (ConfigException e) {
            return new String[0];
        }
        if (configEntry == null) {
            return new String[0];
        }
        // Retrieve the children.
        Set<DN> children = configEntry.getChildren().keySet();
        ArrayList<String> names = new ArrayList<String>(children.size());
        for (DN child : children) {
            // Assume that RDNs are single-valued and can be trimmed.
            AttributeValue av = child.rdn().getAttributeValue(0);
            names.add(av.toString().trim());
        }
        return names.toArray(new String[names.size()]);
    }
    if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
      // The values maybe empty because of a previous exception.
      if (exception == null) {
        exception = new PropertyIsMandatoryException(pd);
      }
    /**
     * Determines whether or not the named managed object exists.
     *
     * @param path
     *            The path of the named managed object.
     * @return Returns <code>true</code> if the named managed object exists,
     *         <code>false</code> otherwise.
     */
    public boolean managedObjectExists(ManagedObjectPath<?, ?> path) {
        // Get the configuration entry.
        DN targetDN = DNBuilder.create(path);
        try {
            return (getManagedObjectConfigEntry(targetDN) != null);
        } catch (ConfigException e) {
            // Assume it doesn't exist.
            return false;
        }
    }
    if (exception != null) {
      throw exception;
    } else {
      return pvalues;
    }
  }
  // Gets the attribute associated with a property from a ConfigEntry.
  private List<AttributeValue> getAttribute(ManagedObjectDefinition<?, ?> d,
      PropertyDefinition<?> pd, ConfigEntry configEntry) {
    // TODO: we create a default attribute type if it is
    // undefined. We should log a warning here if this is the case
    // since the attribute should have been defined.
    String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
    AttributeType type = DirectoryServer.getAttributeType(attrID, true);
    AttributeValueDecoder<AttributeValue> decoder =
      new AttributeValueDecoder<AttributeValue>() {
      public AttributeValue decode(AttributeValue value)
          throws DirectoryException {
        return value;
      }
    };
    List<AttributeValue> values = new LinkedList<AttributeValue>();
    try {
      configEntry.getEntry().getAttributeValues(type, decoder, values);
    } catch (DirectoryException e) {
      // Should not happen.
      throw new RuntimeException(e);
    }
    return values;
  }
  // Get the default values for the specified property.
  private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p,
      PropertyDefinition<T> pd, ConfigEntry newConfigEntry)
      throws DefaultBehaviorException {
    DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
    return v.find(p, pd);
  }
  // Gets a config entry required for a managed object and throws a
  // config exception on failure.
  private ConfigEntry getManagedObjectConfigEntry(
      DN dn) throws ConfigException {
    ConfigEntry configEntry;
    try {
      configEntry = DirectoryServer.getConfigEntry(dn);
    } catch (ConfigException e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      LocalizableMessage message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
          String.valueOf(dn), stackTraceToSingleLineString(e));
      throw new ConfigException(message, e);
    /**
     * Decodes a configuration entry into the required type of server managed
     * object.
     *
     * @param <C>
     *            The type of client managed object configuration that the path
     *            definition refers to.
     * @param <S>
     *            The type of server managed object configuration that the path
     *            definition refers to.
     * @param path
     *            The location of the server managed object.
     * @param configEntry
     *            The configuration entry that should be decoded.
     * @return Returns the new server-side managed object from the provided
     *         definition and configuration entry.
     * @throws DefinitionDecodingException
     *             If the managed object's type could not be determined.
     * @throws ServerManagedObjectDecodingException
     *             If one or more of the managed object's properties could not
     *             be decoded.
     */
    <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode(
            ManagedObjectPath<C, S> path, ConfigEntry configEntry) throws DefinitionDecodingException,
            ServerManagedObjectDecodingException {
        return decode(path, configEntry, null);
    }
    // The configuration handler is free to return null indicating
    // that the entry does not exist.
    if (configEntry == null) {
      LocalizableMessage message = ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
          .get(String.valueOf(dn));
      throw new ConfigException(message);
    /**
     * Decodes a configuration entry into the required type of server managed
     * object.
     *
     * @param <C>
     *            The type of client managed object configuration that the path
     *            definition refers to.
     * @param <S>
     *            The type of server managed object configuration that the path
     *            definition refers to.
     * @param path
     *            The location of the server managed object.
     * @param configEntry
     *            The configuration entry that should be decoded.
     * @param newConfigEntry
     *            Optional new configuration that does not exist yet in the
     *            configuration back-end. This will be used for resolving
     *            inherited default values.
     * @return Returns the new server-side managed object from the provided
     *         definition and configuration entry.
     * @throws DefinitionDecodingException
     *             If the managed object's type could not be determined.
     * @throws ServerManagedObjectDecodingException
     *             If one or more of the managed object's properties could not
     *             be decoded.
     */
    <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode(
            ManagedObjectPath<C, S> path, ConfigEntry configEntry, ConfigEntry newConfigEntry)
            throws DefinitionDecodingException, ServerManagedObjectDecodingException {
        // First determine the correct definition to use for the entry.
        // This could either be the provided definition, or one of its
        // sub-definitions.
        DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
        AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
        ManagedObjectDefinition<? extends C, ? extends S> mod = d.resolveManagedObjectDefinition(resolver);
        // Build the managed object's properties.
        List<PropertyException> exceptions = new LinkedList<PropertyException>();
        Map<PropertyDefinition<?>, SortedSet<?>> properties = new HashMap<PropertyDefinition<?>, SortedSet<?>>();
        for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
            List<AttributeValue> values = getAttribute(mod, pd, configEntry);
            try {
                SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
                properties.put(pd, pvalues);
            } catch (PropertyException e) {
                exceptions.add(e);
            }
        }
        // If there were no decoding problems then return the managed
        // object, otherwise throw an operations exception.
        ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties, configEntry);
        if (exceptions.isEmpty()) {
            return mo;
        } else {
            throw new ServerManagedObjectDecodingException(mo, exceptions);
        }
    }
    return configEntry;
  }
  // Validate that a relation definition belongs to the path.
  private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
    if (tmp != rd) {
      throw new IllegalArgumentException("The relation " + rd.getName()
          + " is not associated with a " + d.getName());
    // Decode helper method required to avoid generics warning.
    private <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<S> decodeAux(
            ManagedObjectPath<? super C, ? super S> path, ManagedObjectDefinition<C, S> d,
            Map<PropertyDefinition<?>, SortedSet<?>> properties, ConfigEntry configEntry) {
        ManagedObjectPath<C, S> newPath = path.asSubType(d);
        return new ServerManagedObject<S>(newPath, d, properties, configEntry);
    }
  }
    // Create a property using the provided string values.
    private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path, PropertyDefinition<T> pd,
            List<AttributeValue> values, ConfigEntry newConfigEntry) throws PropertyException {
        PropertyException exception = null;
        SortedSet<T> pvalues = new TreeSet<T>(pd);
        if (!values.isEmpty()) {
            // The property has values defined for it.
            for (AttributeValue value : values) {
                try {
                    pvalues.add(ValueDecoder.decode(pd, value));
                } catch (IllegalPropertyValueStringException e) {
                    exception = e;
                }
            }
        } else {
            // No values defined so get the defaults.
            try {
                pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
            } catch (DefaultBehaviorException e) {
                exception = e;
            }
        }
        if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
            // This exception takes precedence over previous exceptions.
            exception = new PropertyIsSingleValuedException(pd);
            T value = pvalues.first();
            pvalues.clear();
            pvalues.add(value);
        }
        if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
            // The values maybe empty because of a previous exception.
            if (exception == null) {
                exception = new PropertyIsMandatoryException(pd);
            }
        }
        if (exception != null) {
            throw exception;
        } else {
            return pvalues;
        }
    }
    // Gets the attribute associated with a property from a ConfigEntry.
    private List<AttributeValue> getAttribute(ManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd,
            ConfigEntry configEntry) {
        // TODO: we create a default attribute type if it is
        // undefined. We should log a warning here if this is the case
        // since the attribute should have been defined.
        String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
        AttributeType type = DirectoryServer.getAttributeType(attrID, true);
        AttributeValueDecoder<AttributeValue> decoder = new AttributeValueDecoder<AttributeValue>() {
            public AttributeValue decode(AttributeValue value) throws DirectoryException {
                return value;
            }
        };
        List<AttributeValue> values = new LinkedList<AttributeValue>();
        try {
            configEntry.getEntry().getAttributeValues(type, decoder, values);
        } catch (DirectoryException e) {
            // Should not happen.
            throw new RuntimeException(e);
        }
        return values;
    }
    // Get the default values for the specified property.
    private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p, PropertyDefinition<T> pd,
            ConfigEntry newConfigEntry) throws DefaultBehaviorException {
        DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
        return v.find(p, pd);
    }
    // Gets a config entry required for a managed object and throws a
    // config exception on failure.
    private ConfigEntry getManagedObjectConfigEntry(DN dn) throws ConfigException {
        ConfigEntry configEntry;
        try {
            configEntry = DirectoryServer.getConfigEntry(dn);
        } catch (ConfigException e) {
            if (debugEnabled()) {
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            LocalizableMessage message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(String.valueOf(dn),
                    stackTraceToSingleLineString(e));
            throw new ConfigException(message, e);
        }
        // The configuration handler is free to return null indicating
        // that the entry does not exist.
        if (configEntry == null) {
            LocalizableMessage message = ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST.get(String.valueOf(dn));
            throw new ConfigException(message);
        }
        return configEntry;
    }
    // Validate that a relation definition belongs to the path.
    private void validateRelationDefinition(ManagedObjectPath<?, ?> path, RelationDefinition<?, ?> rd)
            throws IllegalArgumentException {
        AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
        RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
        if (tmp != rd) {
            throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a "
                    + d.getName());
        }
    }
}