From 2ba8e81b3d42cdacb4bf15d77ca681660595ee46 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Tue, 11 Sep 2007 23:37:29 +0000
Subject: [PATCH] Partial fix for issue 1449: improve server-side referential integrity support.
---
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java | 761 ++++++++++++++++++++++++++++++++++-----
opendj-sdk/opends/src/messages/messages/admin.properties | 16
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java | 14
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java | 2
opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java | 307 +++++++++++++++
5 files changed, 979 insertions(+), 121 deletions(-)
diff --git a/opendj-sdk/opends/src/messages/messages/admin.properties b/opendj-sdk/opends/src/messages/messages/admin.properties
index 5089f2e..3b03a62 100644
--- a/opendj-sdk/opends/src/messages/messages/admin.properties
+++ b/opendj-sdk/opends/src/messages/messages/admin.properties
@@ -260,4 +260,18 @@
SEVERE_ERR_CONSTRAINT_VIOLATION_EXCEPTION_PLURAL_113=The following \
constraint violations occurred: %s
SEVERE_ERR_SERVER_REFINT_DANGLING_REFERENCE_114=The value "%s" in \
- property "%s" in the %s in entry "%s" refers to a non-existent %s at "%s"
\ No newline at end of file
+ property "%s" in the %s in entry "%s" refers to a non-existent %s \
+ in entry "%s"
+SEVERE_ERR_SERVER_REFINT_SOURCE_ENABLED_TARGET_DISABLED_115=The value \
+ "%s" in property "%s" in the enabled %s in entry "%s" refers to a \
+ disabled %s in entry "%s"
+SEVERE_ERR_SERVER_REFINT_TARGET_DISABLED_116=The value "%s" in \
+ property "%s" in the %s in entry "%s" refers to a disabled %s in \
+ entry "%s"
+SEVERE_ERR_SERVER_REFINT_CANNOT_DELETE_117=The %s in entry "%s" \
+ cannot be deleted because it is referenced by the "%s" property \
+ of the %s in entry "%s"
+SEVERE_ERR_SERVER_REFINT_CANNOT_DISABLE_118=The %s in entry "%s" \
+ cannot be disabled because it is referenced by the "%s" property \
+ of the %s in entry "%s"
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java b/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
index 77b4046..f06b6c3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
@@ -34,14 +34,21 @@
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import java.util.SortedSet;
import org.opends.messages.Message;
import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerConstraintHandler;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.config.ConfigException;
+import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
@@ -285,11 +292,124 @@
/**
+ * A change listener which prevents the named component from being
+ * disabled.
+ */
+ private class ReferentialIntegrityChangeListener implements
+ ConfigurationChangeListener<S> {
+
+ // The error message which should be returned if an attempt is
+ // made to disable the referenced component.
+ private final Message message;
+
+ // The path of the referenced component.
+ private final ManagedObjectPath<C, S> path;
+
+
+
+ // Creates a new referential integrity delete listener.
+ private ReferentialIntegrityChangeListener(ManagedObjectPath<C, S> path,
+ Message message) {
+ this.path = path;
+ this.message = message;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationChange(S configuration) {
+ throw new IllegalStateException("Attempting to disable a referenced "
+ + configuration.definition().getUserFriendlyName());
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(S configuration,
+ List<Message> unacceptableReasons) {
+ // Always prevent the referenced component from being
+ // disabled.
+ PropertyProvider provider = configuration.properties();
+ Collection<Boolean> values = provider
+ .getPropertyValues(getTargetEnabledPropertyDefinition());
+ if (values.iterator().next() == false) {
+ unacceptableReasons.add(message);
+ return false;
+ }
+ return true;
+ }
+
+
+
+ // Gets the path associated with this listener.
+ private ManagedObjectPath<C, S> getManagedObjectPath() {
+ return path;
+ }
+
+ }
+
+
+
+ /**
+ * A delete listener which prevents the named component from being
+ * deleted.
+ */
+ private class ReferentialIntegrityDeleteListener implements
+ ConfigurationDeleteListener<S> {
+
+ // The DN of the referenced configuration entry.
+ private final DN dn;
+
+ // The error message which should be returned if an attempt is
+ // made to delete the referenced component.
+ private final Message message;
+
+
+
+ // Creates a new referential integrity delete listener.
+ private ReferentialIntegrityDeleteListener(DN dn, Message message) {
+ this.dn = dn;
+ this.message = message;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationDelete(S configuration) {
+ throw new IllegalStateException("Attempting to delete a referenced "
+ + configuration.definition().getUserFriendlyName());
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationDeleteAcceptable(S configuration,
+ List<Message> unacceptableReasons) {
+ if (configuration.dn().equals(dn)) {
+ // Always prevent deletion of the referenced component.
+ unacceptableReasons.add(message);
+ return false;
+ }
+
+ return true;
+ }
+
+ }
+
+
+
+ /**
* The server-side constraint handler implementation.
*/
- private static class ServerHandler
- <C extends ConfigurationClient, S extends Configuration>
- extends ServerConstraintHandler {
+ private class ServerHandler extends ServerConstraintHandler {
// The associated property definition.
private final AggregationPropertyDefinition<C, S> pd;
@@ -311,23 +431,178 @@
Collection<Message> unacceptableReasons) throws ConfigException {
SortedSet<String> names = managedObject.getPropertyValues(pd);
ServerManagementContext context = ServerManagementContext.getInstance();
- boolean isUsable = true;
+ BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
+ BooleanPropertyDefinition spd = pd.getSourceEnabledPropertyDefinition();
+ Message thisUFN = managedObject.getManagedObjectDefinition()
+ .getUserFriendlyName();
+ String thisDN = managedObject.getDN().toString();
+ Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
+ boolean isUsable = true;
for (String name : names) {
ManagedObjectPath<C, S> path = pd.getChildPath(name);
+ String thatDN = path.toDN().toString();
+
if (!context.managedObjectExists(path)) {
Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name, pd
- .getName(), managedObject.getManagedObjectDefinition()
- .getUserFriendlyName(), managedObject.getDN().toString(), pd
- .getRelationDefinition().getUserFriendlyName(), path.toDN()
- .toString());
+ .getName(), thisUFN, thisDN, thatUFN, thatDN);
unacceptableReasons.add(msg);
isUsable = false;
+ } else if (tpd != null) {
+ // Check that the referenced component is enabled.
+ ServerManagedObject<? extends S> ref = context.getManagedObject(path);
+
+ if (spd != null) {
+ // Target must be enabled but only if the source is
+ // enabled.
+ if (managedObject.getPropertyValue(spd)
+ && !ref.getPropertyValue(tpd)) {
+ Message msg = ERR_SERVER_REFINT_SOURCE_ENABLED_TARGET_DISABLED
+ .get(name, pd.getName(), thisUFN, thisDN, thatUFN, thatDN);
+ unacceptableReasons.add(msg);
+ isUsable = false;
+ }
+ } else {
+ // Target must always be enabled.
+ if (!ref.getPropertyValue(tpd)) {
+ Message msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name, pd
+ .getName(), thisUFN, thisDN, thatUFN, thatDN);
+ unacceptableReasons.add(msg);
+ isUsable = false;
+ }
+ }
}
}
return isUsable;
}
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void performPostAdd(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // First make sure existing listeners associated with this
+ // managed object are removed. This is required in order to
+ // prevent multiple change listener registrations from
+ // occurring, for example if this call-back is invoked multiple
+ // times after the same add event.
+ performPostDelete(managedObject);
+
+ // Add change and delete listeners against all referenced
+ // components.
+ BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
+ BooleanPropertyDefinition spd = pd.getSourceEnabledPropertyDefinition();
+ Message thisUFN = managedObject.getManagedObjectDefinition()
+ .getUserFriendlyName();
+ String thisDN = managedObject.getDN().toString();
+ Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
+
+ // Referenced managed objects will only need a change listener
+ // if they have can be disabled.
+ boolean needsChangeListeners;
+ if (tpd != null) {
+ if (spd == null) {
+ needsChangeListeners = true;
+ } else {
+ needsChangeListeners = managedObject.getPropertyValue(spd);
+ }
+ } else {
+ needsChangeListeners = false;
+ }
+
+ // Delete listeners need to be registered against the parent
+ // entry of the referenced components.
+ ServerManagementContext context = ServerManagementContext.getInstance();
+ ManagedObjectPath<?, ?> parentPath = pd.getParentPath();
+ ServerManagedObject<?> parent = context.getManagedObject(parentPath);
+
+ // Create entries in the listener tables.
+ List<ReferentialIntegrityDeleteListener> dlist =
+ new LinkedList<ReferentialIntegrityDeleteListener>();
+ deleteListeners.put(managedObject.getDN(), dlist);
+
+ List<ReferentialIntegrityChangeListener> clist =
+ new LinkedList<ReferentialIntegrityChangeListener>();
+ changeListeners.put(managedObject.getDN(), clist);
+
+ for (String name : managedObject.getPropertyValues(pd)) {
+ ManagedObjectPath<C, S> path = pd.getChildPath(name);
+ DN dn = path.toDN();
+ String thatDN = dn.toString();
+
+ // Register the delete listener.
+ Message msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN, pd
+ .getName(), thisUFN, thisDN);
+ ReferentialIntegrityDeleteListener dl =
+ new ReferentialIntegrityDeleteListener(dn, msg);
+ parent.registerDeleteListener(pd.getRelationDefinition(), dl);
+ dlist.add(dl);
+
+ // Register the change listener if required.
+ if (needsChangeListeners) {
+ ServerManagedObject<? extends S> ref = context.getManagedObject(path);
+ msg = ERR_SERVER_REFINT_CANNOT_DISABLE.get(thatUFN, thatDN, pd
+ .getName(), thisUFN, thisDN);
+ ReferentialIntegrityChangeListener cl =
+ new ReferentialIntegrityChangeListener(path, msg);
+ ref.registerChangeListener(cl);
+ clist.add(cl);
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void performPostDelete(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Remove any registered delete and change listeners.
+ ServerManagementContext context = ServerManagementContext.getInstance();
+ DN dn = managedObject.getDN();
+
+ // Delete listeners need to be deregistered against the parent
+ // entry of the referenced components.
+ ManagedObjectPath<?, ?> parentPath = pd.getParentPath();
+ ServerManagedObject<?> parent = context.getManagedObject(parentPath);
+ if (deleteListeners.containsKey(dn)) {
+ for (ReferentialIntegrityDeleteListener dl : deleteListeners.get(dn)) {
+ parent.deregisterDeleteListener(pd.getRelationDefinition(), dl);
+ }
+ deleteListeners.remove(dn);
+ }
+
+ // Change listeners need to be deregistered from their
+ // associated referenced component.
+ if (changeListeners.containsKey(dn)) {
+ for (ReferentialIntegrityChangeListener cl : changeListeners.get(dn)) {
+ ManagedObjectPath<C, S> path = cl.getManagedObjectPath();
+ ServerManagedObject<? extends S> ref = context.getManagedObject(path);
+ ref.deregisterChangeListener(cl);
+ }
+ changeListeners.remove(dn);
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void performPostModify(ServerManagedObject<?> managedObject)
+ throws ConfigException {
+ // Remove all the constraints associated with this managed
+ // object and then re-register them.
+ performPostDelete(managedObject);
+ performPostAdd(managedObject);
+ }
}
@@ -349,11 +624,23 @@
* @return Returns the new aggregation property definition builder.
*/
public static <C extends ConfigurationClient, S extends Configuration>
- Builder<C, S> createBuilder(
+ Builder<C, S> createBuilder(
AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
return new Builder<C, S>(d, propertyName);
}
+ // The active server-side referential integrity change listeners
+ // associated with this property.
+ private final Map<DN, List<ReferentialIntegrityChangeListener>>
+ changeListeners =
+ new HashMap<DN, List<ReferentialIntegrityChangeListener>>();
+
+ // The active server-side referential integrity delete listeners
+ // associated with this property.
+ private final Map<DN, List<ReferentialIntegrityDeleteListener>>
+ deleteListeners =
+ new HashMap<DN, List<ReferentialIntegrityDeleteListener>>();
+
// The name of the managed object which is the parent of the
// aggregated managed objects.
private final ManagedObjectPath<?, ?> parentPath;
@@ -502,7 +789,7 @@
* {@inheritDoc}
*/
public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
- ServerConstraintHandler handler = new ServerHandler<C, S>(this);
+ ServerConstraintHandler handler = new ServerHandler(this);
return Collections.singleton(handler);
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java
index 8effbd0..7a05390 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java
@@ -81,7 +81,7 @@
return "ds-cfg-virtual-attribute-base-dn";
} else if (pd == (PropertyDefinition<?>)td.getOptionalMultiValuedDNProperty2PropertyDefinition()) {
return "ds-cfg-virtual-attribute-group-dn";
- } else if (pd == (PropertyDefinition<?>)td.getAggregationPropertyPropertyDefinition()) {
+ } else if (pd.getName().equals("aggregation-property")) {
return "ds-cfg-backend-base-dn";
} else {
throw new RuntimeException("Unexpected test-child property"
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java
index 53e2e37..82c1a1a 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java
@@ -228,6 +228,20 @@
/**
+ * Adds a property definition temporarily with test child
+ * definition, replacing any existing property definition with the
+ * same name.
+ *
+ * @param pd
+ * The property definition.
+ */
+ public static void addPropertyDefinition(PropertyDefinition<?> pd) {
+ TestChildCfgDefn.getInstance().registerPropertyDefinition(pd);
+ }
+
+
+
+ /**
* Removes a constraint from the test child definition.
*
* @param constraint
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
index 78048c0..502eeb2 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
@@ -28,7 +28,9 @@
+import java.net.ServerSocket;
import java.util.Collection;
+import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -37,16 +39,31 @@
import org.opends.messages.Message;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
+import org.opends.server.admin.AdministratorAction;
+import org.opends.server.admin.AggregationPropertyDefinition;
import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfg;
+import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
+import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
+import org.opends.server.admin.std.client.ConnectionHandlerCfgClient;
+import org.opends.server.admin.std.client.LDAPConnectionHandlerCfgClient;
+import org.opends.server.admin.std.client.RootCfgClient;
+import org.opends.server.admin.std.meta.ConnectionHandlerCfgDefn;
+import org.opends.server.admin.std.meta.LDAPConnectionHandlerCfgDefn;
+import org.opends.server.admin.std.server.ConnectionHandlerCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.ResultCode;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -57,9 +74,70 @@
/**
* Test cases for aggregations on the server-side.
*/
-@Test(sequential=true)
+@Test(sequential = true)
public final class AggregationTest extends AdminTestCase {
+ /**
+ * Dummy change listener for triggering change constraint
+ * call-backs.
+ */
+ private static final class DummyChangeListener 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;
+ }
+ }
+
+
+
+ /**
+ * Dummy delete listener for triggering delete constraint
+ * call-backs.
+ */
+ private static final class DummyDeleteListener 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;
+ }
+ }
+
+ private static final String TEST_CHILD_7_DN = "cn=test child 7,cn=test children,cn=test parent 1,cn=test parents,cn=config";
+
+ private static final String TEST_CHILD_6_DN = "cn=test child 6,cn=test children,cn=test parent 1,cn=test parents,cn=config";
+
+ // The name of the test connection handler.
+ private static final String TEST_CONNECTION_HANDLER_NAME = "Test Connection Handler";
+
// Test child 1 LDIF.
private static final String[] TEST_CHILD_1 = new String[] {
"dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
@@ -72,17 +150,6 @@
"ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real"
};
-
-
- // Assert that the values of child 1 are correct.
- private void assertChild1(TestChildCfg child) {
- Assert.assertEquals(child.getMandatoryClassProperty(),
- "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
- Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
- DirectoryServer.getAttributeType("description"));
- assertSetEquals(child.getAggregationProperty(), new String[0]);
- }
-
// Test child 2 LDIF.
private static final String[] TEST_CHILD_2 = new String[] {
"dn: cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config",
@@ -96,23 +163,6 @@
"ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=connection handlers, cn=config"
};
-
-
- // Assert that the values of child 2 are correct.
- private void assertChild2(TestChildCfg child) {
- Assert.assertEquals(child.getMandatoryClassProperty(),
- "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
- Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
- DirectoryServer.getAttributeType("description"));
-
- // Test normalization.
- assertSetEquals(child.getAggregationProperty(), "LDAP Connection Handler");
- assertSetEquals(child.getAggregationProperty(),
- " LDAP Connection Handler ");
- assertSetEquals(child.getAggregationProperty(),
- " ldap connection HANDLER ");
- }
-
// Test child 3 LDIF (invalid reference).
private static final String[] TEST_CHILD_3 = new String[] {
"dn: cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config",
@@ -140,20 +190,6 @@
"ds-cfg-backend-base-dn: cn=LDAPS Connection Handler, cn=connection handlers, cn=config"
};
-
-
- // Assert that the values of child 4 are correct.
- private void assertChild4(TestChildCfg child) {
- Assert.assertEquals(child.getMandatoryClassProperty(),
- "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
- Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
- DirectoryServer.getAttributeType("description"));
- assertSetEquals(child.getAggregationProperty(), "LDAPS Connection Handler",
- "LDAP Connection Handler");
- }
-
-
-
// Test child 5 LDIF.
private static final String[] TEST_CHILD_5 = new String[] {
"dn: cn=test child 5,cn=test children,cn=test parent 1,cn=test parents,cn=config",
@@ -169,6 +205,34 @@
"ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=connection handlers, cn=config"
};
+ // Test child 6 LDIF.
+ private static final String[] TEST_CHILD_6 = new String[] {
+ "dn: cn=test child 6,cn=test children,cn=test parent 1,cn=test parents,cn=config",
+ "objectclass: top",
+ "objectclass: ds-cfg-test-child-dummy",
+ "cn: test child 6",
+ "ds-cfg-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",
+ "ds-cfg-backend-base-dn: cn=" + TEST_CONNECTION_HANDLER_NAME
+ + ", cn=connection handlers, cn=config"
+ };
+
+ // Test child 7 LDIF.
+ private static final String[] TEST_CHILD_7 = new String[] {
+ "dn: cn=test child 7,cn=test children,cn=test parent 1,cn=test parents,cn=config",
+ "objectclass: top",
+ "objectclass: ds-cfg-test-child-dummy",
+ "cn: test child 7",
+ "ds-cfg-virtual-attribute-enabled: false",
+ "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",
+ "ds-cfg-backend-base-dn: cn=" + TEST_CONNECTION_HANDLER_NAME
+ + ", cn=connection handlers, cn=config"
+ };
+
// Test LDIF.
private static final String[] TEST_LDIF = new String[] {
// Base entries.
@@ -198,6 +262,17 @@
// JNDI LDAP context.
private JNDIDirContextAdaptor adaptor = null;
+ // The saved test child configuration "aggregation-property"
+ // property definition.
+ private AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg> aggregationPropertyDefinitionDefault = null;
+
+ // An aggregation where the target must be enabled if the source is
+ // enabled.
+ private AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg> aggregationPropertyDefinitionTargetAndSourceMustBeEnabled = null;
+
+ // An aggregation where the target must be enabled.
+ private AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg> aggregationPropertyDefinitionTargetMustBeEnabled = null;
+
/**
@@ -215,6 +290,42 @@
// Add test managed objects.
TestCaseUtils.addEntries(TEST_LDIF);
+
+ // Save the aggregation property definition so that it can be
+ // replaced and restored later.
+ aggregationPropertyDefinitionDefault = TestChildCfgDefn.getInstance()
+ .getAggregationPropertyPropertyDefinition();
+
+ // Create the two test aggregation properties.
+ AggregationPropertyDefinition.Builder<ConnectionHandlerCfgClient, ConnectionHandlerCfg> builder;
+ TestChildCfgDefn d = TestChildCfgDefn.getInstance();
+ builder = AggregationPropertyDefinition.createBuilder(d,
+ "aggregation-property");
+ builder.setOption(PropertyOption.MULTI_VALUED);
+ builder.setAdministratorAction(new AdministratorAction(
+ AdministratorAction.Type.NONE, d, "aggregation-property"));
+ builder
+ .setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider<String>());
+ builder.setParentPath(ManagedObjectPath.valueOf("/"));
+ builder.setRelationDefinition("connection-handler");
+ builder.setManagedObjectDefinition(ConnectionHandlerCfgDefn.getInstance());
+ builder.setTargetEnabledPropertyName("enabled");
+ aggregationPropertyDefinitionTargetMustBeEnabled = builder.getInstance();
+
+ builder = AggregationPropertyDefinition.createBuilder(d,
+ "aggregation-property");
+ builder.setOption(PropertyOption.MULTI_VALUED);
+ builder.setAdministratorAction(new AdministratorAction(
+ AdministratorAction.Type.NONE, d, "aggregation-property"));
+ builder
+ .setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider<String>());
+ builder.setParentPath(ManagedObjectPath.valueOf("/"));
+ builder.setRelationDefinition("connection-handler");
+ builder.setManagedObjectDefinition(ConnectionHandlerCfgDefn.getInstance());
+ builder.setTargetEnabledPropertyName("enabled");
+ builder.setSourceEnabledPropertyName("mandatory-boolean-property");
+ aggregationPropertyDefinitionTargetAndSourceMustBeEnabled = builder
+ .getInstance();
}
@@ -229,6 +340,9 @@
public void tearDown() throws Exception {
TestCfg.cleanup();
+ // Restore the test child aggregation definition.
+ TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
+
// Remove test entries.
deleteSubtree("cn=test parents,cn=config");
}
@@ -236,50 +350,6 @@
/**
- * Tests that aggregation contains no values when it
- * contains does not contain any DN attribute values.
- *
- * @throws Exception
- * If the test unexpectedly fails.
- */
- @Test
- public void testAggregationEmpty() throws Exception {
- // Add the entry.
- TestCaseUtils.addEntry(TEST_CHILD_1);
-
- try {
- TestParentCfg parent = getParent("test parent 1");
- assertChild1(parent.getTestChild("test child 1"));
- } finally {
- deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
- }
- }
-
-
-
- /**
- * Tests that aggregation contains single valid value when it
- * contains a single valid DN attribute values.
- *
- * @throws Exception
- * If the test unexpectedly fails.
- */
- @Test
- public void testAggregationSingle() throws Exception {
- // Add the entry.
- TestCaseUtils.addEntry(TEST_CHILD_2);
-
- try {
- TestParentCfg parent = getParent("test parent 1");
- assertChild2(parent.getTestChild("test child 2"));
- } finally {
- deleteSubtree("cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
- }
- }
-
-
-
- /**
* Tests that aggregation is rejected when the LDAP DN contains a
* valid RDN but an invalid parent DN.
*
@@ -329,28 +399,6 @@
/**
- * Tests that aggregation contains multiple valid values when it
- * contains a multiple valid DN attribute values.
- *
- * @throws Exception
- * If the test unexpectedly fails.
- */
- @Test
- public void testAggregationMultipleValues() throws Exception {
- // Add the entry.
- TestCaseUtils.addEntry(TEST_CHILD_4);
-
- try {
- TestParentCfg parent = getParent("test parent 1");
- assertChild4(parent.getTestChild("test child 4"));
- } finally {
- deleteSubtree("cn=test child 4,cn=test children,cn=test parent 1,cn=test parents,cn=config");
- }
- }
-
-
-
- /**
* Tests that aggregation is rejected by a constraint violation when
* the DN values are dangling.
*
@@ -385,6 +433,476 @@
+ /**
+ * Tests that aggregation is rejected by a constraint violation when
+ * an enabled component references a disabled component and the
+ * referenced component must always be enabled.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAggregationDisabledReference1() throws Exception {
+ // Add the entry and the connection handler.
+ TestCaseUtils.addEntry(TEST_CHILD_6);
+ try {
+ createConnectionHandler(false);
+ } catch (Exception e) {
+ deleteSubtree(TEST_CHILD_6_DN);
+ throw e;
+ }
+
+ // Register the temporary aggregation definition.
+ TestCfg.removeConstraint(aggregationPropertyDefinitionDefault);
+ TestCfg
+ .addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
+ TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled);
+
+ try {
+ TestParentCfg parent = getParent("test parent 1");
+ parent.getTestChild("test child 6");
+ Assert
+ .fail("Unexpectedly added test child 6 when it had a disabled reference");
+ } catch (ConfigException e) {
+ // Check that we have a constraint violation as the cause.
+ Throwable cause = e.getCause();
+ if (cause instanceof ConstraintViolationException) {
+ ConstraintViolationException cve = (ConstraintViolationException) cause;
+ Collection<Message> causes = cve.getMessages();
+ Assert.assertEquals(causes.size(), 1);
+ } else {
+ // Got an unexpected cause.
+ throw e;
+ }
+ } finally {
+ // Put back the default aggregation definition.
+ TestCfg
+ .removeConstraint(aggregationPropertyDefinitionTargetMustBeEnabled);
+ TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
+ TestCfg.addConstraint(aggregationPropertyDefinitionDefault);
+
+ try {
+ deleteSubtree(TEST_CHILD_6_DN);
+ } finally {
+ deleteConnectionHandler();
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that aggregation is rejected by a constraint violation when
+ * a disabled component references a disabled component and the
+ * referenced component must always be enabled.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAggregationDisabledReference2() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_7);
+ try {
+ createConnectionHandler(false);
+ } catch (Exception e) {
+ deleteSubtree(TEST_CHILD_7_DN);
+ throw e;
+ }
+
+ // Register the temporary aggregation definition.
+ TestCfg.removeConstraint(aggregationPropertyDefinitionDefault);
+ TestCfg
+ .addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
+ TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled);
+
+ try {
+ TestParentCfg parent = getParent("test parent 1");
+ parent.getTestChild("test child 7");
+ Assert
+ .fail("Unexpectedly added test child 7 when it had a disabled reference");
+ } catch (ConfigException e) {
+ // Check that we have a constraint violation as the cause.
+ Throwable cause = e.getCause();
+ if (cause instanceof ConstraintViolationException) {
+ ConstraintViolationException cve = (ConstraintViolationException) cause;
+ Collection<Message> causes = cve.getMessages();
+ Assert.assertEquals(causes.size(), 1);
+ } else {
+ // Got an unexpected cause.
+ throw e;
+ }
+ } finally {
+ // Put back the default aggregation definition.
+ TestCfg
+ .removeConstraint(aggregationPropertyDefinitionTargetMustBeEnabled);
+ TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
+ TestCfg.addConstraint(aggregationPropertyDefinitionDefault);
+
+ try {
+ deleteSubtree(TEST_CHILD_7_DN);
+ } finally {
+ deleteConnectionHandler();
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that aggregation is rejected by a constraint violation when
+ * an enabled component references a disabled component and the
+ * referenced component must always be enabled when the referencing
+ * component is enabled.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAggregationDisabledReference3() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_6);
+ try {
+ createConnectionHandler(false);
+ } catch (Exception e) {
+ deleteSubtree(TEST_CHILD_6_DN);
+ throw e;
+ }
+
+ // Register the temporary aggregation definition.
+ TestCfg.removeConstraint(aggregationPropertyDefinitionDefault);
+ TestCfg
+ .addPropertyDefinition(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+ TestCfg
+ .addConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+
+ try {
+ TestParentCfg parent = getParent("test parent 1");
+ parent.getTestChild("test child 6");
+ Assert
+ .fail("Unexpectedly added test child 6 when it had a disabled reference");
+ } catch (ConfigException e) {
+ // Check that we have a constraint violation as the cause.
+ Throwable cause = e.getCause();
+ if (cause instanceof ConstraintViolationException) {
+ ConstraintViolationException cve = (ConstraintViolationException) cause;
+ Collection<Message> causes = cve.getMessages();
+ Assert.assertEquals(causes.size(), 1);
+ } else {
+ // Got an unexpected cause.
+ throw e;
+ }
+ } finally {
+ // Put back the default aggregation definition.
+ TestCfg
+ .removeConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+ TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
+ TestCfg.addConstraint(aggregationPropertyDefinitionDefault);
+
+ try {
+ deleteSubtree(TEST_CHILD_6_DN);
+ } finally {
+ deleteConnectionHandler();
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that aggregation is allowed when a disabled component
+ * references a disabled component and the referenced component must
+ * always be enabled when the referencing component is enabled.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAggregationDisabledReference4() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_7);
+ try {
+ createConnectionHandler(false);
+ } catch (Exception e) {
+ deleteSubtree(TEST_CHILD_7_DN);
+ throw e;
+ }
+
+ // Register the temporary aggregation definition.
+ TestCfg.removeConstraint(aggregationPropertyDefinitionDefault);
+ TestCfg
+ .addPropertyDefinition(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+ TestCfg
+ .addConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+
+ try {
+ TestParentCfg parent = getParent("test parent 1");
+ parent.getTestChild("test child 7");
+ } finally {
+ // Put back the default aggregation definition.
+ TestCfg
+ .removeConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+ TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
+ TestCfg.addConstraint(aggregationPropertyDefinitionDefault);
+
+ try {
+ deleteSubtree(TEST_CHILD_7_DN);
+ } finally {
+ deleteConnectionHandler();
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that aggregation contains no values when it contains does
+ * not contain any DN attribute values.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAggregationEmpty() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_1);
+
+ try {
+ TestParentCfg parent = getParent("test parent 1");
+ assertChild1(parent.getTestChild("test child 1"));
+ } finally {
+ deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+ }
+ }
+
+
+
+ /**
+ * Tests that aggregation contains multiple valid values when it
+ * contains a multiple valid DN attribute values.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAggregationMultipleValues() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_4);
+
+ try {
+ TestParentCfg parent = getParent("test parent 1");
+ assertChild4(parent.getTestChild("test child 4"));
+ } finally {
+ deleteSubtree("cn=test child 4,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+ }
+ }
+
+
+
+ /**
+ * Tests that aggregation contains single valid value when it
+ * contains a single valid DN attribute values.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testAggregationSingle() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_2);
+
+ try {
+ TestParentCfg parent = getParent("test parent 1");
+ assertChild2(parent.getTestChild("test child 2"));
+ } finally {
+ deleteSubtree("cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
+ }
+ }
+
+
+
+ /**
+ * Tests that it is impossible to delete a referenced component when
+ * the referenced component must always exist regardless of whether
+ * the referencing component is enabled or not.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testCannotDeleteReferencedComponent() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_7);
+ try {
+ createConnectionHandler(true);
+ } catch (Exception e) {
+ deleteSubtree(TEST_CHILD_7_DN);
+ throw e;
+ }
+
+ // Register the temporary aggregation definition.
+ TestCfg.removeConstraint(aggregationPropertyDefinitionDefault);
+ TestCfg
+ .addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
+ TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled);
+
+ ConfigurationDeleteListener<TestChildCfg> dl = new DummyDeleteListener();
+ ConfigurationChangeListener<TestChildCfg> cl = new DummyChangeListener();
+ try {
+ // Retrieve the parent and child managed objects and register
+ // delete and change listeners respectively in order to trigger
+ // the constraint call-backs.
+ TestParentCfg parent = getParent("test parent 1");
+ parent.addTestChildDeleteListener(dl);
+
+ TestChildCfg child = parent.getTestChild("test child 7");
+ child.addChangeListener(cl);
+
+ // Now attempt to delete the referenced connection handler.
+ // This should fail.
+ try {
+ deleteConnectionHandler();
+ Assert.fail("Successfully deleted a referenced component");
+ } catch (OperationRejectedException e) {
+ // This is the expected exception - do nothing.
+ }
+ } finally {
+ try {
+ deleteSubtree(TEST_CHILD_7_DN);
+ } finally {
+ try {
+ deleteConnectionHandler();
+ } catch (ManagedObjectNotFoundException e) {
+ // Ignore as it may have been deleted already.
+ } finally {
+ // Remove the temporary delete listener.
+ TestParentCfg parent = getParent("test parent 1");
+ parent.removeTestChildDeleteListener(dl);
+
+ // Put back the default aggregation definition.
+ TestCfg
+ .removeConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+ TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
+ TestCfg.addConstraint(aggregationPropertyDefinitionDefault);
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Tests that it is impossible to disable a referenced component
+ * when the referenced component must always be enabled regardless
+ * of whether the referencing component is enabled or not.
+ *
+ * @throws Exception
+ * If the test unexpectedly fails.
+ */
+ @Test
+ public void testCannotDisableReferencedComponent() throws Exception {
+ // Add the entry.
+ TestCaseUtils.addEntry(TEST_CHILD_7);
+ try {
+ createConnectionHandler(true);
+ } catch (Exception e) {
+ deleteSubtree(TEST_CHILD_7_DN);
+ throw e;
+ }
+
+ // Register the temporary aggregation definition.
+ TestCfg.removeConstraint(aggregationPropertyDefinitionDefault);
+ TestCfg
+ .addPropertyDefinition(aggregationPropertyDefinitionTargetMustBeEnabled);
+ TestCfg.addConstraint(aggregationPropertyDefinitionTargetMustBeEnabled);
+
+ ConfigurationDeleteListener<TestChildCfg> dl = new DummyDeleteListener();
+ ConfigurationChangeListener<TestChildCfg> cl = new DummyChangeListener();
+ try {
+ // Retrieve the parent and child managed objects and register
+ // delete and change listeners respectively in order to trigger
+ // the constraint call-backs.
+ TestParentCfg parent = getParent("test parent 1");
+ parent.addTestChildDeleteListener(dl);
+
+ TestChildCfg child = parent.getTestChild("test child 7");
+ child.addChangeListener(cl);
+
+ // Now attempt to disable the referenced connection handler.
+ // This should fail.
+ try {
+ RootCfgClient root = TestCaseUtils.getRootConfiguration();
+ ConnectionHandlerCfgClient client = root
+ .getConnectionHandler(TEST_CONNECTION_HANDLER_NAME);
+ client.setEnabled(false);
+ client.commit();
+ Assert.fail("Successfully disabled a referenced component");
+ } catch (OperationRejectedException e) {
+ // This is the expected exception - do nothing.
+ }
+ } finally {
+ try {
+ deleteSubtree(TEST_CHILD_7_DN);
+ } finally {
+ try {
+ deleteConnectionHandler();
+ } finally {
+ // Remove the temporary delete listener.
+ TestParentCfg parent = getParent("test parent 1");
+ parent.removeTestChildDeleteListener(dl);
+
+ // Put back the default aggregation definition.
+ TestCfg
+ .removeConstraint(aggregationPropertyDefinitionTargetAndSourceMustBeEnabled);
+ TestCfg.addPropertyDefinition(aggregationPropertyDefinitionDefault);
+ TestCfg.addConstraint(aggregationPropertyDefinitionDefault);
+ }
+ }
+ }
+ }
+
+
+
+ // Assert that the values of child 1 are correct.
+ private void assertChild1(TestChildCfg child) {
+ Assert.assertEquals(child.getMandatoryClassProperty(),
+ "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
+ Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
+ DirectoryServer.getAttributeType("description"));
+ assertSetEquals(child.getAggregationProperty(), new String[0]);
+ }
+
+
+
+ // Assert that the values of child 2 are correct.
+ private void assertChild2(TestChildCfg child) {
+ Assert.assertEquals(child.getMandatoryClassProperty(),
+ "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
+ Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
+ DirectoryServer.getAttributeType("description"));
+
+ // Test normalization.
+ assertSetEquals(child.getAggregationProperty(), "LDAP Connection Handler");
+ assertSetEquals(child.getAggregationProperty(),
+ " LDAP Connection Handler ");
+ assertSetEquals(child.getAggregationProperty(),
+ " ldap connection HANDLER ");
+ }
+
+
+
+ // Assert that the values of child 4 are correct.
+ private void assertChild4(TestChildCfg child) {
+ Assert.assertEquals(child.getMandatoryClassProperty(),
+ "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
+ Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
+ DirectoryServer.getAttributeType("description"));
+ assertSetEquals(child.getAggregationProperty(), "LDAPS Connection Handler",
+ "LDAP Connection Handler");
+ }
+
+
+
// Asserts that the actual set of DNs contains the expected values.
private void assertSetEquals(SortedSet<String> actual, String... expected) {
SortedSet<String> values = new TreeSet<String>(TestChildCfgDefn
@@ -399,6 +917,31 @@
+ // Creates a test connection handler for testing.
+ private void createConnectionHandler(boolean enabled) throws Exception {
+ ServerSocket freeSocket = TestCaseUtils.bindFreePort();
+ int freePort = freeSocket.getLocalPort();
+ freeSocket.close();
+
+ RootCfgClient root = TestCaseUtils.getRootConfiguration();
+ LDAPConnectionHandlerCfgClient client = root.createConnectionHandler(
+ LDAPConnectionHandlerCfgDefn.getInstance(),
+ TEST_CONNECTION_HANDLER_NAME, null);
+ client.setEnabled(enabled);
+ client.setListenPort(freePort);
+ client.commit();
+ }
+
+
+
+ // Deletes the test connection handler after testing.
+ private void deleteConnectionHandler() throws Exception {
+ RootCfgClient root = TestCaseUtils.getRootConfiguration();
+ root.removeConnectionHandler(TEST_CONNECTION_HANDLER_NAME);
+ }
+
+
+
// Deletes the named sub-tree.
private void deleteSubtree(String dn) throws Exception {
getAdaptor().deleteSubtree(new LdapName(dn));
--
Gitblit v1.10.0