From 429235331815195cfdc50ff1b0f31be270ed5eba 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.

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java |  761 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 652 insertions(+), 109 deletions(-)

diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
index 78048c0..502eeb2 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
+++ b/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