From 2249aa0a04b99d513828d8d60c2a8bd7d936b336 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 03 Sep 2007 23:45:45 +0000
Subject: [PATCH] Fix issue 1451: constraint and dependency support.
---
opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java | 84 +
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/MockConstraint.java | 189 ++++
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java | 631 -------------
opends/src/server/org/opends/server/admin/server/ServerConstraintHandler.java | 207 ++++
opends/src/server/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java | 25
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AdminTestCaseUtils.java | 54 +
opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java | 111 +
opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java | 116 +
opends/src/server/org/opends/server/admin/Constraint.java | 17
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java | 519 +++++++++++
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java | 10
opends/src/server/org/opends/server/admin/server/ServerManagementContext.java | 727 ++++++++++++++++
12 files changed, 1,998 insertions(+), 692 deletions(-)
diff --git a/opends/src/server/org/opends/server/admin/Constraint.java b/opends/src/server/org/opends/server/admin/Constraint.java
index 85af348..be8e4ca 100644
--- a/opends/src/server/org/opends/server/admin/Constraint.java
+++ b/opends/src/server/org/opends/server/admin/Constraint.java
@@ -31,6 +31,7 @@
import java.util.Collection;
import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.server.ServerConstraintHandler;
@@ -68,9 +69,23 @@
* @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
+ * but maybe empty (indicating that the constraint can only
* be enforced on the server-side).
*/
Collection<ClientConstraintHandler> getClientConstraintHandlers();
+
+
+ /**
+ * Gets the server-side constraint handlers which will be used to
+ * enforce this constraint within the server.
+ *
+ * @return Returns the server-side constraint handlers which will be
+ * used to enforce this constraint within the server. The
+ * returned collection must not be <code>null</code> and
+ * must not be empty, since constraints must always be
+ * enforced on the server.
+ */
+ Collection<ServerConstraintHandler> getServerConstraintHandlers();
+
}
diff --git a/opends/src/server/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java
index 3f6e405..c70daa6 100644
--- a/opends/src/server/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java
@@ -25,17 +25,16 @@
* Portions Copyright 2007 Sun Microsystems, Inc.
*/
package org.opends.server.admin.server;
-import org.opends.messages.Message;
import java.util.List;
-import org.opends.server.admin.DecodingException;
-
+import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
+
/**
* Common features of config listener adaptors.
*/
@@ -51,22 +50,6 @@
/**
- * Convert a decoding exception to an unacceptable reason.
- *
- * @param e
- * The decoding exception.
- * @param unacceptableReason
- * The builder to which messages should be appended.
- */
- protected final void generateUnacceptableReason(
- DecodingException e, MessageBuilder unacceptableReason) {
- // FIXME: generate a property OpenDS style message.
- unacceptableReason.append(e.getLocalizedMessage());
- }
-
-
-
- /**
* Concatenate a list of messages into a single message.
*
* @param reasons
@@ -74,8 +57,8 @@
* @param unacceptableReason
* The single message to which messages should be appended.
*/
- protected final void generateUnacceptableReason(
- List<Message> reasons, MessageBuilder unacceptableReason) {
+ protected final void generateUnacceptableReason(List<Message> reasons,
+ MessageBuilder unacceptableReason) {
boolean isFirst = true;
for (Message reason : reasons) {
if (isFirst) {
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
index 08c9c1d..33017e5 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -25,26 +25,34 @@
* Portions Copyright 2007 Sun Microsystems, Inc.
*/
package org.opends.server.admin.server;
-import org.opends.messages.Message;
+import static org.opends.messages.AdminMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
import java.util.LinkedList;
import java.util.List;
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
import org.opends.server.admin.Configuration;
+import org.opends.server.admin.Constraint;
import org.opends.server.admin.DecodingException;
import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
-import org.opends.server.admin.RelationDefinition;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ResultCode;
-import org.opends.messages.MessageBuilder;
+
/**
@@ -58,20 +66,28 @@
final class ConfigAddListenerAdaptor<S extends Configuration> extends
AbstractConfigListenerAdaptor implements ConfigAddListener {
- // The managed object path of the parent.
- private final ManagedObjectPath<?, ?> path;
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ // Cached configuration object between accept/apply callbacks.
+ private S cachedConfiguration;
+
+ // Cached managed object between accept/apply callbacks.
+ private ServerManagedObject<? extends S> cachedManagedObject;
// The instantiable relation.
private final InstantiableRelationDefinition<?, S> instantiableRelation;
- // The optional relation.
- private final OptionalRelationDefinition<?, S> optionalRelation;
-
// The underlying add listener.
private final ConfigurationAddListener<S> listener;
- // Cached configuration object between accept/apply callbacks.
- private S cachedConfiguration;
+ // The optional relation.
+ private final OptionalRelationDefinition<?, S> optionalRelation;
+
+ // The managed object path of the parent.
+ private final ManagedObjectPath<?, ?> path;
@@ -94,6 +110,7 @@
this.optionalRelation = null;
this.listener = listener;
this.cachedConfiguration = null;
+ this.cachedManagedObject = null;
}
@@ -117,6 +134,7 @@
this.instantiableRelation = null;
this.listener = listener;
this.cachedConfiguration = null;
+ this.cachedManagedObject = null;
}
@@ -124,8 +142,7 @@
/**
* {@inheritDoc}
*/
- public ConfigChangeResult applyConfigurationAdd(
- ConfigEntry configEntry) {
+ public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry) {
if (optionalRelation != null) {
// Optional managed objects are located directly beneath the
// parent and have a well-defined name. We need to make sure
@@ -140,7 +157,28 @@
// Cached objects are guaranteed to be from previous acceptable
// callback.
- return listener.applyConfigurationAdd(cachedConfiguration);
+ ConfigChangeResult result = listener
+ .applyConfigurationAdd(cachedConfiguration);
+
+ // Now apply post constraint call-backs.
+ if (result.getResultCode() == ResultCode.SUCCESS) {
+ ManagedObjectDefinition<?, ?> d = cachedManagedObject
+ .getManagedObjectDefinition();
+ for (Constraint constraint : d.getAllConstraints()) {
+ for (ServerConstraintHandler handler : constraint
+ .getServerConstraintHandlers()) {
+ try {
+ handler.performAddPostCondition(cachedManagedObject);
+ } catch (ConfigException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+ }
+
+ return result;
}
@@ -154,11 +192,9 @@
AttributeValue av = dn.getRDN().getAttributeValue(0);
String name = av.getStringValue().trim();
- ManagedObjectPath<?, ?> childPath;
- RelationDefinition<?, S> r;
+ ManagedObjectPath<?, S> childPath;
if (instantiableRelation != null) {
childPath = path.child(instantiableRelation, name);
- r = instantiableRelation;
} else {
// Optional managed objects are located directly beneath the
// parent and have a well-defined name. We need to make sure
@@ -169,21 +205,46 @@
// Doesn't apply to us.
return true;
}
-
- r = optionalRelation;
}
- ServerManagedObject<? extends S> mo;
try {
- mo = ServerManagedObject.decode(childPath, r
- .getChildDefinition(), configEntry, configEntry);
+ ServerManagementContext context = ServerManagementContext.getInstance();
+ cachedManagedObject = context.decode(childPath, configEntry, configEntry);
} catch (DecodingException e) {
- generateUnacceptableReason(e, unacceptableReason);
+ unacceptableReason.append(e.getMessageObject());
return false;
}
- cachedConfiguration = mo.getConfiguration();
+ cachedConfiguration = cachedManagedObject.getConfiguration();
List<Message> reasons = new LinkedList<Message>();
+
+ // Enforce any constraints.
+ boolean isAcceptable = true;
+ ManagedObjectDefinition<?, ?> d = cachedManagedObject
+ .getManagedObjectDefinition();
+ for (Constraint constraint : d.getAllConstraints()) {
+ for (ServerConstraintHandler handler : constraint
+ .getServerConstraintHandlers()) {
+ try {
+ if (!handler.isAddAcceptable(cachedManagedObject, reasons)) {
+ isAcceptable = false;
+ }
+ } catch (ConfigException e) {
+ Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
+ .getMessageObject());
+ reasons.add(message);
+ isAcceptable = false;
+ }
+ }
+ }
+
+ // Give up immediately if a constraint violation occurs.
+ if (!isAcceptable) {
+ generateUnacceptableReason(reasons, unacceptableReason);
+ return false;
+ }
+
+ // Let the add listener decide.
if (listener.isConfigurationAddAcceptable(cachedConfiguration, reasons)) {
return true;
} else {
@@ -195,9 +256,9 @@
/**
- * Get the configuiration add listener associated with this adaptor.
+ * Get the configuration add listener associated with this adaptor.
*
- * @return Returns the configuiration add listener associated with
+ * @return Returns the configuration add listener associated with
* this adaptor.
*/
ConfigurationAddListener<S> getConfigurationAddListener() {
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
index f1aa32c..0cc4e9f 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
@@ -25,11 +25,14 @@
* Portions Copyright 2007 Sun Microsystems, Inc.
*/
package org.opends.server.admin.server;
+
+
+
import org.opends.messages.Message;
-
-
+import static org.opends.messages.AdminMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
+
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
@@ -40,10 +43,12 @@
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.Configuration;
+import org.opends.server.admin.Constraint;
import org.opends.server.admin.DecodingException;
import org.opends.server.admin.DefaultBehaviorProvider;
import org.opends.server.admin.DefaultBehaviorProviderVisitor;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
+import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
@@ -60,6 +65,7 @@
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ResultCode;
import org.opends.server.util.StaticUtils;
@@ -199,9 +205,6 @@
// Cached managed object between accept/apply call-backs.
private ServerManagedObject<? extends S> cachedManagedObject;
- // The managed object definition.
- private final AbstractManagedObjectDefinition<?, S> d;
-
// The names of entries that this change listener depends on.
private final Set<DN> dependencies;
@@ -216,7 +219,7 @@
private final ConfigurationChangeListener<? super S> listener;
// The managed object path.
- private final ManagedObjectPath<?, ?> path;
+ private final ManagedObjectPath<?, S> path;
@@ -225,17 +228,13 @@
*
* @param path
* The managed object path.
- * @param d
- * The managed object definition.
* @param listener
* The underlying change listener.
*/
- public ConfigChangeListenerAdaptor(ManagedObjectPath<?, ?> path,
- AbstractManagedObjectDefinition<?, S> d,
+ public ConfigChangeListenerAdaptor(ManagedObjectPath<?, S> path,
ConfigurationChangeListener<? super S> listener) {
this.path = path;
this.dn = DNBuilder.create(path);
- this.d = d;
this.listener = listener;
this.cachedManagedObject = null;
@@ -245,6 +244,7 @@
this.dependencies = new HashSet<DN>();
this.dependencyListener = new DependencyConfigChangeListener(dn, this);
+ AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
Visitor.find(path, pd, dependencies);
}
@@ -311,8 +311,28 @@
// listener lists.
cachedManagedObject.setConfigEntry(configEntry);
- return listener.applyConfigurationChange(cachedManagedObject
- .getConfiguration());
+ ConfigChangeResult result = listener
+ .applyConfigurationChange(cachedManagedObject.getConfiguration());
+
+ // Now apply post constraint call-backs.
+ if (result.getResultCode() == ResultCode.SUCCESS) {
+ ManagedObjectDefinition<?, ?> d = cachedManagedObject
+ .getManagedObjectDefinition();
+ for (Constraint constraint : d.getAllConstraints()) {
+ for (ServerConstraintHandler handler : constraint
+ .getServerConstraintHandlers()) {
+ try {
+ handler.performModifyPostCondition(cachedManagedObject);
+ } catch (ConfigException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+ }
+
+ return result;
}
@@ -350,14 +370,42 @@
public boolean configChangeIsAcceptable(ConfigEntry configEntry,
MessageBuilder unacceptableReason, ConfigEntry newConfigEntry) {
try {
- cachedManagedObject = ServerManagedObject.decode(path, d, configEntry,
- newConfigEntry);
+ ServerManagementContext context = ServerManagementContext.getInstance();
+ cachedManagedObject = context.decode(path, configEntry, newConfigEntry);
} catch (DecodingException e) {
- generateUnacceptableReason(e, unacceptableReason);
+ unacceptableReason.append(e.getMessageObject());
return false;
}
List<Message> reasons = new LinkedList<Message>();
+
+ // Enforce any constraints.
+ boolean isAcceptable = true;
+ ManagedObjectDefinition<?, ?> d = cachedManagedObject
+ .getManagedObjectDefinition();
+ for (Constraint constraint : d.getAllConstraints()) {
+ for (ServerConstraintHandler handler : constraint
+ .getServerConstraintHandlers()) {
+ try {
+ if (!handler.isModifyAcceptable(cachedManagedObject, reasons)) {
+ isAcceptable = false;
+ }
+ } catch (ConfigException e) {
+ Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
+ .getMessageObject());
+ reasons.add(message);
+ isAcceptable = false;
+ }
+ }
+ }
+
+ // Give up immediately if a constraint violation occurs.
+ if (!isAcceptable) {
+ generateUnacceptableReason(reasons, unacceptableReason);
+ return false;
+ }
+
+ // Let the change listener decide.
if (listener.isConfigurationChangeAcceptable(cachedManagedObject
.getConfiguration(), reasons)) {
return true;
@@ -390,8 +438,8 @@
if (configEntry != null) {
return configEntry;
} else {
- Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST.
- get(String.valueOf(dn));
+ Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
+ .get(String.valueOf(dn));
ErrorLogger.logError(message);
}
} catch (ConfigException e) {
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
index 962599a..443a278 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
@@ -25,26 +25,34 @@
* Portions Copyright 2007 Sun Microsystems, Inc.
*/
package org.opends.server.admin.server;
-import org.opends.messages.Message;
+import static org.opends.messages.AdminMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
import java.util.LinkedList;
import java.util.List;
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
import org.opends.server.admin.Configuration;
+import org.opends.server.admin.Constraint;
import org.opends.server.admin.DecodingException;
import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
-import org.opends.server.admin.RelationDefinition;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ResultCode;
-import org.opends.messages.MessageBuilder;
+
/**
@@ -56,24 +64,31 @@
* The type of server configuration handled by the delete
* listener.
*/
-final class ConfigDeleteListenerAdaptor<S extends Configuration>
- extends AbstractConfigListenerAdaptor implements
- ConfigDeleteListener {
+final class ConfigDeleteListenerAdaptor<S extends Configuration> extends
+ AbstractConfigListenerAdaptor implements ConfigDeleteListener {
- // The managed object path of the parent.
- private final ManagedObjectPath<?, ?> path;
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ // Cached configuration object between accept/apply callbacks.
+ private S cachedConfiguration;
+
+ // Cached managed object between accept/apply callbacks.
+ private ServerManagedObject<? extends S> cachedManagedObject;
// The instantiable relation.
private final InstantiableRelationDefinition<?, S> instantiableRelation;
- // The optional relation.
- private final OptionalRelationDefinition<?, S> optionalRelation;
-
// The underlying delete listener.
private final ConfigurationDeleteListener<S> listener;
- // Cached configuration object between accept/apply callbacks.
- private S cachedConfiguration;
+ // The optional relation.
+ private final OptionalRelationDefinition<?, S> optionalRelation;
+
+ // The managed object path of the parent.
+ private final ManagedObjectPath<?, ?> path;
@@ -96,6 +111,7 @@
this.instantiableRelation = relation;
this.listener = listener;
this.cachedConfiguration = null;
+ this.cachedManagedObject = null;
}
@@ -119,6 +135,7 @@
this.instantiableRelation = null;
this.listener = listener;
this.cachedConfiguration = null;
+ this.cachedManagedObject = null;
}
@@ -126,8 +143,7 @@
/**
* {@inheritDoc}
*/
- public ConfigChangeResult applyConfigurationDelete(
- ConfigEntry configEntry) {
+ public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
if (optionalRelation != null) {
// Optional managed objects are located directly beneath the
// parent and have a well-defined name. We need to make sure
@@ -142,7 +158,28 @@
// Cached objects are guaranteed to be from previous acceptable
// callback.
- return listener.applyConfigurationDelete(cachedConfiguration);
+ ConfigChangeResult result = listener
+ .applyConfigurationDelete(cachedConfiguration);
+
+ // Now apply post constraint call-backs.
+ if (result.getResultCode() == ResultCode.SUCCESS) {
+ ManagedObjectDefinition<?, ?> d = cachedManagedObject
+ .getManagedObjectDefinition();
+ for (Constraint constraint : d.getAllConstraints()) {
+ for (ServerConstraintHandler handler : constraint
+ .getServerConstraintHandlers()) {
+ try {
+ handler.performDeletePostCondition(cachedManagedObject);
+ } catch (ConfigException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+ }
+
+ return result;
}
@@ -156,11 +193,9 @@
AttributeValue av = dn.getRDN().getAttributeValue(0);
String name = av.getStringValue().trim();
- ManagedObjectPath<?, ?> childPath;
- RelationDefinition<?, S> r;
+ ManagedObjectPath<?, S> childPath;
if (instantiableRelation != null) {
childPath = path.child(instantiableRelation, name);
- r = instantiableRelation;
} else {
// Optional managed objects are located directly beneath the
// parent and have a well-defined name. We need to make sure
@@ -171,21 +206,46 @@
// Doesn't apply to us.
return true;
}
-
- r = optionalRelation;
}
- ServerManagedObject<? extends S> mo;
try {
- mo = ServerManagedObject.decode(childPath, r
- .getChildDefinition(), configEntry);
+ ServerManagementContext context = ServerManagementContext.getInstance();
+ cachedManagedObject = context.decode(childPath, configEntry);
} catch (DecodingException e) {
- generateUnacceptableReason(e, unacceptableReason);
+ unacceptableReason.append(e.getMessageObject());
return false;
}
- cachedConfiguration = mo.getConfiguration();
+ cachedConfiguration = cachedManagedObject.getConfiguration();
List<Message> reasons = new LinkedList<Message>();
+
+ // Enforce any constraints.
+ boolean isAcceptable = true;
+ ManagedObjectDefinition<?, ?> d = cachedManagedObject
+ .getManagedObjectDefinition();
+ for (Constraint constraint : d.getAllConstraints()) {
+ for (ServerConstraintHandler handler : constraint
+ .getServerConstraintHandlers()) {
+ try {
+ if (!handler.isDeleteAcceptable(cachedManagedObject, reasons)) {
+ isAcceptable = false;
+ }
+ } catch (ConfigException e) {
+ Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
+ .getMessageObject());
+ reasons.add(message);
+ isAcceptable = false;
+ }
+ }
+ }
+
+ // Give up immediately if a constraint violation occurs.
+ if (!isAcceptable) {
+ generateUnacceptableReason(reasons, unacceptableReason);
+ return false;
+ }
+
+ // Let the delete listener decide.
if (listener.isConfigurationDeleteAcceptable(cachedConfiguration,
reasons)) {
return true;
@@ -201,8 +261,8 @@
* Get the configuration delete listener associated with this
* adaptor.
*
- * @return Returns the configuration delete listener associated
- * with this adaptor.
+ * @return Returns the configuration delete listener associated with
+ * this adaptor.
*/
ConfigurationDeleteListener<S> getConfigurationDeleteListener() {
return listener;
diff --git a/opends/src/server/org/opends/server/admin/server/ServerConstraintHandler.java b/opends/src/server/org/opends/server/admin/server/ServerConstraintHandler.java
new file mode 100644
index 0000000..652d8f8
--- /dev/null
+++ b/opends/src/server/org/opends/server/admin/server/ServerConstraintHandler.java
@@ -0,0 +1,207 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.Collection;
+
+import org.opends.messages.Message;
+import org.opends.server.config.ConfigException;
+
+
+
+/**
+ * An interface for performing server-side constraint validation.
+ * <p>
+ * Constraints are evaluated immediately before and after write
+ * operations are performed. Server-side constraints are evaluated in
+ * two phases: the first phase determines if the proposed add, delete,
+ * or modification is acceptable according to the constraint. If one
+ * or more constraints fails, the write write operation is refused,
+ * and the client will receive an
+ * <code>OperationRejectedException</code> exception. The second
+ * phase is invoked once the add, delete, or modification request has
+ * been allowed and any changes applied. The second phase gives the
+ * constraint handler a chance to register listener call-backs if
+ * required.
+ * <p>
+ * A server constraint handler must override at least one of the
+ * provided methods.
+ *
+ * @see org.opends.server.admin.Constraint
+ */
+public abstract class ServerConstraintHandler {
+
+ /**
+ * Creates a new server constraint handler.
+ */
+ protected ServerConstraintHandler() {
+ // No implementation required.
+ }
+
+
+
+ /**
+ * 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 managedObject
+ * The new managed object.
+ * @param unacceptableReasons
+ * A list of messages to which error messages should be
+ * added.
+ * @return Returns <code>true</code> if this constraint is
+ * satisfied, or <code>false</code> if it is not.
+ * @throws ConfigException
+ * If an configuration exception prevented this constraint
+ * from being evaluated.
+ */
+ public boolean isAddAcceptable(ServerManagedObject<?> managedObject,
+ Collection<Message> unacceptableReasons) throws ConfigException {
+ return true;
+ }
+
+
+
+ /**
+ * Determines whether or not the existing managed object which is
+ * about to be deleted from the server configuration satisfies this
+ * constraint.
+ * <p>
+ * If the constraint is not satisfied, the implementation must
+ * return <code>false</code> and add a message describing why the
+ * constraint was not satisfied.
+ * <p>
+ * The default implementation is to return <code>true</code>.
+ *
+ * @param managedObject
+ * The managed object which is about to be deleted.
+ * @param unacceptableReasons
+ * A list of messages to which error messages should be
+ * added.
+ * @return Returns <code>true</code> if this constraint is
+ * satisfied, or <code>false</code> if it is not.
+ * @throws ConfigException
+ * If an configuration exception prevented this constraint
+ * from being evaluated.
+ */
+ public boolean isDeleteAcceptable(ServerManagedObject<?> managedObject,
+ Collection<Message> unacceptableReasons) throws ConfigException {
+ 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 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 ConfigException
+ * If an configuration exception prevented this constraint
+ * from being evaluated.
+ */
+ public boolean isModifyAcceptable(ServerManagedObject<?> managedObject,
+ Collection<Message> unacceptableReasons) throws ConfigException {
+ return true;
+ }
+
+
+
+ /**
+ * Perform any add post-condition processing.
+ * <p>
+ * The default implementation is to do nothing.
+ *
+ * @param managedObject
+ * The managed object which was added.
+ * @throws ConfigException
+ * If the post-condition processing fails due to a
+ * configuration exception.
+ */
+ public void performAddPostCondition(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Do nothing.
+ }
+
+
+
+ /**
+ * Perform any delete post-condition processing.
+ * <p>
+ * The default implementation is to do nothing.
+ *
+ * @param managedObject
+ * The managed object which was deleted.
+ * @throws ConfigException
+ * If the post-condition processing fails due to a
+ * configuration exception.
+ */
+ public void performDeletePostCondition(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Do nothing.
+ }
+
+
+
+ /**
+ * Perform any modify post-condition processing.
+ * <p>
+ * The default implementation is to do nothing.
+ *
+ * @param managedObject
+ * The managed object which was modified.
+ * @throws ConfigException
+ * If the post-condition processing fails due to a
+ * configuration exception.
+ */
+ public void performModifyPostCondition(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Do nothing.
+ }
+}
diff --git a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java b/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
index c453cbe..0c527d8 100644
--- a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
+++ b/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -26,55 +26,28 @@
*/
package org.opends.server.admin.server;
-import org.opends.messages.Message;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
-import org.opends.server.admin.AbstractManagedObjectDefinition;
-import org.opends.server.admin.AliasDefaultBehaviorProvider;
+import org.opends.messages.AdminMessages;
+import org.opends.messages.Message;
import org.opends.server.admin.Configuration;
-import org.opends.server.admin.DefaultBehaviorException;
-import org.opends.server.admin.DefaultBehaviorProviderVisitor;
-import org.opends.server.admin.DefinedDefaultBehaviorProvider;
-import org.opends.server.admin.DefinitionDecodingException;
-import org.opends.server.admin.DefinitionResolver;
-import org.opends.server.admin.IllegalPropertyValueException;
-import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.InstantiableRelationDefinition;
-import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectDefinition;
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;
-import org.opends.server.admin.PropertyIsSingleValuedException;
-import org.opends.server.admin.PropertyNotFoundException;
-import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelationDefinition;
-import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.SingletonRelationDefinition;
-import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
-import org.opends.server.admin.DefinitionDecodingException.Reason;
-import org.opends.server.admin.std.meta.RootCfgDefn;
-import org.opends.server.admin.std.server.RootCfg;
-import org.opends.server.api.AttributeValueDecoder;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
@@ -82,12 +55,8 @@
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.messages.AdminMessages;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.AttributeValue;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.DirectoryException;
@@ -102,518 +71,44 @@
PropertyProvider {
/**
- * A default behavior visitor used for retrieving the default values
- * of a property.
- *
- * @param <T>
- * The type of the property.
- */
- private static class DefaultValueFinder<T> implements
- DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
-
- /**
- * Get the default values for the specified property.
- *
- * @param <T>
- * The type of the property.
- * @param p
- * The managed object path of the current managed object.
- * @param pd
- * The property definition.
- * @param newConfigEntry
- * Optional new configuration entry which does not yet
- * exist in the configuration back-end.
- * @return Returns the default values for the specified property.
- * @throws DefaultBehaviorException
- * If the default values could not be retrieved or
- * decoded properly.
- */
- public static <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p,
- PropertyDefinition<T> pd, ConfigEntry newConfigEntry)
- throws DefaultBehaviorException {
- DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
- return v.find(p, pd);
- }
-
- // Any exception that occurred whilst retrieving inherited default
- // values.
- private DefaultBehaviorException exception = null;
-
- // The path of the managed object containing the next property.
- private ManagedObjectPath<?, ?> nextPath = null;
-
- // The next property whose default values were required.
- private PropertyDefinition<T> nextProperty = null;
-
- // Optional new configuration entry which does not yet exist in
- // the configuration back-end.
- private ConfigEntry newConfigEntry;
-
-
-
- // Private constructor.
- private DefaultValueFinder(ConfigEntry newConfigEntry) {
- this.newConfigEntry = newConfigEntry;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public Collection<T> visitAbsoluteInherited(
- AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
- try {
- return getInheritedProperty(d.getManagedObjectPath(), d
- .getManagedObjectDefinition(), d.getPropertyName());
- } catch (DefaultBehaviorException e) {
- exception = e;
- return Collections.emptySet();
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
- return Collections.emptySet();
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
- Void p) {
- Collection<String> stringValues = d.getDefaultValues();
- List<T> values = new ArrayList<T>(stringValues.size());
-
- for (String stringValue : stringValues) {
- try {
- values.add(nextProperty.decodeValue(stringValue));
- } catch (IllegalPropertyValueStringException e) {
- exception = new DefaultBehaviorException(nextProperty, e);
- break;
- }
- }
-
- return values;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public Collection<T> visitRelativeInherited(
- RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
- try {
- return getInheritedProperty(d.getManagedObjectPath(nextPath), d
- .getManagedObjectDefinition(), d.getPropertyName());
- } catch (DefaultBehaviorException e) {
- exception = e;
- return Collections.emptySet();
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
- Void p) {
- return Collections.emptySet();
- }
-
-
-
- // Find the default values for the next path/property.
- private Collection<T> find(ManagedObjectPath<?, ?> p,
- PropertyDefinition<T> pd) throws DefaultBehaviorException {
- nextPath = p;
- nextProperty = pd;
-
- Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
- this, null);
-
- if (exception != null) {
- throw exception;
- }
-
- if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
- throw new DefaultBehaviorException(pd,
- new PropertyIsSingleValuedException(pd));
- }
-
- return values;
- }
-
-
-
- // Get an inherited property value.
- @SuppressWarnings("unchecked")
- private Collection<T> getInheritedProperty(ManagedObjectPath target,
- AbstractManagedObjectDefinition<?, ?> d, String propertyName)
- throws DefaultBehaviorException {
- // First check that the requested type of managed object
- // corresponds to the path.
- AbstractManagedObjectDefinition<?, ?> supr = target
- .getManagedObjectDefinition();
- if (!supr.isParentOf(d)) {
- throw new DefaultBehaviorException(
- nextProperty, new DefinitionDecodingException(supr,
- Reason.WRONG_TYPE_INFORMATION));
- }
-
- // Save the current property in case of recursion.
- PropertyDefinition<T> pd1 = nextProperty;
-
- try {
- // Get the actual managed object definition.
- DN dn = DNBuilder.create(target);
- ConfigEntry configEntry;
- if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
- configEntry = newConfigEntry;
- } else {
- configEntry = getManagedObjectConfigEntry(dn);
- }
-
- DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
- ManagedObjectDefinition<?, ?> mod = d
- .resolveManagedObjectDefinition(resolver);
-
- PropertyDefinition<T> pd2;
- try {
- PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
- pd2 = pd1.getClass().cast(pdTmp);
- } catch (IllegalArgumentException e) {
- throw new PropertyNotFoundException(propertyName);
- } catch (ClassCastException e) {
- // FIXME: would be nice to throw a better exception here.
- throw new PropertyNotFoundException(propertyName);
- }
-
- List<String> stringValues = getAttribute(mod, pd2, configEntry);
- if (stringValues.isEmpty()) {
- // Recursively retrieve this property's default values.
- Collection<T> tmp = find(target, pd2);
- Collection<T> values = new ArrayList<T>(tmp.size());
- for (T value : tmp) {
- pd1.validateValue(value);
- values.add(value);
- }
- return values;
- } else {
- Collection<T> values = new ArrayList<T>(stringValues.size());
- for (String s : stringValues) {
- values.add(pd1.decodeValue(s));
- }
- return values;
- }
- } catch (DefinitionDecodingException e) {
- throw new DefaultBehaviorException(pd1, e);
- } catch (PropertyNotFoundException e) {
- throw new DefaultBehaviorException(pd1, e);
- } catch (IllegalPropertyValueException e) {
- throw new DefaultBehaviorException(pd1, e);
- } catch (IllegalPropertyValueStringException e) {
- throw new DefaultBehaviorException(pd1, e);
- } catch (ConfigException e) {
- throw new DefaultBehaviorException(pd1, e);
- }
- }
- }
-
-
-
- /**
- * A definition resolver that determines the managed object
- * definition from the object classes of a ConfigEntry.
- */
- private static class MyDefinitionResolver implements DefinitionResolver {
-
- // The config entry.
- private final ConfigEntry entry;
-
-
-
- // Private constructor.
- private MyDefinitionResolver(ConfigEntry entry) {
- this.entry = entry;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
- String oc = LDAPProfile.getInstance().getObjectClass(d);
- return entry.hasObjectClass(oc);
- }
- };
-
- /**
- * The root server managed object.
- */
- private static final ServerManagedObject<RootCfg> ROOT =
- new ServerManagedObject<RootCfg>(
- ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections
- .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
-
- /**
* The tracer object for the debug logger.
*/
private static final DebugTracer TRACER = getTracer();
-
-
- /**
- * Decodes a configuration entry into the required type of server
- * managed object.
- *
- * @param <S>
- * The type of server configuration represented by the
- * decoded server managed object.
- * @param path
- * The location of the server managed object.
- * @param definition
- * The required managed object type.
- * @param configEntry
- * The configuration entry that should be decoded.
- * @return Returns the new server-side managed object from the
- * provided definition and configuration entry.
- * @throws DefinitionDecodingException
- * If the managed object's type could not be determined.
- * @throws ServerManagedObjectDecodingException
- * If one or more of the managed object's properties could
- * not be decoded.
- */
- static <S extends Configuration> ServerManagedObject<? extends S> decode(
- ManagedObjectPath<?, ?> path,
- AbstractManagedObjectDefinition<?, S> definition,
- ConfigEntry configEntry) throws DefinitionDecodingException,
- ServerManagedObjectDecodingException {
- return decode(path, definition, configEntry, null);
- }
-
-
-
- /**
- * Decodes a configuration entry into the required type of server
- * managed object.
- *
- * @param <S>
- * The type of server configuration represented by the
- * decoded server managed object.
- * @param path
- * The location of the server managed object.
- * @param definition
- * The required managed object type.
- * @param configEntry
- * The configuration entry that should be decoded.
- * @param newConfigEntry
- * Optional new configuration that does not exist yet in
- * the configuration back-end. This will be used for
- * resolving inherited default values.
- * @return Returns the new server-side managed object from the
- * provided definition and configuration entry.
- * @throws DefinitionDecodingException
- * If the managed object's type could not be determined.
- * @throws ServerManagedObjectDecodingException
- * If one or more of the managed object's properties could
- * not be decoded.
- */
- static <S extends Configuration> ServerManagedObject<? extends S> decode(
- ManagedObjectPath<?, ?> path,
- AbstractManagedObjectDefinition<?, S> definition,
- ConfigEntry configEntry, ConfigEntry newConfigEntry)
- throws DefinitionDecodingException, ServerManagedObjectDecodingException {
- // First determine the correct definition to use for the entry.
- // This could either be the provided definition, or one of its
- // sub-definitions.
- DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
- ManagedObjectDefinition<?, ? extends S> mod = definition
- .resolveManagedObjectDefinition(resolver);
-
- // Build the managed object's properties.
- List<PropertyException> exceptions = new LinkedList<PropertyException>();
- Map<PropertyDefinition<?>, SortedSet<?>> properties =
- new HashMap<PropertyDefinition<?>, SortedSet<?>>();
- for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
- List<String> values = getAttribute(mod, pd, configEntry);
- try {
- decodeProperty(properties, path, pd, values, newConfigEntry);
- } catch (PropertyException e) {
- exceptions.add(e);
- }
- }
-
- // If there were no decoding problems then return the managed
- // object, otherwise throw an operations exception.
- ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties,
- configEntry);
- if (exceptions.isEmpty()) {
- return mo;
- } else {
- throw new ServerManagedObjectDecodingException(mo, exceptions);
- }
- }
-
-
-
- /**
- * Gets the root server managed object.
- *
- * @return Returns the root server managed object.
- */
- static ServerManagedObject<RootCfg> getRootManagedObject() {
- return ROOT;
- }
-
-
-
- // Decode helper method required to avoid generics warning.
- private static <S extends Configuration> ServerManagedObject<S> decodeAux(
- ManagedObjectPath<?, ?> path, ManagedObjectDefinition<?, S> d,
- Map<PropertyDefinition<?>, SortedSet<?>> properties,
- ConfigEntry configEntry) {
- return new ServerManagedObject<S>(path, d, properties, configEntry);
- }
-
-
-
- // Create a property using the provided string values.
- private static <T> void decodeProperty(
- Map<PropertyDefinition<?>, SortedSet<?>> properties,
- ManagedObjectPath<?, ?> path, PropertyDefinition<T> pd,
- List<String> stringValues, ConfigEntry newConfigEntry)
- throws PropertyException {
- PropertyException exception = null;
- SortedSet<T> values = new TreeSet<T>(pd);
-
- if (!stringValues.isEmpty()) {
- // The property has values defined for it.
- for (String value : stringValues) {
- try {
- values.add(pd.decodeValue(value));
- } catch (IllegalPropertyValueStringException e) {
- exception = e;
- }
- }
- } else {
- // No values defined so get the defaults.
- try {
- values.addAll(DefaultValueFinder.getDefaultValues(path, pd,
- newConfigEntry));
- } catch (DefaultBehaviorException e) {
- exception = e;
- }
- }
-
- if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
- // This exception takes precedence over previous exceptions.
- exception = new PropertyIsSingleValuedException(pd);
- T value = values.first();
- values.clear();
- values.add(value);
- }
-
- if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
- // The values maybe empty because of a previous exception.
- if (exception == null) {
- exception = new PropertyIsMandatoryException(pd);
- }
- }
-
- // TODO: If an exception occurs should we leave the property
- // empty?
- properties.put(pd, values);
- if (exception != null) {
- throw exception;
- }
- }
-
-
-
- // Gets the attribute associated with a property from a ConfigEntry.
- private static List<String> getAttribute(ManagedObjectDefinition<?, ?> d,
- PropertyDefinition<?> pd, ConfigEntry configEntry) {
- // TODO: we create a default attribute type if it is
- // undefined. We should log a warning here if this is the case
- // since the attribute should have been defined.
- String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
- AttributeType type = DirectoryServer.getAttributeType(attrID, true);
- AttributeValueDecoder<String> decoder =
- new AttributeValueDecoder<String>() {
-
- public String decode(AttributeValue value) throws DirectoryException {
- return value.getStringValue();
- }
- };
-
- List<String> values = new LinkedList<String>();
- try {
- configEntry.getEntry().getAttributeValues(type, decoder, values);
- } catch (DirectoryException e) {
- // Should not happen.
- throw new RuntimeException(e);
- }
- return values;
- }
-
-
-
- // Gets a config entry required for a managed object and throws a
- // config exception on failure.
- private static ConfigEntry getManagedObjectConfigEntry(DN dn)
- throws ConfigException {
- ConfigEntry configEntry;
- try {
- configEntry = DirectoryServer.getConfigEntry(dn);
- } catch (ConfigException e) {
- if (debugEnabled()) {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = AdminMessages.ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
- String.valueOf(dn), stackTraceToSingleLineString(e));
- throw new ConfigException(message, e);
- }
-
- // The configuration handler is free to return null indicating
- // that the entry does not exist.
- if (configEntry == null) {
- Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST.
- get(String.valueOf(dn));
- throw new ConfigException(message);
- }
-
- return configEntry;
- }
-
// The configuration entry associated with this server managed
// object (null if root).
private ConfigEntry configEntry;
+ // The management context.
+ private final ServerManagementContext context = ServerManagementContext
+ .getInstance();
+
// The managed object's definition.
private final ManagedObjectDefinition<?, S> definition;
// The managed object path identifying this managed object's
// location.
- private final ManagedObjectPath<?, ?> path;
+ private final ManagedObjectPath<?, S> path;
// The managed object's properties.
private final Map<PropertyDefinition<?>, SortedSet<?>> properties;
- // Create an new server side managed object.
- private ServerManagedObject(ManagedObjectPath<?, ?> path,
+ /**
+ * Creates an new server side managed object.
+ *
+ * @param path
+ * The managed object path.
+ * @param d
+ * The managed object definition.
+ * @param properties
+ * The managed object's properties.
+ * @param configEntry
+ * The configuration entry associated with the managed
+ * object.
+ */
+ ServerManagedObject(ManagedObjectPath<?, S> path,
ManagedObjectDefinition<?, S> d,
Map<PropertyDefinition<?>, SortedSet<?>> properties,
ConfigEntry configEntry) {
@@ -764,8 +259,7 @@
InstantiableRelationDefinition<?, M> d, String name)
throws IllegalArgumentException, ConfigException {
validateRelationDefinition(d);
- ManagedObjectPath<?, ?> childPath = path.child(d, name);
- return getChild(childPath, d);
+ return context.getManagedObject(path.child(d, name));
}
@@ -790,8 +284,7 @@
OptionalRelationDefinition<?, M> d) throws IllegalArgumentException,
ConfigException {
validateRelationDefinition(d);
- ManagedObjectPath<?, ?> childPath = path.child(d);
- return getChild(childPath, d);
+ return context.getManagedObject(path.child(d));
}
@@ -816,8 +309,7 @@
SingletonRelationDefinition<?, M> d) throws IllegalArgumentException,
ConfigException {
validateRelationDefinition(d);
- ManagedObjectPath<?, ?> childPath = path.child(d);
- return getChild(childPath, d);
+ return context.getManagedObject(path.child(d));
}
@@ -869,7 +361,7 @@
*
* @return Returns the path of this server managed object.
*/
- public ManagedObjectPath<?, ?> getManagedObjectPath() {
+ public ManagedObjectPath<?, S> getManagedObjectPath() {
return path;
}
@@ -949,15 +441,7 @@
public boolean hasChild(OptionalRelationDefinition<?, ?> d)
throws IllegalArgumentException {
validateRelationDefinition(d);
-
- // Get the configuration entry.
- DN targetDN = DNBuilder.create(path, d);
- try {
- return (getManagedObjectConfigEntry(targetDN) != null);
- } catch (ConfigException e) {
- // Assume it doesn't exist.
- return false;
- }
+ return context.managedObjectExists(path.child(d));
}
@@ -976,30 +460,7 @@
public String[] listChildren(InstantiableRelationDefinition<?, ?> d)
throws IllegalArgumentException {
validateRelationDefinition(d);
-
- // Get the target entry.
- DN targetDN = DNBuilder.create(path, d);
- ConfigEntry configEntry;
- try {
- configEntry = DirectoryServer.getConfigEntry(targetDN);
- } catch (ConfigException e) {
- return new String[0];
- }
-
- if (configEntry == null) {
- return new String[0];
- }
-
- // Retrieve the children.
- Set<DN> children = configEntry.getChildren().keySet();
- ArrayList<String> names = new ArrayList<String>(children.size());
- for (DN child : children) {
- // Assume that RDNs are single-valued and can be trimmed.
- AttributeValue av = child.getRDN().getAttributeValue(0);
- names.add(av.getStringValue().trim());
- }
-
- return names.toArray(new String[names.size()]);
+ return context.listManagedObjects(path, d);
}
@@ -1048,8 +509,8 @@
* If the optional relation definition is not associated
* with this managed object's definition.
* @throws ConfigException
- * If the configuration entry associated with the
- * optional relation could not be retrieved.
+ * If the configuration entry associated with the optional
+ * relation could not be retrieved.
*/
public <M extends Configuration> void registerAddListener(
OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
@@ -1073,7 +534,7 @@
public void registerChangeListener(
ConfigurationChangeListener<? super S> listener) {
ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path,
- definition, listener);
+ listener);
configEntry.registerChangeListener(adaptor);
}
@@ -1123,8 +584,8 @@
* If the optional relation definition is not associated
* with this managed object's definition.
* @throws ConfigException
- * If the configuration entry associated with the
- * optional relation could not be retrieved.
+ * If the configuration entry associated with the optional
+ * relation could not be retrieved.
*/
public <M extends Configuration> void registerDeleteListener(
OptionalRelationDefinition<?, M> d,
@@ -1206,26 +667,6 @@
- // Get a child managed object.
- private <M extends Configuration> ServerManagedObject<? extends M> getChild(
- ManagedObjectPath<?, ?> childPath, RelationDefinition<?, M> d)
- throws ConfigException {
- // Get the configuration entry.
- DN targetDN = DNBuilder.create(childPath);
- ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
- try {
- return decode(childPath, d.getChildDefinition(), configEntry);
- } catch (DefinitionDecodingException e) {
- throw ConfigExceptionFactory.getInstance()
- .createDecodingExceptionAdaptor(targetDN, e);
- } catch (ServerManagedObjectDecodingException e) {
- throw ConfigExceptionFactory.getInstance()
- .createDecodingExceptionAdaptor(e);
- }
- }
-
-
-
// Gets a config entry required for a listener and throws a config
// exception on failure or returns null if the entry does not exist.
private ConfigEntry getListenerConfigEntry(DN dn) throws ConfigException {
@@ -1284,8 +725,8 @@
}
// No parent entry could be found.
- Message message = AdminMessages.ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER.get(
- String.valueOf(baseDN));
+ Message message = AdminMessages.ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER
+ .get(String.valueOf(baseDN));
throw new ConfigException(message);
}
@@ -1313,8 +754,8 @@
// object.
private void validateRelationDefinition(RelationDefinition<?, ?> rd)
throws IllegalArgumentException {
- RelationDefinition<?, ?> tmp =
- definition.getRelationDefinition(rd.getName());
+ RelationDefinition<?, ?> tmp = definition.getRelationDefinition(rd
+ .getName());
if (tmp != rd) {
throw new IllegalArgumentException("The relation " + rd.getName()
+ " is not associated with a " + definition.getName());
diff --git a/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java b/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
index fa303aa..ea2f943 100644
--- a/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
+++ b/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
@@ -29,8 +29,60 @@
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.StaticUtils.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.opends.messages.AdminMessages;
+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.DefaultBehaviorException;
+import org.opends.server.admin.DefaultBehaviorProviderVisitor;
+import org.opends.server.admin.DefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.DefinitionResolver;
+import org.opends.server.admin.IllegalPropertyValueException;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.PropertyIsMandatoryException;
+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.std.meta.RootCfgDefn;
import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.api.AttributeValueDecoder;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
@@ -39,10 +91,255 @@
*/
public final class ServerManagementContext {
+ /**
+ * A default behavior visitor used for retrieving the default values
+ * of a property.
+ *
+ * @param <T>
+ * The type of the property.
+ */
+ private class DefaultValueFinder<T> implements
+ DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
+
+ // Any exception that occurred whilst retrieving inherited default
+ // values.
+ private DefaultBehaviorException exception = null;
+
+ // Optional new configuration entry which does not yet exist in
+ // the configuration back-end.
+ private ConfigEntry newConfigEntry;
+
+ // The path of the managed object containing the next property.
+ private ManagedObjectPath<?, ?> nextPath = null;
+
+ // The next property whose default values were required.
+ private PropertyDefinition<T> nextProperty = null;
+
+
+
+ // Private constructor.
+ private DefaultValueFinder(ConfigEntry newConfigEntry) {
+ this.newConfigEntry = newConfigEntry;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<T> visitAbsoluteInherited(
+ AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
+ try {
+ return getInheritedProperty(d.getManagedObjectPath(), d
+ .getManagedObjectDefinition(), d.getPropertyName());
+ } catch (DefaultBehaviorException e) {
+ exception = e;
+ return Collections.emptySet();
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
+ return Collections.emptySet();
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
+ Void p) {
+ Collection<String> stringValues = d.getDefaultValues();
+ List<T> values = new ArrayList<T>(stringValues.size());
+
+ for (String stringValue : stringValues) {
+ try {
+ values.add(nextProperty.decodeValue(stringValue));
+ } catch (IllegalPropertyValueStringException e) {
+ exception = new DefaultBehaviorException(nextProperty, e);
+ break;
+ }
+ }
+
+ return values;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<T> visitRelativeInherited(
+ RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
+ try {
+ return getInheritedProperty(d.getManagedObjectPath(nextPath), d
+ .getManagedObjectDefinition(), d.getPropertyName());
+ } catch (DefaultBehaviorException e) {
+ exception = e;
+ return Collections.emptySet();
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
+ Void p) {
+ return Collections.emptySet();
+ }
+
+
+
+ // Find the default values for the next path/property.
+ private Collection<T> find(ManagedObjectPath<?, ?> p,
+ PropertyDefinition<T> pd) throws DefaultBehaviorException {
+ nextPath = p;
+ nextProperty = pd;
+
+ Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
+ this, null);
+
+ if (exception != null) {
+ throw exception;
+ }
+
+ if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+ throw new DefaultBehaviorException(pd,
+ new PropertyIsSingleValuedException(pd));
+ }
+
+ return values;
+ }
+
+
+
+ // Get an inherited property value.
+ @SuppressWarnings("unchecked")
+ private Collection<T> getInheritedProperty(ManagedObjectPath target,
+ AbstractManagedObjectDefinition<?, ?> d, String propertyName)
+ throws DefaultBehaviorException {
+ // First check that the requested type of managed object
+ // corresponds to the path.
+ AbstractManagedObjectDefinition<?, ?> supr = target
+ .getManagedObjectDefinition();
+ if (!supr.isParentOf(d)) {
+ throw new DefaultBehaviorException(
+ nextProperty, new DefinitionDecodingException(supr,
+ Reason.WRONG_TYPE_INFORMATION));
+ }
+
+ // Save the current property in case of recursion.
+ PropertyDefinition<T> pd1 = nextProperty;
+
+ try {
+ // Get the actual managed object definition.
+ DN dn = DNBuilder.create(target);
+ ConfigEntry configEntry;
+ if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
+ configEntry = newConfigEntry;
+ } else {
+ configEntry = getManagedObjectConfigEntry(dn);
+ }
+
+ DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+ ManagedObjectDefinition<?, ?> mod = d
+ .resolveManagedObjectDefinition(resolver);
+
+ PropertyDefinition<T> pd2;
+ try {
+ PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
+ pd2 = pd1.getClass().cast(pdTmp);
+ } catch (IllegalArgumentException e) {
+ throw new PropertyNotFoundException(propertyName);
+ } catch (ClassCastException e) {
+ // FIXME: would be nice to throw a better exception here.
+ throw new PropertyNotFoundException(propertyName);
+ }
+
+ List<String> stringValues = getAttribute(mod, pd2, configEntry);
+ if (stringValues.isEmpty()) {
+ // Recursively retrieve this property's default values.
+ Collection<T> tmp = find(target, pd2);
+ Collection<T> values = new ArrayList<T>(tmp.size());
+ for (T value : tmp) {
+ pd1.validateValue(value);
+ values.add(value);
+ }
+ return values;
+ } else {
+ Collection<T> values = new ArrayList<T>(stringValues.size());
+ for (String s : stringValues) {
+ values.add(pd1.decodeValue(s));
+ }
+ return values;
+ }
+ } catch (DefinitionDecodingException e) {
+ throw new DefaultBehaviorException(pd1, e);
+ } catch (PropertyNotFoundException e) {
+ throw new DefaultBehaviorException(pd1, e);
+ } catch (IllegalPropertyValueException e) {
+ throw new DefaultBehaviorException(pd1, e);
+ } catch (IllegalPropertyValueStringException e) {
+ throw new DefaultBehaviorException(pd1, e);
+ } catch (ConfigException e) {
+ throw new DefaultBehaviorException(pd1, e);
+ }
+ }
+ }
+
+
+
+ /**
+ * A definition resolver that determines the managed object
+ * definition from the object classes of a ConfigEntry.
+ */
+ private class MyDefinitionResolver implements DefinitionResolver {
+
+ // The config entry.
+ private final ConfigEntry entry;
+
+
+
+ // Private constructor.
+ private MyDefinitionResolver(ConfigEntry entry) {
+ this.entry = entry;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
+ String oc = LDAPProfile.getInstance().getObjectClass(d);
+ return entry.hasObjectClass(oc);
+ }
+ };
+
// Singleton instance.
private final static ServerManagementContext INSTANCE =
new ServerManagementContext();
+ /**
+ * The root server managed object.
+ */
+ private static final ServerManagedObject<RootCfg> ROOT =
+ new ServerManagedObject<RootCfg>(
+ ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections
+ .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
+
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
/**
@@ -64,6 +361,121 @@
/**
+ * Gets the named managed object.
+ *
+ * @param <C>
+ * The type of client managed object configuration that the
+ * path definition refers to.
+ * @param <S>
+ * The type of server managed object configuration that the
+ * path definition refers to.
+ * @param path
+ * The path of the managed object.
+ * @return Returns the named managed object.
+ * @throws ConfigException
+ * If the named managed object could not be found or if it
+ * could not be decoded.
+ */
+ public <C extends ConfigurationClient, S extends Configuration>
+ ServerManagedObject<? extends S> getManagedObject(
+ ManagedObjectPath<C, S> path) throws ConfigException {
+ // Get the configuration entry.
+ DN targetDN = DNBuilder.create(path);
+ ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
+ try {
+ return decode(path, configEntry);
+ } catch (DefinitionDecodingException e) {
+ throw ConfigExceptionFactory.getInstance()
+ .createDecodingExceptionAdaptor(targetDN, e);
+ } catch (ServerManagedObjectDecodingException e) {
+ throw ConfigExceptionFactory.getInstance()
+ .createDecodingExceptionAdaptor(e);
+ }
+ }
+
+
+
+ /**
+ * 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 PropertyException
+ * If the managed object was found but the requested
+ * property could not be decoded.
+ * @throws ConfigException
+ * If the named managed object could not be found or if it
+ * could not be decoded.
+ */
+ public <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
+ PropertyDefinition<PD> pd) throws IllegalArgumentException,
+ ConfigException, PropertyException {
+ SortedSet<PD> values = getPropertyValues(path, pd);
+ if (values.isEmpty()) {
+ return null;
+ } else {
+ return values.first();
+ }
+ }
+
+
+
+ /**
+ * Gets the effective values of a property in the named managed
+ * object.
+ *
+ * @param <PD>
+ * The type of the property to be retrieved.
+ * @param path
+ * The path of the managed object containing the property.
+ * @param pd
+ * The property to be retrieved.
+ * @return Returns the property's effective values, or an empty set
+ * if there are no values defined.
+ * @throws IllegalArgumentException
+ * If the property definition is not associated with the
+ * referenced managed object's definition.
+ * @throws PropertyException
+ * If the managed object was found but the requested
+ * property could not be decoded.
+ * @throws ConfigException
+ * If the named managed object could not be found or if it
+ * could not be decoded.
+ */
+ public <PD> SortedSet<PD> getPropertyValues(ManagedObjectPath<?, ?> path,
+ PropertyDefinition<PD> pd) throws IllegalArgumentException,
+ ConfigException, PropertyException {
+ DN dn = DNBuilder.create(path);
+ ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
+
+ DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+ AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+ ManagedObjectDefinition<?, ?> mod;
+
+ try {
+ mod = d.resolveManagedObjectDefinition(resolver);
+ } catch (DefinitionDecodingException e) {
+ throw ConfigExceptionFactory.getInstance()
+ .createDecodingExceptionAdaptor(dn, e);
+ }
+
+ List<String> values = getAttribute(mod, pd, configEntry);
+ return decodeProperty(path, pd, values, null);
+ }
+
+
+
+ /**
* Get the root configuration manager associated with this
* management context.
*
@@ -84,6 +496,319 @@
* associated with this management context.
*/
public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
- return ServerManagedObject.getRootManagedObject();
+ return ROOT;
+ }
+
+
+
+ /**
+ * Lists the child managed objects of the named parent managed
+ * object.
+ *
+ * @param <C>
+ * The type of client managed object configuration that the
+ * relation definition refers to.
+ * @param <S>
+ * The type of server managed object configuration that the
+ * relation definition refers to.
+ * @param parent
+ * The path of the parent managed object.
+ * @param rd
+ * The instantiable relation definition.
+ * @return Returns the names of the child managed objects.
+ * @throws IllegalArgumentException
+ * If the relation definition is not associated with the
+ * parent managed object's definition.
+ */
+ public <C extends ConfigurationClient, S extends Configuration>
+ String[] listManagedObjects(
+ ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
+ throws IllegalArgumentException {
+ validateRelationDefinition(parent, rd);
+
+ // Get the target entry.
+ DN targetDN = DNBuilder.create(parent, rd);
+ ConfigEntry configEntry;
+ try {
+ configEntry = DirectoryServer.getConfigEntry(targetDN);
+ } catch (ConfigException e) {
+ return new String[0];
+ }
+
+ if (configEntry == null) {
+ return new String[0];
+ }
+
+ // Retrieve the children.
+ Set<DN> children = configEntry.getChildren().keySet();
+ ArrayList<String> names = new ArrayList<String>(children.size());
+ for (DN child : children) {
+ // Assume that RDNs are single-valued and can be trimmed.
+ AttributeValue av = child.getRDN().getAttributeValue(0);
+ names.add(av.getStringValue().trim());
+ }
+
+ return names.toArray(new String[names.size()]);
+ }
+
+
+
+ /**
+ * Determines whether or not the named managed object exists.
+ *
+ * @param path
+ * The path of the named managed object.
+ * @return Returns <code>true</code> if the named managed object
+ * exists, <code>false</code> otherwise.
+ */
+ public boolean managedObjectExists(ManagedObjectPath<?, ?> path) {
+ // Get the configuration entry.
+ DN targetDN = DNBuilder.create(path);
+ try {
+ return (getManagedObjectConfigEntry(targetDN) != null);
+ } catch (ConfigException e) {
+ // Assume it doesn't exist.
+ return false;
+ }
+ }
+
+
+
+ /**
+ * Decodes a configuration entry into the required type of server
+ * managed object.
+ *
+ * @param <C>
+ * The type of client managed object configuration that the
+ * path definition refers to.
+ * @param <S>
+ * The type of server managed object configuration that the
+ * path definition refers to.
+ * @param path
+ * The location of the server managed object.
+ * @param configEntry
+ * The configuration entry that should be decoded.
+ * @return Returns the new server-side managed object from the
+ * provided definition and configuration entry.
+ * @throws DefinitionDecodingException
+ * If the managed object's type could not be determined.
+ * @throws ServerManagedObjectDecodingException
+ * If one or more of the managed object's properties could
+ * not be decoded.
+ */
+ <C extends ConfigurationClient, S extends Configuration>
+ ServerManagedObject<? extends S> decode(
+ ManagedObjectPath<C, S> path, ConfigEntry configEntry)
+ throws DefinitionDecodingException, ServerManagedObjectDecodingException {
+ return decode(path, configEntry, null);
+ }
+
+
+
+ /**
+ * Decodes a configuration entry into the required type of server
+ * managed object.
+ *
+ * @param <C>
+ * The type of client managed object configuration that the
+ * path definition refers to.
+ * @param <S>
+ * The type of server managed object configuration that the
+ * path definition refers to.
+ * @param path
+ * The location of the server managed object.
+ * @param configEntry
+ * The configuration entry that should be decoded.
+ * @param newConfigEntry
+ * Optional new configuration that does not exist yet in
+ * the configuration back-end. This will be used for
+ * resolving inherited default values.
+ * @return Returns the new server-side managed object from the
+ * provided definition and configuration entry.
+ * @throws DefinitionDecodingException
+ * If the managed object's type could not be determined.
+ * @throws ServerManagedObjectDecodingException
+ * If one or more of the managed object's properties could
+ * not be decoded.
+ */
+ <C extends ConfigurationClient, S extends Configuration>
+ ServerManagedObject<? extends S> decode(
+ ManagedObjectPath<C, S> path, ConfigEntry configEntry,
+ ConfigEntry newConfigEntry) throws DefinitionDecodingException,
+ ServerManagedObjectDecodingException {
+ // First determine the correct definition to use for the entry.
+ // This could either be the provided definition, or one of its
+ // sub-definitions.
+ DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+ AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
+ ManagedObjectDefinition<? extends C, ? extends S> mod = d
+ .resolveManagedObjectDefinition(resolver);
+
+ // Build the managed object's properties.
+ List<PropertyException> exceptions = new LinkedList<PropertyException>();
+ Map<PropertyDefinition<?>, SortedSet<?>> properties =
+ new HashMap<PropertyDefinition<?>, SortedSet<?>>();
+ for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+ List<String> values = getAttribute(mod, pd, configEntry);
+ try {
+ SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
+ properties.put(pd, pvalues);
+ } catch (PropertyException e) {
+ exceptions.add(e);
+ }
+ }
+
+ // If there were no decoding problems then return the managed
+ // object, otherwise throw an operations exception.
+ ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties,
+ configEntry);
+ if (exceptions.isEmpty()) {
+ return mo;
+ } else {
+ throw new ServerManagedObjectDecodingException(mo, exceptions);
+ }
+ }
+
+
+
+ // Decode helper method required to avoid generics warning.
+ private <C extends ConfigurationClient, S extends Configuration>
+ ServerManagedObject<S> decodeAux(
+ ManagedObjectPath<? super C, ? super S> path,
+ ManagedObjectDefinition<C, S> d,
+ Map<PropertyDefinition<?>, SortedSet<?>> properties,
+ ConfigEntry configEntry) {
+ ManagedObjectPath<C, S> newPath = path.asSubType(d);
+ return new ServerManagedObject<S>(newPath, d, properties, configEntry);
+ }
+
+
+
+ // Create a property using the provided string values.
+ private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path,
+ PropertyDefinition<T> pd, List<String> stringValues,
+ ConfigEntry newConfigEntry) throws PropertyException {
+ PropertyException exception = null;
+ SortedSet<T> values = new TreeSet<T>(pd);
+
+ if (!stringValues.isEmpty()) {
+ // The property has values defined for it.
+ for (String value : stringValues) {
+ try {
+ values.add(pd.decodeValue(value));
+ } catch (IllegalPropertyValueStringException e) {
+ exception = e;
+ }
+ }
+ } else {
+ // No values defined so get the defaults.
+ try {
+ values.addAll(getDefaultValues(path, pd, newConfigEntry));
+ } catch (DefaultBehaviorException e) {
+ exception = e;
+ }
+ }
+
+ if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+ // This exception takes precedence over previous exceptions.
+ exception = new PropertyIsSingleValuedException(pd);
+ T value = values.first();
+ values.clear();
+ values.add(value);
+ }
+
+ if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+ // The values maybe empty because of a previous exception.
+ if (exception == null) {
+ exception = new PropertyIsMandatoryException(pd);
+ }
+ }
+
+ if (exception != null) {
+ throw exception;
+ } else {
+ return values;
+ }
+ }
+
+
+
+ // Gets the attribute associated with a property from a ConfigEntry.
+ private List<String> getAttribute(ManagedObjectDefinition<?, ?> d,
+ PropertyDefinition<?> pd, ConfigEntry configEntry) {
+ // TODO: we create a default attribute type if it is
+ // undefined. We should log a warning here if this is the case
+ // since the attribute should have been defined.
+ String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
+ AttributeType type = DirectoryServer.getAttributeType(attrID, true);
+ AttributeValueDecoder<String> decoder =
+ new AttributeValueDecoder<String>() {
+
+ public String decode(AttributeValue value) throws DirectoryException {
+ return value.getStringValue();
+ }
+ };
+
+ List<String> values = new LinkedList<String>();
+ try {
+ configEntry.getEntry().getAttributeValues(type, decoder, values);
+ } catch (DirectoryException e) {
+ // Should not happen.
+ throw new RuntimeException(e);
+ }
+ return values;
+ }
+
+
+
+ // Get the default values for the specified property.
+ private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p,
+ PropertyDefinition<T> pd, ConfigEntry newConfigEntry)
+ throws DefaultBehaviorException {
+ DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
+ return v.find(p, pd);
+ }
+
+
+
+ // Gets a config entry required for a managed object and throws a
+ // config exception on failure.
+ private ConfigEntry getManagedObjectConfigEntry(
+ DN dn) throws ConfigException {
+ ConfigEntry configEntry;
+ try {
+ configEntry = DirectoryServer.getConfigEntry(dn);
+ } catch (ConfigException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message = AdminMessages.ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
+ String.valueOf(dn), stackTraceToSingleLineString(e));
+ throw new ConfigException(message, e);
+ }
+
+ // The configuration handler is free to return null indicating
+ // that the entry does not exist.
+ if (configEntry == null) {
+ Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
+ .get(String.valueOf(dn));
+ throw new ConfigException(message);
+ }
+
+ return configEntry;
+ }
+
+
+
+ // Validate that a relation definition belongs to the path.
+ private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
+ RelationDefinition<?, ?> rd) throws IllegalArgumentException {
+ AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+ RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+ if (tmp != rd) {
+ throw new IllegalArgumentException("The relation " + rd.getName()
+ + " is not associated with a " + d.getName());
+ }
}
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java
index ff8f056..a0c6033 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockConstraint.java
@@ -39,6 +39,7 @@
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerConstraintHandler;
@@ -138,4 +139,13 @@
return Collections.<ClientConstraintHandler> singleton(new Handler());
}
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
+ return Collections.emptySet();
+ }
+
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AdminTestCaseUtils.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AdminTestCaseUtils.java
index 8526388..b65ad39 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AdminTestCaseUtils.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AdminTestCaseUtils.java
@@ -30,8 +30,13 @@
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.SingletonRelationDefinition;
+import org.opends.server.admin.std.meta.RootCfgDefn;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.types.Entry;
@@ -44,6 +49,15 @@
*/
public final class AdminTestCaseUtils {
+ // The relation name which will be used for dummy configurations. A
+ // deliberately obfuscated name is chosen to avoid clashes.
+ private static final String DUMMY_TEST_RELATION = "*dummy*test*relation*";
+
+ // Indicates if the dummy relation profile has been registered.
+ private static boolean isProfileRegistered = false;
+
+
+
// Prevent instantiation.
private AdminTestCaseUtils() {
// No implementation required.
@@ -71,9 +85,9 @@
ConfigEntry configEntry = new ConfigEntry(entry, null);
try {
- ServerManagedObject<? extends S> mo = ServerManagedObject
- .decode(ManagedObjectPath.emptyPath(), definition,
- configEntry);
+ ServerManagementContext context = ServerManagementContext.getInstance();
+ ServerManagedObject<? extends S> mo = context.decode(getPath(definition),
+ configEntry);
return mo.getConfiguration();
} catch (DefinitionDecodingException e) {
throw ConfigExceptionFactory.getInstance()
@@ -83,4 +97,38 @@
.createDecodingExceptionAdaptor(e);
}
}
+
+
+
+ // Construct a dummy path.
+ private synchronized static <C extends ConfigurationClient, S extends Configuration>
+ ManagedObjectPath<C, S> getPath(AbstractManagedObjectDefinition<C, S> d) {
+ if (!isProfileRegistered) {
+ LDAPProfile.Wrapper profile = new LDAPProfile.Wrapper() {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getRelationRDNSequence(RelationDefinition<?, ?> r) {
+ if (r.getName().equals(DUMMY_TEST_RELATION)) {
+ return "cn=dummy configuration,cn=config";
+ } else {
+ return null;
+ }
+ }
+
+ };
+
+ LDAPProfile.getInstance().pushWrapper(profile);
+ isProfileRegistered = true;
+ }
+
+ SingletonRelationDefinition.Builder<C, S> builder =
+ new SingletonRelationDefinition.Builder<C, S>(
+ RootCfgDefn.getInstance(), DUMMY_TEST_RELATION, d);
+ ManagedObjectPath<?, ?> root = ManagedObjectPath.emptyPath();
+ return root.child(builder.getInstance());
+
+ }
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java
new file mode 100644
index 0000000..a182534
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java
@@ -0,0 +1,519 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.List;
+
+import javax.naming.OperationNotSupportedException;
+import javax.naming.ldap.LdapName;
+
+import org.opends.messages.Message;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.AdminTestCase;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.MockLDAPProfile;
+import org.opends.server.admin.TestCfg;
+import org.opends.server.admin.TestChildCfg;
+import org.opends.server.admin.TestChildCfgDefn;
+import org.opends.server.admin.TestParentCfg;
+import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.AddOperation;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ResultCode;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+
+
+/**
+ * Test cases for constraints on the server-side.
+ */
+public final class ConstraintTest extends AdminTestCase {
+
+ // Child DN.
+ private static final String TEST_CHILD_1_DN =
+ "cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config";
+
+
+
+ /**
+ * A test child add listener.
+ */
+ private static class AddListener implements
+ ConfigurationAddListener<TestChildCfg> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) {
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationAddAcceptable(TestChildCfg configuration,
+ List<Message> unacceptableReasons) {
+ return true;
+ }
+
+ }
+
+
+
+ /**
+ * A test child delete listener.
+ */
+ private static class DeleteListener implements
+ ConfigurationDeleteListener<TestChildCfg> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationDelete(
+ TestChildCfg configuration) {
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationDeleteAcceptable(TestChildCfg configuration,
+ List<Message> unacceptableReasons) {
+ return true;
+ }
+
+ }
+
+
+
+ /**
+ * A test child change listener.
+ */
+ private static class ChangeListener implements
+ ConfigurationChangeListener<TestChildCfg> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationChange(
+ TestChildCfg configuration) {
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(TestChildCfg configuration,
+ List<Message> unacceptableReasons) {
+ return true;
+ }
+
+ }
+
+ // Test child 1 LDIF.
+ private static final String[] TEST_CHILD_1 = new String[] {
+ "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
+ "objectclass: top",
+ "objectclass: ds-cfg-virtual-attribute",
+ "cn: test child 1",
+ "ds-cfg-virtual-attribute-enabled: true",
+ "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
+ "ds-cfg-virtual-attribute-type: description",
+ "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real"
+ };
+
+ // Test LDIF.
+ private static final String[] TEST_LDIF = new String[] {
+ // Base entries.
+ "dn: cn=test parents,cn=config",
+ "objectclass: top",
+ "objectclass: ds-cfg-branch",
+ "cn: test parents",
+ "",
+ // Parent 1 - uses default values for
+ // optional-multi-valued-dn-property.
+ "dn: cn=test parent 1,cn=test parents,cn=config",
+ "objectclass: top",
+ "objectclass: ds-cfg-virtual-attribute",
+ "cn: test parent 1",
+ "ds-cfg-virtual-attribute-enabled: true",
+ "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
+ "ds-cfg-virtual-attribute-type: description",
+ "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real",
+ "",
+ // Child base entries.
+ "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config",
+ "objectclass: top",
+ "objectclass: ds-cfg-branch",
+ "cn: test children",
+ "",
+ };
+
+ // JNDI LDAP context.
+ private JNDIDirContextAdaptor adaptor = null;
+
+
+
+ /**
+ * Sets up tests
+ *
+ * @throws Exception
+ * If the server could not be initialized.
+ */
+ @BeforeClass
+ public void setUp() throws Exception {
+ // This test suite depends on having the schema available, so
+ // we'll start the server.
+ TestCaseUtils.startServer();
+ LDAPProfile.getInstance().pushWrapper(new MockLDAPProfile());
+
+ // Add test managed objects.
+ TestCaseUtils.addEntries(TEST_LDIF);
+ }
+
+
+
+ /**
+ * Tears down test environment.
+ *
+ * @throws Exception
+ * If the test entries could not be removed.
+ */
+ @AfterClass
+ public void tearDown() throws Exception {
+ LDAPProfile.getInstance().popWrapper();
+ TestCfg.cleanup();
+
+ // Remove test entries.
+ deleteSubtree("cn=test parents,cn=config");
+ }
+
+
+
+ /**
+ * Tests that an add constraint can succeed.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAddConstraintSuccess() throws Exception {
+ TestParentCfg parent = getParent("test parent 1");
+ AddListener listener = new AddListener();
+ parent.addTestChildAddListener(listener);
+
+ MockConstraint constraint = new MockConstraint(true, false, false);
+ TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+ try {
+ try {
+ // Add the entry.
+ addEntry(ResultCode.SUCCESS, TEST_CHILD_1);
+ } finally {
+ try {
+ deleteSubtree(TEST_CHILD_1_DN);
+ } catch (Exception e) {
+ // Do nothing.
+ }
+ }
+ } finally {
+ TestChildCfgDefn.getInstance().removeConstraint(constraint);
+ parent.removeTestChildAddListener(listener);
+ }
+ }
+
+
+
+ /**
+ * Tests that an add constraint can fail.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAddConstraintFail() throws Exception {
+ TestParentCfg parent = getParent("test parent 1");
+ AddListener listener = new AddListener();
+ parent.addTestChildAddListener(listener);
+
+ MockConstraint constraint = new MockConstraint(false, true, true);
+ TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+ try {
+ try {
+ // Add the entry.
+ addEntry(ResultCode.UNWILLING_TO_PERFORM, TEST_CHILD_1);
+ } finally {
+ try {
+ deleteSubtree(TEST_CHILD_1_DN);
+ } catch (Exception e) {
+ // Do nothing.
+ }
+ }
+ } finally {
+ TestChildCfgDefn.getInstance().removeConstraint(constraint);
+ parent.removeTestChildAddListener(listener);
+ }
+ }
+
+
+
+ /**
+ * Tests that a delete constraint can succeed.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testDeleteConstraintSuccess() throws Exception {
+ TestParentCfg parent = getParent("test parent 1");
+ DeleteListener listener = new DeleteListener();
+ parent.addTestChildDeleteListener(listener);
+
+ MockConstraint constraint = new MockConstraint(false, false, true);
+ TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+ try {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_1);
+
+ // Now delete it - this should trigger the constraint.
+ deleteSubtree(TEST_CHILD_1_DN);
+ } finally {
+ TestChildCfgDefn.getInstance().removeConstraint(constraint);
+ parent.removeTestChildDeleteListener(listener);
+
+ try {
+ // Clean up.
+ deleteSubtree(TEST_CHILD_1_DN);
+ } catch (Exception e) {
+ // Ignore.
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that a delete constraint can fail.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testDeleteConstraintFail() throws Exception {
+ TestParentCfg parent = getParent("test parent 1");
+ DeleteListener listener = new DeleteListener();
+ parent.addTestChildDeleteListener(listener);
+
+ MockConstraint constraint = new MockConstraint(true, true, false);
+ TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+ try {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_1);
+ try {
+ // Now delete it - this should trigger the constraint.
+ deleteSubtree(TEST_CHILD_1_DN);
+
+ // Should not have succeeded.
+ Assert.fail("Delete constraint failed to prevent deletion");
+ } catch (OperationNotSupportedException e) {
+ // Ignore - this is the expected exception.
+ }
+ } finally {
+ TestChildCfgDefn.getInstance().removeConstraint(constraint);
+ parent.removeTestChildDeleteListener(listener);
+
+ try {
+ // Clean up.
+ deleteSubtree(TEST_CHILD_1_DN);
+ } catch (Exception e) {
+ // Ignore.
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that a modify constraint can succeed.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testChangeConstraintSuccess() throws Exception {
+ TestParentCfg parent = getParent("test parent 1");
+
+ MockConstraint constraint = new MockConstraint(false, true, false);
+ TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+ try {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_1);
+ TestChildCfg child = parent.getTestChild("test child 1");
+
+ ChangeListener listener = new ChangeListener();
+ child.addChangeListener(listener);
+
+ // Now modify it.
+ String[] changes = new String[] {
+ "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
+ "changetype: modify",
+ "replace: ds-cfg-virtual-attribute-base-dn",
+ "ds-cfg-virtual-attribute-base-dn: dc=new value 1,dc=com",
+ "ds-cfg-virtual-attribute-base-dn: dc=new value 2,dc=com",
+ "-",
+ "replace: ds-cfg-virtual-attribute-group-dn",
+ "ds-cfg-virtual-attribute-group-dn: dc=new value 3,dc=com",
+ "ds-cfg-virtual-attribute-group-dn: dc=new value 4,dc=com"
+ };
+
+ int result = TestCaseUtils.applyModifications(changes);
+ Assert.assertEquals(result, ResultCode.SUCCESS.getIntValue());
+ } finally {
+ TestChildCfgDefn.getInstance().removeConstraint(constraint);
+ try {
+ deleteSubtree(TEST_CHILD_1_DN);
+ } catch (Exception e) {
+ // Ignore.
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that a modify constraint can fail.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testChangeConstraintFail() throws Exception {
+ TestParentCfg parent = getParent("test parent 1");
+
+ MockConstraint constraint = new MockConstraint(true, false, true);
+ TestChildCfgDefn.getInstance().addConstraint(constraint);
+
+ try {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_1);
+ TestChildCfg child = parent.getTestChild("test child 1");
+
+ ChangeListener listener = new ChangeListener();
+ child.addChangeListener(listener);
+
+ // Now modify it.
+ String[] changes = new String[] {
+ "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
+ "changetype: modify",
+ "replace: ds-cfg-virtual-attribute-base-dn",
+ "ds-cfg-virtual-attribute-base-dn: dc=new value 1,dc=com",
+ "ds-cfg-virtual-attribute-base-dn: dc=new value 2,dc=com",
+ "-",
+ "replace: ds-cfg-virtual-attribute-group-dn",
+ "ds-cfg-virtual-attribute-group-dn: dc=new value 3,dc=com",
+ "ds-cfg-virtual-attribute-group-dn: dc=new value 4,dc=com"
+ };
+
+ int result = TestCaseUtils.applyModifications(changes);
+ Assert
+ .assertEquals(result, ResultCode.UNWILLING_TO_PERFORM.getIntValue());
+ } finally {
+ TestChildCfgDefn.getInstance().removeConstraint(constraint);
+ try {
+ deleteSubtree(TEST_CHILD_1_DN);
+ } catch (Exception e) {
+ // Ignore.
+ }
+ }
+ }
+
+
+
+ // Add an entry and check its result.
+ private void addEntry(ResultCode expected, String... lines) throws Exception {
+ Entry entry = TestCaseUtils.makeEntry(lines);
+
+ InternalClientConnection conn = InternalClientConnection
+ .getRootConnection();
+
+ AddOperation add = conn.processAdd(entry.getDN(), entry.getObjectClasses(),
+ entry.getUserAttributes(), entry.getOperationalAttributes());
+
+ Assert.assertEquals(add.getResultCode(), expected, add.getErrorMessage()
+ .toString());
+ }
+
+
+
+ // Deletes the named sub-tree.
+ private void deleteSubtree(String dn) throws Exception {
+ getAdaptor().deleteSubtree(new LdapName(dn));
+ }
+
+
+
+ // Gets the JNDI connection for the test server instance.
+ private synchronized JNDIDirContextAdaptor getAdaptor() throws Exception {
+ if (adaptor == null) {
+ adaptor = JNDIDirContextAdaptor.simpleBind("127.0.0.1", TestCaseUtils
+ .getServerLdapPort(), "cn=directory manager", "password");
+ }
+ return adaptor;
+ }
+
+
+
+ // Gets the named parent configuration.
+ private TestParentCfg getParent(String name) throws IllegalArgumentException,
+ ConfigException {
+ ServerManagementContext ctx = ServerManagementContext.getInstance();
+ ServerManagedObject<RootCfg> root = ctx.getRootConfigurationManagedObject();
+ TestParentCfg parent = root.getChild(
+ TestCfg.getTestOneToManyParentRelationDefinition(), name)
+ .getConfiguration();
+ return parent;
+ }
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/MockConstraint.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/MockConstraint.java
new file mode 100644
index 0000000..e5d9368
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/MockConstraint.java
@@ -0,0 +1,189 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.Constraint;
+import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.DN;
+import org.testng.Assert;
+
+
+
+/**
+ * A mock constraint which can be configured to refuse various types
+ * of operation.
+ */
+public final class MockConstraint implements Constraint {
+
+ /**
+ * Mock server constraint handler.
+ */
+ private class Handler extends ServerConstraintHandler {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAddAcceptable(ServerManagedObject<?> managedObject,
+ Collection<Message> unacceptableReasons) throws ConfigException {
+ if (!allowAdds) {
+ unacceptableReasons.add(Message.raw("Adds not allowed"));
+ }
+
+ return allowAdds;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDeleteAcceptable(ServerManagedObject<?> managedObject,
+ Collection<Message> unacceptableReasons) throws ConfigException {
+ if (!allowDeletes) {
+ unacceptableReasons.add(Message.raw("Deletes not allowed"));
+ }
+
+ return allowDeletes;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isModifyAcceptable(ServerManagedObject<?> managedObject,
+ Collection<Message> unacceptableReasons) throws ConfigException {
+ if (!allowModifies) {
+ unacceptableReasons.add(Message.raw("Modifies not allowed"));
+ }
+
+ return allowModifies;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void performAddPostCondition(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Make sure that the associated config entry exists.
+ DN targetDN = managedObject.getDN();
+ ConfigEntry configEntry = DirectoryServer.getConfigEntry(targetDN);
+ Assert.assertNotNull(configEntry);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void performDeletePostCondition(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Make sure that the associated config entry does not exist.
+ DN targetDN = managedObject.getDN();
+ ConfigEntry configEntry = DirectoryServer.getConfigEntry(targetDN);
+ Assert.assertNull(configEntry);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void performModifyPostCondition(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Make sure that the associated config entry exists.
+ DN targetDN = managedObject.getDN();
+ ConfigEntry configEntry = DirectoryServer.getConfigEntry(targetDN);
+ Assert.assertNotNull(configEntry);
+ }
+
+ }
+
+ // 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.emptySet();
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
+ return Collections.<ServerConstraintHandler> singleton(new Handler());
+ }
+
+}
--
Gitblit v1.10.0