/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008 Sun Microsystems, Inc.
*/
package org.opends.server.admin.server;
import java.util.List;
import javax.naming.OperationNotSupportedException;
import javax.naming.ldap.LdapName;
import org.opends.messages.Message;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.Entry;
import org.opends.server.types.ResultCode;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Test cases for constraints on the server-side.
*/
public final class ConstraintTest extends AdminTestCase {
// Child DN.
private static final String TEST_CHILD_1_DN = "cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config";
/**
* A test child add listener.
*/
private static class AddListener implements
ConfigurationAddListener<TestChildCfg> {
/**
* {@inheritDoc}
*/
public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) {
return new ConfigChangeResult(ResultCode.SUCCESS, false);
}
/**
* {@inheritDoc}
*/
public boolean isConfigurationAddAcceptable(TestChildCfg configuration,
List<Message> unacceptableReasons) {
return true;
}
}
/**
* A test child delete listener.
*/
private static class DeleteListener implements
ConfigurationDeleteListener<TestChildCfg> {
/**
* {@inheritDoc}
*/
public ConfigChangeResult applyConfigurationDelete(
TestChildCfg configuration) {
return new ConfigChangeResult(ResultCode.SUCCESS, false);
}
/**
* {@inheritDoc}
*/
public boolean isConfigurationDeleteAcceptable(TestChildCfg configuration,
List<Message> unacceptableReasons) {
return true;
}
}
/**
* A test child change listener.
*/
private static class ChangeListener implements
ConfigurationChangeListener<TestChildCfg> {
/**
* {@inheritDoc}
*/
public ConfigChangeResult applyConfigurationChange(
TestChildCfg configuration) {
return new ConfigChangeResult(ResultCode.SUCCESS, false);
}
/**
* {@inheritDoc}
*/
public boolean isConfigurationChangeAcceptable(TestChildCfg configuration,
List<Message> unacceptableReasons) {
return true;
}
}
// Test child 1 LDIF.
private static final String[] TEST_CHILD_1 = new String[] {
"dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
"objectclass: top",
"objectclass: ds-cfg-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 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",
"",
// Child base entries.
"dn:cn=test children,cn=test parent 1,cn=test parents,cn=config",
"objectclass: top",
"objectclass: ds-cfg-branch",
"cn: test children",
"",
};
// JNDI LDAP context.
private JNDIDirContextAdaptor adaptor = null;
/**
* Sets up tests
*
* @throws Exception
* If the server could not be initialized.
*/
@BeforeClass
public void setUp() throws Exception {
// This test suite depends on having the schema available, so
// we'll start the server.
TestCaseUtils.startServer();
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 retrieval can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testGetManagedObjectSuccess() throws Exception {
MockConstraint constraint = new MockConstraint(true, false);
try {
TestCaseUtils.addEntry(TEST_CHILD_1);
TestCfg.addConstraint(constraint);
TestParentCfg parent = getParent("test parent 1");
parent.getTestChild("test child 1");
} finally {
TestCfg.removeConstraint(constraint);
try {
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Do nothing.
}
}
}
/**
* Tests that retrieval can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testGetManagedObjectFail() throws Exception {
MockConstraint constraint = new MockConstraint(false, true);
try {
TestCaseUtils.addEntry(TEST_CHILD_1);
TestCfg.addConstraint(constraint);
TestParentCfg parent = getParent("test parent 1");
parent.getTestChild("test child 1");
} catch (ConfigException e) {
Throwable cause = e.getCause();
if (cause instanceof ConstraintViolationException) {
ConstraintViolationException cve = (ConstraintViolationException) cause;
Assert.assertEquals(cve.getMessages().size(), 1);
Assert.assertSame(cve.getManagedObject().getManagedObjectDefinition(),
TestChildCfgDefn.getInstance());
} else {
// Wrong cause.
throw e;
}
} finally {
TestCfg.removeConstraint(constraint);
try {
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Do nothing.
}
}
}
/**
* Tests that an add constraint can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testAddConstraintSuccess() throws Exception {
TestParentCfg parent = getParent("test parent 1");
AddListener listener = new AddListener();
parent.addTestChildAddListener(listener);
MockConstraint constraint = new MockConstraint(true, false);
TestCfg.addConstraint(constraint);
try {
try {
// Add the entry.
addEntry(ResultCode.SUCCESS, TEST_CHILD_1);
} finally {
try {
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Do nothing.
}
}
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildAddListener(listener);
}
}
/**
* Tests that an add constraint can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testAddConstraintFail() throws Exception {
TestParentCfg parent = getParent("test parent 1");
AddListener listener = new AddListener();
parent.addTestChildAddListener(listener);
MockConstraint constraint = new MockConstraint(false, true);
TestCfg.addConstraint(constraint);
try {
try {
// Add the entry.
addEntry(ResultCode.UNWILLING_TO_PERFORM, TEST_CHILD_1);
} finally {
try {
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Do nothing.
}
}
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildAddListener(listener);
}
}
/**
* Tests that a delete constraint can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testDeleteConstraintSuccess() throws Exception {
TestParentCfg parent = getParent("test parent 1");
DeleteListener listener = new DeleteListener();
parent.addTestChildDeleteListener(listener);
MockConstraint constraint = new MockConstraint(false, true);
TestCfg.addConstraint(constraint);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
// Now delete it - this should trigger the constraint.
deleteSubtree(TEST_CHILD_1_DN);
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildDeleteListener(listener);
try {
// Clean up.
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Ignore.
}
}
}
/**
* Tests that a delete constraint can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testDeleteConstraintFail() throws Exception {
TestParentCfg parent = getParent("test parent 1");
DeleteListener listener = new DeleteListener();
parent.addTestChildDeleteListener(listener);
MockConstraint constraint = new MockConstraint(true, false);
TestCfg.addConstraint(constraint);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
try {
// Now delete it - this should trigger the constraint.
deleteSubtree(TEST_CHILD_1_DN);
// Should not have succeeded.
Assert.fail("Delete constraint failed to prevent deletion");
} catch (OperationNotSupportedException e) {
// Ignore - this is the expected exception.
}
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildDeleteListener(listener);
try {
// Clean up.
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Ignore.
}
}
}
/**
* Tests that a modify constraint can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testChangeConstraintSuccess() throws Exception {
TestParentCfg parent = getParent("test parent 1");
MockConstraint constraint = new MockConstraint(true, false);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
TestChildCfg child = parent.getTestChild("test child 1");
TestCfg.addConstraint(constraint);
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"
};
int result = TestCaseUtils.applyModifications(true, changes);
Assert.assertEquals(result, ResultCode.SUCCESS.getIntValue());
} finally {
TestCfg.removeConstraint(constraint);
try {
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Ignore.
}
}
}
/**
* Tests that a modify constraint can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testChangeConstraintFail() throws Exception {
TestParentCfg parent = getParent("test parent 1");
MockConstraint constraint = new MockConstraint(false, true);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
TestChildCfg child = parent.getTestChild("test child 1");
TestCfg.addConstraint(constraint);
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"
};
int result = TestCaseUtils.applyModifications(true, changes);
Assert
.assertEquals(result, ResultCode.UNWILLING_TO_PERFORM.getIntValue());
} finally {
TestCfg.removeConstraint(constraint);
try {
deleteSubtree(TEST_CHILD_1_DN);
} catch (Exception e) {
// Ignore.
}
}
}
// Add an entry and check its result.
private void addEntry(ResultCode expected, String... lines) throws Exception {
Entry entry = TestCaseUtils.makeEntry(lines);
InternalClientConnection conn = InternalClientConnection
.getRootConnection();
AddOperation add = conn.processAdd(entry.getDN(), entry.getObjectClasses(),
entry.getUserAttributes(), entry.getOperationalAttributes());
Assert.assertEquals(add.getResultCode(), expected, add.getErrorMessage()
.toString());
}
// Deletes the named sub-tree.
private void deleteSubtree(String dn) throws Exception {
getAdaptor().deleteSubtree(new LdapName(dn));
}
// Gets the JNDI connection for the test server instance.
private synchronized JNDIDirContextAdaptor getAdaptor() throws Exception {
if (adaptor == null) {
adaptor = JNDIDirContextAdaptor.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;
}
}