/* * 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 2015 ForgeRock AS */ package org.forgerock.opendj.config.server; import static org.fest.assertions.Assertions.assertThat; import static org.forgerock.opendj.ldif.LDIF.makeEntry; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.SortedSet; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageBuilder; import org.forgerock.opendj.config.AdminTestCase; import org.forgerock.opendj.config.TestCfg; import org.forgerock.opendj.config.TestChildCfg; import org.forgerock.opendj.config.TestParentCfg; import org.forgerock.opendj.config.server.spi.ConfigAddListener; import org.forgerock.opendj.config.server.spi.ConfigChangeListener; import org.forgerock.opendj.config.server.spi.ConfigurationRepository; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.schema.Schema; import org.mockito.ArgumentCaptor; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @SuppressWarnings("javadoc") /** * Test default behavior on the server side, by checking values of configuration objects. */ public final class DefaultBehaviorTest extends AdminTestCase { private static class TestConfigurationAddListener implements ConfigurationAddListener<TestChildCfg> { private TestChildCfg childCfg; public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) { return new ConfigChangeResult(); } /** Gets the child configuration checking that it has the expected name. */ public TestChildCfg getChildCfg(String expectedName) { Assert.assertNotNull(childCfg); Assert.assertEquals(childCfg.dn().rdn().getFirstAVA().getAttributeValue().toString(), expectedName); return childCfg; } public boolean isConfigurationAddAcceptable(TestChildCfg configuration, List<LocalizableMessage> unacceptableReasons) { childCfg = configuration; return true; } } private static class TestConfigurationChangeListener implements ConfigurationChangeListener<TestChildCfg> { private TestChildCfg childCfg; public ConfigChangeResult applyConfigurationChange(TestChildCfg configuration) { return new ConfigChangeResult(); } /** Gets the child configuration checking that it has the expected name. */ public TestChildCfg getChildCfg(String expectedName) { Assert.assertNotNull(childCfg); Assert.assertEquals(childCfg.dn().rdn().getFirstAVA().getAttributeValue().toString(), expectedName); return childCfg; } public boolean isConfigurationChangeAcceptable(TestChildCfg configuration, List<LocalizableMessage> unacceptableReasons) { childCfg = configuration; return true; } } // @Checkstyle:off static final Entry CONFIG = makeEntry( "dn: cn=config", "objectClass: top", "objectClass: ds-cfg-root-config", "cn: config"); static final Entry TEST_PARENTS = makeEntry( "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. */ static final List<String> LDIF_TEST_PARENT_1 = Arrays.asList( "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"); static final Entry TEST_PARENT_1 = makeEntry(LDIF_TEST_PARENT_1); /** Parent 2 - overrides default values for optional-multi-valued-dn-property. */ static final Entry TEST_PARENT_2 = makeEntry( "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"); static final Entry TEST_CHILD_BASE_1 = makeEntry( "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top", "objectclass: ds-cfg-branch", "cn: test children"); static final Entry TEST_CHILD_BASE_2 = makeEntry( "dn:cn=test children,cn=test parent 2,cn=test parents,cn=config", "objectclass: top", "objectclass: ds-cfg-branch", "cn: test children"); private static final List<String> LDIF_TEST_CHILD_1 = Arrays.asList( "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"); private static final Entry TEST_CHILD_1 = makeEntry(LDIF_TEST_CHILD_1); private static final List<String> NEW_ATTRS_1 = Arrays.asList( "ds-cfg-base-dn: dc=new value 1,dc=com", "ds-cfg-base-dn: dc=new value 2,dc=com", "ds-cfg-group-dn: dc=new value 3,dc=com", "ds-cfg-group-dn: dc=new value 4,dc=com"); private static final List<String> NEW_ATTRS_2 = Arrays.asList( "ds-cfg-base-dn: dc=new value 1,dc=com", "ds-cfg-base-dn: dc=new value 2,dc=com"); private static final List<String> NEW_ATTRS_3 = Arrays.asList( "ds-cfg-group-dn: dc=new value 1,dc=com", "ds-cfg-group-dn: dc=new value 2,dc=com"); private static final Entry TEST_CHILD_2 = makeEntry( "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"); private static final Entry TEST_CHILD_3 = makeEntry( "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"); private static final Entry TEST_CHILD_4 = makeEntry( "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"); // @Checkstyle:on @BeforeClass public void setUp() throws Exception { TestCfg.setUp(); } @AfterClass public void tearDown() throws Exception { TestCfg.cleanup(); } @DataProvider Object[][] childConfigurationsValues() { return new Object[][] { // parent entry, child base entry, child entry, // expected first dn property values, // expected second dn property values { TEST_PARENT_1, TEST_CHILD_BASE_1, TEST_CHILD_1, Arrays.asList("dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com"), Arrays.asList("dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com") }, { TEST_PARENT_1, TEST_CHILD_BASE_1, TEST_CHILD_2, Arrays.asList("dc=default value c2v1,dc=com", "dc=default value c2v2,dc=com"), Arrays.asList("dc=default value c2v1,dc=com", "dc=default value c2v2,dc=com") }, { TEST_PARENT_1, TEST_CHILD_BASE_1, TEST_CHILD_3, Arrays.asList("dc=default value c3v1,dc=com", "dc=default value c3v2,dc=com"), Arrays.asList("dc=default value c3v3,dc=com", "dc=default value c3v4,dc=com") }, { TEST_PARENT_2, TEST_CHILD_BASE_2, TEST_CHILD_4, Arrays.asList("dc=default value p2v1,dc=com", "dc=default value p2v2,dc=com"), Arrays.asList("dc=default value p2v1,dc=com", "dc=default value p2v2,dc=com") } }; } /** * Test that a child config have correct values when accessed from its * parent config. */ @Test(dataProvider = "childConfigurationsValues") public void testChildValues(Entry testParent, Entry testBaseChild, Entry testChild, List<String> valuesForOptionalDNProperty1, List<String> valuesForOptionalDNProperty2) throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(testParent, testBaseChild, testChild); ServerManagementContext context = new ServerManagementContext(configRepository); TestParentCfg parentCfg = getParentCfg(testParent, context); // assert assertChildHasCorrectValues(parentCfg.getTestChild(entryName(testChild)), valuesForOptionalDNProperty1, valuesForOptionalDNProperty2); } /** * Test that a child config have correct values when accessed through an add * listener. */ @Test(dataProvider = "childConfigurationsValues") public void testAddListenerChildValues(Entry testParent, Entry testBaseChild, Entry testChild, List<String> valuesForOptionalDNProperty1, List<String> valuesForOptionalDNProperty2) throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(testParent, testBaseChild, testChild); ServerManagementContext context = new ServerManagementContext(configRepository); TestParentCfg parentCfg = getParentCfg(testParent, context); TestConfigurationAddListener addListener = new TestConfigurationAddListener(); parentCfg.addTestChildAddListener(addListener); // act simulateEntryAdd(testChild, configRepository); // assert assertChildHasCorrectValues(addListener.getChildCfg(entryName(testChild)), valuesForOptionalDNProperty1, valuesForOptionalDNProperty2); } @DataProvider Object[][] childConfigurationsValuesForChangeListener() { return new Object[][] { // new entry after change, expected first dn property values, // expected second dn property values { makeEntryFrom(LDIF_TEST_CHILD_1, NEW_ATTRS_1), Arrays.asList("dc=new value 1,dc=com", "dc=new value 2,dc=com"), Arrays.asList("dc=new value 3,dc=com", "dc=new value 4,dc=com") }, { makeEntryFrom(LDIF_TEST_CHILD_1, NEW_ATTRS_2), Arrays.asList("dc=new value 1,dc=com", "dc=new value 2,dc=com"), Arrays.asList("dc=new value 1,dc=com", "dc=new value 2,dc=com") }, { makeEntryFrom(LDIF_TEST_CHILD_1, NEW_ATTRS_3), Arrays.asList("dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com"), Arrays.asList("dc=new value 1,dc=com", "dc=new value 2,dc=com") }, { makeEntryFrom(LDIF_TEST_PARENT_1, NEW_ATTRS_2), Arrays.asList("dc=new value 1,dc=com", "dc=new value 2,dc=com"), Arrays.asList("dc=new value 1,dc=com", "dc=new value 2,dc=com") } }; } /** * Tests that a child config have correct values when accessed through an * change listener. The defaulted properties are replaced with some real * values. */ @Test(dataProvider = "childConfigurationsValuesForChangeListener") public void testChangeListenerChildValues(Entry newEntry, List<String> valuesForOptionalDNProperty1, List<String> valuesForOptionalDNProperty2) throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(TEST_PARENT_1, TEST_CHILD_BASE_1, TEST_CHILD_1); ServerManagementContext context = new ServerManagementContext(configRepository); TestParentCfg parentCfg = getParentCfg(TEST_PARENT_1, context); TestChildCfg childCfg = parentCfg.getTestChild(entryName(TEST_CHILD_1)); TestConfigurationChangeListener changeListener = new TestConfigurationChangeListener(); childCfg.addChangeListener(changeListener); // act simulateEntryChange(newEntry, configRepository); // assert assertChildHasCorrectValues(changeListener.getChildCfg(entryName(TEST_CHILD_1)), valuesForOptionalDNProperty1, valuesForOptionalDNProperty2); } @DataProvider Object[][] parentConfigurationsValues() { return new Object[][] { // parent entry, expected first dn property values, expected second // dn property values { TEST_PARENT_1, Arrays.asList("dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com") }, { TEST_PARENT_2, Arrays.asList("dc=default value p2v1,dc=com", "dc=default value p2v2,dc=com") } }; } /** * Tests that parent configuration has correct values. */ @Test(dataProvider = "parentConfigurationsValues") public void testParentValues(Entry parentEntry, List<String> valuesForOptionalDNProperty) throws Exception { ConfigurationRepository configRepository = createConfigRepositoryWithEntries(parentEntry); ServerManagementContext context = new ServerManagementContext(configRepository); TestParentCfg parent = getParentCfg(parentEntry, context); assertThat(parent.getMandatoryClassProperty()).isEqualTo( "org.opends.server.extensions.UserDefinedVirtualAttributeProvider"); assertThat(parent.getMandatoryReadOnlyAttributeTypeProperty()).isEqualTo( Schema.getDefaultSchema().getAttributeType("description")); assertDNSetEquals(parent.getOptionalMultiValuedDNProperty(), valuesForOptionalDNProperty); } /** * Simulate an entry add by triggering configAddIsAcceptable method of last * registered add listener. */ private void simulateEntryAdd(Entry entry, ConfigurationRepository configRepository) throws IOException { // use argument capture to retrieve the actual listener ArgumentCaptor<ConfigAddListener> registeredListener = ArgumentCaptor.forClass(ConfigAddListener.class); verify(configRepository).registerAddListener(eq(entry.getName().parent()), registeredListener.capture()); registeredListener.getValue().configAddIsAcceptable(entry, new LocalizableMessageBuilder()); } /** * Simulate an entry change by triggering configChangeIsAcceptable method on * last registered change listener. */ private void simulateEntryChange(Entry newEntry, ConfigurationRepository configRepository) { // use argument capture to retrieve the actual listener ArgumentCaptor<ConfigChangeListener> registeredListener = ArgumentCaptor.forClass(ConfigChangeListener.class); verify(configRepository).registerChangeListener(eq(newEntry.getName()), registeredListener.capture()); registeredListener.getValue().configChangeIsAcceptable(newEntry, new LocalizableMessageBuilder()); } private void assertChildHasCorrectValues(TestChildCfg child, List<String> dnProperty1, List<String> dnProperty2) { assertThat(child.getMandatoryClassProperty()).isEqualTo( "org.opends.server.extensions.UserDefinedVirtualAttributeProvider"); assertThat(child.getMandatoryReadOnlyAttributeTypeProperty()).isEqualTo( Schema.getDefaultSchema().getAttributeType("description")); assertDNSetEquals(child.getOptionalMultiValuedDNProperty1(), dnProperty1); assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(), dnProperty2); } /** Asserts that the actual set of DNs contains the expected values. */ private void assertDNSetEquals(SortedSet<DN> actualDNs, List<String> expectedDNs) { String[] actualStrings = new String[actualDNs.size()]; int i = 0; for (DN dn : actualDNs) { actualStrings[i] = dn.toString(); i++; } assertThat(actualStrings).containsOnly(expectedDNs.toArray(new Object[expectedDNs.size()])); } /** Make an entry by combining two lists. */ static Entry makeEntryFrom(List<String> base, List<String> attrs) { List<String> ldif = new ArrayList<>(base); ldif.addAll(attrs); return makeEntry(ldif.toArray(new String[0])); } }