From a8eb24ede6506e89a7a8b9478aea003490d7eaba Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 03 Sep 2007 13:33:50 +0000
Subject: [PATCH] Partial fix for issue 1451: admin framework constraint and dependency support.

---
 opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java                   |   37 +
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java                  |   10 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java           |   22 
 opendj-sdk/opends/src/messages/messages/admin.properties                                                     |    2 
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ClientConstraintHandler.java                     |  156 +++++
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java                             |  144 ++---
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java |  201 +++++++
 opendj-sdk/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java                    |   73 ++
 opendj-sdk/opends/src/server/org/opends/server/admin/client/OperationRejectedException.java                  |  109 ++-
 opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java                                  |  171 +++++
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java                      |   40 
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java                           |  359 +++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java                               |   22 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java |  141 +++++
 opendj-sdk/opends/src/server/org/opends/server/admin/Constraint.java                                         |   76 ++
 15 files changed, 1,389 insertions(+), 174 deletions(-)

diff --git a/opendj-sdk/opends/src/messages/messages/admin.properties b/opendj-sdk/opends/src/messages/messages/admin.properties
index 328a882..79bf72f 100644
--- a/opendj-sdk/opends/src/messages/messages/admin.properties
+++ b/opendj-sdk/opends/src/messages/messages/admin.properties
@@ -186,3 +186,5 @@
  administrator's properties
 INFO_ADMIN_ARG_USERID_DESCRIPTION_73=The administrator's unique identifier. \
  This is a required argument
+SEVERE_ERR_OPERATION_REJECTED_DEFAULT_74=The operation was rejected for an \
+ unspecified reason
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java b/opendj-sdk/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
index 2c7769f..8d6c75e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
@@ -26,7 +26,6 @@
  */
 
 package org.opends.server.admin;
-import org.opends.messages.Message;
 
 
 
@@ -35,12 +34,14 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.Set;
 
+import org.opends.messages.Message;
 import org.opends.server.admin.DefinitionDecodingException.Reason;
 
 
@@ -68,6 +69,10 @@
   // The parent managed object definition if applicable.
   private final AbstractManagedObjectDefinition<? super C, ? super S> parent;
 
+  // The set of constraints associated with this managed object
+  // definition.
+  private final Collection<Constraint> constraints;
+
   // The set of property definitions applicable to this managed object
   // definition.
   private final Map<String, PropertyDefinition<?>> propertyDefinitions;
@@ -76,6 +81,10 @@
   // definition.
   private final Map<String, RelationDefinition<?, ?>> relationDefinitions;
 
+  // The set of all constraints associated with this managed object
+  // definition including inherited constraints.
+  private final Collection<Constraint> allConstraints;
+
   // The set of all property definitions associated with this managed
   // object definition including inherited property definitions.
   private final Map<String, PropertyDefinition<?>> allPropertyDefinitions;
@@ -106,8 +115,10 @@
       AbstractManagedObjectDefinition<? super C, ? super S> parent) {
     this.name = name;
     this.parent = parent;
+    this.constraints = new LinkedList<Constraint>();
     this.propertyDefinitions = new HashMap<String, PropertyDefinition<?>>();
     this.relationDefinitions = new HashMap<String, RelationDefinition<?,?>>();
+    this.allConstraints = new LinkedList<Constraint>();
     this.allPropertyDefinitions = new HashMap<String, PropertyDefinition<?>>();
     this.allRelationDefinitions =
       new HashMap<String, RelationDefinition<?, ?>>();
@@ -119,6 +130,8 @@
     if (parent != null) {
       parent.children.put(name, this);
 
+      allConstraints.addAll(parent.getAllConstraints());
+
       for (PropertyDefinition<?> pd : parent.getAllPropertyDefinitions()) {
         allPropertyDefinitions.put(pd.getName(), pd);
       }
@@ -158,6 +171,20 @@
 
 
   /**
+   * Get all the constraints associated with this type of managed
+   * object. The returned collection will contain inherited
+   * constraints.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         constraints associated with this type of managed object.
+   */
+  public final Collection<Constraint> getAllConstraints() {
+    return Collections.unmodifiableCollection(allConstraints);
+  }
+
+
+
+  /**
    * Get all the property definitions associated with this type of
    * managed object. The returned collection will contain inherited
    * property definitions.
@@ -263,6 +290,19 @@
 
 
   /**
+   * Get the constraints defined by this managed object definition.
+   * The returned collection will not contain inherited constraints.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         constraints defined by this managed object definition.
+   */
+  public final Collection<Constraint> getConstraints() {
+    return Collections.unmodifiableCollection(constraints);
+  }
+
+
+
+  /**
    * Gets the optional description of this managed object definition
    * in the default locale.
    *
@@ -635,6 +675,22 @@
 
 
   /**
+   * Deregister a constraint from the managed object definition.
+   * <p>
+   * This method <b>must not</b> be called by applications and is
+   * only intended for internal testing.
+   *
+   * @param constraint
+   *          The constraint to be deregistered.
+   */
+  protected final void deregisterConstraint(Constraint constraint) {
+    constraints.remove(constraint);
+    allConstraints.remove(constraint);
+  }
+
+
+
+  /**
    * Deregister a relation definition from the managed object
    * definition.
    * <p>
@@ -655,6 +711,21 @@
 
 
   /**
+   * Register a constraint with the managed object definition.
+   * <p>
+   * This method <b>must not</b> be called by applications.
+   *
+   * @param constraint
+   *          The constraint to be registered.
+   */
+  protected final void registerConstraint(Constraint constraint) {
+    constraints.add(constraint);
+    allConstraints.add(constraint);
+  }
+
+
+
+  /**
    * Register a property definition with the managed object definition,
    * overriding any existing property definition with the same name.
    * <p>
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/Constraint.java b/opendj-sdk/opends/src/server/org/opends/server/admin/Constraint.java
new file mode 100644
index 0000000..85af348
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/Constraint.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+
+
+import java.util.Collection;
+
+import org.opends.server.admin.client.ClientConstraintHandler;
+
+
+
+/**
+ * An interface for enforcing constraints and dependencies between
+ * managed objects and their properties. Constraints express
+ * relationships between managed objects and their properties, for
+ * example:
+ * <ul>
+ * <li>referential integrity: where one managed object references
+ * another a constraint can enforce referential integrity. The
+ * constraint can prevent creation of references to non-existent
+ * managed objects, and also prevent deletion of referenced managed
+ * objects
+ * <li>property dependencies: for example, when a boolean property is
+ * <code>true</code>, one or more additional properties must be
+ * specified. This is useful for features like SSL, which when
+ * enabled, requires that various SSL related configuration options
+ * are specified
+ * <li>property constraints: for example, when an upper limit
+ * property must not have a value which is less than the lower limit
+ * property.
+ * </ul>
+ * On the client-side constraints are enforced immediately before a
+ * write operation is performed. That is to say, immediately before a
+ * new managed object is created, changes to a managed object are
+ * applied, or an existing managed object is deleted.
+ */
+public interface Constraint {
+
+  /**
+   * Gets the client-side constraint handlers which will be used to
+   * enforce this constraint in client applications.
+   *
+   * @return Returns the client-side constraint handlers which will be
+   *         used to enforce this constraint in client applications.
+   *         The returned collection must not be <code>null</code>
+   *         but maybe empty (indicating that the constrain can only
+   *         be enforced on the server-side).
+   */
+  Collection<ClientConstraintHandler> getClientConstraintHandlers();
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ClientConstraintHandler.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ClientConstraintHandler.java
new file mode 100644
index 0000000..88bbfab
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ClientConstraintHandler.java
@@ -0,0 +1,156 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.client;
+
+
+
+import java.util.Collection;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.ManagedObjectPath;
+
+
+
+/**
+ * An interface for performing client-side constraint validation.
+ * <p>
+ * Constraints are evaluated immediately before the client performs a
+ * write operation. If one or more constraints fails, the write
+ * operation is refused and fails with an
+ * {@link OperationRejectedException}.
+ * <p>
+ * A client constraint handler must override at least one of the
+ * provided methods.
+ *
+ * @see org.opends.server.admin.Constraint
+ */
+public abstract class ClientConstraintHandler {
+
+  /**
+   * Determines whether or not the newly created managed object which
+   * is about to be added to 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.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param context
+   *          The management context.
+   * @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 AuthorizationException
+   *           If an authorization failure prevented this constraint
+   *           from being evaluated.
+   * @throws CommunicationException
+   *           If a communications problem prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isAddAcceptable(ManagementContext context,
+      ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+      throws AuthorizationException, CommunicationException {
+    return true;
+  }
+
+
+
+  /**
+   * Determines whether or not the changes to an existing managed
+   * object which are about to be committed to 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.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param context
+   *          The management context.
+   * @param managedObject
+   *          The modified 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.
+   * @throws AuthorizationException
+   *           If an authorization failure prevented this constraint
+   *           from being evaluated.
+   * @throws CommunicationException
+   *           If a communications problem prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isModifyAcceptable(ManagementContext context,
+      ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+      throws AuthorizationException, CommunicationException {
+    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.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param context
+   *          The management context.
+   * @param path
+   *          The path of the managed object which is about to be
+   *          deleted.
+   * @param unacceptableReasons
+   *          A list of messages to which error messages should be
+   *          added.
+   * @return Returns <code>true</code> if this constraint is
+   *         satisfied, or <code>false</code> if it is not.
+   * @throws AuthorizationException
+   *           If an authorization failure prevented this constraint
+   *           from being evaluated.
+   * @throws CommunicationException
+   *           If a communications problem prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isDeleteAcceptable(ManagementContext context,
+      ManagedObjectPath<?, ?> path, Collection<Message> unacceptableReasons)
+      throws AuthorizationException, CommunicationException {
+    return true;
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java
index e2be911..a7cba0f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java
@@ -115,9 +115,9 @@
    *           this managed object is being modified but it has been
    *           removed from the server by another client.
    * @throws OperationRejectedException
-   *           If the server refuses to add or modify this managed
-   *           object due to some server-side constraint which cannot
-   *           be satisfied.
+   *           If this managed object cannot be added or modified due
+   *           to some client-side or server-side constraint which
+   *           cannot be satisfied.
    * @throws AuthorizationException
    *           If the server refuses to add or modify this managed
    *           object because the client does not have the correct
@@ -590,16 +590,16 @@
    *           If the managed object could not be removed because it
    *           could not found on the server.
    * @throws OperationRejectedException
-   *           If the server refuses to remove the managed object due
-   *           to some server-side constraint which cannot be
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
    *           satisfied (for example, if it is referenced by another
    *           managed object).
    * @throws ConcurrentModificationException
    *           If this managed object has been removed from the server
    *           by another client.
    * @throws AuthorizationException
-   *           If the server refuses to make the list the managed
-   *           objects because the client does not have the correct
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
    *           privileges.
    * @throws CommunicationException
    *           If the client cannot contact the server due to an
@@ -631,16 +631,16 @@
    *           If the managed object could not be removed because it
    *           could not found on the server.
    * @throws OperationRejectedException
-   *           If the server refuses to remove the managed object due
-   *           to some server-side constraint which cannot be
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
    *           satisfied (for example, if it is referenced by another
    *           managed object).
    * @throws ConcurrentModificationException
    *           If this managed object has been removed from the server
    *           by another client.
    * @throws AuthorizationException
-   *           If the server refuses to make the list the managed
-   *           objects because the client does not have the correct
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
    *           privileges.
    * @throws CommunicationException
    *           If the client cannot contact the server due to an
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java
index 818706a..1586ff0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java
@@ -29,6 +29,19 @@
 
 
 
+import java.util.SortedSet;
+
+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.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.client.spi.Driver;
 import org.opends.server.admin.std.client.RootCfgClient;
 
 
@@ -48,6 +61,232 @@
 
 
   /**
+   * Deletes the named instantiable child managed object from the
+   * named parent managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @return Returns <code>true</code> if the named instantiable
+   *         child managed object was found, or <code>false</code>
+   *         if it was not found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    return getDriver().deleteManagedObject(parent, rd, name);
+  }
+
+
+
+  /**
+   * Deletes the optional child managed object from the named parent
+   * managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The optional relation definition.
+   * @return Returns <code>true</code> if the optional child managed
+   *         object was found, or <code>false</code> if it was not
+   *         found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    return getDriver().deleteManagedObject(parent, rd);
+  }
+
+
+
+  /**
+   * Gets the named managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param path
+   *          The path of the managed object.
+   * @return Returns the named managed object.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getManagedObject(
+      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    return getDriver().getManagedObject(path);
+  }
+
+
+
+  /**
+   * Gets the effective value of a property in the named managed
+   * object.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective value, or
+   *         <code>null</code> if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
+      PropertyDefinition<PD> pd) throws IllegalArgumentException,
+      DefinitionDecodingException, AuthorizationException,
+      ManagedObjectNotFoundException, CommunicationException,
+      PropertyException {
+    return getDriver().getPropertyValue(path, pd);
+  }
+
+
+
+  /**
+   * Gets the effective values of a property in the named managed
+   * object.
+   * <p>
+   * Implementations MUST NOT not use
+   * {@link #getManagedObject(ManagedObjectPath)} to read the
+   * referenced managed object in its entirety. Specifically,
+   * implementations MUST only attempt to resolve the default values
+   * for the requested property and its dependencies (if it uses
+   * inherited defaults). This is to avoid infinite recursion where a
+   * managed object contains a property which inherits default values
+   * from another property in the same managed object.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective values, or an empty set
+   *         if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <PD> SortedSet<PD> getPropertyValues(
+      ManagedObjectPath<?, ?> path, PropertyDefinition<PD> pd)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      AuthorizationException, ManagedObjectNotFoundException,
+      CommunicationException, PropertyException {
+    return getDriver().getPropertyValues(path, pd);
+  }
+
+
+
+  /**
    * Gets the root configuration client associated with this
    * management context.
    *
@@ -67,7 +306,123 @@
    * @return Returns the root configuration managed object associated
    *         with this management context.
    */
-  public abstract
-  ManagedObject<RootCfgClient> getRootConfigurationManagedObject();
+  public final
+  ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
+    return getDriver().getRootConfigurationManagedObject();
+  }
 
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    return getDriver().listManagedObjects(parent, rd);
+  }
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object which are a sub-type of the specified managed object
+   * definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param d
+   *          The managed object definition.
+   * @return Returns the names of the child managed objects which are
+   *         a sub-type of the specified managed object definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    return getDriver().listManagedObjects(parent, rd, d);
+  }
+
+
+
+  /**
+   * Determines whether or not the named managed object exists.
+   *
+   * @param path
+   *          The path of the named managed object.
+   * @return Returns <code>true</code> if the named managed object
+   *         exists, <code>false</code> otherwise.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final boolean managedObjectExists(ManagedObjectPath<?, ?> path)
+      throws ManagedObjectNotFoundException, AuthorizationException,
+      CommunicationException {
+    return getDriver().managedObjectExists(path);
+  }
+
+
+
+  /**
+   * Gets the driver associated with this management context.
+   *
+   * @return Returns the driver associated with this management
+   *         context.
+   */
+  protected abstract Driver getDriver();
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/OperationRejectedException.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/OperationRejectedException.java
index 45095ee..a2736f0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/OperationRejectedException.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/OperationRejectedException.java
@@ -26,15 +26,29 @@
  */
 
 package org.opends.server.admin.client;
-import org.opends.messages.Message;
 
 
 
+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.util.Validator;
+
+
 
 /**
- * This exception is thrown when the server refuses to create, delete,
- * or modify a managed object due to some server-side constraint that
- * cannot be satisified and which cannot be enforced by the client.
+ * This exception is thrown when the client or server refuses to
+ * create, delete, or modify a managed object due to one or more
+ * constraints that cannot be satisfied.
+ * <p>
+ * Operations can be rejected either by a client-side constraint
+ * violation triggered by {@link ClientConstraintHandler}, or by a
+ * server-side error.
  * <p>
  * For example, the Directory Server might not be able perform an
  * operation due to some OS related problem, such as lack of disk
@@ -49,48 +63,81 @@
 
 
 
-  /**
-   * Create an operation rejected exception.
-   */
-  public OperationRejectedException() {
-    // No implementation required.
+  // Merge the messages into a single message.
+  private static Message getSingleMessage(Collection<Message> messages) {
+    Validator.ensureNotNull(messages);
+    Validator.ensureTrue(!messages.isEmpty());
+
+    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;
+
 
 
   /**
-   * Create an operation rejected exception with a cause.
+   * Creates a new operation rejected exception with the provided
+   * messages.
    *
-   * @param cause
-   *          The cause.
+   * @param messages
+   *          The messages describing the constraint violations that
+   *          occurred (must be non-<code>null</code> and
+   *          non-empty).
    */
-  public OperationRejectedException(Throwable cause) {
-    super(cause);
+  public OperationRejectedException(Collection<Message> messages) {
+    super(getSingleMessage(messages));
+
+    this.messages = new ArrayList<Message>(messages);
   }
 
 
 
   /**
-   * Create an operation rejected exception with a message and cause.
+   * Creates a new operation rejected exception with the provided
+   * message.
    *
    * @param message
-   *          The message.
-   * @param cause
-   *          The cause.
-   */
-  public OperationRejectedException(Message message, Throwable cause) {
-    super(message, cause);
-  }
-
-
-
-  /**
-   * Create an operation rejected exception with a message.
-   *
-   * @param message
-   *          The message.
+   *          The message describing the constraint violation that
+   *          occurred (must be non-<code>null</code> and
+   *          non-empty).
    */
   public OperationRejectedException(Message message) {
-    super(message);
+    this(Collections.singleton(message));
   }
+
+
+
+  /**
+   * Creates a new operation rejected exception with a default
+   * message.
+   */
+  public OperationRejectedException() {
+    this(ERR_OPERATION_REJECTED_DEFAULT.get());
+  }
+
+
+
+  /**
+   * 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);
+  }
+
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
index 497f100..a76fd7d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
@@ -47,6 +47,7 @@
 import javax.naming.directory.Attributes;
 import javax.naming.ldap.LdapName;
 
+import org.opends.messages.Message;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.ConfigurationClient;
@@ -59,7 +60,6 @@
 import org.opends.server.admin.ManagedObjectDefinition;
 import org.opends.server.admin.ManagedObjectNotFoundException;
 import org.opends.server.admin.ManagedObjectPath;
-import org.opends.server.admin.OptionalRelationDefinition;
 import org.opends.server.admin.PropertyDefinition;
 import org.opends.server.admin.PropertyException;
 import org.opends.server.admin.PropertyIsMandatoryException;
@@ -74,6 +74,8 @@
 import org.opends.server.admin.client.OperationRejectedException;
 import org.opends.server.admin.client.spi.Driver;
 import org.opends.server.admin.client.spi.PropertySet;
+import org.opends.server.admin.std.client.RootCfgClient;
+import org.opends.server.admin.std.meta.RootCfgDefn;
 
 
 
@@ -85,6 +87,9 @@
   // The LDAP connection.
   private final LDAPConnection connection;
 
+  // The LDAP management context.
+  private final LDAPManagementContext context;
+
   // The LDAP profile which should be used to construct LDAP
   // requests and decode LDAP responses.
   private final LDAPProfile profile;
@@ -95,12 +100,16 @@
    * Creates a new LDAP driver using the specified LDAP connection and
    * profile.
    *
+   * @param context
+   *          The LDAP management context.
    * @param connection
    *          The LDAP connection.
    * @param profile
    *          The LDAP profile.
    */
-  public LDAPDriver(LDAPConnection connection, LDAPProfile profile) {
+  public LDAPDriver(LDAPManagementContext context, LDAPConnection connection,
+      LDAPProfile profile) {
+    this.context = context;
     this.connection = connection;
     this.profile = profile;
   }
@@ -112,48 +121,6 @@
    */
   @Override
   public <C extends ConfigurationClient, S extends Configuration>
-  boolean deleteManagedObject(
-      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
-      String name) throws IllegalArgumentException,
-      ManagedObjectNotFoundException, OperationRejectedException,
-      AuthorizationException, CommunicationException {
-    validateRelationDefinition(parent, rd);
-
-    if (!managedObjectExists(parent)) {
-      throw new ManagedObjectNotFoundException();
-    }
-
-    return removeManagedObject(parent.child(rd, name));
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public <C extends ConfigurationClient, S extends Configuration>
-  boolean deleteManagedObject(
-      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
-      throws IllegalArgumentException, ManagedObjectNotFoundException,
-      OperationRejectedException, AuthorizationException,
-      CommunicationException {
-    validateRelationDefinition(parent, rd);
-
-    if (!managedObjectExists(parent)) {
-      throw new ManagedObjectNotFoundException();
-    }
-
-    return removeManagedObject(parent.child(rd));
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public <C extends ConfigurationClient, S extends Configuration>
   ManagedObject<? extends C> getManagedObject(
       ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
       ManagedObjectDecodingException, ManagedObjectNotFoundException,
@@ -289,6 +256,18 @@
    * {@inheritDoc}
    */
   @Override
+  public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
+    return new LDAPManagedObject<RootCfgClient>(this,
+        RootCfgDefn.getInstance(), ManagedObjectPath.emptyPath(),
+        new PropertySet(), true, null);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
   public <C extends ConfigurationClient, S extends Configuration>
   String[] listManagedObjects(
       ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
@@ -341,7 +320,7 @@
       return true;
     }
 
-    ManagedObjectPath<?,?> parent = path.parent();
+    ManagedObjectPath<?, ?> parent = path.parent();
     LdapName dn = LDAPNameBuilder.create(parent, profile);
     if (!entryExists(dn)) {
       throw new ManagedObjectNotFoundException();
@@ -354,6 +333,43 @@
 
 
   /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected <C extends ConfigurationClient, S extends Configuration>
+  void deleteManagedObject(
+      ManagedObjectPath<C, S> path) throws OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    // Delete the entry and any subordinate entries.
+    LdapName dn = LDAPNameBuilder.create(path, profile);
+    try {
+      connection.deleteSubtree(dn);
+    } catch (OperationNotSupportedException e) {
+      // Unwilling to perform.
+      if (e.getMessage() != null) {
+        throw new OperationRejectedException();
+      } else {
+        Message m = Message.raw("%s", e.getMessage());
+        throw new OperationRejectedException(m);
+      }
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected LDAPManagementContext getManagementContext() {
+    return context;
+  }
+
+
+
+  /**
    * Adapts a naming exception to an appropriate admin client
    * exception.
    *
@@ -543,42 +559,4 @@
 
     return d.resolveManagedObjectDefinition(resolver);
   }
-
-
-
-  // Remove the named managed object.
-  private boolean removeManagedObject(ManagedObjectPath<?, ?> path)
-      throws CommunicationException, AuthorizationException,
-      OperationRejectedException, ManagedObjectNotFoundException {
-    if (!managedObjectExists(path)) {
-      return false;
-    }
-
-    // Delete the entry and any subordinate entries.
-    LdapName dn = LDAPNameBuilder.create(path, profile);
-    try {
-      connection.deleteSubtree(dn);
-    } catch (OperationNotSupportedException e) {
-      // Unwilling to perform.
-      throw new OperationRejectedException(e);
-    } catch (NamingException e) {
-      adaptNamingException(e);
-    }
-
-    return true;
-  }
-
-
-
-  // Validate that a relation definition belongs to this managed
-  // object.
-  private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
-      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
-    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
-    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
-    if (tmp != rd) {
-      throw new IllegalArgumentException("The relation " + rd.getName()
-          + " is not associated with a " + d.getName());
-    }
-  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
index e74232a..2a80dbc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -40,6 +40,7 @@
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 
+import org.opends.messages.Message;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.ConfigurationClient;
 import org.opends.server.admin.InstantiableRelationDefinition;
@@ -59,8 +60,6 @@
 import org.opends.server.admin.client.spi.Driver;
 import org.opends.server.admin.client.spi.Property;
 import org.opends.server.admin.client.spi.PropertySet;
-import org.opends.server.admin.std.client.RootCfgClient;
-import org.opends.server.admin.std.meta.RootCfgDefn;
 
 
 
@@ -74,22 +73,6 @@
 final class LDAPManagedObject<T extends ConfigurationClient> extends
     AbstractManagedObject<T> {
 
-  /**
-   * Constructs a root LDAP managed object associated with the
-   * provided LDAP driver.
-   *
-   * @param driver
-   *          The LDAP management driver.
-   * @return Returns a root LDAP managed object associated with the
-   *         provided LDAP driver.
-   */
-  static ManagedObject<RootCfgClient> getRootManagedObject(
-      LDAPDriver driver) {
-    return new LDAPManagedObject<RootCfgClient>(driver, RootCfgDefn
-        .getInstance(), ManagedObjectPath.emptyPath(), new PropertySet(), true,
-        null);
-  }
-
   // The LDAP management driver associated with this managed object.
   private final LDAPDriver driver;
 
@@ -176,7 +159,12 @@
           driver.getLDAPConnection().createEntry(dn, attributes);
         } catch (OperationNotSupportedException e) {
           // Unwilling to perform.
-          throw new OperationRejectedException(e);
+          if (e.getMessage() != null) {
+            throw new OperationRejectedException();
+          } else {
+            Message m = Message.raw("%s", e.getMessage());
+            throw new OperationRejectedException(m);
+          }
         } catch (NamingException e) {
           driver.adaptNamingException(e);
         }
@@ -220,7 +208,12 @@
       throw new ManagedObjectAlreadyExistsException();
     } catch (OperationNotSupportedException e) {
       // Unwilling to perform.
-      throw new OperationRejectedException(e);
+      if (e.getMessage() != null) {
+        throw new OperationRejectedException();
+      } else {
+        Message m = Message.raw("%s", e.getMessage());
+        throw new OperationRejectedException(m);
+      }
     } catch (NamingException e) {
       driver.adaptNamingException(e);
     }
@@ -269,7 +262,12 @@
         throw new AuthorizationException(e);
       } catch (OperationNotSupportedException e) {
         // Unwilling to perform.
-        throw new OperationRejectedException(e);
+        if (e.getMessage() != null) {
+          throw new OperationRejectedException();
+        } else {
+          Message m = Message.raw("%s", e.getMessage());
+          throw new OperationRejectedException(m);
+        }
       } catch (NamingException e) {
         // Just treat it as a communication problem.
         throw new CommunicationException(e);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java
index 9e107dc..a38ce53 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java
@@ -30,9 +30,8 @@
 
 
 import org.opends.server.admin.LDAPProfile;
-import org.opends.server.admin.client.ManagedObject;
 import org.opends.server.admin.client.ManagementContext;
-import org.opends.server.admin.std.client.RootCfgClient;
+import org.opends.server.admin.client.spi.Driver;
 import org.opends.server.util.Validator;
 
 
@@ -63,7 +62,7 @@
   // Private constructor.
   private LDAPManagementContext(LDAPConnection connection,
       LDAPProfile profile) {
-    this.driver = new LDAPDriver(connection, profile);
+    this.driver = new LDAPDriver(this, connection, profile);
   }
 
 
@@ -71,7 +70,8 @@
   /**
    * {@inheritDoc}
    */
-  public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
-    return LDAPManagedObject.getRootManagedObject(driver);
+  @Override
+  protected Driver getDriver() {
+    return driver;
   }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
index 44480e0..5f82e45 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
@@ -36,9 +36,11 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import org.opends.messages.Message;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.Constraint;
 import org.opends.server.admin.DefaultBehaviorException;
 import org.opends.server.admin.DefinitionDecodingException;
 import org.opends.server.admin.IllegalPropertyValueException;
@@ -57,11 +59,13 @@
 import org.opends.server.admin.RelationDefinition;
 import org.opends.server.admin.SingletonRelationDefinition;
 import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.ClientConstraintHandler;
 import org.opends.server.admin.client.CommunicationException;
 import org.opends.server.admin.client.ConcurrentModificationException;
 import org.opends.server.admin.client.IllegalManagedObjectNameException;
 import org.opends.server.admin.client.ManagedObject;
 import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.ManagementContext;
 import org.opends.server.admin.client.MissingMandatoryPropertiesException;
 import org.opends.server.admin.client.OperationRejectedException;
 
@@ -150,6 +154,30 @@
       throw new MissingMandatoryPropertiesException(exceptions);
     }
 
+    // Now enforce any constraints.
+    List<Message> messages = new LinkedList<Message>();
+    boolean isAcceptable = true;
+    ManagementContext context = getDriver().getManagementContext();
+
+    for (Constraint constraint : definition.getAllConstraints()) {
+      for (ClientConstraintHandler handler : constraint
+          .getClientConstraintHandlers()) {
+        if (existsOnServer) {
+          if (!handler.isModifyAcceptable(context, this, messages)) {
+            isAcceptable = false;
+          }
+        } else {
+          if (!handler.isAddAcceptable(context, this, messages)) {
+            isAcceptable = false;
+          }
+        }
+      }
+    }
+
+    if (!isAcceptable) {
+      throw new OperationRejectedException(messages);
+    }
+
     // Commit the managed object.
     if (existsOnServer) {
       modifyExistingManagedObject();
@@ -497,8 +525,9 @@
    *           If the managed object's parent has been removed by
    *           another client.
    * @throws OperationRejectedException
-   *           If the server refuses to add this managed object due to
-   *           some server-side constraint which cannot be satisfied.
+   *           If the managed object cannot be added due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied.
    * @throws AuthorizationException
    *           If the server refuses to add this managed object
    *           because the client does not have the correct
@@ -566,8 +595,8 @@
    *           If this managed object has been removed from the server
    *           by another client.
    * @throws OperationRejectedException
-   *           If the server refuses to modify this managed object due
-   *           to some server-side constraint which cannot be
+   *           If the managed object cannot be added due to some
+   *           client-side or server-side constraint which cannot be
    *           satisfied.
    * @throws AuthorizationException
    *           If the server refuses to modify this managed object
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java
index 354f2aa..9f5de09 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java
@@ -32,15 +32,18 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 import java.util.SortedSet;
 
+import org.opends.messages.Message;
 import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
 import org.opends.server.admin.AliasDefaultBehaviorProvider;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.Constraint;
 import org.opends.server.admin.DefaultBehaviorException;
 import org.opends.server.admin.DefaultBehaviorProviderVisitor;
 import org.opends.server.admin.DefinedDefaultBehaviorProvider;
@@ -55,14 +58,18 @@
 import org.opends.server.admin.PropertyIsSingleValuedException;
 import org.opends.server.admin.PropertyNotFoundException;
 import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.RelationDefinition;
 import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
 import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
 import org.opends.server.admin.DefinitionDecodingException.Reason;
 import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.ClientConstraintHandler;
 import org.opends.server.admin.client.CommunicationException;
 import org.opends.server.admin.client.ManagedObject;
 import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.ManagementContext;
 import org.opends.server.admin.client.OperationRejectedException;
+import org.opends.server.admin.std.client.RootCfgClient;
 
 
 
@@ -308,24 +315,28 @@
    * @throws ManagedObjectNotFoundException
    *           If the parent managed object could not be found.
    * @throws OperationRejectedException
-   *           If the server refuses to remove the child managed
-   *           object due to some server-side constraint which cannot
-   *           be satisfied (for example, if it is referenced by
-   *           another managed object).
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
    * @throws AuthorizationException
-   *           If the server refuses to make the list the managed
-   *           objects because the client does not have the correct
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
    *           privileges.
    * @throws CommunicationException
    *           If the client cannot contact the server due to an
    *           underlying communication problem.
    */
-  public abstract <C extends ConfigurationClient, S extends Configuration>
+  public final <C extends ConfigurationClient, S extends Configuration>
   boolean deleteManagedObject(
       ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
       String name) throws IllegalArgumentException,
       ManagedObjectNotFoundException, OperationRejectedException,
-      AuthorizationException, CommunicationException;
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+    ManagedObjectPath<?, ?> child = parent.child(rd, name);
+    return doDeleteManagedObject(child);
+  }
 
 
 
@@ -352,24 +363,28 @@
    * @throws ManagedObjectNotFoundException
    *           If the parent managed object could not be found.
    * @throws OperationRejectedException
-   *           If the server refuses to remove the child managed
-   *           object due to some server-side constraint which cannot
-   *           be satisfied (for example, if it is referenced by
-   *           another managed object).
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
    * @throws AuthorizationException
-   *           If the server refuses to make the list the managed
-   *           objects because the client does not have the correct
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
    *           privileges.
    * @throws CommunicationException
    *           If the client cannot contact the server due to an
    *           underlying communication problem.
    */
-  public abstract <C extends ConfigurationClient, S extends Configuration>
+  public final <C extends ConfigurationClient, S extends Configuration>
   boolean deleteManagedObject(
       ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
       throws IllegalArgumentException, ManagedObjectNotFoundException,
       OperationRejectedException, AuthorizationException,
-      CommunicationException;
+      CommunicationException {
+    validateRelationDefinition(parent, rd);
+    ManagedObjectPath<?, ?> child = parent.child(rd);
+    return doDeleteManagedObject(child);
+  }
 
 
 
@@ -507,6 +522,18 @@
 
 
   /**
+   * Gets the root configuration managed object associated with this
+   * management context driver.
+   *
+   * @return Returns the root configuration managed object associated
+   *         with this management context driver.
+   */
+  public abstract
+  ManagedObject<RootCfgClient> getRootConfigurationManagedObject();
+
+
+
+  /**
    * Lists the child managed objects of the named parent managed
    * object.
    *
@@ -611,6 +638,40 @@
 
 
   /**
+   * Deletes the named managed object.
+   * <p>
+   * Implementations do not need check whether the named managed
+   * object exists, nor do they need to enforce client constraints.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param path
+   *          The path of the managed object to be deleted.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           server-side constraint which cannot be satisfied (for
+   *           example, if it is referenced by another managed
+   *           object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  protected abstract <C extends ConfigurationClient, S extends Configuration>
+  void deleteManagedObject(
+      ManagedObjectPath<C, S> path) throws OperationRejectedException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
    * Gets the default values for the specified property.
    *
    * @param <PD>
@@ -634,4 +695,82 @@
     return v.find(p, pd);
   }
 
+
+
+  /**
+   * Gets the management context associated with this driver.
+   *
+   * @return Returns the management context associated with this
+   *         driver.
+   */
+  protected abstract ManagementContext getManagementContext();
+
+
+
+  /**
+   * Validate that a relation definition belongs to the managed object
+   * referenced by the provided path.
+   *
+   * @param path
+   *          The parent managed object path.
+   * @param rd
+   *          The relation definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition does not belong to the
+   *           managed object definition.
+   */
+  protected final void validateRelationDefinition(ManagedObjectPath<?, ?> path,
+      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
+    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + d.getName());
+    }
+  }
+
+
+
+  // Remove a managed object, first ensuring that the parent exists,
+  // then ensuring that the child exists, before ensuring that any
+  // constraints are satisfied.
+  private <C extends ConfigurationClient, S extends Configuration>
+  boolean doDeleteManagedObject(
+      ManagedObjectPath<C, S> path) throws ManagedObjectNotFoundException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    // First make sure that the parent exists.
+    if (!managedObjectExists(path.parent())) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    // Make sure that the targeted managed object exists.
+    if (!managedObjectExists(path)) {
+      return false;
+    }
+
+    // The targeted managed object is guaranteed to exist, so enforce
+    // any constraints.
+    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+    List<Message> messages = new LinkedList<Message>();
+    boolean isAcceptable = true;
+
+    for (Constraint constraint : d.getAllConstraints()) {
+      for (ClientConstraintHandler handler : constraint
+          .getClientConstraintHandlers()) {
+        ManagementContext context = getManagementContext();
+        if (!handler.isDeleteAcceptable(context, path, messages)) {
+          isAcceptable = false;
+        }
+      }
+    }
+
+    if (!isAcceptable) {
+      throw new OperationRejectedException(messages);
+    }
+
+    deleteManagedObject(path);
+    return true;
+  }
+
 }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
index 781ed0f..7d91ae3 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
@@ -180,6 +180,28 @@
   private TestChildCfgDefn() {
     super("test-child", null);
   }
+  
+  
+  
+  /**
+   * Adds a constraint temporarily with this test definition.
+   * 
+   * @param constraint The constraint.
+   */
+  public void addConstraint(Constraint constraint) {
+    registerConstraint(constraint);
+  }
+  
+  
+  
+  /**
+   * Removes a constraint from this test definition.
+   * 
+   * @param constraint The constraint.
+   */
+  public void removeConstraint(Constraint constraint) {
+    deregisterConstraint(constraint);
+  }
 
 
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java
index 46bdbae..9c7e8b1 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java
@@ -40,6 +40,7 @@
 
 import org.opends.server.TestCaseUtils;
 import org.opends.server.admin.AdminTestCase;
+import org.opends.server.admin.Constraint;
 import org.opends.server.admin.DefinitionDecodingException;
 import org.opends.server.admin.LDAPProfile;
 import org.opends.server.admin.ManagedObjectAlreadyExistsException;
@@ -797,6 +798,206 @@
 
 
 
+  /**
+   * Tests creation of a child managed object succeeds when registered
+   * add constraints succeed.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @Test
+  public void testAddConstraintSuccess() throws Exception {
+    Constraint constraint = new MockConstraint(true, false, false);
+    TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+    try {
+      CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
+          "cn=test child new,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+      c.importLDIF(TEST_LDIF);
+      c.addExpectedAttribute("cn", "test child new");
+      c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
+      c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
+      c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
+          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
+      c.addExpectedAttribute("ds-cfg-virtual-attribute-type", "description");
+
+      ManagementContext ctx = LDAPManagementContext.createFromContext(c);
+      TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
+      TestChildCfgClient child = parent.createTestChild(TestChildCfgDefn
+          .getInstance(), "test child new", null);
+      child.setMandatoryBooleanProperty(true);
+      child.setMandatoryReadOnlyAttributeTypeProperty(DirectoryServer
+          .getAttributeType("description"));
+      child.commit();
+
+      c.assertEntryIsCreated();
+    } finally {
+      // Clean up.
+      TestChildCfgDefn.getInstance().removeConstraint(constraint);
+    }
+  }
+
+
+
+  /**
+   * Tests creation of a child managed object fails when registered
+   * add constraints fail.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @Test(expectedExceptions=OperationRejectedException.class)
+  public void testAddConstraintFail() throws Exception {
+    Constraint constraint = new MockConstraint(false, true, true);
+    TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+    try {
+      CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
+          "cn=test child new,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+      c.importLDIF(TEST_LDIF);
+      c.addExpectedAttribute("cn", "test child new");
+      c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
+      c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
+      c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
+          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
+      c.addExpectedAttribute("ds-cfg-virtual-attribute-type", "description");
+
+      ManagementContext ctx = LDAPManagementContext.createFromContext(c);
+      TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
+      TestChildCfgClient child = parent.createTestChild(TestChildCfgDefn
+          .getInstance(), "test child new", null);
+      child.setMandatoryBooleanProperty(true);
+      child.setMandatoryReadOnlyAttributeTypeProperty(DirectoryServer
+          .getAttributeType("description"));
+      child.commit();
+      Assert.fail("The add constraint failed to prevent creation of the managed object");
+    } finally {
+      // Clean up.
+      TestChildCfgDefn.getInstance().removeConstraint(constraint);
+    }
+  }
+
+
+
+  /**
+   * Tests removal of a child managed object succeeds when registered
+   * remove constraints succeed.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @Test
+  public void testRemoveConstraintSuccess() throws Exception {
+    Constraint constraint = new MockConstraint(false, false, true);
+    TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+    try {
+      DeleteSubtreeMockLDAPConnection c = new DeleteSubtreeMockLDAPConnection(
+          "cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+      c.importLDIF(TEST_LDIF);
+      ManagementContext ctx = LDAPManagementContext.createFromContext(c);
+      TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
+      parent.removeTestChild("test child 1");
+      c.assertSubtreeIsDeleted();
+    } finally {
+      // Clean up.
+      TestChildCfgDefn.getInstance().removeConstraint(constraint);
+    }
+  }
+
+
+
+  /**
+   * Tests removal of a child managed object fails when registered
+   * remove constraints fails.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @Test(expectedExceptions=OperationRejectedException.class)
+  public void testRemoveConstraintFail() throws Exception {
+    Constraint constraint = new MockConstraint(true, true, false);
+    TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+    try {
+      DeleteSubtreeMockLDAPConnection c = new DeleteSubtreeMockLDAPConnection(
+          "cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+      c.importLDIF(TEST_LDIF);
+      ManagementContext ctx = LDAPManagementContext.createFromContext(c);
+      TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
+      parent.removeTestChild("test child 1");
+      Assert.fail("The remove constraint failed to prevent removal of the managed object");
+    } finally {
+      // Clean up.
+      TestChildCfgDefn.getInstance().removeConstraint(constraint);
+    }
+  }
+
+
+
+  /**
+   * Tests modification of a child managed object succeeds when
+   * registered remove constraints succeed.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @Test
+  public void testModifyConstraintSuccess() throws Exception {
+    Constraint constraint = new MockConstraint(false, true, false);
+    TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+    try {
+      ModifyEntryMockLDAPConnection c = new ModifyEntryMockLDAPConnection(
+          "cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+      c.importLDIF(TEST_LDIF);
+      c.addExpectedModification("ds-cfg-virtual-attribute-base-dn");
+      ManagementContext ctx = LDAPManagementContext.createFromContext(c);
+      TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
+      TestChildCfgClient child = parent.getTestChild("test child 2");
+      child.setOptionalMultiValuedDNProperty1(Collections.<DN> emptySet());
+      child.commit();
+      Assert.assertTrue(c.isEntryModified());
+    } finally {
+      // Clean up.
+      TestChildCfgDefn.getInstance().removeConstraint(constraint);
+    }
+  }
+
+
+
+  /**
+   * Tests modification of a child managed object fails when
+   * registered remove constraints fails.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @Test(expectedExceptions = OperationRejectedException.class)
+  public void testModifyConstraintFail() throws Exception {
+    Constraint constraint = new MockConstraint(true, false, true);
+    TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+    try {
+      ModifyEntryMockLDAPConnection c = new ModifyEntryMockLDAPConnection(
+          "cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+      c.importLDIF(TEST_LDIF);
+      c.addExpectedModification("ds-cfg-virtual-attribute-base-dn");
+      ManagementContext ctx = LDAPManagementContext.createFromContext(c);
+      TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
+      TestChildCfgClient child = parent.getTestChild("test child 2");
+      child.setOptionalMultiValuedDNProperty1(Collections.<DN> emptySet());
+      child.commit();
+      Assert
+          .fail("The modify constraint failed to prevent modification of the managed object");
+    } finally {
+      // Clean up.
+      TestChildCfgDefn.getInstance().removeConstraint(constraint);
+    }
+  }
+
+
+
   // 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()];
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java
new file mode 100644
index 0000000..ff8f056
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.client.ldap;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.Constraint;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+
+
+
+/**
+ * A mock constraint which can be configured to refuse various types
+ * of operation.
+ */
+public final class MockConstraint implements Constraint {
+
+  /**
+   * Mock client constraint handler.
+   */
+  private class Handler extends ClientConstraintHandler {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isAddAcceptable(ManagementContext context,
+        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      if (!allowAdds) {
+        unacceptableReasons.add(Message.raw("Adds not allowed"));
+      }
+
+      return allowAdds;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isDeleteAcceptable(ManagementContext context,
+        ManagedObjectPath<?, ?> path, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      if (!allowDeletes) {
+        unacceptableReasons.add(Message.raw("Deletes not allowed"));
+      }
+
+      return allowDeletes;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isModifyAcceptable(ManagementContext context,
+        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      if (!allowModifies) {
+        unacceptableReasons.add(Message.raw("Modifies not allowed"));
+      }
+
+      return allowModifies;
+    }
+
+  }
+
+  // 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;
+
+
+
+  /**
+   * Creates a new mock constraint.
+   *
+   * @param allowAdds
+   *          Determines if add operations are allowed.
+   * @param allowModifies
+   *          Determines if modify operations are allowed.
+   * @param allowDeletes
+   *          Determines if delete operations are allowed.
+   */
+  public MockConstraint(boolean allowAdds, boolean allowModifies,
+      boolean allowDeletes) {
+    this.allowAdds = allowAdds;
+    this.allowModifies = allowModifies;
+    this.allowDeletes = allowDeletes;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
+    return Collections.<ClientConstraintHandler> singleton(new Handler());
+  }
+
+}

--
Gitblit v1.10.0