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