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

Nicolas Capponi
20.37.2013 3f6373b52c42fd596d3659d335542909cfdd5fbb
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/AggregationPropertyDefinition.java
@@ -348,7 +348,7 @@
        public boolean isUsable(ServerManagedObject<?> managedObject, Collection<LocalizableMessage> unacceptableReasons)
                throws ConfigException {
            SortedSet<String> names = managedObject.getPropertyValues(AggregationPropertyDefinition.this);
            ServerManagementContext context = ServerManagementContext.getInstance();
            ServerManagementContext context = managedObject.getServerContext();
            LocalizableMessage thisUFN = managedObject.getManagedObjectDefinition().getUserFriendlyName();
            String thisDN = managedObject.getDN().toString();
            LocalizableMessage thatUFN = getRelationDefinition().getUserFriendlyName();
@@ -404,7 +404,7 @@
            // Delete listeners need to be registered against the parent
            // entry of the referenced components.
            ServerManagementContext context = ServerManagementContext.getInstance();
            ServerManagementContext context = managedObject.getServerContext();
            ManagedObjectPath<?, ?> parentPath = getParentPath();
            ServerManagedObject<?> parent = context.getManagedObject(parentPath);
@@ -443,7 +443,7 @@
        @Override
        public void performPostDelete(ServerManagedObject<?> managedObject) throws ConfigException {
            // Remove any registered delete and change listeners.
            ServerManagementContext context = ServerManagementContext.getInstance();
            ServerManagementContext context = managedObject.getServerContext();
            DN dn = managedObject.getDN();
            // Delete listeners need to be deregistered against the parent
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java
@@ -25,47 +25,41 @@
 */
package org.opends.server.admin.server;
import java.util.Collection;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * Common features of config listener adaptors.
 */
abstract class AbstractConfigListenerAdaptor {
  /**
   * Create a new config listener adaptor.
   */
  protected AbstractConfigListenerAdaptor() {
    // No implementation required.
  }
  /**
   * Concatenate a list of messages into a single message.
   *
   * @param reasons
   *          The list of messages to concatenate.
   * @param unacceptableReason
   *          The single message to which messages should be appended.
   */
  protected final void generateUnacceptableReason(Collection<LocalizableMessage> reasons,
      LocalizableMessageBuilder unacceptableReason) {
    boolean isFirst = true;
    for (LocalizableMessage reason : reasons) {
      if (isFirst) {
        isFirst = false;
      } else {
        unacceptableReason.append("  ");
      }
      unacceptableReason.append(reason);
    /**
     * Create a new config listener adaptor.
     */
    protected AbstractConfigListenerAdaptor() {
        // No implementation required.
    }
  }
    /**
     * Concatenate a list of messages into a single message.
     *
     * @param reasons
     *            The list of messages to concatenate.
     * @param unacceptableReason
     *            The single message to which messages should be appended.
     */
    protected final void generateUnacceptableReason(Collection<LocalizableMessage> reasons,
            LocalizableMessageBuilder unacceptableReason) {
        boolean isFirst = true;
        for (LocalizableMessage reason : reasons) {
            if (isFirst) {
                isFirst = false;
            } else {
                unacceptableReason.append("  ");
            }
            unacceptableReason.append(reason);
        }
    }
}
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -25,8 +25,6 @@
 */
package org.opends.server.admin.server;
import java.util.LinkedList;
import java.util.List;
@@ -43,229 +41,214 @@
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.types.ConfigChangeResult;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.ResultCode;
/**
 * An adaptor class which converts {@link ConfigAddListener} callbacks
 * to {@link ServerManagedObjectAddListener} callbacks.
 * An adaptor class which converts {@link ConfigAddListener} callbacks to
 * {@link ServerManagedObjectAddListener} callbacks.
 *
 * @param <S>
 *          The type of server configuration handled by the add
 *          listener.
 *            The type of server configuration handled by the add listener.
 */
final class ConfigAddListenerAdaptor<S extends Configuration> extends
    AbstractConfigListenerAdaptor implements ConfigAddListener {
final class ConfigAddListenerAdaptor<S extends Configuration> extends AbstractConfigListenerAdaptor implements
        ConfigAddListener {
  private static final Logger debugLogger = LoggerFactory.getLogger(ConfigAddListenerAdaptor.class);
    private static final Logger debugLogger = LoggerFactory.getLogger(ConfigAddListenerAdaptor.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 add listener.
  private final ServerManagedObjectAddListener<S> listener;
    // The underlying add listener.
    private final ServerManagedObjectAddListener<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;
    private final ServerManagementContext serverContext;
  /**
   * Create a new configuration add listener adaptor for an
   * instantiable relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The instantiable relation.
   * @param listener
   *          The underlying add listener.
   */
  public ConfigAddListenerAdaptor(ManagedObjectPath<?, ?> path,
      InstantiableRelationDefinition<?, S> relation,
      ServerManagedObjectAddListener<S> listener) {
    this.path = path;
    this.instantiableRelation = relation;
    this.optionalRelation = null;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * Create a new configuration add listener adaptor for an optional
   * relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The optional relation.
   * @param listener
   *          The underlying add listener.
   */
  public ConfigAddListenerAdaptor(ManagedObjectPath<?, ?> path,
      OptionalRelationDefinition<?, S> relation,
      ServerManagedObjectAddListener<S> listener) {
    this.path = path;
    this.optionalRelation = relation;
    this.instantiableRelation = null;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * Create a new configuration add listener adaptor for a
   * set relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The set relation.
   * @param listener
   *          The underlying add listener.
   */
  public ConfigAddListenerAdaptor(ManagedObjectPath<?, ?> path,
      SetRelationDefinition<?, S> relation,
      ServerManagedObjectAddListener<S> listener) {
    this.path = path;
    this.instantiableRelation = null;
    this.optionalRelation = null;
    this.setRelation = relation;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(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 add listener adaptor for an instantiable
     * relation.
     *
     * @param context
     *            The server context.
     * @param path
     *            The managed object path of the parent.
     * @param relation
     *            The instantiable relation.
     * @param listener
     *            The underlying add listener.
     */
    public ConfigAddListenerAdaptor(ServerManagementContext context, ManagedObjectPath<?, ?> path,
            InstantiableRelationDefinition<?, S> relation, ServerManagedObjectAddListener<S> listener) {
        this.serverContext = context;
        this.path = path;
        this.instantiableRelation = relation;
        this.optionalRelation = null;
        this.setRelation = null;
        this.listener = listener;
        this.cachedManagedObject = null;
    }
    // Cached objects are guaranteed to be from previous acceptable
    // callback.
    ConfigChangeResult result = listener
        .applyConfigurationAdd(cachedManagedObject);
    /**
     * Create a new configuration add listener adaptor for an optional relation.
     *
     * @param context
     *            The server context.
     * @param path
     *            The managed object path of the parent.
     * @param relation
     *            The optional relation.
     * @param listener
     *            The underlying add listener.
     */
    public ConfigAddListenerAdaptor(ServerManagementContext context, ManagedObjectPath<?, ?> path,
            OptionalRelationDefinition<?, S> relation, ServerManagedObjectAddListener<S> listener) {
        this.serverContext = context;
        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.performPostAdd(cachedManagedObject);
          } catch (ConfigException e) {
              debugLogger.trace("Unable to perform post add", e);
          }
    /**
     * Create a new configuration add listener adaptor for a set relation.
     *
     * @param context
     *            The server context.
     * @param path
     *            The managed object path of the parent.
     * @param relation
     *            The set relation.
     * @param listener
     *            The underlying add listener.
     */
    public ConfigAddListenerAdaptor(ServerManagementContext context, ManagedObjectPath<?, ?> path,
            SetRelationDefinition<?, S> relation, ServerManagedObjectAddListener<S> listener) {
        this.serverContext = context;
        this.path = path;
        this.instantiableRelation = null;
        this.optionalRelation = null;
        this.setRelation = relation;
        this.listener = listener;
        this.cachedManagedObject = null;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationAdd(Entry 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.getName().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.applyConfigurationAdd(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.performPostAdd(cachedManagedObject);
                    } catch (ConfigException e) {
                        debugLogger.trace("Unable to perform post add", e);
                    }
                }
            }
        }
        return result;
    }
    return result;
  }
    /**
     * {@inheritDoc}
     */
    public boolean configAddIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason) {
        DN dn = configEntry.getName();
        String name = dn.rdn().getFirstAVA().getAttributeValue().toString().trim();
  /**
   * {@inheritDoc}
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
      LocalizableMessageBuilder unacceptableReason) {
    DN dn = configEntry.getDN();
    String name = dn.rdn().getFirstAVA().getAttributeValue().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;
                }
            }
            cachedManagedObject = serverContext.decode(childPath, configEntry, 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;
        // Give up immediately if a constraint violation occurs.
        try {
            cachedManagedObject.ensureIsUsable();
        } catch (ConstraintViolationException e) {
            generateUnacceptableReason(e.getMessages(), unacceptableReason);
            return false;
        }
      }
      ServerManagementContext context = ServerManagementContext.getInstance();
      cachedManagedObject = context.decode(childPath, configEntry, configEntry);
    } catch (DecodingException e) {
      unacceptableReason.append(e.getMessageObject());
      return false;
        // Let the add listener decide.
        List<LocalizableMessage> reasons = new LinkedList<LocalizableMessage>();
        if (listener.isConfigurationAddAcceptable(cachedManagedObject, reasons)) {
            return true;
        } else {
            generateUnacceptableReason(reasons, unacceptableReason);
            return false;
        }
    }
    // Give up immediately if a constraint violation occurs.
    try {
      cachedManagedObject.ensureIsUsable();
    } catch (ConstraintViolationException e) {
      generateUnacceptableReason(e.getMessages(), unacceptableReason);
      return false;
    /**
     * Get the server managed object add listener associated with this adaptor.
     *
     * @return Returns the server managed object add listener associated with
     *         this adaptor.
     */
    ServerManagedObjectAddListener<S> getServerManagedObjectAddListener() {
        return listener;
    }
    // Let the add listener decide.
    List<LocalizableMessage> reasons = new LinkedList<LocalizableMessage>();
    if (listener.isConfigurationAddAcceptable(cachedManagedObject, reasons)) {
      return true;
    } else {
      generateUnacceptableReason(reasons, unacceptableReason);
      return false;
    }
  }
  /**
   * Get the server managed object add listener associated with this
   * adaptor.
   *
   * @return Returns the server managed object add listener associated
   *         with this adaptor.
   */
  ServerManagedObjectAddListener<S> getServerManagedObjectAddListener() {
    return listener;
  }
}
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
@@ -54,13 +54,13 @@
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.config.ConfigurationRepository;
import org.opends.server.types.ConfigChangeResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ResultCode;
/**
@@ -97,7 +97,8 @@
         * @param dependencies
         *            Add dependencies names to this collection.
         */
        public static <T> void find(ManagedObjectPath<?, ?> path, PropertyDefinition<T> pd, Collection<DN> dependencies) {
        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);
@@ -167,39 +168,52 @@
        }
    }
    // Cached managed object between accept/apply call-backs.
    /** 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.
    /**
     * 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.
    /** 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.
    /**
     * The listener used to notify this listener when dependency entries are
     * modified.
     */
    private final ConfigChangeListener dependencyListener;
    // The DN associated with this listener.
    /** The DN associated with this listener. */
    private final DN dn;
    // The underlying change listener.
    /** The underlying change listener. */
    private final ServerManagedObjectChangeListener<? super S> listener;
    // The managed object path.
    /** The managed object path. */
    private final ManagedObjectPath<?, S> path;
    /** Repository of configuration entries */
    private final ConfigurationRepository configRepository;
    private final ServerManagementContext serverContext;
    /**
     * Create a new configuration change listener adaptor.
     *
     * @param serverContext
     *            The server context.
     * @param path
     *            The managed object path.
     * @param listener
     *            The underlying change listener.
     */
    public ConfigChangeListenerAdaptor(ManagedObjectPath<?, S> path,
            ServerManagedObjectChangeListener<? super S> listener) {
    public ConfigChangeListenerAdaptor(final ServerManagementContext serverContext,
            final ManagedObjectPath<?, S> path, final ServerManagedObjectChangeListener<? super S> listener) {
        this.serverContext = serverContext;
        configRepository = serverContext.getConfigRepository();
        this.path = path;
        this.dn = DNBuilder.create(path);
        this.listener = listener;
@@ -211,26 +225,25 @@
        this.dependencies = new HashSet<DN>();
        this.dependencyListener = new ConfigChangeListener() {
            public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
                ConfigEntry dependentConfigEntry = getConfigEntry(dn);
            public ConfigChangeResult applyConfigurationChange(Entry configEntry) {
                Entry dependentConfigEntry = getConfigEntry(dn);
                if (dependentConfigEntry != null) {
                    return ConfigChangeListenerAdaptor.this.applyConfigurationChange(dependentConfigEntry);
                } else {
                    // The dependent entry was not found.
                    configEntry.deregisterChangeListener(this);
                    configRepository.deregisterChangeListener(configEntry.getName(), this);
                    return new ConfigChangeResult(ResultCode.SUCCESS, false);
                }
            }
            public boolean configChangeIsAcceptable(ConfigEntry configEntry,
                    LocalizableMessageBuilder unacceptableReason) {
                ConfigEntry dependentConfigEntry = getConfigEntry(dn);
            public boolean configChangeIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason) {
                Entry dependentConfigEntry = getConfigEntry(dn);
                if (dependentConfigEntry != null) {
                    return ConfigChangeListenerAdaptor.this.configChangeIsAcceptable(dependentConfigEntry,
                            unacceptableReason, configEntry);
                } else {
                    // The dependent entry was not found.
                    configEntry.deregisterChangeListener(this);
                    configRepository.deregisterChangeListener(configEntry.getName(), this);
                    return true;
                }
            }
@@ -246,9 +259,9 @@
            // Be careful not to register listeners against the dependent
            // entry itself.
            if (!entryDN.equals(dn)) {
                ConfigEntry configEntry = getConfigEntry(entryDN);
                Entry configEntry = getConfigEntry(entryDN);
                if (configEntry != null) {
                    configEntry.registerChangeListener(dependencyListener);
                    configRepository.registerChangeListener(configEntry.getName(), dependencyListener);
                }
            }
        }
@@ -258,17 +271,16 @@
        // entry is removed.
        this.cleanerListener = new ConfigDeleteListener() {
            public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
            public ConfigChangeResult applyConfigurationDelete(Entry configEntry) {
                // Perform finalization if the deleted entry is the monitored
                // entry.
                if (configEntry.getDN().equals(dn)) {
                if (configEntry.getName().equals(dn)) {
                    finalizeChangeListener();
                }
                return new ConfigChangeResult(ResultCode.SUCCESS, false);
            }
            public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                    LocalizableMessageBuilder unacceptableReason) {
            public boolean configDeleteIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason) {
                // Always acceptable.
                return true;
            }
@@ -277,9 +289,9 @@
        DN parent = dn.parent();
        if (parent != null) {
            ConfigEntry configEntry = getConfigEntry(dn.parent());
            Entry configEntry = getConfigEntry(dn.parent());
            if (configEntry != null) {
                configEntry.registerDeleteListener(cleanerListener);
                configRepository.registerDeleteListener(configEntry.getName(), cleanerListener);
            }
        }
    }
@@ -287,13 +299,13 @@
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
    public ConfigChangeResult applyConfigurationChange(Entry 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);
        cachedManagedObject.setConfigDN(configEntry.getName());
        ConfigChangeResult result = listener.applyConfigurationChange(cachedManagedObject);
@@ -317,7 +329,7 @@
    /**
     * {@inheritDoc}
     */
    public boolean configChangeIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason) {
    public boolean configChangeIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason) {
        return configChangeIsAcceptable(configEntry, unacceptableReason, configEntry);
    }
@@ -338,11 +350,10 @@
     * @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) {
    public boolean configChangeIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason,
            Entry newConfigEntry) {
        try {
            ServerManagementContext context = ServerManagementContext.getInstance();
            cachedManagedObject = context.decode(path, configEntry, newConfigEntry);
            cachedManagedObject = serverContext.decode(path, configEntry, newConfigEntry);
        } catch (DecodingException e) {
            unacceptableReason.append(e.getMessageObject());
            return false;
@@ -373,17 +384,17 @@
    public void finalizeChangeListener() {
        // Remove the dependency listeners.
        for (DN dependency : dependencies) {
            ConfigEntry listenerConfigEntry = getConfigEntry(dependency);
            Entry listenerConfigEntry = getConfigEntry(dependency);
            if (listenerConfigEntry != null) {
                listenerConfigEntry.deregisterChangeListener(dependencyListener);
                configRepository.deregisterChangeListener(listenerConfigEntry.getName(), dependencyListener);
            }
        }
        // Now remove the cleaner listener as it will no longer be
        // needed.
        ConfigEntry parentConfigEntry = getConfigEntry(dn.parent());
        Entry parentConfigEntry = getConfigEntry(dn.parent());
        if (parentConfigEntry != null) {
            parentConfigEntry.deregisterDeleteListener(cleanerListener);
            configRepository.deregisterDeleteListener(parentConfigEntry.getName(), cleanerListener);
        }
    }
@@ -401,11 +412,10 @@
    // Returns the named configuration entry or null if it could not be
    // retrieved.
    private ConfigEntry getConfigEntry(DN dn) {
    private Entry getConfigEntry(DN dn) {
        try {
            ConfigEntry configEntry = DirectoryServer.getConfigEntry(dn);
            if (configEntry != null) {
                return configEntry;
            if (configRepository.hasEntry(dn)) {
                return configRepository.getEntry(dn);
            } else {
                adminLogger.error(ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST, String.valueOf(dn));
            }
@@ -414,7 +424,6 @@
            adminLogger.error(ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT, String.valueOf(dn),
                    StaticUtils.getExceptionMessage(e));
        }
        return null;
    }
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
@@ -43,10 +43,10 @@
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.types.ConfigChangeResult;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.ResultCode;
@@ -81,10 +81,14 @@
    // The managed object path of the parent.
    private final ManagedObjectPath<?, ?> path;
    private final ServerManagementContext serverContext;
    /**
     * Create a new configuration delete listener adaptor for an instantiable
     * relation.
     *
     * @param serverContext
     *            The server context.
     * @param path
     *            The managed object path of the parent.
     * @param relation
@@ -92,8 +96,9 @@
     * @param listener
     *            The underlying delete listener.
     */
    public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path, InstantiableRelationDefinition<?, S> relation,
            ServerManagedObjectDeleteListener<S> listener) {
    public ConfigDeleteListenerAdaptor(ServerManagementContext serverContext, ManagedObjectPath<?, ?> path,
            InstantiableRelationDefinition<?, S> relation, ServerManagedObjectDeleteListener<S> listener) {
        this.serverContext = serverContext;
        this.path = path;
        this.optionalRelation = null;
        this.instantiableRelation = relation;
@@ -105,7 +110,7 @@
    /**
     * Create a new configuration delete listener adaptor for an optional
     * relation.
     *
     * @param serverContext TODO
     * @param path
     *            The managed object path of the parent.
     * @param relation
@@ -113,8 +118,9 @@
     * @param listener
     *            The underlying delete listener.
     */
    public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path, OptionalRelationDefinition<?, S> relation,
            ServerManagedObjectDeleteListener<S> listener) {
    public ConfigDeleteListenerAdaptor(ServerManagementContext serverContext, ManagedObjectPath<?, ?> path,
            OptionalRelationDefinition<?, S> relation, ServerManagedObjectDeleteListener<S> listener) {
        this.serverContext = serverContext;
        this.path = path;
        this.optionalRelation = relation;
        this.instantiableRelation = null;
@@ -125,7 +131,7 @@
    /**
     * Create a new configuration delete listener adaptor for an set relation.
     *
     * @param serverContext TODO
     * @param path
     *            The managed object path of the parent.
     * @param relation
@@ -133,8 +139,9 @@
     * @param listener
     *            The underlying delete listener.
     */
    public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path, SetRelationDefinition<?, S> relation,
            ServerManagedObjectDeleteListener<S> listener) {
    public ConfigDeleteListenerAdaptor(ServerManagementContext serverContext, ManagedObjectPath<?, ?> path,
            SetRelationDefinition<?, S> relation, ServerManagedObjectDeleteListener<S> listener) {
        this.serverContext = serverContext;
        this.path = path;
        this.optionalRelation = null;
        this.instantiableRelation = null;
@@ -146,14 +153,15 @@
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
    @Override
    public ConfigChangeResult applyConfigurationDelete(Entry 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)) {
            if (!configEntry.getName().equals(expectedDN)) {
                // Doesn't apply to us.
                return new ConfigChangeResult(ResultCode.SUCCESS, false);
            }
@@ -183,8 +191,8 @@
    /**
     * {@inheritDoc}
     */
    public boolean configDeleteIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason) {
        DN dn = configEntry.getDN();
    public boolean configDeleteIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason) {
        DN dn = configEntry.getName();
        String name = dn.rdn().getFirstAVA().getAttributeValue().toString().trim();
        try {
@@ -210,8 +218,7 @@
                }
            }
            ServerManagementContext context = ServerManagementContext.getInstance();
            cachedManagedObject = context.decode(childPath, configEntry);
            cachedManagedObject = serverContext.decode(childPath, configEntry);
        } catch (DecodingException e) {
            unacceptableReason.append(e.getMessageObject());
            return false;
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/server/DelayedConfigAddListener.java
@@ -27,13 +27,13 @@
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.config.ConfigurationRepository;
import org.opends.server.types.ConfigChangeResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.i18n.LocalizableMessageBuilder;
@@ -62,6 +62,8 @@
    // registered).
    private final ConfigDeleteListener delayedDeleteListener;
    private final ConfigurationRepository configRepository;
    /**
     * Create a new delayed add listener which will register an add listener
     * with the specified entry when it is added.
@@ -72,12 +74,14 @@
     * @param addListener
     *            The add listener to be added to the subordinate entry when it
     *            is added.
     * @param configRepository TODO
     */
    public DelayedConfigAddListener(DN child, ConfigAddListener addListener) {
    public DelayedConfigAddListener(DN child, ConfigAddListener addListener, ConfigurationRepository configRepository) {
        this.parent = child.parent();
        this.child = child;
        this.delayedAddListener = addListener;
        this.delayedDeleteListener = null;
        this.configRepository = configRepository;
    }
    /**
@@ -90,34 +94,35 @@
     * @param deleteListener
     *            The delete listener to be added to the subordinate entry when
     *            it is added.
     * @param configRepository TODO
     */
    public DelayedConfigAddListener(DN child, ConfigDeleteListener deleteListener) {
    public DelayedConfigAddListener(DN child, ConfigDeleteListener deleteListener, ConfigurationRepository configRepository) {
        this.parent = child.parent();
        this.child = child;
        this.delayedAddListener = null;
        this.configRepository = configRepository;
        this.delayedDeleteListener = deleteListener;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry) {
        if (configEntry.getDN().equals(child)) {
    public ConfigChangeResult applyConfigurationAdd(Entry configEntry) {
        if (configEntry.getName().equals(child)) {
            // The subordinate entry matched our criteria so register the
            // listener(s).
            if (delayedAddListener != null) {
                configEntry.registerAddListener(delayedAddListener);
                configRepository.registerAddListener(configEntry.getName(), delayedAddListener);
            }
            if (delayedDeleteListener != null) {
                configEntry.registerDeleteListener(delayedDeleteListener);
                configRepository.registerDeleteListener(configEntry.getName(), delayedDeleteListener);
            }
            // We are no longer needed.
            try {
                ConfigEntry myEntry = DirectoryServer.getConfigEntry(parent);
                if (myEntry != null) {
                    myEntry.deregisterAddListener(this);
                // We are no longer needed.
                if (configRepository.hasEntry(parent)) {
                    configRepository.deregisterAddListener(parent, this);
                }
            } catch (ConfigException e) {
                debugLogger.trace("Unable to deregister add listener", e);
@@ -132,7 +137,7 @@
    /**
     * {@inheritDoc}
     */
    public boolean configAddIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason) {
    public boolean configAddIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason) {
        // Always acceptable.
        return true;
    }
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java
@@ -27,7 +27,6 @@
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;
@@ -52,13 +51,13 @@
import org.opends.server.api.ConfigAddListener;
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.util.DynamicConstants;
import org.opends.server.config.ConfigurationRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.forgerock.opendj.util.Pair;
/**
 * A server-side managed object.
@@ -71,21 +70,21 @@
    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 DN of configuration entry associated with this server managed object,
     * which is {@code null} for root.
     */
    private DN configDN;
    // The management context.
    private final ServerManagementContext context = ServerManagementContext.getInstance();
    private final ServerManagementContext serverContext;
    // The managed object's definition.
    private final ConfigurationRepository configRepository;
    private final ManagedObjectDefinition<?, S> definition;
    // The managed object path identifying this managed object's
    // location.
    /** 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;
    /**
@@ -97,15 +96,20 @@
     *            The managed object definition.
     * @param properties
     *            The managed object's properties.
     * @param configEntry
     * @param configDN
     *            The configuration entry associated with the managed object.
     * @param context
     *            The server management context.
     */
    ServerManagedObject(ManagedObjectPath<?, S> path, ManagedObjectDefinition<?, S> d,
            Map<PropertyDefinition<?>, SortedSet<?>> properties, ConfigEntry configEntry) {
    ServerManagedObject(final ManagedObjectPath<?, S> path, final ManagedObjectDefinition<?, S> d,
            final Map<PropertyDefinition<?>, SortedSet<?>> properties, final DN configDN,
            final ServerManagementContext context) {
        this.definition = d;
        this.path = path;
        this.properties = properties;
        this.configEntry = configEntry;
        this.configDN = configDN;
        this.serverContext = context;
        this.configRepository = context.getConfigRepository();
    }
    /**
@@ -124,7 +128,6 @@
    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);
    }
@@ -145,7 +148,6 @@
    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);
    }
@@ -166,7 +168,6 @@
    public <M extends Configuration> void deregisterAddListener(OptionalRelationDefinition<?, M> d,
            ConfigurationAddListener<M> listener) throws IllegalArgumentException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d).parent();
        deregisterAddListener(baseDN, listener);
    }
@@ -187,7 +188,6 @@
    public <M extends Configuration> void deregisterAddListener(OptionalRelationDefinition<?, M> d,
            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d).parent();
        deregisterAddListener(baseDN, listener);
    }
@@ -208,7 +208,6 @@
    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);
    }
@@ -229,7 +228,6 @@
    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);
    }
@@ -241,7 +239,7 @@
     *            The configuration change listener.
     */
    public void deregisterChangeListener(ConfigurationChangeListener<? super S> listener) {
        for (ConfigChangeListener l : configEntry.getChangeListeners()) {
        for (ConfigChangeListener l : configRepository.getChangeListeners(configDN)) {
            if (l instanceof ConfigChangeListenerAdaptor) {
                ConfigChangeListenerAdaptor<?> adaptor = (ConfigChangeListenerAdaptor<?>) l;
                ServerManagedObjectChangeListener<?> l2 = adaptor.getServerManagedObjectChangeListener();
@@ -249,7 +247,7 @@
                    ServerManagedObjectChangeListenerAdaptor<?> adaptor2 = (ServerManagedObjectChangeListenerAdaptor<?>) l2;
                    if (adaptor2.getConfigurationChangeListener() == listener) {
                        adaptor.finalizeChangeListener();
                        configEntry.deregisterChangeListener(adaptor);
                        configRepository.deregisterChangeListener(configDN, adaptor);
                    }
                }
            }
@@ -263,12 +261,12 @@
     *            The server managed object change listener.
     */
    public void deregisterChangeListener(ServerManagedObjectChangeListener<? super S> listener) {
        for (ConfigChangeListener l : configEntry.getChangeListeners()) {
        for (ConfigChangeListener l : configRepository.getChangeListeners(configDN)) {
            if (l instanceof ConfigChangeListenerAdaptor) {
                ConfigChangeListenerAdaptor<?> adaptor = (ConfigChangeListenerAdaptor<?>) l;
                if (adaptor.getServerManagedObjectChangeListener() == listener) {
                    adaptor.finalizeChangeListener();
                    configEntry.deregisterChangeListener(adaptor);
                    configRepository.deregisterChangeListener(configDN, adaptor);
                }
            }
        }
@@ -421,7 +419,7 @@
    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));
        return serverContext.getManagedObject(path.child(d, name));
    }
    /**
@@ -443,7 +441,7 @@
    public <M extends Configuration> ServerManagedObject<? extends M> getChild(OptionalRelationDefinition<?, M> d)
            throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        return context.getManagedObject(path.child(d));
        return serverContext.getManagedObject(path.child(d));
    }
    /**
@@ -470,7 +468,7 @@
            String name) throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        return context.getManagedObject(path.child(d, name));
        return serverContext.getManagedObject(path.child(d, name));
    }
    /**
@@ -492,7 +490,16 @@
    public <M extends Configuration> ServerManagedObject<? extends M> getChild(SingletonRelationDefinition<?, M> d)
            throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        return context.getManagedObject(path.child(d));
        return serverContext.getManagedObject(path.child(d));
    }
    /**
     * Returns the server management context used by this object.
     *
     * @return the context
     */
    public ServerManagementContext getServerContext() {
        return serverContext;
    }
    /**
@@ -511,8 +518,8 @@
     *         managed object, or an null DN if this is the root managed object.
     */
    public DN getDN() {
        if (configEntry != null) {
            return configEntry.getDN();
        if (configDN != null) {
            return configDN;
        } else {
            return DN.rootDN();
        }
@@ -600,7 +607,7 @@
     */
    public boolean hasChild(OptionalRelationDefinition<?, ?> d) throws IllegalArgumentException {
        validateRelationDefinition(d);
        return context.managedObjectExists(path.child(d));
        return serverContext.managedObjectExists(path.child(d));
    }
    /**
@@ -616,7 +623,7 @@
     */
    public String[] listChildren(InstantiableRelationDefinition<?, ?> d) throws IllegalArgumentException {
        validateRelationDefinition(d);
        return context.listManagedObjects(path, d);
        return serverContext.listManagedObjects(path, d);
    }
    /**
@@ -632,7 +639,7 @@
     */
    public String[] listChildren(SetRelationDefinition<?, ?> d) throws IllegalArgumentException {
        validateRelationDefinition(d);
        return context.listManagedObjects(path, d);
        return serverContext.listManagedObjects(path, d);
    }
    /**
@@ -678,7 +685,7 @@
            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d);
        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d, listener);
        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(serverContext, path, d, listener);
        registerAddListener(baseDN, adaptor);
    }
@@ -725,7 +732,7 @@
            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d).parent();
        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d, listener);
        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(serverContext, path, d, listener);
        registerAddListener(baseDN, adaptor);
    }
@@ -772,7 +779,7 @@
            ServerManagedObjectAddListener<M> listener) throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d);
        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d, listener);
        ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(serverContext, path, d, listener);
        registerAddListener(baseDN, adaptor);
    }
@@ -793,8 +800,9 @@
     *            The server managed object change listener.
     */
    public void registerChangeListener(ServerManagedObjectChangeListener<? super S> listener) {
        ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path, listener);
        configEntry.registerChangeListener(adaptor);
        ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(serverContext, path, listener);
        configRepository.registerChangeListener(configDN, adaptor);
        // TODO : go toward this
        // Entry entry;
@@ -860,7 +868,7 @@
            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d);
        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d, listener);
        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(serverContext, path, d, listener);
        registerDeleteListener(baseDN, adaptor);
    }
@@ -907,7 +915,7 @@
            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d).parent();
        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d, listener);
        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(serverContext, path, d, listener);
        registerDeleteListener(baseDN, adaptor);
    }
@@ -954,7 +962,7 @@
            ServerManagedObjectDeleteListener<M> listener) throws IllegalArgumentException, ConfigException {
        validateRelationDefinition(d);
        DN baseDN = DNBuilder.create(path, d);
        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d, listener);
        ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(serverContext, path, d, listener);
        registerDeleteListener(baseDN, adaptor);
    }
@@ -1012,30 +1020,29 @@
    }
    /**
     * Update the config entry associated with this server managed object. This
     * Update the config DN 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.
     * update the managed object with the correct config DN.
     *
     * @param configEntry
     *            The configuration entry.
     * @param configDN
     *            The DN of the underlying configuration entry.
     */
    void setConfigEntry(ConfigEntry configEntry) {
        this.configEntry = configEntry;
    void setConfigDN(DN configDN) {
        this.configDN = configDN;
    }
    // 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 (configRepository.hasEntry(baseDN)) {
                for (ConfigAddListener configListener : configRepository.getAddListeners(baseDN)) {
                    if (configListener instanceof ConfigAddListenerAdaptor) {
                        ConfigAddListenerAdaptor<?> adaptor = (ConfigAddListenerAdaptor<?>) configListener;
                        ServerManagedObjectAddListener<?> smoListener = adaptor.getServerManagedObjectAddListener();
                        if (smoListener instanceof ServerManagedObjectAddListenerAdaptor<?>) {
                            ServerManagedObjectAddListenerAdaptor<?> adaptor2 = (ServerManagedObjectAddListenerAdaptor<?>) smoListener;
                            if (adaptor2.getConfigurationAddListener() == listener) {
                                configEntry.deregisterAddListener(adaptor);
                                configRepository.deregisterAddListener(baseDN, adaptor);
                            }
                        }
                    }
@@ -1054,13 +1061,12 @@
    // 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 (configRepository.hasEntry(baseDN)) {
                for (ConfigAddListener configListener : configRepository.getAddListeners(baseDN)) {
                    if (configListener instanceof ConfigAddListenerAdaptor) {
                        ConfigAddListenerAdaptor<?> adaptor = (ConfigAddListenerAdaptor<?>) configListener;
                        if (adaptor.getServerManagedObjectAddListener() == listener) {
                            configEntry.deregisterAddListener(adaptor);
                            configRepository.deregisterAddListener(baseDN, adaptor);
                        }
                    }
                }
@@ -1075,19 +1081,62 @@
        }
    }
    /**
     * Convenience method to retrieve the initial listener and its intermediate
     * adaptor from the provided configListener.
     *
     * @param <T>
     *            Type of the configuration.
     * @param configListener
     *            Listener from wich to extract the initial listener.
     * @return a pair of (intermediate adaptor, intermediate listener) or
     *         {@code Pair.EMPTY} if listener can't be extracted
     */
    static <T extends Configuration> Pair<ConfigAddListenerAdaptor<T>, ConfigurationAddListener<T>>
        extractInitialListener(ConfigAddListener configListener) {
        Pair<ConfigAddListenerAdaptor<T>, ServerManagedObjectAddListener<T>> pair =
                extractIntermediateListener(configListener);
        if (!pair.equals(Pair.EMPTY) && pair.getSecond() instanceof ServerManagedObjectAddListenerAdaptor) {
            ServerManagedObjectAddListenerAdaptor<T> adaptor2 = (ServerManagedObjectAddListenerAdaptor<T>)
                    pair.getSecond();
            return Pair.of(pair.getFirst(), adaptor2.getConfigurationAddListener());
        }
        return Pair.empty();
    }
    /**
     * Convenience method to retrieve the intermediate listener and its
     * intermediate adaptor from the provided configListener.
     *
     * @param <T>
     *            Type of the configuration.
     * @param configListener
     *            Listener from wich to extract the initial listener.
     * @return a pair of (intermediate adaptor, initial listener) or
     *         {@code Pair.EMPTY} if listener can't be extracted
     */
    @SuppressWarnings("unchecked")
    static <T extends Configuration> Pair<ConfigAddListenerAdaptor<T>, ServerManagedObjectAddListener<T>>
        extractIntermediateListener(ConfigAddListener configListener) {
        if (configListener instanceof ConfigAddListenerAdaptor) {
            ConfigAddListenerAdaptor<T> adaptor = ((ConfigAddListenerAdaptor<T>) configListener);
            return Pair.of(adaptor, adaptor.getServerManagedObjectAddListener());
        }
        return Pair.empty();
    }
    // 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 (configRepository.hasEntry(baseDN)) {
                for (ConfigDeleteListener l : configRepository.getDeleteListeners(baseDN)) {
                    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);
                                configRepository.deregisterDeleteListener(baseDN, adaptor);
                            }
                        }
                    }
@@ -1107,13 +1156,12 @@
    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 (configRepository.hasEntry(baseDN)) {
                for (ConfigDeleteListener l : configRepository.getDeleteListeners(baseDN)) {
                    if (l instanceof ConfigDeleteListenerAdaptor) {
                        ConfigDeleteListenerAdaptor<?> adaptor = (ConfigDeleteListenerAdaptor<?>) l;
                        if (adaptor.getServerManagedObjectDeleteListener() == listener) {
                            configEntry.deregisterDeleteListener(adaptor);
                            configRepository.deregisterDeleteListener(baseDN, adaptor);
                        }
                    }
                }
@@ -1128,34 +1176,15 @@
        }
    }
    // 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);
        if (configRepository.hasEntry(baseDN)) {
            configRepository.registerAddListener(baseDN, adaptor);
        } else {
            // The relation entry does not exist yet so register a delayed
            // add listener.
            ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor);
            // The relation entry does not exist yet
            // so register a delayed add listener.
            ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor, configRepository);
            registerDelayedListener(baseDN, delayedListener);
        }
    }
@@ -1163,14 +1192,15 @@
    // 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();
        DN currentDN = baseDN.parent();
        DN previousDN = currentDN;
        while (currentDN != null) {
            if (!configRepository.hasEntry(currentDN)) {
                delayedListener = new DelayedConfigAddListener(currentDN, delayedListener, configRepository);
                previousDN = currentDN;
                currentDN = currentDN.parent();
            } else {
                relationEntry.registerAddListener(delayedListener);
                configRepository.registerAddListener(previousDN, delayedListener);
                return;
            }
        }
@@ -1187,30 +1217,28 @@
        DN parentDN = baseDN.parent();
        int delayWrappers = 0;
        while (parentDN != null) {
            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
            if (relationEntry == null) {
            if (!configRepository.hasEntry(parentDN)) {
                parentDN = parentDN.parent();
                delayWrappers++;
            } else {
                for (ConfigAddListener l : relationEntry.getAddListeners()) {
                    if (l instanceof DelayedConfigAddListener) {
                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
                for (ConfigAddListener configListener : configRepository.getAddListeners(parentDN)) {
                    if (configListener instanceof DelayedConfigAddListener) {
                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) configListener;
                        ConfigAddListener wrappedListener;
                        int i = delayWrappers;
                        for (; i > 0; i--) {
                            wrappedListener = delayListener.getDelayedAddListener();
                            if (wrappedListener != null && wrappedListener instanceof DelayedConfigAddListener) {
                                delayListener = (DelayedConfigAddListener) l;
                                delayListener = (DelayedConfigAddListener) configListener;
                            } else {
                                break;
                            }
                        }
                        if (i > 0) {
                            // There are not enough level of wrapping so this
                            // can't be
                            // the listener we are looking for.
                            // There are not enough level of wrapping
                            // so this can't be the listener we are looking for.
                            continue;
                        }
@@ -1222,7 +1250,7 @@
                            if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
                                ServerManagedObjectAddListenerAdaptor<?> adaptor2 = (ServerManagedObjectAddListenerAdaptor<?>) l2;
                                if (adaptor2.getConfigurationAddListener() == listener) {
                                    relationEntry.deregisterAddListener(l);
                                    configRepository.deregisterAddListener(parentDN, configListener);
                                }
                            }
                        }
@@ -1240,12 +1268,11 @@
        DN parentDN = baseDN.parent();
        int delayWrappers = 0;
        while (parentDN != null) {
            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
            if (relationEntry == null) {
            if (!configRepository.hasEntry(parentDN)) {
                parentDN = parentDN.parent();
                delayWrappers++;
            } else {
                for (ConfigAddListener l : relationEntry.getAddListeners()) {
                for (ConfigAddListener l : configRepository.getAddListeners(parentDN)) {
                    if (l instanceof DelayedConfigAddListener) {
                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
                        ConfigAddListener wrappedListener;
@@ -1261,9 +1288,8 @@
                        }
                        if (i > 0) {
                            // There are not enough level of wrapping so this
                            // can't be
                            // the listener we are looking for.
                            // There are not enough level of wrapping
                            // so this can't be the listener we are looking for.
                            continue;
                        }
@@ -1275,7 +1301,7 @@
                            if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
                                ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 = (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
                                if (adaptor2.getConfigurationDeleteListener() == listener) {
                                    relationEntry.deregisterAddListener(l);
                                    configRepository.deregisterAddListener(parentDN, l);
                                }
                            }
                        }
@@ -1293,30 +1319,28 @@
        DN parentDN = baseDN.parent();
        int delayWrappers = 0;
        while (parentDN != null) {
            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
            if (relationEntry == null) {
            if (!configRepository.hasEntry(parentDN)) {
                parentDN = parentDN.parent();
                delayWrappers++;
            } else {
                for (ConfigAddListener l : relationEntry.getAddListeners()) {
                    if (l instanceof DelayedConfigAddListener) {
                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
                for (ConfigAddListener configListener : configRepository.getAddListeners(parentDN)) {
                    if (configListener instanceof DelayedConfigAddListener) {
                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) configListener;
                        ConfigAddListener wrappedListener;
                        int i = delayWrappers;
                        for (; i > 0; i--) {
                            wrappedListener = delayListener.getDelayedAddListener();
                            if (wrappedListener != null && wrappedListener instanceof DelayedConfigAddListener) {
                                delayListener = (DelayedConfigAddListener) l;
                                delayListener = (DelayedConfigAddListener) configListener;
                            } else {
                                break;
                            }
                        }
                        if (i > 0) {
                            // There are not enough level of wrapping so this
                            // can't be
                            // the listener we are looking for.
                            // There are not enough level of wrapping
                            // so this can't be the listener we are looking for.
                            continue;
                        }
@@ -1325,7 +1349,7 @@
                        if (delayedListener != null && delayedListener instanceof ConfigAddListenerAdaptor) {
                            ConfigAddListenerAdaptor<?> adaptor = (ConfigAddListenerAdaptor<?>) delayedListener;
                            if (adaptor.getServerManagedObjectAddListener() == listener) {
                                relationEntry.deregisterAddListener(l);
                                configRepository.deregisterAddListener(parentDN, configListener);
                            }
                        }
                    }
@@ -1342,30 +1366,28 @@
        DN parentDN = baseDN.parent();
        int delayWrappers = 0;
        while (parentDN != null) {
            ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
            if (relationEntry == null) {
            if (!configRepository.hasEntry(parentDN)) {
                parentDN = parentDN.parent();
                delayWrappers++;
            } else {
                for (ConfigAddListener l : relationEntry.getAddListeners()) {
                    if (l instanceof DelayedConfigAddListener) {
                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) l;
                for (ConfigAddListener configListener : configRepository.getAddListeners(parentDN)) {
                    if (configListener instanceof DelayedConfigAddListener) {
                        DelayedConfigAddListener delayListener = (DelayedConfigAddListener) configListener;
                        ConfigAddListener wrappedListener;
                        int i = delayWrappers;
                        for (; i > 0; i--) {
                            wrappedListener = delayListener.getDelayedAddListener();
                            if (wrappedListener != null && wrappedListener instanceof DelayedConfigAddListener) {
                                delayListener = (DelayedConfigAddListener) l;
                                delayListener = (DelayedConfigAddListener) configListener;
                            } else {
                                break;
                            }
                        }
                        if (i > 0) {
                            // There are not enough level of wrapping so this
                            // can't be
                            // the listener we are looking for.
                            // There are not enough level of wrapping
                            // so this can't be the listener we are looking for.
                            continue;
                        }
@@ -1374,7 +1396,7 @@
                        if (delayedListener != null && delayedListener instanceof ConfigDeleteListenerAdaptor) {
                            ConfigDeleteListenerAdaptor<?> adaptor = (ConfigDeleteListenerAdaptor<?>) delayedListener;
                            if (adaptor.getServerManagedObjectDeleteListener() == listener) {
                                relationEntry.deregisterAddListener(l);
                                configRepository.deregisterAddListener(parentDN, configListener);
                            }
                        }
                    }
@@ -1386,14 +1408,12 @@
    // 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);
        if (configRepository.hasEntry(baseDN)) {
            configRepository.registerDeleteListener(baseDN, adaptor);
        } else {
            // The relation entry does not exist yet so register a delayed
            // add listener.
            ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor);
            // The relation entry does not exist yet
            // so register a delayed add listener.
            ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor, configRepository);
            registerDelayedListener(baseDN, delayedListener);
        }
    }
opendj-sdk/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java
@@ -71,14 +71,15 @@
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.ConfigurationRepository;
import org.opends.server.core.DirectoryServer;
import org.forgerock.opendj.admin.meta.RootCfgDefn;
import org.forgerock.opendj.admin.server.RootCfg;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.AttributeDescription;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.opends.server.util.DynamicConstants;
import org.slf4j.Logger;
@@ -104,7 +105,7 @@
        // Optional new configuration entry which does not yet exist in
        // the configuration back-end.
        private final ConfigEntry newConfigEntry;
        private final Entry newConfigEntry;
        // The path of the managed object containing the next property.
        private ManagedObjectPath<?, ?> nextPath = null;
@@ -113,7 +114,7 @@
        private PropertyDefinition<T> nextProperty = null;
        // Private constructor.
        private DefaultValueFinder(ConfigEntry newConfigEntry) {
        private DefaultValueFinder(Entry newConfigEntry) {
            this.newConfigEntry = newConfigEntry;
        }
@@ -197,7 +198,7 @@
        // Get an inherited property value.
        @SuppressWarnings("unchecked")
        private Collection<T> getInheritedProperty(ManagedObjectPath target,
        private Collection<T> getInheritedProperty(ManagedObjectPath<?,?> target,
                AbstractManagedObjectDefinition<?, ?> definition, String propertyName)
                        throws DefaultBehaviorException {
            // First check that the requested type of managed object
@@ -214,8 +215,8 @@
            try {
                // Get the actual managed object definition.
                DN dn = DNBuilder.create(target);
                ConfigEntry configEntry;
                if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
                Entry configEntry;
                if (newConfigEntry != null && newConfigEntry.getName().equals(dn)) {
                    configEntry = newConfigEntry;
                } else {
                    configEntry = getManagedObjectConfigEntry(dn);
@@ -273,10 +274,10 @@
    private class MyDefinitionResolver implements DefinitionResolver {
        // The config entry.
        private final ConfigEntry entry;
        private final Entry entry;
        // Private constructor.
        private MyDefinitionResolver(ConfigEntry entry) {
        private MyDefinitionResolver(Entry entry) {
            this.entry = entry;
        }
@@ -285,7 +286,15 @@
         */
        public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
            String oc = LDAPProfile.getInstance().getObjectClass(d);
            return entry.hasObjectClass(oc);
         // TODO : use the schema to get object class and check it in the entry
         // Commented because reject any config entry without proper schema loading
         // Previous code was
//            ObjectClass oc = DirectoryServer.getObjectClass(name.toLowerCase());
//            if (oc == null) {
//              oc = DirectoryServer.getDefaultObjectClass(name);
//            }
//            return Entries.containsObjectClass(entry, oc);
            return entry.containsAttribute("objectClass", oc);
        }
    }
@@ -347,28 +356,22 @@
    private static final Logger debugLogger = LoggerFactory.getLogger(ServerManagementContext.class);
    /** The singleton instance. **/
    private final static ServerManagementContext INSTANCE = new ServerManagementContext();
    /**
     * The root server managed object.
     * The root server managed object, lazily initialized.
     */
    private static final ServerManagedObject<RootCfg> ROOT = new ServerManagedObject<RootCfg>(
            ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(),
            Collections.<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
    private volatile ServerManagedObject<RootCfg> root;
    /** Repository of configuration entries */
    private final ConfigurationRepository configRepository;
    /**
     * Get the single server-side management context.
     * Creates a context from the provided configuration repository.
     *
     * @return Returns the single server-side management context.
     * @param repository
     *          The repository of configuration entries.
     */
    public static ServerManagementContext getInstance() {
        return INSTANCE;
    }
    // Private constructor.
    private ServerManagementContext() {
        // No implementation required.
    ServerManagementContext(ConfigurationRepository repository) {
        configRepository = repository;
    }
    /**
@@ -397,7 +400,7 @@
        // Get the configuration entry.
        DN targetDN = DNBuilder.create(path);
        ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
        Entry configEntry = getManagedObjectConfigEntry(targetDN);
        try {
            ServerManagedObject<? extends S> managedObject;
            managedObject = decode(path, configEntry);
@@ -496,7 +499,7 @@
        // Determine the exact type of managed object referenced by the
        // path.
        DN dn = DNBuilder.create(path);
        ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
        Entry configEntry = getManagedObjectConfigEntry(dn);
        DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
        ManagedObjectDefinition<? extends C, ? extends S> managedObjDef;
@@ -520,7 +523,7 @@
     * Get the root configuration manager associated with this management
     * context.
     *
     * @return Returns the root configuration manager associated with this
     * @return the root configuration manager associated with this
     *         management context.
     */
    public RootCfg getRootConfiguration() {
@@ -531,11 +534,22 @@
     * 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.
     * @return the root configuration server managed object
     */
    public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
        return ROOT;
        // Use lazy initialisation because it needs a reference to this server context.
        ServerManagedObject<RootCfg> rootObject = root;
        if (rootObject == null) {
            synchronized(this) {
                rootObject = root;
                if (rootObject == null) {
                    root = rootObject = new ServerManagedObject<RootCfg>(ManagedObjectPath.emptyPath(),
                            RootCfgDefn.getInstance(), Collections.<PropertyDefinition<?>, SortedSet<?>> emptyMap(),
                            null, this);
                }
            }
        }
        return rootObject;
    }
    /**
@@ -562,19 +576,12 @@
        // Get the target entry.
        DN targetDN = DNBuilder.create(parent, relationDef);
        ConfigEntry configEntry;
        Set<DN> children;
        try {
            configEntry = DirectoryServer.getConfigEntry(targetDN);
            children = configRepository.getChildren(targetDN);
        } catch (ConfigException e) {
            return new String[0];
        }
        if (configEntry == null) {
            return new String[0];
        }
        // Retrieve the children.
        Set<DN> children = configEntry.getChildren();
        List<String> names = new ArrayList<String>(children.size());
        for (DN child : children) {
            // Assume that RDNs are single-valued and can be trimmed.
@@ -627,7 +634,7 @@
     *             be decoded.
     */
    <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode(
            ManagedObjectPath<C, S> path, ConfigEntry configEntry) throws DefinitionDecodingException,
            ManagedObjectPath<C, S> path, Entry configEntry) throws DefinitionDecodingException,
            ServerManagedObjectDecodingException {
        return decode(path, configEntry, null);
    }
@@ -659,7 +666,7 @@
     *             be decoded.
     */
    <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode(
            ManagedObjectPath<C, S> path, ConfigEntry configEntry, ConfigEntry newConfigEntry)
            ManagedObjectPath<C, S> path, Entry configEntry, Entry 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
@@ -683,25 +690,25 @@
        // 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);
        ServerManagedObject<? extends S> managedObject = decodeAux(path, mod, properties, configEntry.getName());
        if (exceptions.isEmpty()) {
            return mo;
            return managedObject;
        } else {
            throw new ServerManagedObjectDecodingException(mo, exceptions);
            throw new ServerManagedObjectDecodingException(managedObject, 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) {
            Map<PropertyDefinition<?>, SortedSet<?>> properties, DN configDN) {
        ManagedObjectPath<C, S> newPath = path.asSubType(d);
        return new ServerManagedObject<S>(newPath, d, properties, configEntry);
        return new ServerManagedObject<S>(newPath, d, properties, configDN, this);
    }
    // Create a property using the provided string values.
    private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path, PropertyDefinition<T> propertyDef,
            Iterable<Attribute> attributes, ConfigEntry newConfigEntry) throws PropertyException {
            Iterable<Attribute> attributes, Entry newConfigEntry) throws PropertyException {
        PropertyException exception = null;
        SortedSet<T> pvalues = new TreeSet<T>(propertyDef);
@@ -748,28 +755,50 @@
    // Gets the attribute associated with a property from a ConfigEntry.
    private Iterable<Attribute> getAttribute(ManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd,
            ConfigEntry configEntry) {
            Entry 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);
        return configEntry.getEntry().getAllAttributes(AttributeDescription.create(type));
        return configEntry.getAllAttributes(AttributeDescription.create(type));
    }
    // Get the default values for the specified property.
    private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p, PropertyDefinition<T> pd,
            ConfigEntry newConfigEntry) throws DefaultBehaviorException {
            Entry newConfigEntry) throws DefaultBehaviorException {
        DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
        return v.find(p, pd);
    }
    /**
     * Retrieves a configuration entry corresponding to the provided DN.
     *
     * @param dn
     *            DN of the configuration entry.
     * @return the configuration entry
     * @throws ConfigException
     *             If a problem occurs.
     */
    public Entry getConfigEntry(DN dn) throws ConfigException {
        return configRepository.getEntry(dn);
    }
    /**
     * Returns the repository containing all configuration entries.
     *
     * @return the repository
     */
    public ConfigurationRepository getConfigRepository() {
        return configRepository;
    }
    // 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;
    private Entry getManagedObjectConfigEntry(DN dn) throws ConfigException {
        Entry configEntry;
        try {
            configEntry = DirectoryServer.getConfigEntry(dn);
            configEntry = configRepository.getEntry(dn);
        } catch (ConfigException e) {
            debugLogger.trace("Unable to perform post add", e);
opendj-sdk/opendj-admin/src/main/java/org/opends/server/api/ConfigAddListener.java
@@ -26,7 +26,7 @@
package org.opends.server.api;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.opends.server.config.ConfigEntry;
import org.forgerock.opendj.ldap.Entry;
import org.opends.server.types.ConfigChangeResult;
/**
@@ -48,7 +48,7 @@
     * @return {@code true} if the proposed entry contains an acceptable
     *         configuration, or {@code false} if it does not.
     */
    public boolean configAddIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason);
    public boolean configAddIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason);
    /**
     * Attempts to apply a new configuration based on the provided added entry.
@@ -59,5 +59,5 @@
     * @return Information about the result of processing the configuration
     *         change.
     */
    public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry);
    public ConfigChangeResult applyConfigurationAdd(Entry configEntry);
}
opendj-sdk/opendj-admin/src/main/java/org/opends/server/api/ConfigChangeListener.java
@@ -25,9 +25,9 @@
 */
package org.opends.server.api;
import org.opends.server.config.ConfigEntry;
import org.opends.server.types.ConfigChangeResult;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.opendj.ldap.Entry;
/**
 * This interface defines the methods that a Directory Server component should
@@ -48,7 +48,7 @@
     * @return {@code true} if the proposed entry contains an acceptable
     *         configuration, or {@code false} if it does not.
     */
    public boolean configChangeIsAcceptable(ConfigEntry configEntry, LocalizableMessageBuilder unacceptableReason);
    public boolean configChangeIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason);
    /**
     * Attempts to apply a new configuration to this Directory Server component
@@ -60,5 +60,5 @@
     * @return Information about the result of processing the configuration
     *         change.
     */
    public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry);
    public ConfigChangeResult applyConfigurationChange(Entry configEntry);
}
opendj-sdk/opendj-admin/src/main/java/org/opends/server/api/ConfigDeleteListener.java
@@ -25,50 +25,39 @@
 */
package org.opends.server.api;
import org.opends.server.config.ConfigEntry;
import org.opends.server.types.ConfigChangeResult;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.opendj.ldap.Entry;
/**
 * This interface defines the methods that a Directory Server
 * component should implement if it wishes to be able to receive
 * notification if entries below a configuration entry are removed.
 * This interface defines the methods that a Directory Server component should
 * implement if it wishes to be able to receive notification if entries below a
 * configuration entry are removed.
 */
public interface ConfigDeleteListener
{
  /**
   * Indicates whether it is acceptable to remove the provided
   * configuration entry.
   *
   * @param  configEntry         The configuration entry that will be
   *                             removed from the configuration.
   * @param  unacceptableReason  A buffer to which this method can
   *                             append a human-readable message
   *                             explaining why the proposed delete is
   *                             not acceptable.
   *
   * @return  {@code true} if the proposed entry may be removed from
   *          the configuration, or {@code false} if not.
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                      LocalizableMessageBuilder unacceptableReason);
public interface ConfigDeleteListener {
    /**
     * Indicates whether it is acceptable to remove the provided configuration
     * entry.
     *
     * @param configEntry
     *            The configuration entry that will be removed from the
     *            configuration.
     * @param unacceptableReason
     *            A buffer to which this method can append a human-readable
     *            message explaining why the proposed delete is not acceptable.
     * @return {@code true} if the proposed entry may be removed from the
     *         configuration, or {@code false} if not.
     */
    public boolean configDeleteIsAcceptable(Entry configEntry, LocalizableMessageBuilder unacceptableReason);
  /**
   * Attempts to apply a new configuration based on the provided
   * deleted entry.
   *
   * @param  configEntry  The new configuration entry that has been
   *                      deleted.
   *
   * @return  Information about the result of processing the
   *          configuration change.
   */
  public ConfigChangeResult applyConfigurationDelete(
                                 ConfigEntry configEntry);
    /**
     * Attempts to apply a new configuration based on the provided deleted
     * entry.
     *
     * @param configEntry
     *            The new configuration entry that has been deleted.
     * @return Information about the result of processing the configuration
     *         change.
     */
    public ConfigChangeResult applyConfigurationDelete(Entry configEntry);
}
opendj-sdk/opendj-admin/src/main/java/org/opends/server/config/ConfigEntry.java
File was deleted
opendj-sdk/opendj-admin/src/main/java/org/opends/server/config/ConfigurationRepository.java
@@ -25,23 +25,155 @@
 */
package org.opends.server.config;
import java.util.List;
import java.util.Set;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
/**
 * Provides the configuration elements.
 * Provides configuration entries and listener registration on the entries.
 */
public interface ConfigurationRepository {
    /**
     * Returns the set of children of the provided entry.
     * Returns the set of DNs of children of the entry corresponding to the
     * provided DN. .
     *
     * @param entry
     *            The configuration entry.
     * @return the set of children of the entry
     * @param dn
     *            DN of a configuration entry.
     * @return the set of DN of children of the corresponding entry
     * @throws ConfigException
     *             If a problem occurs during retrieval.
     */
    Set<DN> getChildren(Entry entry);
    Set<DN> getChildren(DN dn) throws ConfigException;
    /**
     * Returns the configuration entry for the provided DN.
     *
     * @param dn
     *            DN of the configuration entry
     * @return the config entry
     * @throws ConfigException
     *             If a problem occurs while trying to retrieve the requested
     *             entry.
     */
    Entry getEntry(DN dn) throws ConfigException;
    /**
     * Checks if the provided DN corresponds to a configuration entry.
     *
     * @param dn
     *            DN of the configuration entry
     * @return {@code true} if and only if there is a configuration entry with
     *         this DN
     * @throws ConfigException
     *             If a problem occurs.
     */
    boolean hasEntry(DN dn) throws ConfigException;
    /**
     * Registers the provided add listener so that it will be notified if any
     * new entries are added immediately below the entry corresponding to the
     * provided DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @param listener
     *            The add listener that should be registered.
     */
    public void registerAddListener(DN dn, ConfigAddListener listener);
    /**
     * Registers the provided delete listener so that it will be notified if any
     * entries are deleted immediately below the entry corresponding to the
     * provided DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @param listener
     *            The delete listener that should be registered.
     */
    public void registerDeleteListener(DN dn, ConfigDeleteListener listener);
    /**
     * Registers the provided change listener so that it will be notified of any
     * changes to the entry corrresponding to provided DN. No check will be made
     * to determine whether the provided listener is already registered.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @param listener
     *            The change listener that should be registered.
     */
    public void registerChangeListener(DN dn, ConfigChangeListener listener);
    /**
     * Deregisters the provided add listener so that it will no longer be
     * notified if any new entries are added immediately below the entry
     * corresponding to the provided DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @param listener
     *            The add listener that should be deregistered.
     */
    public void deregisterAddListener(DN dn, ConfigAddListener listener);
    /**
     * Deregisters the provided delete listener so that it will no longer be
     * notified if any entries are deleted immediately below the entry
     * corresponding to the provided DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @param listener
     *            The delete listener that should be deregistered.
     */
    public void deregisterDeleteListener(DN dn, ConfigDeleteListener listener);
    /**
     * Attempts to deregister the provided change listener with the provided DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @param listener
     *            The change listener to deregister with this DN.
     * @return <CODE>true</CODE> if the specified listener was deregistered, or
     *         <CODE>false</CODE> if it was not.
     */
    public boolean deregisterChangeListener(DN dn, ConfigChangeListener listener);
    /**
     * Retrieves the add listeners that have been registered with the provided
     * DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @return The list of add listeners.
     */
    public List<ConfigAddListener> getAddListeners(DN dn);
    /**
     * Retrieves the delete listeners that have been registered with the
     * provided DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @return The list of delete listeners.
     */
    public List<ConfigDeleteListener> getDeleteListeners(DN dn);
    /**
     * Retrieves the change listeners that have been registered with the
     * provided DN.
     *
     * @param dn
     *            The DN of the configuration entry.
     * @return The list of change listeners.
     */
    public List<ConfigChangeListener> getChangeListeners(DN dn);
}
opendj-sdk/opendj-admin/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -25,13 +25,10 @@
 */
package org.opends.server.core;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.forgerock.opendj.ldap.schema.ObjectClass;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.schema.UnknownSchemaElementException;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
/**
 * TODO : this is a stub, with some default implementations for some methods,
@@ -116,8 +113,4 @@
        return getObjectClass(name);
    }
    public static ConfigEntry getConfigEntry(DN dn) throws ConfigException {
        throw new RuntimeException("Not implemented");
    }
}
opendj-sdk/opendj-admin/src/test/java/org/opends/server/admin/server/AdminTestCaseUtils.java
New file
@@ -0,0 +1,117 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import org.forgerock.opendj.admin.meta.RootCfgDefn;
import org.forgerock.opendj.ldap.Entry;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.config.ConfigException;
/**
 * This class defines some utility functions which can be used by test cases
 * which interact with the admin framework.
 */
public final class AdminTestCaseUtils {
    // The relation name which will be used for dummy configurations. A
    // deliberately obfuscated name is chosen to avoid clashes.
    private static final String DUMMY_TEST_RELATION = "*dummy*test*relation*";
    // Indicates if the dummy relation profile has been registered.
    private static boolean isProfileRegistered = false;
    // Prevent instantiation.
    private AdminTestCaseUtils() {
        // No implementation required.
    }
    /**
     * Decodes a configuration entry into the required type of server
     * configuration.
     *
     * @param <S>
     *            The type of server configuration to be decoded.
     * @param definition
     *            The required definition of the required managed object.
     * @param entry
     *            An entry containing the configuration to be decoded.
     * @return Returns the new server-side configuration.
     * @throws ConfigException
     *             If the entry could not be decoded.
     */
    public static <S extends Configuration> S getConfiguration(ServerManagementContext context,
            AbstractManagedObjectDefinition<?, S> definition, Entry entry) throws ConfigException {
        try {
            ServerManagedObject<? extends S> mo = context.decode(getPath(definition), entry);
            // Ensure constraints are satisfied.
            mo.ensureIsUsable();
            return mo.getConfiguration();
        } catch (DefinitionDecodingException e) {
            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(entry.getName(), e);
        } catch (ServerManagedObjectDecodingException e) {
            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e);
        } catch (ConstraintViolationException e) {
            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e);
        }
    }
    // Construct a dummy path.
    private synchronized static <C extends ConfigurationClient, S extends Configuration> ManagedObjectPath<C, S> getPath(
            AbstractManagedObjectDefinition<C, S> d) {
        if (!isProfileRegistered) {
            LDAPProfile.Wrapper profile = new LDAPProfile.Wrapper() {
                @Override
                public String getRelationRDNSequence(RelationDefinition<?, ?> r) {
                    if (r.getName().equals(DUMMY_TEST_RELATION)) {
                        return "cn=dummy configuration,cn=config";
                    } else {
                        return null;
                    }
                }
            };
            LDAPProfile.getInstance().pushWrapper(profile);
            isProfileRegistered = true;
        }
        SingletonRelationDefinition.Builder<C, S> builder = new SingletonRelationDefinition.Builder<C, S>(
                RootCfgDefn.getInstance(), DUMMY_TEST_RELATION, d);
        ManagedObjectPath<?, ?> root = ManagedObjectPath.emptyPath();
        return root.child(builder.getInstance());
    }
}
opendj-sdk/opendj-admin/src/test/java/org/opends/server/admin/server/AggregationServerTest.java
New file
@@ -0,0 +1,814 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS
 */
package org.opends.server.admin.server;
import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.naming.ldap.LdapName;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.admin.client.ConnectionHandlerCfgClient;
import org.forgerock.opendj.admin.client.LDAPConnectionHandlerCfgClient;
import org.forgerock.opendj.admin.client.RootCfgClient;
import org.forgerock.opendj.admin.meta.LDAPConnectionHandlerCfgDefn;
import org.forgerock.opendj.admin.server.ConnectionHandlerCfg;
import org.forgerock.opendj.admin.server.RootCfg;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.AdministratorAction;
import org.opends.server.admin.AggregationPropertyDefinition;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.condition.Conditions;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ConfigChangeResult;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * Test cases for aggregations on the server-side.
 */
@Test(singleThreaded = true)
public final class AggregationServerTest extends AdminTestCase {
    /**
     * Dummy change listener for triggering change constraint call-backs.
     */
    private static final class DummyChangeListener implements ConfigurationChangeListener<TestChildCfg> {
        /**
         * {@inheritDoc}
         */
        @Override
        public ConfigChangeResult applyConfigurationChange(TestChildCfg configuration) {
            return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isConfigurationChangeAcceptable(TestChildCfg configuration, List<LocalizableMessage> unacceptableReasons) {
            return true;
        }
    }
    /**
     * Dummy delete listener for triggering delete constraint call-backs.
     */
    private static final class DummyDeleteListener implements ConfigurationDeleteListener<TestChildCfg> {
        /**
         * {@inheritDoc}
         */
        @Override
        public ConfigChangeResult applyConfigurationDelete(TestChildCfg configuration) {
            return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isConfigurationDeleteAcceptable(TestChildCfg configuration, List<LocalizableMessage> unacceptableReasons) {
            return true;
        }
    }
    private static final String TEST_CHILD_7_DN = "cn=test child 7,cn=test children,cn=test parent 1,cn=test parents,cn=config";
    private static final String TEST_CHILD_6_DN = "cn=test child 6,cn=test children,cn=test parent 1,cn=test parents,cn=config";
    /** The name of the test connection handler. */
    private static final String TEST_CONNECTION_HANDLER_NAME = "Test Connection Handler";
    /** Test child 1 LDIF. */
    private static final String[] TEST_CHILD_1 = new String[] {
            "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 1", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real" };
    /** Test child 2 LDIF. */
    private static final String[] TEST_CHILD_2 = new String[] {
            "dn: cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 2", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-rotation-policy: cn=LDAP Connection Handler, cn=connection handlers, cn=config" };
    /** Test child 3 LDIF (invalid reference). */
    private static final String[] TEST_CHILD_3 = new String[] {
            "dn: cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 3", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-rotation-policy: cn=LDAP Connection Handler, cn=bad rdn, cn=config" };
    /** Test child 4 LDIF. */
    private static final String[] TEST_CHILD_4 = new String[] {
            "dn: cn=test child 4,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 4", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-rotation-policy: cn=LDAP Connection Handler, cn=connection handlers, cn=config",
            "ds-cfg-rotation-policy: cn=LDAPS Connection Handler, cn=connection handlers, cn=config" };
    /** Test child 5 LDIF. */
    private static final String[] TEST_CHILD_5 = new String[] {
            "dn: cn=test child 5,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 5", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-rotation-policy: cn=BAD Connection Handler 1, cn=connection handlers, cn=config",
            "ds-cfg-rotation-policy: cn=BAD Connection Handler 2, cn=connection handlers, cn=config",
            "ds-cfg-rotation-policy: cn=LDAP Connection Handler, cn=connection handlers, cn=config" };
    /** Test child 6 LDIF. */
    private static final String[] TEST_CHILD_6 = new String[] {
            "dn: cn=test child 6,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 6", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-rotation-policy: cn=" + TEST_CONNECTION_HANDLER_NAME + ", cn=connection handlers, cn=config" };
    /** Test child 7 LDIF. */
    private static final String[] TEST_CHILD_7 = new String[] {
            "dn: cn=test child 7,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 7", "ds-cfg-enabled: false",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-rotation-policy: cn=" + TEST_CONNECTION_HANDLER_NAME + ", cn=connection handlers, cn=config" };
    /** Test LDIF. */
    private static final String[] TEST_LDIF = new String[] {
            // Base entries.
            "dn: cn=test parents,cn=config",
            "objectclass: top",
            "objectclass: ds-cfg-branch",
            "cn: test parents",
            "",
            // Parent 1.
            "dn: cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-parent-dummy", "cn: test parent 1", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real", "",
            // Child base entries.
            "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-branch", "cn: test children", "" };
    /**
     * The saved test child configuration "aggregation-property" property
     * definition.
     */
    private AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg>
        aggregationPropertyDefinitionDefault = null;
    /**
     * An aggregation where the target must be enabled if the source is enabled.
     */
    private AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg>
        aggregationPropertyDefinitionTargetAndSourceMustBeEnabled = null;
    /** An aggregation where the target must be enabled. */
    private AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg>
        aggregationPropertyDefinitionTargetMustBeEnabled = null;
    /**
     * Sets up tests
     *
     * @throws Exception
     *             If the server could not be initialized.
     */
    @BeforeClass
    public void setUp() throws Exception {
        TestCfg.setUp();
        // Add test managed objects.
        TestCaseUtils.addEntries(TEST_LDIF);
        // Save the aggregation property definition so that it can be
        // replaced and restored later.
        aggregationPropertyDefinitionDefault = TestChildCfgDefn.getInstance()
                .getAggregationPropertyPropertyDefinition();
        // Create the two test aggregation properties.
        AggregationPropertyDefinition.Builder<ConnectionHandlerCfgClient, ConnectionHandlerCfg> builder;
        TestChildCfgDefn d = TestChildCfgDefn.getInstance();
        builder = AggregationPropertyDefinition.createBuilder(d, "aggregation-property");
        builder.setOption(PropertyOption.MULTI_VALUED);
        builder.setAdministratorAction(new AdministratorAction(AdministratorAction.Type.NONE, d,
                "aggregation-property"));
        builder.setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider<String>());
        builder.setParentPath("/");
        builder.setRelationDefinition("connection-handler");
        builder.setTargetIsEnabledCondition(Conditions.contains("enabled", "true"));
        aggregationPropertyDefinitionTargetMustBeEnabled = builder.getInstance();
        TestCfg.initializePropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
        builder = AggregationPropertyDefinition.createBuilder(d, "aggregation-property");
        builder.setOption(PropertyOption.MULTI_VALUED);
        builder.setAdministratorAction(new AdministratorAction(AdministratorAction.Type.NONE, d,
                "aggregation-property"));
        builder.setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider<String>());
        builder.setParentPath("/");
        builder.setRelationDefinition("connection-handler");
        builder.setTargetIsEnabledCondition(Conditions.contains("enabled", "true"));
        builder.setTargetNeedsEnablingCondition(Conditions.contains("mandatory-boolean-property", "true"));
        aggregationPropertyDefinitionTargetAndSourceMustBeEnabled = builder.getInstance();
        TestCfg.initializePropertyDefinition(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
    }
    /**
     * Tears down test environment.
     *
     * @throws Exception
     *             If the test entries could not be removed.
     */
    @AfterClass
    public void tearDown() throws Exception {
        TestCfg.cleanup();
        // Restore the test child aggregation definition.
        TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
        // Remove test entries.
        deleteSubtree("cn=test parents,cn=config");
    }
    /**
     * Tests that aggregation is rejected when the LDAP DN contains a valid RDN
     * but an invalid parent DN.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationBadBaseDN() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_3);
        try {
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 3");
            Assert.fail("Unexpectedly added test child 3 when it had a bad aggregation value");
        } catch (ConfigException e) {
            // Check that we have a decoding exception as the cause and
            // there was only one cause the illegal property value.
            Throwable cause = e.getCause();
            if (cause instanceof ServerManagedObjectDecodingException) {
                ServerManagedObjectDecodingException de = (ServerManagedObjectDecodingException) cause;
                Collection<PropertyException> causes = de.getCauses();
                Assert.assertEquals(causes.size(), 1);
                cause = causes.iterator().next();
                if (cause instanceof IllegalPropertyValueStringException) {
                    IllegalPropertyValueStringException pe = (IllegalPropertyValueStringException) cause;
                    Assert.assertEquals(pe.getPropertyDefinition(), TestChildCfgDefn.getInstance()
                            .getAggregationPropertyPropertyDefinition());
                    Assert.assertEquals(pe.getIllegalValueString(),
                            "cn=LDAP Connection Handler, cn=bad rdn, cn=config");
                } else {
                    // Got an unexpected cause.
                    throw e;
                }
            } else {
                // Got an unexpected cause.
                throw e;
            }
        } finally {
            deleteSubtree("cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that aggregation is rejected by a constraint violation when the DN
     * values are dangling.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationDanglingReference() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_5);
        try {
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 5");
            Assert.fail("Unexpectedly added test child 5 when it had a dangling reference");
        } catch (ConfigException e) {
            // Check that we have a constraint violation as the cause.
            Throwable cause = e.getCause();
            if (cause instanceof ConstraintViolationException) {
                ConstraintViolationException cve = (ConstraintViolationException) cause;
                Collection<LocalizableMessage> causes = cve.getMessages();
                Assert.assertEquals(causes.size(), 2);
            } else {
                // Got an unexpected cause.
                throw e;
            }
        } finally {
            deleteSubtree("cn=test child 5,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that aggregation is rejected by a constraint violation when an
     * enabled component references a disabled component and the referenced
     * component must always be enabled.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationDisabledReference1() throws Exception {
        // Add the entry and the connection handler.
        TestCaseUtils.addEntry(TEST_CHILD_6);
        try {
            createConnectionHandler(false);
        } catch (Exception e) {
            deleteSubtree(TEST_CHILD_6_DN);
            throw e;
        }
        // Register the temporary aggregation definition.
        TestCfg.removeConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
        TestCfg.addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
        TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
        try {
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 6");
            Assert.fail("Unexpectedly added test child 6 when it had a disabled reference");
        } catch (ConfigException e) {
            // Check that we have a constraint violation as the cause.
            Throwable cause = e.getCause();
            if (cause instanceof ConstraintViolationException) {
                ConstraintViolationException cve = (ConstraintViolationException) cause;
                Collection<LocalizableMessage> causes = cve.getMessages();
                Assert.assertEquals(causes.size(), 1);
            } else {
                // Got an unexpected cause.
                throw e;
            }
        } finally {
            // Put back the default aggregation definition.
            TestCfg.removeConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
            TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
            TestCfg.addConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
            try {
                deleteSubtree(TEST_CHILD_6_DN);
            } finally {
                deleteConnectionHandler();
            }
        }
    }
    /**
     * Tests that aggregation is rejected by a constraint violation when a
     * disabled component references a disabled component and the referenced
     * component must always be enabled.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationDisabledReference2() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_7);
        try {
            createConnectionHandler(false);
        } catch (Exception e) {
            deleteSubtree(TEST_CHILD_7_DN);
            throw e;
        }
        // Register the temporary aggregation definition.
        TestCfg.removeConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
        TestCfg.addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
        TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
        try {
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 7");
            Assert.fail("Unexpectedly added test child 7 when it had a disabled reference");
        } catch (ConfigException e) {
            // Check that we have a constraint violation as the cause.
            Throwable cause = e.getCause();
            if (cause instanceof ConstraintViolationException) {
                ConstraintViolationException cve = (ConstraintViolationException) cause;
                Collection<LocalizableMessage> causes = cve.getMessages();
                Assert.assertEquals(causes.size(), 1);
            } else {
                // Got an unexpected cause.
                throw e;
            }
        } finally {
            // Put back the default aggregation definition.
            TestCfg.removeConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
            TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
            TestCfg.addConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
            try {
                deleteSubtree(TEST_CHILD_7_DN);
            } finally {
                deleteConnectionHandler();
            }
        }
    }
    /**
     * Tests that aggregation is rejected by a constraint violation when an
     * enabled component references a disabled component and the referenced
     * component must always be enabled when the referencing component is
     * enabled.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationDisabledReference3() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_6);
        try {
            createConnectionHandler(false);
        } catch (Exception e) {
            deleteSubtree(TEST_CHILD_6_DN);
            throw e;
        }
        // Register the temporary aggregation definition.
        TestCfg.removeConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
        TestCfg.addPropertyDefinition(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
        TestCfg.addConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled.getSourceConstraint());
        try {
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 6");
            Assert.fail("Unexpectedly added test child 6 when it had a disabled reference");
        } catch (ConfigException e) {
            // Check that we have a constraint violation as the cause.
            Throwable cause = e.getCause();
            if (cause instanceof ConstraintViolationException) {
                ConstraintViolationException cve = (ConstraintViolationException) cause;
                Collection<LocalizableMessage> causes = cve.getMessages();
                Assert.assertEquals(causes.size(), 1);
            } else {
                // Got an unexpected cause.
                throw e;
            }
        } finally {
            // Put back the default aggregation definition.
            TestCfg.removeConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled.getSourceConstraint());
            TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
            TestCfg.addConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
            try {
                deleteSubtree(TEST_CHILD_6_DN);
            } finally {
                deleteConnectionHandler();
            }
        }
    }
    /**
     * Tests that aggregation is allowed when a disabled component references a
     * disabled component and the referenced component must always be enabled
     * when the referencing component is enabled.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationDisabledReference4() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_7);
        try {
            createConnectionHandler(false);
        } catch (Exception e) {
            deleteSubtree(TEST_CHILD_7_DN);
            throw e;
        }
        // Register the temporary aggregation definition.
        TestCfg.removeConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
        TestCfg.addPropertyDefinition(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
        TestCfg.addConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled.getSourceConstraint());
        try {
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 7");
        } finally {
            // Put back the default aggregation definition.
            TestCfg.removeConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled.getSourceConstraint());
            TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
            TestCfg.addConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
            try {
                deleteSubtree(TEST_CHILD_7_DN);
            } finally {
                deleteConnectionHandler();
            }
        }
    }
    /**
     * Tests that aggregation contains no values when it contains does not
     * contain any DN attribute values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationEmpty() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_1);
        try {
            TestParentCfg parent = getParent("test parent 1");
            assertChild1(parent.getTestChild("test child 1"));
        } finally {
            deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that aggregation contains multiple valid values when it contains a
     * multiple valid DN attribute values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationMultipleValues() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_4);
        try {
            TestParentCfg parent = getParent("test parent 1");
            assertChild4(parent.getTestChild("test child 4"));
        } finally {
            deleteSubtree("cn=test child 4,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that aggregation contains single valid value when it contains a
     * single valid DN attribute values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAggregationSingle() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_2);
        try {
            TestParentCfg parent = getParent("test parent 1");
            assertChild2(parent.getTestChild("test child 2"));
        } finally {
            deleteSubtree("cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that it is impossible to delete a referenced component when the
     * referenced component must always exist regardless of whether the
     * referencing component is enabled or not.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testCannotDeleteReferencedComponent() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_7);
        try {
            createConnectionHandler(true);
        } catch (Exception e) {
            deleteSubtree(TEST_CHILD_7_DN);
            throw e;
        }
        // Register the temporary aggregation definition.
        TestCfg.removeConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
        TestCfg.addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
        TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
        ConfigurationDeleteListener<TestChildCfg> dl = new DummyDeleteListener();
        ConfigurationChangeListener<TestChildCfg> cl = new DummyChangeListener();
        try {
            // Retrieve the parent and child managed objects and register
            // delete and change listeners respectively in order to trigger
            // the constraint call-backs.
            TestParentCfg parent = getParent("test parent 1");
            parent.addTestChildDeleteListener(dl);
            TestChildCfg child = parent.getTestChild("test child 7");
            child.addChangeListener(cl);
            // Now attempt to delete the referenced connection handler.
            // This should fail.
            try {
                deleteConnectionHandler();
                Assert.fail("Successfully deleted a referenced component");
            } catch (OperationRejectedException e) {
                // This is the expected exception - do nothing.
            }
        } finally {
            try {
                deleteSubtree(TEST_CHILD_7_DN);
            } finally {
                try {
                    deleteConnectionHandler();
                } catch (ManagedObjectNotFoundException e) {
                    // Ignore as it may have been deleted already.
                } finally {
                    // Remove the temporary delete listener.
                    TestParentCfg parent = getParent("test parent 1");
                    parent.removeTestChildDeleteListener(dl);
                    // Put back the default aggregation definition.
                    TestCfg.removeConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
                    TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
                    TestCfg.addConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
                }
            }
        }
    }
    /**
     * Tests that it is impossible to disable a referenced component when the
     * referenced component must always be enabled regardless of whether the
     * referencing component is enabled or not.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testCannotDisableReferencedComponent() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_7);
        try {
            createConnectionHandler(true);
        } catch (Exception e) {
            deleteSubtree(TEST_CHILD_7_DN);
            throw e;
        }
        // Register the temporary aggregation definition.
        TestCfg.removeConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
        TestCfg.addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
        TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
        ConfigurationDeleteListener<TestChildCfg> dl = new DummyDeleteListener();
        ConfigurationChangeListener<TestChildCfg> cl = new DummyChangeListener();
        try {
            // Retrieve the parent and child managed objects and register
            // delete and change listeners respectively in order to trigger
            // the constraint call-backs.
            TestParentCfg parent = getParent("test parent 1");
            parent.addTestChildDeleteListener(dl);
            TestChildCfg child = parent.getTestChild("test child 7");
            child.addChangeListener(cl);
            // Now attempt to disable the referenced connection handler.
            // This should fail.
            try {
                RootCfgClient root = TestCaseUtils.getRootConfiguration();
                ConnectionHandlerCfgClient client = root.getConnectionHandler(TEST_CONNECTION_HANDLER_NAME);
                client.setEnabled(false);
                client.commit();
                Assert.fail("Successfully disabled a referenced component");
            } catch (OperationRejectedException e) {
                // This is the expected exception - do nothing.
            }
        } finally {
            try {
                deleteSubtree(TEST_CHILD_7_DN);
            } finally {
                try {
                    deleteConnectionHandler();
                } finally {
                    // Remove the temporary delete listener.
                    TestParentCfg parent = getParent("test parent 1");
                    parent.removeTestChildDeleteListener(dl);
                    // Put back the default aggregation definition.
                    TestCfg.removeConstraint(aggregationPropertyDefinitionTargetMustBeEnabled.getSourceConstraint());
                    TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
                    TestCfg.addConstraint(aggregationPropertyDefinitionDefault.getSourceConstraint());
                }
            }
        }
    }
    /** Assert that the values of child 1 are correct. */
    private void assertChild1(TestChildCfg child) {
        Assert.assertEquals(child.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertSetEquals(child.getAggregationProperty(), new String[0]);
    }
    /** Assert that the values of child 2 are correct. */
    private void assertChild2(TestChildCfg child) {
        Assert.assertEquals(child.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        // Test normalization.
        assertSetEquals(child.getAggregationProperty(), "LDAP Connection Handler");
        assertSetEquals(child.getAggregationProperty(), "  LDAP   Connection  Handler ");
        assertSetEquals(child.getAggregationProperty(), "  ldap connection HANDLER ");
    }
    /** Assert that the values of child 4 are correct. */
    private void assertChild4(TestChildCfg child) {
        Assert.assertEquals(child.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertSetEquals(child.getAggregationProperty(), "LDAPS Connection Handler", "LDAP Connection Handler");
    }
    /** Asserts that the actual set of DNs contains the expected values. */
    private void assertSetEquals(SortedSet<String> actual, String... expected) {
        SortedSet<String> values = new TreeSet<String>(TestChildCfgDefn.getInstance()
                .getAggregationPropertyPropertyDefinition());
        if (expected != null) {
            for (String value : expected) {
                values.add(value);
            }
        }
        Assert.assertEquals((Object) actual, (Object) values);
    }
    /** Creates a test connection handler for testing. */
    private void createConnectionHandler(boolean enabled) throws Exception {
        RootCfgClient root = TestCaseUtils.getRootConfiguration();
        LDAPConnectionHandlerCfgClient client = root.createConnectionHandler(
                LDAPConnectionHandlerCfgDefn.getInstance(), TEST_CONNECTION_HANDLER_NAME, null);
        client.setEnabled(enabled);
        client.setListenPort(1000);
        client.commit();
    }
    /** Deletes the test connection handler after testing. */
    private void deleteConnectionHandler() throws Exception {
        RootCfgClient root = TestCaseUtils.getRootConfiguration();
        root.removeConnectionHandler(TEST_CONNECTION_HANDLER_NAME);
    }
    /** Gets the named parent configuration. */
    private TestParentCfg getParent(String name) throws IllegalArgumentException, ConfigException {
        ServerManagedObject<RootCfg> root = serverContext.getRootConfigurationManagedObject();
        return root.getChild(TestCfg.getTestOneToManyParentRelationDefinition(), name).getConfiguration();
    }
}
opendj-sdk/opendj-admin/src/test/java/org/opends/server/admin/server/ConstraintTest.java
New file
@@ -0,0 +1,489 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import java.util.List;
import javax.naming.OperationNotSupportedException;
import javax.naming.ldap.LdapName;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.types.ConfigChangeResult;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * Test cases for constraints on the server-side.
 */
public final class ConstraintTest extends AdminTestCase {
    // Child DN.
    private static final String TEST_CHILD_1_DN = "cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config";
    /**
     * A test child add listener.
     */
    private static class AddListener implements ConfigurationAddListener<TestChildCfg> {
        /**
         * {@inheritDoc}
         */
        public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) {
            return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
        /**
         * {@inheritDoc}
         */
        public boolean isConfigurationAddAcceptable(TestChildCfg configuration, List<Message> unacceptableReasons) {
            return true;
        }
    }
    /**
     * A test child delete listener.
     */
    private static class DeleteListener implements ConfigurationDeleteListener<TestChildCfg> {
        /**
         * {@inheritDoc}
         */
        public ConfigChangeResult applyConfigurationDelete(TestChildCfg configuration) {
            return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
        /**
         * {@inheritDoc}
         */
        public boolean isConfigurationDeleteAcceptable(TestChildCfg configuration, List<Message> unacceptableReasons) {
            return true;
        }
    }
    /**
     * A test child change listener.
     */
    private static class ChangeListener implements ConfigurationChangeListener<TestChildCfg> {
        /**
         * {@inheritDoc}
         */
        public ConfigChangeResult applyConfigurationChange(TestChildCfg configuration) {
            return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
        /**
         * {@inheritDoc}
         */
        public boolean isConfigurationChangeAcceptable(TestChildCfg configuration, List<Message> unacceptableReasons) {
            return true;
        }
    }
    // Test child 1 LDIF.
    private static final String[] TEST_CHILD_1 = new String[] {
            "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 1", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real" };
    // Test LDIF.
    private static final String[] TEST_LDIF = new String[] {
            // Base entries.
            "dn: cn=test parents,cn=config",
            "objectclass: top",
            "objectclass: ds-cfg-branch",
            "cn: test parents",
            "",
            // Parent 1 - uses default values for
            // optional-multi-valued-dn-property.
            "dn: cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-parent-dummy", "cn: test parent 1", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real", "",
            // Child base entries.
            "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-branch", "cn: test children", "", };
    // JNDI LDAP context.
    private JNDIDirContextAdaptor adaptor = null;
    /**
     * Sets up tests
     *
     * @throws Exception
     *             If the server could not be initialized.
     */
    @BeforeClass
    public void setUp() throws Exception {
        // This test suite depends on having the schema available, so
        // we'll start the server.
        TestCaseUtils.startServer();
        TestCfg.setUp();
        // Add test managed objects.
        TestCaseUtils.addEntries(TEST_LDIF);
    }
    /**
     * Tears down test environment.
     *
     * @throws Exception
     *             If the test entries could not be removed.
     */
    @AfterClass
    public void tearDown() throws Exception {
        TestCfg.cleanup();
        // Remove test entries.
        deleteSubtree("cn=test parents,cn=config");
    }
    /**
     * Tests that retrieval can succeed.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testGetManagedObjectSuccess() throws Exception {
        MockConstraint constraint = new MockConstraint(true, false);
        try {
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestCfg.addConstraint(constraint);
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 1");
        } finally {
            TestCfg.removeConstraint(constraint);
            try {
                deleteSubtree(TEST_CHILD_1_DN);
            } catch (Exception e) {
                // Do nothing.
            }
        }
    }
    /**
     * Tests that retrieval can fail.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testGetManagedObjectFail() throws Exception {
        MockConstraint constraint = new MockConstraint(false, true);
        try {
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestCfg.addConstraint(constraint);
            TestParentCfg parent = getParent("test parent 1");
            parent.getTestChild("test child 1");
        } catch (ConfigException e) {
            Throwable cause = e.getCause();
            if (cause instanceof ConstraintViolationException) {
                ConstraintViolationException cve = (ConstraintViolationException) cause;
                Assert.assertEquals(cve.getMessages().size(), 1);
                Assert.assertSame(cve.getManagedObject().getManagedObjectDefinition(), TestChildCfgDefn.getInstance());
            } else {
                // Wrong cause.
                throw e;
            }
        } finally {
            TestCfg.removeConstraint(constraint);
            try {
                deleteSubtree(TEST_CHILD_1_DN);
            } catch (Exception e) {
                // Do nothing.
            }
        }
    }
    /**
     * Tests that an add constraint can succeed.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAddConstraintSuccess() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        AddListener listener = new AddListener();
        parent.addTestChildAddListener(listener);
        MockConstraint constraint = new MockConstraint(true, false);
        TestCfg.addConstraint(constraint);
        try {
            try {
                // Add the entry.
                addEntry(ResultCode.SUCCESS, TEST_CHILD_1);
            } finally {
                try {
                    deleteSubtree(TEST_CHILD_1_DN);
                } catch (Exception e) {
                    // Do nothing.
                }
            }
        } finally {
            TestCfg.removeConstraint(constraint);
            parent.removeTestChildAddListener(listener);
        }
    }
    /**
     * Tests that an add constraint can fail.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAddConstraintFail() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        AddListener listener = new AddListener();
        parent.addTestChildAddListener(listener);
        MockConstraint constraint = new MockConstraint(false, true);
        TestCfg.addConstraint(constraint);
        try {
            try {
                // Add the entry.
                addEntry(ResultCode.UNWILLING_TO_PERFORM, TEST_CHILD_1);
            } finally {
                try {
                    deleteSubtree(TEST_CHILD_1_DN);
                } catch (Exception e) {
                    // Do nothing.
                }
            }
        } finally {
            TestCfg.removeConstraint(constraint);
            parent.removeTestChildAddListener(listener);
        }
    }
    /**
     * Tests that a delete constraint can succeed.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testDeleteConstraintSuccess() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        DeleteListener listener = new DeleteListener();
        parent.addTestChildDeleteListener(listener);
        MockConstraint constraint = new MockConstraint(false, true);
        TestCfg.addConstraint(constraint);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            // Now delete it - this should trigger the constraint.
            deleteSubtree(TEST_CHILD_1_DN);
        } finally {
            TestCfg.removeConstraint(constraint);
            parent.removeTestChildDeleteListener(listener);
            try {
                // Clean up.
                deleteSubtree(TEST_CHILD_1_DN);
            } catch (Exception e) {
                // Ignore.
            }
        }
    }
    /**
     * Tests that a delete constraint can fail.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testDeleteConstraintFail() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        DeleteListener listener = new DeleteListener();
        parent.addTestChildDeleteListener(listener);
        MockConstraint constraint = new MockConstraint(true, false);
        TestCfg.addConstraint(constraint);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            try {
                // Now delete it - this should trigger the constraint.
                deleteSubtree(TEST_CHILD_1_DN);
                // Should not have succeeded.
                Assert.fail("Delete constraint failed to prevent deletion");
            } catch (OperationNotSupportedException e) {
                // Ignore - this is the expected exception.
            }
        } finally {
            TestCfg.removeConstraint(constraint);
            parent.removeTestChildDeleteListener(listener);
            try {
                // Clean up.
                deleteSubtree(TEST_CHILD_1_DN);
            } catch (Exception e) {
                // Ignore.
            }
        }
    }
    /**
     * Tests that a modify constraint can succeed.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChangeConstraintSuccess() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        MockConstraint constraint = new MockConstraint(true, false);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestChildCfg child = parent.getTestChild("test child 1");
            TestCfg.addConstraint(constraint);
            ChangeListener listener = new ChangeListener();
            child.addChangeListener(listener);
            // Now modify it.
            String[] changes = new String[] {
                    "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
                    "changetype: modify", "replace: ds-cfg-base-dn", "ds-cfg-base-dn: dc=new value 1,dc=com",
                    "ds-cfg-base-dn: dc=new value 2,dc=com", "-", "replace: ds-cfg-group-dn",
                    "ds-cfg-group-dn: dc=new value 3,dc=com", "ds-cfg-group-dn: dc=new value 4,dc=com" };
            int result = TestCaseUtils.applyModifications(true, changes);
            Assert.assertEquals(result, ResultCode.SUCCESS.getIntValue());
        } finally {
            TestCfg.removeConstraint(constraint);
            try {
                deleteSubtree(TEST_CHILD_1_DN);
            } catch (Exception e) {
                // Ignore.
            }
        }
    }
    /**
     * Tests that a modify constraint can fail.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChangeConstraintFail() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        MockConstraint constraint = new MockConstraint(false, true);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestChildCfg child = parent.getTestChild("test child 1");
            TestCfg.addConstraint(constraint);
            ChangeListener listener = new ChangeListener();
            child.addChangeListener(listener);
            // Now modify it.
            String[] changes = new String[] {
                    "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
                    "changetype: modify", "replace: ds-cfg-base-dn", "ds-cfg-base-dn: dc=new value 1,dc=com",
                    "ds-cfg-base-dn: dc=new value 2,dc=com", "-", "replace: ds-cfg-group-dn",
                    "ds-cfg-group-dn: dc=new value 3,dc=com", "ds-cfg-group-dn: dc=new value 4,dc=com" };
            int result = TestCaseUtils.applyModifications(true, changes);
            Assert.assertEquals(result, ResultCode.UNWILLING_TO_PERFORM.getIntValue());
        } finally {
            TestCfg.removeConstraint(constraint);
            try {
                deleteSubtree(TEST_CHILD_1_DN);
            } catch (Exception e) {
                // Ignore.
            }
        }
    }
    // Add an entry and check its result.
    private void addEntry(ResultCode expected, String... lines) throws Exception {
        Entry entry = TestCaseUtils.makeEntry(lines);
        InternalClientConnection conn = InternalClientConnection.getRootConnection();
        AddOperation add = conn.processAdd(entry.getDN(), entry.getObjectClasses(), entry.getUserAttributes(),
                entry.getOperationalAttributes());
        Assert.assertEquals(add.getResultCode(), expected, add.getErrorMessage().toString());
    }
    // Deletes the named sub-tree.
    private void deleteSubtree(String dn) throws Exception {
        getAdaptor().deleteSubtree(new LdapName(dn));
    }
    // Gets the JNDI connection for the test server instance.
    private synchronized JNDIDirContextAdaptor getAdaptor() throws Exception {
        if (adaptor == null) {
            adaptor = JNDIDirContextAdaptor.simpleSSLBind("127.0.0.1", TestCaseUtils.getServerAdminPort(),
                    "cn=directory manager", "password");
        }
        return adaptor;
    }
    // Gets the named parent configuration.
    private TestParentCfg getParent(String name) throws IllegalArgumentException, ConfigException {
        ServerManagedObject<RootCfg> root = serverContext.getRootConfigurationManagedObject();
        TestParentCfg parent = root.getChild(TestCfg.getTestOneToManyParentRelationDefinition(), name)
                .getConfiguration();
        return parent;
    }
}
opendj-sdk/opendj-admin/src/test/java/org/opends/server/admin/server/DNBuilderTest.java
New file
@@ -0,0 +1,165 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import static org.testng.Assert.*;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgClient;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfgDefn;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * Test cases for the server DNBuilder class.
 */
public final class DNBuilderTest extends AdminTestCase {
    /**
     * Sets up tests
     *
     * @throws Exception
     *             If the server could not be initialized.
     */
    @BeforeClass
    public void setUp() throws Exception {
        disableClassValidationForProperties();
        TestCfg.setUp();
    }
    /**
     * Tears down test environment.
     */
    @AfterClass
    public void tearDown() {
        TestCfg.cleanup();
    }
    /**
     * Tests construction of a DN from a managed object path containing a
     * subordinate one-to-many relationship.
     *
     * @throws Exception
     *             If an unexpected exception occurred.
     */
    @Test
    public void testCreateOneToMany() throws Exception {
        // First create the path.
        ManagedObjectPath<? extends ConfigurationClient, ? extends Configuration> path = ManagedObjectPath
                .emptyPath();
        path = path.child(TestCfg.getTestOneToManyParentRelationDefinition(), "test-parent-1");
        path = path.child(TestParentCfgDefn.getInstance().getTestChildrenRelationDefinition(), "test-child-1");
        // Now serialize it.
        DN actual = DNBuilder.create(path);
        DN expected = DN.valueOf("cn=test-child-1,cn=test children,cn=test-parent-1,cn=test parents,cn=config");
        assertEquals(actual, expected);
    }
    /**
     * Tests construction of a DN from a managed object path containing a
     * subordinate one-to-one relationship.
     *
     * @throws Exception
     *             If an unexpected exception occurred.
     */
    @Test
    public void testCreateOneToOne() throws Exception {
        // First create the path.
        ManagedObjectPath<? extends ConfigurationClient, ? extends Configuration> path = ManagedObjectPath
                .emptyPath();
        SingletonRelationDefinition.Builder<TestChildCfgClient, TestChildCfg> b = new SingletonRelationDefinition.Builder<TestChildCfgClient, TestChildCfg>(
                TestParentCfgDefn.getInstance(), "singleton-test-child", TestChildCfgDefn.getInstance());
        final SingletonRelationDefinition<TestChildCfgClient, TestChildCfg> r2 = b.getInstance();
        LDAPProfile.Wrapper wrapper = new LDAPProfile.Wrapper() {
            /**
             * {@inheritDoc}
             */
            @Override
            public String getRelationRDNSequence(RelationDefinition<?, ?> r) {
                if (r == r2) {
                    return "cn=singleton-test-child";
                } else {
                    return null;
                }
            }
        };
        path = path.child(TestCfg.getTestOneToManyParentRelationDefinition(), "test-parent-1");
        path = path.child(r2);
        // Now serialize it.
        LDAPProfile.getInstance().pushWrapper(wrapper);
        try {
            DN actual = DNBuilder.create(path);
            DN expected = DN.valueOf("cn=singleton-test-child,cn=test-parent-1,cn=test parents,cn=config");
            assertEquals(actual, expected);
        } finally {
            LDAPProfile.getInstance().popWrapper();
        }
    }
    /**
     * Tests construction of a DN from a managed object path containing a
     * subordinate one-to-zero-or-one relationship.
     *
     * @throws Exception
     *             If an unexpected exception occurred.
     */
    @Test
    public void testCreateOneToZeroOrOne() throws Exception {
        // First create the path.
        ManagedObjectPath<? extends ConfigurationClient, ? extends Configuration> path = ManagedObjectPath
                .emptyPath();
        path = path.child(TestCfg.getTestOneToManyParentRelationDefinition(), "test-parent-1");
        path = path.child(TestParentCfgDefn.getInstance().getOptionalTestChildRelationDefinition());
        // Now serialize it.
        DN actual = DNBuilder.create(path);
        DN expected = DN.valueOf("cn=optional test child,cn=test-parent-1,cn=test parents,cn=config");
        assertEquals(actual, expected);
    }
}
opendj-sdk/opendj-admin/src/test/java/org/opends/server/admin/server/DefaultBehaviorTest.java
New file
@@ -0,0 +1,711 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import java.util.List;
import java.util.SortedSet;
import javax.naming.ldap.LdapName;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ConfigChangeResult;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * Test cases for default behavior on the server-side.
 */
public final class DefaultBehaviorTest extends AdminTestCase {
    /**
     * A test child add listener.
     */
    private static class AddListener implements ConfigurationAddListener<TestChildCfg> {
        // The child configuration that was added.
        private TestChildCfg child;
        /**
         * Creates a new add listener.
         */
        public AddListener() {
            // No implementation required.
        }
        /**
         * {@inheritDoc}
         */
        public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) {
            return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
        /**
         * Gets the child configuration checking that it has the expected name.
         *
         * @param expectedName
         *            The child's expected name.
         * @return Returns the child configuration.
         */
        public TestChildCfg getChild(String expectedName) {
            Assert.assertNotNull(child);
            Assert.assertEquals(child.dn().getRDN().getAttributeValue(0).getValue().toString(), expectedName);
            return child;
        }
        /**
         * {@inheritDoc}
         */
        public boolean isConfigurationAddAcceptable(TestChildCfg configuration, List<Message> unacceptableReasons) {
            child = configuration;
            return true;
        }
    }
    /**
     * A test child change listener.
     */
    private static class ChangeListener implements ConfigurationChangeListener<TestChildCfg> {
        // The child configuration that was changed.
        private TestChildCfg child;
        /**
         * Creates a new change listener.
         */
        public ChangeListener() {
            // No implementation required.
        }
        /**
         * {@inheritDoc}
         */
        public ConfigChangeResult applyConfigurationChange(TestChildCfg configuration) {
            return new ConfigChangeResult(ResultCode.SUCCESS, false);
        }
        /**
         * Gets the child configuration checking that it has the expected name.
         *
         * @param expectedName
         *            The child's expected name.
         * @return Returns the child configuration.
         */
        public TestChildCfg getChild(String expectedName) {
            Assert.assertNotNull(child);
            Assert.assertEquals(child.dn().getRDN().getAttributeValue(0).getValue().toString(), expectedName);
            return child;
        }
        /**
         * {@inheritDoc}
         */
        public boolean isConfigurationChangeAcceptable(TestChildCfg configuration, List<Message> unacceptableReasons) {
            child = configuration;
            return true;
        }
    }
    // Test child 1 LDIF.
    private static final String[] TEST_CHILD_1 = new String[] {
            "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 1", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real" };
    // Test child 2 LDIF.
    private static final String[] TEST_CHILD_2 = new String[] {
            "dn: cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 2", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-base-dn: dc=default value c2v1,dc=com", "ds-cfg-base-dn: dc=default value c2v2,dc=com" };
    // Test child 3 LDIF.
    private static final String[] TEST_CHILD_3 = new String[] {
            "dn: cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 3", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-base-dn: dc=default value c3v1,dc=com", "ds-cfg-base-dn: dc=default value c3v2,dc=com",
            "ds-cfg-group-dn: dc=default value c3v3,dc=com", "ds-cfg-group-dn: dc=default value c3v4,dc=com" };
    // Test child 4 LDIF.
    private static final String[] TEST_CHILD_4 = new String[] {
            "dn: cn=test child 4,cn=test children,cn=test parent 2,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-child-dummy", "cn: test child 4", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real" };
    // Test LDIF.
    private static final String[] TEST_LDIF = new String[] {
            // Base entries.
            "dn: cn=test parents,cn=config",
            "objectclass: top",
            "objectclass: ds-cfg-branch",
            "cn: test parents",
            "",
            // Parent 1 - uses default values for
            // optional-multi-valued-dn-property.
            "dn: cn=test parent 1,cn=test parents,cn=config",
            "objectclass: top",
            "objectclass: ds-cfg-test-parent-dummy",
            "cn: test parent 1",
            "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description",
            "ds-cfg-conflict-behavior: virtual-overrides-real",
            "",
            // Parent 2 - overrides default values for
            // optional-multi-valued-dn-property.
            "dn: cn=test parent 2,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-test-parent-dummy", "cn: test parent 2", "ds-cfg-enabled: true",
            "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
            "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real",
            "ds-cfg-base-dn: dc=default value p2v1,dc=com",
            "ds-cfg-base-dn: dc=default value p2v2,dc=com",
            "",
            // Child base entries.
            "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-branch", "cn: test children", "",
            "dn:cn=test children,cn=test parent 2,cn=test parents,cn=config", "objectclass: top",
            "objectclass: ds-cfg-branch", "cn: test children", "" };
    // JNDI LDAP context.
    private JNDIDirContextAdaptor adaptor = null;
    /**
     * Sets up tests
     *
     * @throws Exception
     *             If the server could not be initialized.
     */
    @BeforeClass
    public void setUp() throws Exception {
        // This test suite depends on having the schema available, so
        // we'll start the server.
        TestCaseUtils.startServer();
        TestCfg.setUp();
        // Add test managed objects.
        TestCaseUtils.addEntries(TEST_LDIF);
    }
    /**
     * Tears down test environment.
     *
     * @throws Exception
     *             If the test entries could not be removed.
     */
    @AfterClass
    public void tearDown() throws Exception {
        TestCfg.cleanup();
        // Remove test entries.
        deleteSubtree("cn=test parents,cn=config");
    }
    /**
     * Tests that children have correct values when accessed through an add
     * listener.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAddListenerChildValues1() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        AddListener listener = new AddListener();
        parent.addTestChildAddListener(listener);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            try {
                assertChild1(listener.getChild("test child 1"));
            } finally {
                deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
            }
        } finally {
            parent.removeTestChildAddListener(listener);
        }
    }
    /**
     * Tests that children have correct values when accessed through an add
     * listener.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAddListenerChildValues2() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        AddListener listener = new AddListener();
        parent.addTestChildAddListener(listener);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_2);
            try {
                assertChild2(listener.getChild("test child 2"));
            } finally {
                deleteSubtree("cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
            }
        } finally {
            parent.removeTestChildAddListener(listener);
        }
    }
    /**
     * Tests that children have correct values when accessed through an add
     * listener.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAddListenerChildValues3() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        AddListener listener = new AddListener();
        parent.addTestChildAddListener(listener);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_3);
            try {
                assertChild3(listener.getChild("test child 3"));
            } finally {
                deleteSubtree("cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config");
            }
        } finally {
            parent.removeTestChildAddListener(listener);
        }
    }
    /**
     * Tests that children have correct values when accessed through an add
     * listener.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testAddListenerChildValues4() throws Exception {
        TestParentCfg parent = getParent("test parent 2");
        AddListener listener = new AddListener();
        parent.addTestChildAddListener(listener);
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_4);
            try {
                assertChild4(listener.getChild("test child 4"));
            } finally {
                deleteSubtree("cn=test child 4,cn=test children,cn=test parent 2,cn=test parents,cn=config");
            }
        } finally {
            parent.removeTestChildAddListener(listener);
        }
    }
    /**
     * Tests that children have correct values when accessed through a change
     * listener. This test replaces the defaulted properties with real values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChangeListenerChildValues1() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestChildCfg child = parent.getTestChild("test child 1");
            ChangeListener listener = new ChangeListener();
            child.addChangeListener(listener);
            // Now modify it.
            String[] changes = new String[] {
                    "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
                    "changetype: modify", "replace: ds-cfg-base-dn", "ds-cfg-base-dn: dc=new value 1,dc=com",
                    "ds-cfg-base-dn: dc=new value 2,dc=com", "-", "replace: ds-cfg-group-dn",
                    "ds-cfg-group-dn: dc=new value 3,dc=com", "ds-cfg-group-dn: dc=new value 4,dc=com" };
            TestCaseUtils.applyModifications(true, changes);
            // Make sure that the change listener was notified and the
            // modified child contains the correct values.
            child = listener.getChild("test child 1");
            Assert.assertEquals(child.getMandatoryClassProperty(),
                    "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
            Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                    DirectoryServer.getAttributeType("description"));
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=new value 1,dc=com",
                    "dc=new value 2,dc=com");
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=new value 3,dc=com",
                    "dc=new value 4,dc=com");
        } finally {
            deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that children have correct values when accessed through a change
     * listener. This test makes sure that default values inherited from within
     * the modified component itself behave as expected.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChangeListenerChildValues2() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestChildCfg child = parent.getTestChild("test child 1");
            ChangeListener listener = new ChangeListener();
            child.addChangeListener(listener);
            // Now modify it.
            String[] changes = new String[] {
                    "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
                    "changetype: modify", "replace: ds-cfg-base-dn", "ds-cfg-base-dn: dc=new value 1,dc=com",
                    "ds-cfg-base-dn: dc=new value 2,dc=com" };
            TestCaseUtils.applyModifications(true, changes);
            // Make sure that the change listener was notified and the
            // modified child contains the correct values.
            child = listener.getChild("test child 1");
            Assert.assertEquals(child.getMandatoryClassProperty(),
                    "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
            Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                    DirectoryServer.getAttributeType("description"));
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=new value 1,dc=com",
                    "dc=new value 2,dc=com");
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=new value 1,dc=com",
                    "dc=new value 2,dc=com");
        } finally {
            deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that children have correct values when accessed through a change
     * listener. This test makes sure that default values inherited from outside
     * the modified component behave as expected.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChangeListenerChildValues3() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestChildCfg child = parent.getTestChild("test child 1");
            ChangeListener listener = new ChangeListener();
            child.addChangeListener(listener);
            // Now modify it.
            String[] changes = new String[] {
                    "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
                    "changetype: modify", "replace: ds-cfg-group-dn", "ds-cfg-group-dn: dc=new value 1,dc=com",
                    "ds-cfg-group-dn: dc=new value 2,dc=com" };
            TestCaseUtils.applyModifications(true, changes);
            // Make sure that the change listener was notified and the
            // modified child contains the correct values.
            child = listener.getChild("test child 1");
            Assert.assertEquals(child.getMandatoryClassProperty(),
                    "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
            Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                    DirectoryServer.getAttributeType("description"));
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=domain1,dc=com", "dc=domain2,dc=com",
                    "dc=domain3,dc=com");
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=new value 1,dc=com",
                    "dc=new value 2,dc=com");
        } finally {
            deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that children have correct values when accessed through a change
     * listener. This test makes sure that a component is notified when the
     * default values it inherits from another component are modified.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChangeListenerChildValues4() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        try {
            // Add the entry.
            TestCaseUtils.addEntry(TEST_CHILD_1);
            TestChildCfg child = parent.getTestChild("test child 1");
            ChangeListener listener = new ChangeListener();
            child.addChangeListener(listener);
            // Now modify the parent.
            String[] changes = new String[] { "dn: cn=test parent 1,cn=test parents,cn=config", "changetype: modify",
                    "replace: ds-cfg-base-dn", "ds-cfg-base-dn: dc=new value 1,dc=com",
                    "ds-cfg-base-dn: dc=new value 2,dc=com" };
            TestCaseUtils.applyModifications(true, changes);
            // Make sure that the change listener was notified and the
            // modified child contains the correct values.
            child = listener.getChild("test child 1");
            Assert.assertEquals(child.getMandatoryClassProperty(),
                    "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
            Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                    DirectoryServer.getAttributeType("description"));
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=new value 1,dc=com",
                    "dc=new value 2,dc=com");
            assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=new value 1,dc=com",
                    "dc=new value 2,dc=com");
        } finally {
            deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
            // Undo the modifications.
            String[] changes = new String[] { "dn: cn=test parent 1,cn=test parents,cn=config", "changetype: modify",
                    "delete: ds-cfg-base-dn" };
            TestCaseUtils.applyModifications(true, changes);
        }
    }
    /**
     * Tests that children have correct values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChildValues1() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_1);
        try {
            TestParentCfg parent = getParent("test parent 1");
            assertChild1(parent.getTestChild("test child 1"));
        } finally {
            deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that children have correct values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChildValues2() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_2);
        try {
            TestParentCfg parent = getParent("test parent 1");
            assertChild2(parent.getTestChild("test child 2"));
        } finally {
            deleteSubtree("cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that children have correct values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChildValues3() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_3);
        try {
            TestParentCfg parent = getParent("test parent 1");
            assertChild3(parent.getTestChild("test child 3"));
        } finally {
            deleteSubtree("cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that children have correct values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testChildValues4() throws Exception {
        // Add the entry.
        TestCaseUtils.addEntry(TEST_CHILD_4);
        try {
            TestParentCfg parent = getParent("test parent 2");
            assertChild4(parent.getTestChild("test child 4"));
        } finally {
            deleteSubtree("cn=test child 4,cn=test children,cn=test parent 2,cn=test parents,cn=config");
        }
    }
    /**
     * Tests that parent 1 has correct values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testParentValues1() throws Exception {
        TestParentCfg parent = getParent("test parent 1");
        Assert.assertEquals(parent.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(parent.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertDNSetEquals(parent.getOptionalMultiValuedDNProperty(), "dc=domain1,dc=com", "dc=domain2,dc=com",
                "dc=domain3,dc=com");
    }
    /**
     * Tests that parent 2 has correct values.
     *
     * @throws Exception
     *             If the test unexpectedly fails.
     */
    @Test
    public void testParentValues2() throws Exception {
        TestParentCfg parent = getParent("test parent 2");
        Assert.assertEquals(parent.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(parent.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertDNSetEquals(parent.getOptionalMultiValuedDNProperty(), "dc=default value p2v1,dc=com",
                "dc=default value p2v2,dc=com");
    }
    // Assert that the values of child 1 are correct.
    private void assertChild1(TestChildCfg child) {
        Assert.assertEquals(child.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=domain1,dc=com", "dc=domain2,dc=com",
                "dc=domain3,dc=com");
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=domain1,dc=com", "dc=domain2,dc=com",
                "dc=domain3,dc=com");
    }
    // Assert that the values of child 2 are correct.
    private void assertChild2(TestChildCfg child) {
        Assert.assertEquals(child.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=default value c2v1,dc=com",
                "dc=default value c2v2,dc=com");
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=default value c2v1,dc=com",
                "dc=default value c2v2,dc=com");
    }
    // Assert that the values of child 3 are correct.
    private void assertChild3(TestChildCfg child) {
        Assert.assertEquals(child.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=default value c3v1,dc=com",
                "dc=default value c3v2,dc=com");
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=default value c3v3,dc=com",
                "dc=default value c3v4,dc=com");
    }
    // Assert that the values of child 4 are correct.
    private void assertChild4(TestChildCfg child) {
        Assert.assertEquals(child.getMandatoryClassProperty(),
                "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
        Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
                DirectoryServer.getAttributeType("description"));
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), "dc=default value p2v1,dc=com",
                "dc=default value p2v2,dc=com");
        assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), "dc=default value p2v1,dc=com",
                "dc=default value p2v2,dc=com");
    }
    // Asserts that the actual set of DNs contains the expected values.
    private void assertDNSetEquals(SortedSet<DN> actual, String... expected) {
        String[] actualStrings = new String[actual.size()];
        int i = 0;
        for (DN dn : actual) {
            actualStrings[i] = dn.toString();
            i++;
        }
        Assert.assertEqualsNoOrder(actualStrings, expected);
    }
    // Deletes the named sub-tree.
    private void deleteSubtree(String dn) throws Exception {
        getAdaptor().deleteSubtree(new LdapName(dn));
    }
    // Gets the JNDI connection for the test server instance.
    private synchronized JNDIDirContextAdaptor getAdaptor() throws Exception {
        if (adaptor == null) {
            adaptor = JNDIDirContextAdaptor.simpleSSLBind("127.0.0.1", TestCaseUtils.getServerAdminPort(),
                    "cn=directory manager", "password");
        }
        return adaptor;
    }
    // Gets the named parent configuration.
    private TestParentCfg getParent(String name) throws IllegalArgumentException, ConfigException {
        ServerManagedObject<RootCfg> root = serverContext.getRootConfigurationManagedObject();
        TestParentCfg parent = root.getChild(TestCfg.getTestOneToManyParentRelationDefinition(), name)
                .getConfiguration();
        return parent;
    }
}
opendj-sdk/opendj-admin/src/test/java/org/opends/server/admin/server/ListenerTest.java
New file
@@ -0,0 +1,275 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import static org.fest.assertions.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import org.forgerock.opendj.admin.server.RootCfg;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.TestCaseUtils;
import org.mockito.ArgumentCaptor;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigException;
import org.opends.server.config.ConfigurationRepository;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@SuppressWarnings({"javadoc", "rawtypes", "unchecked"})
public class ListenerTest extends AdminTestCase {
    private static final DN ROOT_CONFIG_DN = DN.valueOf("cn=config");
    private static final DN TEST_PARENTS_DN = DN.valueOf("cn=test parents,cn=config");
    @BeforeClass
    public void setUp() throws Exception {
        disableClassValidationForProperties();
        TestCfg.setUp();
    }
    @AfterClass
    public void tearDown() throws Exception {
        TestCfg.cleanup();
    }
    private Entry getTestParentEntry() throws Exception {
        return TestCaseUtils.makeEntry(
            "dn: cn=test parents,cn=config",
            "objectclass: top",
            "objectclass: ds-cfg-branch",
            "cn: test parents");
    }
    /**
     * Create a mock of ConfigurationRepository with provided DNs registered.
     */
    private ConfigurationRepository createConfigRepositoryWithDNs(DN...dns) throws ConfigException {
        ConfigurationRepository configRepository = mock(ConfigurationRepository.class);
        for (DN dn : dns) {
            when(configRepository.hasEntry(dn)).thenReturn(true);
        }
        return configRepository;
    }
    /** Register a listener for test parent entry and return the actual registered listener */
    private ConfigAddListener registerAddListenerForTestParent(ConfigurationRepository configRepository,
            ServerManagedObject<RootCfg> root, ConfigurationAddListener<TestParentCfg> parentListener)
                throws Exception {
        root.registerAddListener(TestCfg.getTestOneToManyParentRelationDefinition(), parentListener);
        ArgumentCaptor<ConfigAddListener> registered = ArgumentCaptor.forClass(ConfigAddListener.class);
        verify(configRepository).registerAddListener(eq(TEST_PARENTS_DN), registered.capture());
        return registered.getValue();
    }
    /** Register a listener for test parent entry in delayed scenario and return the actual registered listener */
    private DelayedConfigAddListener registerAddListenerForTestParentDelayed(ConfigurationRepository configRepository,
            ServerManagedObject<RootCfg> root, ConfigurationAddListener<TestParentCfg> parentListener)
                throws Exception {
        root.registerAddListener(TestCfg.getTestOneToManyParentRelationDefinition(), parentListener);
        ArgumentCaptor<DelayedConfigAddListener> registered = ArgumentCaptor.forClass(DelayedConfigAddListener.class);
        verify(configRepository).registerAddListener(eq(ROOT_CONFIG_DN), registered.capture());
        return registered.getValue();
    }
    @Test
    public void testRegisterAddListenerWithInstantiableRelationImmediate() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN, TEST_PARENTS_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        root.registerAddListener(TestCfg.getTestOneToManyParentRelationDefinition(),
                mock(ConfigurationAddListener.class));
        verify(configRepository).registerAddListener(eq(TEST_PARENTS_DN),
                isA(ConfigAddListener.class));
    }
    @Test
    public void testRegisterAddListenerWithInstantiableRelationDelayed() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        ConfigurationAddListener<TestParentCfg> parentListener = mock(ConfigurationAddListener.class);
        root.registerAddListener(TestCfg.getTestOneToManyParentRelationDefinition(), parentListener);
        ArgumentCaptor<DelayedConfigAddListener> registered = ArgumentCaptor.forClass(DelayedConfigAddListener.class);
        verify(configRepository).registerAddListener(eq(ROOT_CONFIG_DN), registered.capture());
        // check that actual listener is the one provided to the root
        ConfigurationAddListener<?> actualListener =
            ((ServerManagedObjectAddListenerAdaptor<?>)
                ((ConfigAddListenerAdaptor<?>) registered.getValue().getDelayedAddListener()).
                    getServerManagedObjectAddListener()).getConfigurationAddListener();
        assertThat(actualListener).isEqualTo(parentListener);
    }
    @Test
    public void testRegisterAddListenerWithInstantiableRelationDelayedThenActualized() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        // register a listener to root
        ConfigurationAddListener<TestParentCfg> parentListener = mock(ConfigurationAddListener.class);
        root.registerAddListener(TestCfg.getTestOneToManyParentRelationDefinition(), parentListener);
        // get the delayed listener registered to configuration repository
        ArgumentCaptor<DelayedConfigAddListener> registered = ArgumentCaptor.forClass(DelayedConfigAddListener.class);
        verify(configRepository).registerAddListener(eq(ROOT_CONFIG_DN), registered.capture());
        // now simulate the add of target entry
        String parentDN = "cn=test parents,cn=config";
        when(configRepository.hasEntry(DN.valueOf(parentDN))).thenReturn(true);
        registered.getValue().applyConfigurationAdd(getTestParentEntry());
        // check that listener is added for target entry and deleted for its parent entry
        ConfigAddListenerAdaptor listener =
                (ConfigAddListenerAdaptor<?>) registered.getValue().getDelayedAddListener();
        verify(configRepository).registerAddListener(DN.valueOf(parentDN), listener);
        verify(configRepository).deregisterAddListener(ROOT_CONFIG_DN, registered.getValue());
    }
    @Test
    public void testRegisterAddListenerWithOptionalRelation() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        root.registerAddListener(TestCfg.getTestOneToZeroOrOneParentRelationDefinition(),
            mock(ConfigurationAddListener.class));
        verify(configRepository).registerAddListener(eq(ROOT_CONFIG_DN),
                isA(ConfigAddListener.class));
    }
    @Test
    public void testRegisterDeleteListenerWithInstantiableRelationImmediate() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN, TEST_PARENTS_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        root.registerDeleteListener(TestCfg.getTestOneToManyParentRelationDefinition(),
                mock(ConfigurationDeleteListener.class));
        verify(configRepository).registerDeleteListener(eq(TEST_PARENTS_DN),
                isA(ConfigDeleteListener.class));
    }
    @Test
    public void testRegisterDeleteListenerWithInstantiableRelationDelayed() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        ConfigurationDeleteListener<TestParentCfg> parentListener = mock(ConfigurationDeleteListener.class);
        root.registerDeleteListener(TestCfg.getTestOneToManyParentRelationDefinition(), parentListener);
        ArgumentCaptor<DelayedConfigAddListener> argument = ArgumentCaptor.forClass(DelayedConfigAddListener.class);
        verify(configRepository).registerAddListener(eq(ROOT_CONFIG_DN), argument.capture());
        // check that actual listener is the one provided to the root
        ConfigurationDeleteListener actualListener =
                ((ServerManagedObjectDeleteListenerAdaptor)
                        ((ConfigDeleteListenerAdaptor)argument.getValue().getDelayedDeleteListener()).
                            getServerManagedObjectDeleteListener()).getConfigurationDeleteListener();
        assertThat(actualListener).isEqualTo(parentListener);
    }
    @Test
    public void testRegisterDeleteListenerWithOptionalRelation() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        root.registerDeleteListener(TestCfg.getTestOneToZeroOrOneParentRelationDefinition(),
                mock(ConfigurationDeleteListener.class));
        verify(configRepository).registerDeleteListener(eq(ROOT_CONFIG_DN),
                isA(ConfigDeleteListener.class));
    }
    @Test
    public void testRegisterChangeListener() throws Exception {
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        root.setConfigDN(ROOT_CONFIG_DN);
        root.registerChangeListener(mock(ConfigurationChangeListener.class));
        verify(configRepository).registerChangeListener(eq(ROOT_CONFIG_DN),
                isA(ConfigChangeListener.class));
    }
    @Test
    public void testDeregisterAddListenerWithInstantiableRelationImmediate() throws Exception {
        // arrange
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN, TEST_PARENTS_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        ConfigurationAddListener<TestParentCfg> parentListener = mock(ConfigurationAddListener.class);
        ConfigAddListener registeredListener =
                registerAddListenerForTestParent(configRepository, root, parentListener);
        when(configRepository.getAddListeners(TEST_PARENTS_DN)).thenReturn(Arrays.asList(registeredListener));
        // act
        root.deregisterAddListener(TestCfg.getTestOneToManyParentRelationDefinition(), parentListener);
        // assert
        verify(configRepository).deregisterAddListener(eq(TEST_PARENTS_DN), same(registeredListener));
    }
    @Test
    public void testDeregisterAddListenerWithInstantiableRelationDelayed() throws Exception {
        // arrange
        ConfigurationRepository configRepository = createConfigRepositoryWithDNs(ROOT_CONFIG_DN);
        ServerManagementContext context = new ServerManagementContext(configRepository);
        ServerManagedObject<RootCfg> root = context.getRootConfigurationManagedObject();
        ConfigurationAddListener<TestParentCfg> parentListener = mock(ConfigurationAddListener.class);
        ConfigAddListener registeredListener =
                registerAddListenerForTestParentDelayed(configRepository, root, parentListener);
        when(configRepository.getAddListeners(ROOT_CONFIG_DN)).thenReturn(Arrays.asList(registeredListener));
        // act
        root.deregisterAddListener(TestCfg.getTestOneToManyParentRelationDefinition(), parentListener);
        // assert
        verify(configRepository).deregisterAddListener(eq(ROOT_CONFIG_DN), same(registeredListener));
    }
}
opendj-sdk/opendj-admin/src/test/java/org/opends/server/admin/server/MockConstraint.java
New file
@@ -0,0 +1,146 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import java.util.Collection;
import java.util.Collections;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.admin.Constraint;
import org.opends.server.admin.client.ClientConstraintHandler;
import org.opends.server.config.ConfigException;
import org.opends.server.config.ConfigurationRepository;
import org.testng.Assert;
/**
 * A mock constraint which can be configured to refuse various types of
 * operation.
 */
public final class MockConstraint extends Constraint {
    /**
     * Mock server constraint handler.
     */
    private class Handler extends ServerConstraintHandler {
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isDeleteAllowed(ServerManagedObject<?> managedObject,
                Collection<LocalizableMessage> unacceptableReasons) throws ConfigException {
            if (!isDeleteAllowed) {
                unacceptableReasons.add(LocalizableMessage.raw("Configuration cannot be deleted."));
            }
            return isDeleteAllowed;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isUsable(ServerManagedObject<?> managedObject,
                Collection<LocalizableMessage> unacceptableReasons) throws ConfigException {
            if (!isUsable) {
                unacceptableReasons.add(LocalizableMessage.raw("Configuration is not usable."));
            }
            return isUsable;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void performPostAdd(ServerManagedObject<?> managedObject) throws ConfigException {
            // Make sure that the associated config entry exists.
            DN targetDN = managedObject.getDN();
            Assert.assertTrue(configRepository.hasEntry(targetDN));
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void performPostDelete(ServerManagedObject<?> managedObject) throws ConfigException {
            // Make sure that the associated config entry does not exist.
            DN targetDN = managedObject.getDN();
            Assert.assertTrue(configRepository.hasEntry(targetDN));
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void performPostModify(ServerManagedObject<?> managedObject) throws ConfigException {
            // Make sure that the associated config entry exists.
            DN targetDN = managedObject.getDN();
            Assert.assertTrue(configRepository.hasEntry(targetDN));
        }
    }
    // Determines if delete operations are allowed.
    private final boolean isDeleteAllowed;
    // Determines if configurations can be decoded.
    private final boolean isUsable;
    private final ConfigurationRepository configRepository;
    /**
     * Creates a new mock constraint.
     *
     * @param isUsable
     *            Determines if configurations can be decoded.
     * @param isDeleteAllowed
     *            Determines if delete operations are allowed.
     * @param configRepository
     *            Configuration entries.
     */
    public MockConstraint(boolean isUsable, boolean isDeleteAllowed, ConfigurationRepository configRepository) {
        this.isUsable = isUsable;
        this.isDeleteAllowed = isDeleteAllowed;
        this.configRepository = configRepository;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
        return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
        return Collections.<ServerConstraintHandler> singleton(new Handler());
    }
}