/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.server.admin.server;

import java.util.List;
import java.util.SortedSet;

import javax.naming.ldap.LdapName;

import org.forgerock.i18n.LocalizableMessage;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.std.server.RootCfg;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.opends.server.types.DN;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/**
 * Test cases for default behavior on the server-side.
 */
public final class DefaultBehaviorTest extends AdminTestCase {

  /**
   * A test child add listener.
   */
  private static class AddListener implements
      ConfigurationAddListener<TestChildCfg> {

    /** The child configuration that was added. */
    private TestChildCfg child;



    /**
     * Creates a new add listener.
     */
    public AddListener() {
      // No implementation required.
    }



    /** {@inheritDoc} */
    public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) {
      return new ConfigChangeResult();
    }



    /**
     * Gets the child configuration checking that it has the expected
     * name.
     *
     * @param expectedName
     *          The child's expected name.
     * @return Returns the child configuration.
     */
    public TestChildCfg getChild(String expectedName) {
      Assert.assertNotNull(child);
      Assert.assertEquals(child.dn().rdn().getAttributeValue(0).toString(), expectedName);
      return child;
    }



    /** {@inheritDoc} */
    public boolean isConfigurationAddAcceptable(TestChildCfg configuration,
        List<LocalizableMessage> unacceptableReasons) {
      child = configuration;
      return true;
    }

  }



  /**
   * A test child change listener.
   */
  private static class ChangeListener implements
      ConfigurationChangeListener<TestChildCfg> {

    /** The child configuration that was changed. */
    private TestChildCfg child;



    /**
     * Creates a new change listener.
     */
    public ChangeListener() {
      // No implementation required.
    }



    /** {@inheritDoc} */
    public ConfigChangeResult applyConfigurationChange(TestChildCfg configuration) {
      return new ConfigChangeResult();
    }



    /**
     * Gets the child configuration checking that it has the expected
     * name.
     *
     * @param expectedName
     *          The child's expected name.
     * @return Returns the child configuration.
     */
    public TestChildCfg getChild(String expectedName) {
      Assert.assertNotNull(child);
      Assert.assertEquals(child.dn().rdn().getAttributeValue(0).toString(), expectedName);
      return child;
    }



    /** {@inheritDoc} */
    public boolean isConfigurationChangeAcceptable(TestChildCfg configuration,
        List<LocalizableMessage> unacceptableReasons) {
      child = configuration;
      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-test-child-dummy",
      "cn: test child 1",
      "ds-cfg-enabled: true",
      "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-attribute-type: description",
      "ds-cfg-conflict-behavior: virtual-overrides-real"
  };

  /** 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",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 2",
      "ds-cfg-enabled: true",
      "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-attribute-type: description",
      "ds-cfg-conflict-behavior: virtual-overrides-real",
      "ds-cfg-base-dn: dc=default value c2v1,dc=com",
      "ds-cfg-base-dn: dc=default value c2v2,dc=com"
  };

  /** Test child 3 LDIF. */
  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",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 3",
      "ds-cfg-enabled: true",
      "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-attribute-type: description",
      "ds-cfg-conflict-behavior: virtual-overrides-real",
      "ds-cfg-base-dn: dc=default value c3v1,dc=com",
      "ds-cfg-base-dn: dc=default value c3v2,dc=com",
      "ds-cfg-group-dn: dc=default value c3v3,dc=com",
      "ds-cfg-group-dn: dc=default value c3v4,dc=com"
  };

  /** Test child 4 LDIF. */
  private static final String[] TEST_CHILD_4 = new String[] {
      "dn: cn=test child 4,cn=test children,cn=test parent 2,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 4",
      "ds-cfg-enabled: true",
      "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-attribute-type: description",
      "ds-cfg-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-test-parent-dummy",
      "cn: test parent 1",
      "ds-cfg-enabled: true",
      "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-attribute-type: description",
      "ds-cfg-conflict-behavior: virtual-overrides-real",
      "",
      // Parent 2 - overrides default values for
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 2,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 2",
      "ds-cfg-enabled: true",
      "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-attribute-type: description",
      "ds-cfg-conflict-behavior: virtual-overrides-real",
      "ds-cfg-base-dn: dc=default value p2v1,dc=com",
      "ds-cfg-base-dn: dc=default value p2v2,dc=com",
      "",
      // 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",
      "",
      "dn:cn=test children,cn=test parent 2,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-branch",
      "cn: test children",
      ""
  };

  /** JNDI LDAP context. */
  private JNDIDirContextAdaptor adaptor;



  /**
   * 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();
    TestCfg.setUp();

    // 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 {
    TestCfg.cleanup();

    // Remove test entries.
    deleteSubtree("cn=test parents,cn=config");
  }



  /**
   * Tests that children have correct values when accessed through an
   * add listener.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAddListenerChildValues1() throws Exception {
    TestParentCfg parent = getParent("test parent 1");
    AddListener listener = new AddListener();
    parent.addTestChildAddListener(listener);

    try {
      // Add the entry.
      TestCaseUtils.addEntry(TEST_CHILD_1);
      try {
        assertChild1(listener.getChild("test child 1"));
      } finally {
        deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
      }
    } finally {
      parent.removeTestChildAddListener(listener);
    }
  }



  /**
   * Tests that children have correct values when accessed through an
   * add listener.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAddListenerChildValues2() throws Exception {
    TestParentCfg parent = getParent("test parent 1");
    AddListener listener = new AddListener();
    parent.addTestChildAddListener(listener);

    try {
      // Add the entry.
      TestCaseUtils.addEntry(TEST_CHILD_2);
      try {
        assertChild2(listener.getChild("test child 2"));
      } finally {
        deleteSubtree("cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
      }
    } finally {
      parent.removeTestChildAddListener(listener);
    }
  }



  /**
   * Tests that children have correct values when accessed through an
   * add listener.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAddListenerChildValues3() throws Exception {
    TestParentCfg parent = getParent("test parent 1");
    AddListener listener = new AddListener();
    parent.addTestChildAddListener(listener);

    try {
      // Add the entry.
      TestCaseUtils.addEntry(TEST_CHILD_3);
      try {
        assertChild3(listener.getChild("test child 3"));
      } finally {
        deleteSubtree("cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config");
      }
    } finally {
      parent.removeTestChildAddListener(listener);
    }
  }



  /**
   * Tests that children have correct values when accessed through an
   * add listener.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAddListenerChildValues4() throws Exception {
    TestParentCfg parent = getParent("test parent 2");
    AddListener listener = new AddListener();
    parent.addTestChildAddListener(listener);

    try {
      // Add the entry.
      TestCaseUtils.addEntry(TEST_CHILD_4);
      try {
        assertChild4(listener.getChild("test child 4"));
      } finally {
        deleteSubtree("cn=test child 4,cn=test children,cn=test parent 2,cn=test parents,cn=config");
      }
    } finally {
      parent.removeTestChildAddListener(listener);
    }
  }



  /**
   * Tests that children have correct values when accessed through a
   * change listener. This test replaces the defaulted properties with
   * real values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChangeListenerChildValues1() throws Exception {
    TestParentCfg parent = getParent("test parent 1");

    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-base-dn",
          "ds-cfg-base-dn: dc=new value 1,dc=com",
          "ds-cfg-base-dn: dc=new value 2,dc=com",
          "-",
          "replace: ds-cfg-group-dn",
          "ds-cfg-group-dn: dc=new value 3,dc=com",
          "ds-cfg-group-dn: dc=new value 4,dc=com"
      };
      TestCaseUtils.applyModifications(true, changes);

      // Make sure that the change listener was notified and the
      // modified child contains the correct values.
      child = listener.getChild("test child 1");

      Assert.assertEquals(child.getMandatoryClassProperty(),
          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
      Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
          DirectoryServer.getAttributeTypeOrNull("description"));
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
          "dc=new value 1,dc=com", "dc=new value 2,dc=com");
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
          "dc=new value 3,dc=com", "dc=new value 4,dc=com");
    } finally {
      deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }



  /**
   * Tests that children have correct values when accessed through a
   * change listener. This test makes sure that default values
   * inherited from within the modified component itself behave as
   * expected.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChangeListenerChildValues2() throws Exception {
    TestParentCfg parent = getParent("test parent 1");

    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-base-dn",
          "ds-cfg-base-dn: dc=new value 1,dc=com",
          "ds-cfg-base-dn: dc=new value 2,dc=com"
      };
      TestCaseUtils.applyModifications(true, changes);

      // Make sure that the change listener was notified and the
      // modified child contains the correct values.
      child = listener.getChild("test child 1");

      Assert.assertEquals(child.getMandatoryClassProperty(),
          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
      Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
          DirectoryServer.getAttributeTypeOrNull("description"));
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
          "dc=new value 1,dc=com", "dc=new value 2,dc=com");
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
          "dc=new value 1,dc=com", "dc=new value 2,dc=com");
    } finally {
      deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }



  /**
   * Tests that children have correct values when accessed through a
   * change listener. This test makes sure that default values
   * inherited from outside the modified component behave as expected.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChangeListenerChildValues3() throws Exception {
    TestParentCfg parent = getParent("test parent 1");

    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-group-dn",
          "ds-cfg-group-dn: dc=new value 1,dc=com",
          "ds-cfg-group-dn: dc=new value 2,dc=com"
      };
      TestCaseUtils.applyModifications(true, changes);

      // Make sure that the change listener was notified and the
      // modified child contains the correct values.
      child = listener.getChild("test child 1");

      Assert.assertEquals(child.getMandatoryClassProperty(),
          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
      Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
          DirectoryServer.getAttributeTypeOrNull("description"));
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
          "dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com");
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
          "dc=new value 1,dc=com", "dc=new value 2,dc=com");
    } finally {
      deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }



  /**
   * Tests that children have correct values when accessed through a
   * change listener. This test makes sure that a component is
   * notified when the default values it inherits from another
   * component are modified.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChangeListenerChildValues4() throws Exception {
    TestParentCfg parent = getParent("test parent 1");

    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 the parent.
      String[] changes = new String[] {
          "dn: cn=test parent 1,cn=test parents,cn=config",
          "changetype: modify",
          "replace: ds-cfg-base-dn",
          "ds-cfg-base-dn: dc=new value 1,dc=com",
          "ds-cfg-base-dn: dc=new value 2,dc=com"
      };
      TestCaseUtils.applyModifications(true, changes);

      // Make sure that the change listener was notified and the
      // modified child contains the correct values.
      child = listener.getChild("test child 1");

      Assert.assertEquals(child.getMandatoryClassProperty(),
          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
      Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
          DirectoryServer.getAttributeTypeOrNull("description"));
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
          "dc=new value 1,dc=com", "dc=new value 2,dc=com");
      assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
          "dc=new value 1,dc=com", "dc=new value 2,dc=com");
    } finally {
      deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");

      // Undo the modifications.
      String[] changes = new String[] {
          "dn: cn=test parent 1,cn=test parents,cn=config",
          "changetype: modify",
          "delete: ds-cfg-base-dn"
      };
      TestCaseUtils.applyModifications(true, changes);
    }
  }



  /**
   * Tests that children have correct values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChildValues1() 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 children have correct values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChildValues2() 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 children have correct values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChildValues3() throws Exception {
    // Add the entry.
    TestCaseUtils.addEntry(TEST_CHILD_3);

    try {
      TestParentCfg parent = getParent("test parent 1");
      assertChild3(parent.getTestChild("test child 3"));
    } finally {
      deleteSubtree("cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }



  /**
   * Tests that children have correct values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testChildValues4() throws Exception {
    // Add the entry.
    TestCaseUtils.addEntry(TEST_CHILD_4);

    try {
      TestParentCfg parent = getParent("test parent 2");
      assertChild4(parent.getTestChild("test child 4"));
    } finally {
      deleteSubtree("cn=test child 4,cn=test children,cn=test parent 2,cn=test parents,cn=config");
    }
  }



  /**
   * Tests that parent 1 has correct values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testParentValues1() throws Exception {
    TestParentCfg parent = getParent("test parent 1");

    Assert.assertEquals(parent.getMandatoryClassProperty(),
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
    Assert.assertEquals(parent.getMandatoryReadOnlyAttributeTypeProperty(),
        DirectoryServer.getAttributeTypeOrNull("description"));
    assertDNSetEquals(parent.getOptionalMultiValuedDNProperty(),
        "dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com");
  }



  /**
   * Tests that parent 2 has correct values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testParentValues2() throws Exception {
    TestParentCfg parent = getParent("test parent 2");

    Assert.assertEquals(parent.getMandatoryClassProperty(),
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
    Assert.assertEquals(parent.getMandatoryReadOnlyAttributeTypeProperty(),
        DirectoryServer.getAttributeTypeOrNull("description"));
    assertDNSetEquals(parent.getOptionalMultiValuedDNProperty(),
        "dc=default value p2v1,dc=com", "dc=default value p2v2,dc=com");
  }



  /** 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.getAttributeTypeOrNull("description"));
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
        "dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com");
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
        "dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com");
  }



  /** 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.getAttributeTypeOrNull("description"));
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
        "dc=default value c2v1,dc=com", "dc=default value c2v2,dc=com");
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
        "dc=default value c2v1,dc=com", "dc=default value c2v2,dc=com");
  }



  /** Assert that the values of child 3 are correct. */
  private void assertChild3(TestChildCfg child) {
    Assert.assertEquals(child.getMandatoryClassProperty(),
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
    Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
        DirectoryServer.getAttributeTypeOrNull("description"));
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
        "dc=default value c3v1,dc=com", "dc=default value c3v2,dc=com");
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
        "dc=default value c3v3,dc=com", "dc=default value c3v4,dc=com");
  }



  /** 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.getAttributeTypeOrNull("description"));
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(),
        "dc=default value p2v1,dc=com", "dc=default value p2v2,dc=com");
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
        "dc=default value p2v1,dc=com", "dc=default value p2v2,dc=com");
  }



  /** Asserts that the actual set of DNs contains the expected values. */
  private void assertDNSetEquals(SortedSet<DN> actual, String... expected) {
    String[] actualStrings = new String[actual.size()];
    int i = 0;
    for (DN dn : actual) {
      actualStrings[i] = dn.toString();
      i++;
    }
    Assert.assertEqualsNoOrder(actualStrings, expected);
  }



  /** 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.simpleSSLBind("127.0.0.1", TestCaseUtils
          .getServerAdminPort(), "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;
  }
}
