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

matthew_swift
06.57.2007 3360705577c8bb82d31e593cc5890aebeca063c1
Improvements to the server-side constraint violation APIs. Now there are just two server-side constraint enforcement call-backs: isUsable and isDeleteAllowed. The first is invoked whenever a managed object is decoded (except in the case where it's about to be deleted). The second is invoked whenever a managed object is about to be deleted. With this change we will now detect constraint violations during server initialization, not just when config change/add/delete events occur.
1 files added
11 files modified
616 ■■■■ changed files
opends/src/messages/messages/admin.properties 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java 31 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java 42 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java 12 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ConfigExceptionFactory.java 26 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ConstraintViolationException.java 179 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ServerConstraintHandler.java 94 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java 58 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ServerManagementContext.java 11 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java 90 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/MockConstraint.java 65 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/admin.properties
@@ -255,3 +255,7 @@
 recognized
SEVERE_ERR_COMMUNICATION_EXCEPTION_DEFAULT_CAUSE_111=A communication problem \
 occurred while contacting the server: %s
SEVERE_ERR_CONSTRAINT_VIOLATION_EXCEPTION_SINGLE_112=The following \
 constraint violation occurred: %s
SEVERE_ERR_CONSTRAINT_VIOLATION_EXCEPTION_PLURAL_113=The following \
 constraint violations occurred: %s
opends/src/server/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java
@@ -28,7 +28,7 @@
import java.util.List;
import java.util.Collection;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
@@ -57,7 +57,7 @@
   * @param unacceptableReason
   *          The single message to which messages should be appended.
   */
  protected final void generateUnacceptableReason(List<Message> reasons,
  protected final void generateUnacceptableReason(Collection<Message> reasons,
      MessageBuilder unacceptableReason) {
    boolean isFirst = true;
    for (Message reason : reasons) {
opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -28,7 +28,6 @@
import static org.opends.messages.AdminMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import java.util.LinkedList;
@@ -168,7 +167,7 @@
        for (ServerConstraintHandler handler : constraint
            .getServerConstraintHandlers()) {
          try {
            handler.performAddPostCondition(cachedManagedObject);
            handler.performPostAdd(cachedManagedObject);
          } catch (ConfigException e) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
@@ -216,35 +215,17 @@
    }
    cachedConfiguration = cachedManagedObject.getConfiguration();
    List<Message> reasons = new LinkedList<Message>();
    // Enforce any constraints.
    boolean isAcceptable = true;
    ManagedObjectDefinition<?, ?> d = cachedManagedObject
        .getManagedObjectDefinition();
    for (Constraint constraint : d.getAllConstraints()) {
      for (ServerConstraintHandler handler : constraint
          .getServerConstraintHandlers()) {
        try {
          if (!handler.isAddAcceptable(cachedManagedObject, reasons)) {
            isAcceptable = false;
          }
        } catch (ConfigException e) {
          Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
              .getMessageObject());
          reasons.add(message);
          isAcceptable = false;
        }
      }
    }
    // Give up immediately if a constraint violation occurs.
    if (!isAcceptable) {
      generateUnacceptableReason(reasons, unacceptableReason);
    try {
      cachedManagedObject.ensureIsUsable();
    } catch (ConstraintViolationException e) {
      generateUnacceptableReason(e.getMessages(), unacceptableReason);
      return false;
    }
    // Let the add listener decide.
    List<Message> reasons = new LinkedList<Message>();
    if (listener.isConfigurationAddAcceptable(cachedConfiguration, reasons)) {
      return true;
    } else {
opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
@@ -28,9 +28,6 @@
import org.opends.messages.Message;
import static org.opends.messages.AdminMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import java.util.Collection;
@@ -39,6 +36,9 @@
import java.util.List;
import java.util.Set;
import org.opends.messages.AdminMessages;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
@@ -57,11 +57,8 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.loggers.ErrorLogger;
import org.opends.messages.AdminMessages;
import org.opends.messages.MessageBuilder;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
@@ -322,7 +319,7 @@
        for (ServerConstraintHandler handler : constraint
            .getServerConstraintHandlers()) {
          try {
            handler.performModifyPostCondition(cachedManagedObject);
            handler.performPostModify(cachedManagedObject);
          } catch (ConfigException e) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
@@ -377,35 +374,16 @@
      return false;
    }
    List<Message> reasons = new LinkedList<Message>();
    // Enforce any constraints.
    boolean isAcceptable = true;
    ManagedObjectDefinition<?, ?> d = cachedManagedObject
        .getManagedObjectDefinition();
    for (Constraint constraint : d.getAllConstraints()) {
      for (ServerConstraintHandler handler : constraint
          .getServerConstraintHandlers()) {
        try {
          if (!handler.isModifyAcceptable(cachedManagedObject, reasons)) {
            isAcceptable = false;
          }
        } catch (ConfigException e) {
          Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
              .getMessageObject());
          reasons.add(message);
          isAcceptable = false;
        }
      }
    }
    // Give up immediately if a constraint violation occurs.
    if (!isAcceptable) {
      generateUnacceptableReason(reasons, unacceptableReason);
    try {
      cachedManagedObject.ensureIsUsable();
    } catch (ConstraintViolationException e) {
      generateUnacceptableReason(e.getMessages(), unacceptableReason);
      return false;
    }
    // Let the change listener decide.
    List<Message> reasons = new LinkedList<Message>();
    if (listener.isConfigurationChangeAcceptable(cachedManagedObject
        .getConfiguration(), reasons)) {
      return true;
opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
@@ -169,7 +169,7 @@
        for (ServerConstraintHandler handler : constraint
            .getServerConstraintHandlers()) {
          try {
            handler.performDeletePostCondition(cachedManagedObject);
            handler.performPostDelete(cachedManagedObject);
          } catch (ConfigException e) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
@@ -220,27 +220,27 @@
    List<Message> reasons = new LinkedList<Message>();
    // Enforce any constraints.
    boolean isAcceptable = true;
    boolean isDeleteAllowed = true;
    ManagedObjectDefinition<?, ?> d = cachedManagedObject
        .getManagedObjectDefinition();
    for (Constraint constraint : d.getAllConstraints()) {
      for (ServerConstraintHandler handler : constraint
          .getServerConstraintHandlers()) {
        try {
          if (!handler.isDeleteAcceptable(cachedManagedObject, reasons)) {
            isAcceptable = false;
          if (!handler.isDeleteAllowed(cachedManagedObject, reasons)) {
            isDeleteAllowed = false;
          }
        } catch (ConfigException e) {
          Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
              .getMessageObject());
          reasons.add(message);
          isAcceptable = false;
          isDeleteAllowed = false;
        }
      }
    }
    // Give up immediately if a constraint violation occurs.
    if (!isAcceptable) {
    if (!isDeleteAllowed) {
      generateUnacceptableReason(reasons, unacceptableReason);
      return false;
    }
opends/src/server/org/opends/server/admin/server/ConfigExceptionFactory.java
@@ -96,7 +96,6 @@
  public ConfigException createDecodingExceptionAdaptor(
      ServerManagedObjectDecodingException e) {
    DN dn = e.getPartialManagedObject().getDN();
    Message message =
            AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.get(
@@ -108,13 +107,32 @@
  /**
   * Create an exception that describes a problem that occurred when attempting
   * to load and instantiate a class.
   * Create a configuration exception from a constraints violation
   * decoding exception.
   *
   * @param e
   *          The constraints violation decoding exception.
   * @return Returns the configuration exception.
   */
  public ConfigException createDecodingExceptionAdaptor(
      ConstraintViolationException e) {
    DN dn = e.getManagedObject().getDN();
    Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM
        .get(String.valueOf(dn), stackTraceToSingleLineString(e));
    return new ConfigException(message, e);
  }
  /**
   * Create an exception that describes a problem that occurred when
   * attempting to load and instantiate a class.
   *
   * @param dn
   *          The dn of the configuration entry was being processed.
   * @param className
   *          The name of the class that could not be loaded or instantiated.
   *          The name of the class that could not be loaded or
   *          instantiated.
   * @param e
   *          The exception that occurred.
   * @return Returns the configuration exception.
opends/src/server/org/opends/server/admin/server/ConstraintViolationException.java
New file
@@ -0,0 +1,179 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import static org.opends.messages.AdminMessages.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.DecodingException;
import org.opends.server.util.Validator;
/**
 * This exception is thrown when the server refuses to use or delete a
 * managed object due to one or more constraints that cannot be
 * satisfied.
 */
public class ConstraintViolationException extends DecodingException {
  /**
   * Serialization ID.
   */
  private static final long serialVersionUID = -4902443848460011875L;
  // The server managed object.
  private final ServerManagedObject<?> managedObject;
  // Gets the default message.
  private static Message getDefaultMessage(Collection<Message> messages) {
    Validator.ensureNotNull(messages);
    Validator.ensureTrue(!messages.isEmpty());
    if (messages.size() == 1) {
      return ERR_CONSTRAINT_VIOLATION_EXCEPTION_SINGLE.get(messages.iterator()
          .next());
    } else {
      return ERR_CONSTRAINT_VIOLATION_EXCEPTION_PLURAL
          .get(getSingleMessage(messages));
    }
  }
  // Merge the messages into a single message.
  private static Message getSingleMessage(Collection<Message> messages) {
    if (messages.size() == 1) {
      return messages.iterator().next();
    } else {
      MessageBuilder builder = new MessageBuilder();
      boolean isFirst = true;
      for (Message m : messages) {
        if (!isFirst) {
          builder.append(";  ");
        }
        builder.append(m);
        isFirst = false;
      }
      return builder.toMessage();
    }
  }
  // The messages describing the constraint violations that occurred.
  private final Collection<Message> messages;
  /**
   * Creates a new constraint violation exception with the provided
   * messages.
   *
   * @param managedObject
   *          The server managed object which caused the constraint
   *          violations.
   * @param messages
   *          The messages describing the constraint violations that
   *          occurred (must be non-<code>null</code> and
   *          non-empty).
   */
  public ConstraintViolationException(ServerManagedObject<?> managedObject,
      Collection<Message> messages) {
    super(getDefaultMessage(messages));
    this.managedObject = managedObject;
    this.messages = new ArrayList<Message>(messages);
  }
  /**
   * Creates a new constraint violation exception with the provided
   * message.
   *
   * @param managedObject
   *          The server managed object which caused the constraint
   *          violations.
   * @param message
   *          The message describing the constraint violation that
   *          occurred.
   */
  public ConstraintViolationException(ServerManagedObject<?> managedObject,
      Message message) {
    this(managedObject, Collections.singleton(message));
  }
  /**
   * Gets an unmodifiable collection view of the messages describing
   * the constraint violations that occurred.
   *
   * @return Returns an unmodifiable collection view of the messages
   *         describing the constraint violations that occurred.
   */
  public Collection<Message> getMessages() {
    return Collections.unmodifiableCollection(messages);
  }
  /**
   * Creates a single message listing all the messages combined into a
   * single list separated by semi-colons.
   *
   * @return Returns a single message listing all the messages
   *         combined into a single list separated by semi-colons.
   */
  public Message getMessagesAsSingleMessage() {
    return getSingleMessage(messages);
  }
  /**
   * Gets the server managed object which caused the constraint
   * violations.
   *
   * @return Returns the server managed object which caused the
   *         constraint violations.
   */
  public ServerManagedObject<?> getManagedObject() {
    return managedObject;
  }
}
opends/src/server/org/opends/server/admin/server/ServerConstraintHandler.java
@@ -67,42 +67,14 @@
  /**
   * Determines whether or not the newly created managed object which
   * is about to be added to the server configuration satisfies this
   * constraint.
   * Determines whether or not the existing managed object can be
   * deleted from the server's configuration. For example, an
   * implementation might enforce referential integrity by preventing
   * referenced managed objects from being deleted.
   * <p>
   * If the constraint is not satisfied, the implementation must
   * return <code>false</code> and add a message describing why the
   * constraint was not satisfied.
   * <p>
   * The default implementation is to return <code>true</code>.
   *
   * @param managedObject
   *          The new managed object.
   * @param unacceptableReasons
   *          A list of messages to which error messages should be
   *          added.
   * @return Returns <code>true</code> if this constraint is
   *         satisfied, or <code>false</code> if it is not.
   * @throws ConfigException
   *           If an configuration exception prevented this constraint
   *           from being evaluated.
   */
  public boolean isAddAcceptable(ServerManagedObject<?> managedObject,
      Collection<Message> unacceptableReasons) throws ConfigException {
    return true;
  }
  /**
   * Determines whether or not the existing managed object which is
   * about to be deleted from the server configuration satisfies this
   * constraint.
   * <p>
   * If the constraint is not satisfied, the implementation must
   * return <code>false</code> and add a message describing why the
   * constraint was not satisfied.
   * managed object cannot be deleted.
   * <p>
   * The default implementation is to return <code>true</code>.
   *
@@ -112,12 +84,13 @@
   *          A list of messages to which error messages should be
   *          added.
   * @return Returns <code>true</code> if this constraint is
   *         satisfied, or <code>false</code> if it is not.
   *         satisfied, or <code>false</code> if it is not and the
   *         managed object cannot be deleted.
   * @throws ConfigException
   *           If an configuration exception prevented this constraint
   *           from being evaluated.
   */
  public boolean isDeleteAcceptable(ServerManagedObject<?> managedObject,
  public boolean isDeleteAllowed(ServerManagedObject<?> managedObject,
      Collection<Message> unacceptableReasons) throws ConfigException {
    return true;
  }
@@ -125,28 +98,32 @@
  /**
   * Determines whether or not the changes to an existing managed
   * object which are about to be committed to the server
   * configuration satisfies this constraint.
   * Determines whether or not the provided managed object can be used
   * by the server. This method is invoked each time a managed object
   * is decoded by the administration framework: when an attempt is
   * made to add a new configuration, modify an existing
   * configuration, or during server initialization. If the constraint
   * is not satisfied the managed object will be rejected.
   * <p>
   * If the constraint is not satisfied, the implementation must
   * return <code>false</code> and add a message describing why the
   * constraint was not satisfied.
   * managed object is not usable.
   * <p>
   * The default implementation is to return <code>true</code>.
   *
   * @param managedObject
   *          The modified managed object.
   *          The new managed object.
   * @param unacceptableReasons
   *          A list of messages to which error messages should be
   *          added.
   * @return Returns <code>true</code> if this modify is satisfied,
   *         or <code>false</code> if it is not.
   * @return Returns <code>true</code> if this constraint is
   *         satisfied, or <code>false</code> if it is not and the
   *         managed object cannot be used.
   * @throws ConfigException
   *           If an configuration exception prevented this constraint
   *           from being evaluated.
   */
  public boolean isModifyAcceptable(ServerManagedObject<?> managedObject,
  public boolean isUsable(ServerManagedObject<?> managedObject,
      Collection<Message> unacceptableReasons) throws ConfigException {
    return true;
  }
@@ -154,17 +131,22 @@
  /**
   * Perform any add post-condition processing.
   * Performs any post-add processing required by this constraint.
   * This method is invoked after a new managed object has been
   * accepted for use by the administration framework. This might
   * occur during initialization or when a managed object is added at
   * run-time.
   * <p>
   * The default implementation is to do nothing.
   *
   * @param managedObject
   *          The managed object which was added.
   *          The managed object which has just been added to the
   *          server's configuration.
   * @throws ConfigException
   *           If the post-condition processing fails due to a
   *           configuration exception.
   *           If the post-add processing fails due to a configuration
   *           exception.
   */
  public void performAddPostCondition(ServerManagedObject<?> managedObject)
  public void performPostAdd(ServerManagedObject<?> managedObject)
      throws ConfigException {
    // Do nothing.
  }
@@ -172,17 +154,19 @@
  /**
   * Perform any delete post-condition processing.
   * Performs any post-delete processing required by this constraint.
   * This method is invoked after a managed object has been accepted
   * for deletion from the server's configuration.
   * <p>
   * The default implementation is to do nothing.
   *
   * @param managedObject
   *          The managed object which was deleted.
   * @throws ConfigException
   *           If the post-condition processing fails due to a
   *           If the post-delete processing fails due to a
   *           configuration exception.
   */
  public void performDeletePostCondition(ServerManagedObject<?> managedObject)
  public void performPostDelete(ServerManagedObject<?> managedObject)
      throws ConfigException {
    // Do nothing.
  }
@@ -190,17 +174,19 @@
  /**
   * Perform any modify post-condition processing.
   * Performs any post-modify processing required by this constraint.
   * This method is invoked after changes to an existing managed
   * object have been accepted.
   * <p>
   * The default implementation is to do nothing.
   *
   * @param managedObject
   *          The managed object which was modified.
   * @throws ConfigException
   *           If the post-condition processing fails due to a
   *           If the post-modify processing fails due to a
   *           configuration exception.
   */
  public void performModifyPostCondition(ServerManagedObject<?> managedObject)
  public void performPostModify(ServerManagedObject<?> managedObject)
      throws ConfigException {
    // Do nothing.
  }
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -29,9 +29,12 @@
import static org.opends.messages.AdminMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
@@ -40,6 +43,7 @@
import org.opends.messages.AdminMessages;
import org.opends.messages.Message;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.Constraint;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
@@ -536,6 +540,25 @@
    ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path,
        listener);
    configEntry.registerChangeListener(adaptor);
    // Change listener registration usually signifies that a managed
    // object has been accepted and added to the server configuration
    // either during initialization post-add.
    // FIXME: we should prevent multiple invocations in the case where
    // multiple change listeners are registered for the same object.
    for (Constraint constraint : definition.getAllConstraints()) {
      for (ServerConstraintHandler handler : constraint
          .getServerConstraintHandlers()) {
        try {
          handler.performPostAdd(this);
        } catch (ConfigException e) {
          if (debugEnabled()) {
            TRACER.debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
    }
  }
@@ -601,6 +624,41 @@
  /**
   * Determines whether or not this managed object can be used by the
   * server.
   *
   * @throws ConstraintViolationException
   *           If one or more constraints determined that this managed
   *           object cannot be used by the server.
   */
  void ensureIsUsable() throws ConstraintViolationException {
    // Enforce any constraints.
    boolean isUsable = true;
    List<Message> reasons = new LinkedList<Message>();
    for (Constraint constraint : definition.getAllConstraints()) {
      for (ServerConstraintHandler handler : constraint
          .getServerConstraintHandlers()) {
        try {
          if (!handler.isUsable(this, reasons)) {
            isUsable = false;
          }
        } catch (ConfigException e) {
          Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
              .getMessageObject());
          reasons.add(message);
          isUsable = false;
        }
      }
    }
    if (!isUsable) {
      throw new ConstraintViolationException(this, reasons);
    }
  }
  /**
   * Update the config entry associated with this server managed
   * object. This is only intended to be used by change listener call
   * backs in order to update the managed object with the correct
opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
@@ -456,13 +456,22 @@
    DN targetDN = DNBuilder.create(path);
    ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
    try {
      return decode(path, configEntry);
      ServerManagedObject<? extends S> managedObject;
      managedObject = decode(path, configEntry);
      // Enforce any constraints.
      managedObject.ensureIsUsable();
      return managedObject;
    } catch (DefinitionDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(targetDN, e);
    } catch (ServerManagedObjectDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(e);
    } catch (ConstraintViolationException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(e);
    }
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java
@@ -38,6 +38,7 @@
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.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.std.server.RootCfg;
@@ -60,8 +61,7 @@
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";
  private static final String TEST_CHILD_1_DN = "cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config";
@@ -226,6 +226,75 @@
  /**
   * 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
@@ -237,7 +306,7 @@
    AddListener listener = new AddListener();
    parent.addTestChildAddListener(listener);
    MockConstraint constraint = new MockConstraint(true, false, false);
    MockConstraint constraint = new MockConstraint(true, false);
    TestCfg.addConstraint(constraint);
    try {
@@ -271,7 +340,7 @@
    AddListener listener = new AddListener();
    parent.addTestChildAddListener(listener);
    MockConstraint constraint = new MockConstraint(false, true, true);
    MockConstraint constraint = new MockConstraint(false, true);
    TestCfg.addConstraint(constraint);
    try {
@@ -305,7 +374,7 @@
    DeleteListener listener = new DeleteListener();
    parent.addTestChildDeleteListener(listener);
    MockConstraint constraint = new MockConstraint(false, false, true);
    MockConstraint constraint = new MockConstraint(false, true);
    TestCfg.addConstraint(constraint);
    try {
@@ -341,7 +410,7 @@
    DeleteListener listener = new DeleteListener();
    parent.addTestChildDeleteListener(listener);
    MockConstraint constraint = new MockConstraint(true, true, false);
    MockConstraint constraint = new MockConstraint(true, false);
    TestCfg.addConstraint(constraint);
    try {
@@ -381,14 +450,14 @@
  public void testChangeConstraintSuccess() throws Exception {
    TestParentCfg parent = getParent("test parent 1");
    MockConstraint constraint = new MockConstraint(false, true, false);
    TestCfg.addConstraint(constraint);
    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);
@@ -428,15 +497,14 @@
  @Test
  public void testChangeConstraintFail() throws Exception {
    TestParentCfg parent = getParent("test parent 1");
    MockConstraint constraint = new MockConstraint(true, false, true);
    TestCfg.addConstraint(constraint);
    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);
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/MockConstraint.java
@@ -57,13 +57,14 @@
     * {@inheritDoc}
     */
    @Override
    public boolean isAddAcceptable(ServerManagedObject<?> managedObject,
    public boolean isDeleteAllowed(ServerManagedObject<?> managedObject,
        Collection<Message> unacceptableReasons) throws ConfigException {
      if (!allowAdds) {
        unacceptableReasons.add(Message.raw("Adds not allowed"));
      if (!isDeleteAllowed) {
        unacceptableReasons
            .add(Message.raw("Configuration cannot be deleted."));
      }
      return allowAdds;
      return isDeleteAllowed;
    }
@@ -72,13 +73,13 @@
     * {@inheritDoc}
     */
    @Override
    public boolean isDeleteAcceptable(ServerManagedObject<?> managedObject,
    public boolean isUsable(ServerManagedObject<?> managedObject,
        Collection<Message> unacceptableReasons) throws ConfigException {
      if (!allowDeletes) {
        unacceptableReasons.add(Message.raw("Deletes not allowed"));
      if (!isUsable) {
        unacceptableReasons.add(Message.raw("Configuration is not usable."));
      }
      return allowDeletes;
      return isUsable;
    }
@@ -87,22 +88,7 @@
     * {@inheritDoc}
     */
    @Override
    public boolean isModifyAcceptable(ServerManagedObject<?> managedObject,
        Collection<Message> unacceptableReasons) throws ConfigException {
      if (!allowModifies) {
        unacceptableReasons.add(Message.raw("Modifies not allowed"));
      }
      return allowModifies;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void performAddPostCondition(ServerManagedObject<?> managedObject)
    public void performPostAdd(ServerManagedObject<?> managedObject)
        throws ConfigException {
      // Make sure that the associated config entry exists.
      DN targetDN = managedObject.getDN();
@@ -116,7 +102,7 @@
     * {@inheritDoc}
     */
    @Override
    public void performDeletePostCondition(ServerManagedObject<?> managedObject)
    public void performPostDelete(ServerManagedObject<?> managedObject)
        throws ConfigException {
      // Make sure that the associated config entry does not exist.
      DN targetDN = managedObject.getDN();
@@ -130,7 +116,7 @@
     * {@inheritDoc}
     */
    @Override
    public void performModifyPostCondition(ServerManagedObject<?> managedObject)
    public void performPostModify(ServerManagedObject<?> managedObject)
        throws ConfigException {
      // Make sure that the associated config entry exists.
      DN targetDN = managedObject.getDN();
@@ -140,32 +126,25 @@
  }
  // Determines if add operations are allowed.
  private final boolean allowAdds;
  // Determines if modify operations are allowed.
  private final boolean allowModifies;
  // Determines if delete operations are allowed.
  private final boolean allowDeletes;
  private final boolean isDeleteAllowed;
  // Determines if configurations can be decoded.
  private final boolean isUsable;
  /**
   * Creates a new mock constraint.
   *
   * @param allowAdds
   *          Determines if add operations are allowed.
   * @param allowModifies
   *          Determines if modify operations are allowed.
   * @param allowDeletes
   * @param isUsable
   *          Determines if configurations can be decoded.
   * @param isDeleteAllowed
   *          Determines if delete operations are allowed.
   */
  public MockConstraint(boolean allowAdds, boolean allowModifies,
      boolean allowDeletes) {
    this.allowAdds = allowAdds;
    this.allowModifies = allowModifies;
    this.allowDeletes = allowDeletes;
  public MockConstraint(boolean isUsable, boolean isDeleteAllowed) {
    this.isUsable = isUsable;
    this.isDeleteAllowed = isDeleteAllowed;
  }