Added: directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/MiscTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/MiscTest.java?rev=375785&view=auto
==============================================================================
--- directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/MiscTest.java (added)
+++ directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/MiscTest.java Tue Feb 7 16:10:02 2006
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.directory.server;
+
+
+import org.apache.directory.server.core.configuration.MutableDirectoryPartitionConfiguration;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.asn1.util.Asn1StringUtils;
+import org.apache.directory.shared.ldap.message.Control;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+import javax.naming.Context;
+import javax.naming.NoPermissionException;
+import javax.naming.NamingEnumeration;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.*;
+import javax.naming.ldap.InitialLdapContext;
+
+
+/**
+ * A set of miscellanous tests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 375632 $
+ */
+public class MiscTest extends AbstractServerTest
+{
+ /**
+ * Cleans up old database files on creation.
+ */
+ public MiscTest()
+ {
+ }
+
+
+ /**
+ * Customizes setup for each test case.
+ *
+ * @throws Exception
+ */
+ public void setUp() throws Exception
+ {
+ if ( this.getName().equals( "testDisableAnonymousBinds" ) )
+ {
+ configuration.setAllowAnonymousAccess( false );
+ }
+ else if ( this.getName().equals( "testEnableAnonymousBindsOnRootDSE" ) )
+ {
+ configuration.setAllowAnonymousAccess( false );
+ }
+ else if ( this.getName().equals( "testUserAuthOnMixedCaseSuffix" ) )
+ {
+ Set partitions = new HashSet();
+ partitions.addAll( configuration.getContextPartitionConfigurations() );
+ MutableDirectoryPartitionConfiguration partition = new MutableDirectoryPartitionConfiguration();
+ partition.setSuffix( "dc=aPache,dc=org" );
+ Attributes entry = new BasicAttributes( "dc", "aPache", true );
+ Attribute oc = new BasicAttribute( "objectClass" );
+ entry.put( oc );
+ oc.add( "top" );
+ oc.add( "domain" );
+ partition.setName( "apache" );
+ partition.setContextEntry( entry );
+ partition.setIndexedAttributes( Collections.singleton( "dc" ) );
+ partitions.add( partition );
+ configuration.setContextPartitionConfigurations( partitions );
+ }
+ else if ( this.getName().equals( "testAnonymousBindsEnabledBaseSearch" ) )
+ {
+ // allow anonymous access
+ configuration.setAllowAnonymousAccess( true );
+
+ // create a partition to search
+ Set partitions = new HashSet();
+ partitions.addAll( configuration.getContextPartitionConfigurations() );
+ MutableDirectoryPartitionConfiguration partition = new MutableDirectoryPartitionConfiguration();
+ partition.setSuffix( "dc=apache,dc=org" );
+ Attributes entry = new BasicAttributes( "dc", "apache", true );
+ Attribute oc = new BasicAttribute( "objectClass" );
+ entry.put( oc );
+ oc.add( "top" );
+ oc.add( "domain" );
+ partition.setName( "apache" );
+ partition.setContextEntry( entry );
+ partition.setIndexedAttributes( Collections.singleton( "dc" ) );
+ partitions.add( partition );
+ configuration.setContextPartitionConfigurations( partitions );
+ }
+
+
+ super.setUp();
+ }
+
+
+ /**
+ * Test to make sure anonymous binds are disabled when going through
+ * the wire protocol.
+ *
+ * @throws Exception if anything goes wrong
+ */
+ public void testDisableAnonymousBinds() throws Exception
+ {
+ // Use the SUN JNDI provider to hit server port and bind as anonymous
+ InitialDirContext ic = null;
+ final Hashtable env = new Hashtable();
+
+ env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/ou=system" );
+ env.put( Context.SECURITY_AUTHENTICATION, "none" );
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+ boolean connected = false;
+ while( ! connected )
+ {
+ try
+ {
+ ic = new InitialDirContext( env );
+ connected = true;
+ }
+ catch( Exception e )
+ {
+ }
+ }
+
+ try
+ {
+ ic.search( "", "(objectClass=*)", new SearchControls() );
+ fail( "If anonymous binds are disabled we should never get here!" );
+ }
+ catch ( NoPermissionException e )
+ {
+ }
+
+ Attributes attrs = new BasicAttributes( true );
+ Attribute oc = new BasicAttribute( "objectClass" );
+ attrs.put( oc );
+ oc.add( "top" );
+ oc.add( "organizationalUnit" );
+
+ try
+ {
+ ic.createSubcontext( "ou=blah", attrs );
+ }
+ catch( NoPermissionException e )
+ {
+ }
+ }
+
+
+ /**
+ * Test to make sure anonymous binds are allowed on the RootDSE even when disabled
+ * in general when going through the wire protocol.
+ *
+ * @throws Exception if anything goes wrong
+ */
+ public void testEnableAnonymousBindsOnRootDSE() throws Exception
+ {
+ // Use the SUN JNDI provider to hit server port and bind as anonymous
+
+ final Hashtable env = new Hashtable();
+
+ env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/" );
+ env.put( Context.SECURITY_AUTHENTICATION, "none" );
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+ InitialDirContext ctx = new InitialDirContext( env );
+ SearchControls cons = new SearchControls();
+ cons.setSearchScope( SearchControls.OBJECT_SCOPE );
+ NamingEnumeration list = ctx.search( "", "(objectClass=*)", cons );
+ SearchResult result = null;
+ if ( list.hasMore() )
+ {
+ result = ( SearchResult ) list.next();
+ }
+ assertFalse( list.hasMore() );
+ list.close();
+
+ assertNotNull( result );
+ assertEquals( "", result.getName().trim() );
+ }
+
+ /**
+ * Test to make sure that if anonymous binds are allowed a user may search
+ * within a a partition.
+ *
+ * @throws Exception if anything goes wrong
+ */
+ public void testAnonymousBindsEnabledBaseSearch() throws Exception
+ {
+ // Use the SUN JNDI provider to hit server port and bind as anonymous
+
+ final Hashtable env = new Hashtable();
+
+ env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/" );
+ env.put( Context.SECURITY_AUTHENTICATION, "none" );
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+ InitialDirContext ctx = new InitialDirContext( env );
+ SearchControls cons = new SearchControls();
+ cons.setSearchScope( SearchControls.OBJECT_SCOPE );
+ NamingEnumeration list = ctx.search( "dc=apache,dc=org", "(objectClass=*)", cons );
+ SearchResult result = null;
+ if ( list.hasMore() )
+ {
+ result = ( SearchResult ) list.next();
+ }
+ assertFalse( list.hasMore() );
+ list.close();
+
+ assertNotNull( result );
+ assertNotNull( result.getAttributes().get("dc") );
+ }
+
+ /**
+ * Reproduces the problem with
+ * <a href="http://issues.apache.org/jira/browse/DIREVE-239">DIREVE-239</a>.
+ *
+ * @throws Exception if anything goes wrong
+ */
+ public void testAdminAccessBug() throws Exception
+ {
+ // Use the SUN JNDI provider to hit server port and bind as anonymous
+
+ final Hashtable env = new Hashtable();
+
+ env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+ env.put("java.naming.ldap.version", "3");
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+ Attributes attributes = new BasicAttributes();
+ Attribute objectClass = new BasicAttribute( "objectClass" );
+ objectClass.add( "top" );
+ objectClass.add( "organizationalUnit" );
+ attributes.put( objectClass );
+ attributes.put( "ou", "blah" );
+ InitialDirContext ctx = new InitialDirContext( env );
+ ctx.createSubcontext( "ou=blah,ou=system", attributes );
+ SearchControls controls = new SearchControls();
+ controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+ controls.setReturningAttributes( new String[] { "+" } );
+ NamingEnumeration list = ctx.search( "ou=blah,ou=system", "(objectClass=*)", controls );
+ SearchResult result = ( SearchResult ) list.next();
+ list.close();
+ Attribute creatorsName = result.getAttributes().get( "creatorsName" );
+ assertEquals( "", creatorsName.get() );
+ }
+
+
+ /**
+ * Test case for <a href="http://issues.apache.org/jira/browse/DIREVE-284" where users in
+ * mixed case partitions were not able to authenticate properly. This test case creates
+ * a new partition under dc=aPache,dc=org, it then creates the example user in the JIRA
+ * issue and attempts to authenticate as that user.
+ *
+ * @throws Exception if the user cannot authenticate or test fails
+ */
+ public void testUserAuthOnMixedCaseSuffix() throws Exception
+ {
+ final Hashtable env = new Hashtable();
+
+ env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/dc=aPache,dc=org" );
+ env.put("java.naming.ldap.version", "3");
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+ InitialDirContext ctx = new InitialDirContext( env );
+ Attributes attrs = ctx.getAttributes( "" );
+ assertTrue( attrs.get( "dc" ).get().equals( "aPache" ) );
+
+ Attributes user = new BasicAttributes( "cn", "Kate Bush", true );
+ Attribute oc = new BasicAttribute( "objectClass" );
+ oc.add( "top" );
+ oc.add( "person" );
+ oc.add( "organizationalPerson" );
+ oc.add( "inetOrgPerson" );
+ user.put( oc );
+ user.put( "sn", "Bush" );
+ user.put( "userPassword", "Aerial" );
+ ctx.createSubcontext( "cn=Kate Bush", user );
+
+ env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+ env.put( Context.SECURITY_CREDENTIALS, "Aerial" );
+ env.put( Context.SECURITY_PRINCIPAL, "cn=Kate Bush,dc=aPache,dc=org" );
+
+ InitialDirContext userCtx = new InitialDirContext( env );
+ assertNotNull( userCtx );
+ }
+
+
+ /**
+ * Tests to make sure undefined attributes in filter assertions are pruned and do not
+ * result in exceptions.
+ */
+ public void testBogusAttributeInSearchFilter() throws Exception
+ {
+ SearchControls cons = new SearchControls();
+ NamingEnumeration e = sysRoot.search( "", "(bogusAttribute=abc123)", cons );
+ assertNotNull( e );
+ assertEquals( e.getClass(), EmptyEnumeration.class );
+ e = sysRoot.search( "", "(!(bogusAttribute=abc123))", cons );
+ assertNotNull( e );
+ assertEquals( e.getClass(), EmptyEnumeration.class );
+ e = sysRoot.search( "", "(& (bogusAttribute=abc123)(bogusAttribute=abc123) )", cons );
+ assertNotNull( e );
+ assertEquals( e.getClass(), EmptyEnumeration.class );
+ e = sysRoot.search( "", "(& (bogusAttribute=abc123)(ou=abc123) )", cons );
+ assertNotNull( e );
+ assertFalse( e.getClass().equals( EmptyEnumeration.class ) );
+
+ e = sysRoot.search( "", "(OBJECTclass=*)", cons );
+ assertNotNull( e );
+ assertFalse( e.getClass().equals( EmptyEnumeration.class ) );
+
+ e = sysRoot.search( "", "(objectclass=*)", cons );
+ assertNotNull( e );
+ assertFalse( e.getClass().equals( EmptyEnumeration.class ) );
+ }
+
+
+ public void testFailureWithUnsupportedControl() throws Exception
+ {
+ Control unsupported = new Control()
+ {
+ boolean isCritical = true;
+ private static final long serialVersionUID = 1L;
+
+ public String getType()
+ {
+ return "1.1.1.1";
+ }
+
+ public void setType(String oid)
+ {
+ }
+
+ public byte[] getValue()
+ {
+ return new byte[0];
+ }
+
+ public void setValue(byte[] value)
+ {
+ }
+
+ public boolean isCritical()
+ {
+ return isCritical;
+ }
+
+ public void setCritical(boolean isCritical)
+ {
+ this.isCritical = isCritical;
+ }
+
+ public String getID()
+ {
+ return "1.1.1.1";
+ }
+
+ public byte[] getEncodedValue()
+ {
+ return new byte[0];
+ }
+ };
+ final Hashtable env = new Hashtable();
+
+ env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/ou=system" );
+ env.put("java.naming.ldap.version", "3");
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+ env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+ env.put( Context.SECURITY_CREDENTIALS, "secret" );
+ env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+ InitialLdapContext ctx = new InitialLdapContext( env, null );
+
+ Attributes user = new BasicAttributes( "cn", "Kate Bush", true );
+ Attribute oc = new BasicAttribute( "objectClass" );
+ oc.add( "top" );
+ oc.add( "person" );
+ oc.add( "organizationalPerson" );
+ oc.add( "inetOrgPerson" );
+ user.put( oc );
+ user.put( "sn", "Bush" );
+ user.put( "userPassword", "Aerial" );
+ ctx.setRequestControls( new Control[] { unsupported } );
+
+ try
+ {
+ ctx.createSubcontext( "cn=Kate Bush", user );
+ }
+ catch( OperationNotSupportedException e ) {}
+
+ unsupported.setCritical( false );
+ DirContext kate = ctx.createSubcontext( "cn=Kate Bush", user );
+ assertNotNull( kate );
+ assertTrue( ArrayUtils.isEquals( Asn1StringUtils.getBytesUtf8( "Aerial" ),
+ kate.getAttributes( "" ).get( "userPassword" ).get() ) );
+ }
+}
Added: directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyAddTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyAddTest.java?rev=375785&view=auto
==============================================================================
--- directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyAddTest.java (added)
+++ directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyAddTest.java Tue Feb 7 16:10:02 2006
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2004 Solarsis Group LLC.
+ *
+ * Licensed under the Open Software License, Version 2.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/osl-2.1.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.directory.server;
+
+import java.util.Hashtable;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.AttributeInUseException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeIdentifierException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * Testcase with different modify operations on a person entry. Each includes a
+ * single add op only. Created to demonstrate DIREVE-241 ("Adding an already
+ * existing attribute value with a modify operation does not cause an error.").
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ModifyAddTest extends AbstractServerTest
+{
+ private LdapContext ctx = null;
+ public static final String RDN = "cn=Tori Amos";
+ public static final String PERSON_DESCRIPTION = "an American singer-songwriter";
+
+
+
+ /**
+ * Creation of required attributes of a person entry.
+ */
+ protected Attributes getPersonAttributes(String sn, String cn)
+ {
+ Attributes attributes = new BasicAttributes();
+ Attribute attribute = new BasicAttribute("objectClass");
+ attribute.add("top");
+ attribute.add("person");
+ attributes.put(attribute);
+ attributes.put("cn", cn);
+ attributes.put("sn", sn);
+
+ return attributes;
+ }
+
+ /**
+ * Create context and a person entry.
+ */
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ Hashtable env = new Hashtable();
+ env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("java.naming.provider.url", "ldap://localhost:" + port + "/ou=system");
+ env.put("java.naming.security.principal", "uid=admin,ou=system");
+ env.put("java.naming.security.credentials", "secret");
+ env.put("java.naming.security.authentication", "simple");
+
+ ctx = new InitialLdapContext(env, null);
+ assertNotNull(ctx);
+
+ // Create a person with description
+ Attributes attributes = this.getPersonAttributes("Amos", "Tori Amos");
+ attributes.put("description", "an American singer-songwriter");
+ ctx.createSubcontext(RDN, attributes);
+
+ }
+
+ /**
+ * Remove person entry and close context.
+ */
+ public void tearDown() throws Exception
+ {
+ ctx.unbind(RDN);
+ ctx.close();
+ ctx = null;
+ super.tearDown();
+ }
+
+ /**
+ * Add a new attribute to a person entry.
+ *
+ * @throws NamingException
+ */
+ public void testAddNewAttributeValue() throws NamingException
+ {
+
+ // Add telephoneNumber attribute
+ String newValue = "1234567890";
+ Attributes attrs = new BasicAttributes("telephoneNumber", newValue);
+ ctx.modifyAttributes(RDN, DirContext.ADD_ATTRIBUTE, attrs);
+
+ // Verify, that attribute value is added
+ attrs = ctx.getAttributes(RDN);
+ Attribute attr = attrs.get("telephoneNumber");
+ assertNotNull(attr);
+ assertTrue(attr.contains(newValue));
+ assertEquals(1, attr.size());
+ }
+
+ /**
+ * Add a new attribute with two values.
+ *
+ * @throws NamingException
+ */
+ public void testAddNewAttributeValues() throws NamingException
+ {
+
+ // Add telephoneNumber attribute
+ String[] newValues = { "1234567890", "999999999" };
+ Attribute attr = new BasicAttribute("telephoneNumber");
+ attr.add(newValues[0]);
+ attr.add(newValues[1]);
+ Attributes attrs = new BasicAttributes();
+ attrs.put(attr);
+ ctx.modifyAttributes(RDN, DirContext.ADD_ATTRIBUTE, attrs);
+
+ // Verify, that attribute values are present
+ attrs = ctx.getAttributes(RDN);
+ attr = attrs.get("telephoneNumber");
+ assertNotNull(attr);
+ assertTrue(attr.contains(newValues[0]));
+ assertTrue(attr.contains(newValues[1]));
+ assertEquals(newValues.length, attr.size());
+ }
+
+ /**
+ * Add an additional value.
+ *
+ * @throws NamingException
+ */
+ public void testAddAdditionalAttributeValue() throws NamingException
+ {
+
+ // A new description attribute value
+ String newValue = "A new description for this person";
+ assertFalse(newValue.equals(PERSON_DESCRIPTION));
+ Attributes attrs = new BasicAttributes("description", newValue);
+
+ ctx.modifyAttributes(RDN, DirContext.ADD_ATTRIBUTE, attrs);
+
+ // Verify, that attribute value is added
+ attrs = ctx.getAttributes(RDN);
+ Attribute attr = attrs.get("description");
+ assertNotNull(attr);
+ assertTrue(attr.contains(newValue));
+ assertTrue(attr.contains(PERSON_DESCRIPTION));
+ assertEquals(2, attr.size());
+ }
+
+ /**
+ * Try to add an already existing attribute value.
+ *
+ * Expected behaviour: Modify operation fails with an
+ * AttributeInUseException. Original LDAP Error code: 20 (Indicates that the
+ * attribute value specified in a modify or add operation already exists as
+ * a value for that attribute).
+ *
+ * @throws NamingException
+ */
+ public void testAddExistingAttributeValue() throws NamingException
+ {
+
+ // Change description attribute
+ Attributes attrs = new BasicAttributes("description", PERSON_DESCRIPTION);
+ try
+ {
+ ctx.modifyAttributes(RDN, DirContext.ADD_ATTRIBUTE, attrs);
+ fail("Adding an already existing atribute value should fail.");
+ }
+ catch (AttributeInUseException e)
+ {
+ // expected behaviour
+ }
+
+ // Verify, that attribute is still there, and is the only one
+ attrs = ctx.getAttributes(RDN);
+ Attribute attr = attrs.get("description");
+ assertNotNull(attr);
+ assertTrue(attr.contains(PERSON_DESCRIPTION));
+ assertEquals(1, attr.size());
+ }
+
+ /**
+ * Try to add a duplicate attribute value to an entry, where this attribute
+ * is already present (objectclass in this case). Expected behaviour is that
+ * the modify operation causes an error (error code 20, "Attribute or value
+ * exists").
+ *
+ * @throws NamingException
+ */
+ public void testAddDuplicateValueToExistingAttribute() throws NamingException
+ {
+ // modify object classes, add a new value twice
+ Attribute ocls = new BasicAttribute("objectClass", "organizationalPerson");
+ ModificationItem[] modItems = new ModificationItem[2];
+ modItems[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, ocls);
+ modItems[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, ocls);
+ try {
+ ctx.modifyAttributes(RDN, modItems);
+ fail("Adding a duplicate attribute value should cause an error.");
+ } catch (AttributeInUseException ex) {}
+
+ // Check, whether attribute objectClass is unchanged
+ Attributes attrs = ctx.getAttributes(RDN);
+ ocls = attrs.get("objectClass");
+ assertEquals(ocls.size(), 2);
+ assertTrue(ocls.contains("top"));
+ assertTrue(ocls.contains("person"));
+ }
+
+ /**
+ * Try to add a duplicate attribute value to an entry, where this attribute
+ * is not present. Expected behaviour is that the modify operation causes an
+ * error (error code 20, "Attribute or value exists").
+ *
+ * @throws NamingException
+ */
+ public void testAddDuplicateValueToNewAttribute() throws NamingException
+ {
+ // add the same description value twice
+ Attribute desc = new BasicAttribute("description", "another description value besides songwriter");
+ ModificationItem[] modItems = new ModificationItem[2];
+ modItems[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, desc);
+ modItems[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, desc);
+ try {
+ ctx.modifyAttributes(RDN, modItems);
+ fail("Adding a duplicate attribute value should cause an error.");
+ } catch (AttributeInUseException ex) {}
+
+ // Check, whether attribute description is still not present
+ Attributes attrs = ctx.getAttributes(RDN);
+ assertEquals( 1, attrs.get("description").size() );
+ }
+
+ /**
+ * Create an entry with a bad attribute : this should fail.
+ *
+ * @throws NamingException
+ */
+ public void testAddUnexistingAttribute() throws NamingException
+ {
+ Hashtable env = new Hashtable();
+ env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("java.naming.provider.url", "ldap://localhost:" + port + "/ou=system");
+ env.put("java.naming.security.principal", "uid=admin,ou=system");
+ env.put("java.naming.security.credentials", "secret");
+ env.put("java.naming.security.authentication", "simple");
+
+ ctx = new InitialLdapContext(env, null);
+ assertNotNull(ctx);
+
+ // Create a third person with a voice attribute
+ Attributes attributes = this.getPersonAttributes("Jackson", "Michael Jackson");
+ attributes.put("voice", "He is bad ...");
+
+ try
+ {
+ ctx.createSubcontext( "cn=Mickael Jackson", attributes);
+ }
+ catch ( InvalidAttributeIdentifierException iaie)
+ {
+ assertTrue( true );
+ return;
+ }
+
+ fail( "Should never reach this point" );
+ }
+
+ /**
+ * Modu=ify the entry with a bad attribute : this should fail
+ *
+ * @throws NamingException
+ */
+ public void testSearchBadAttribute() throws NamingException
+ {
+ // Add a not existing attribute
+ String newValue = "unbelievable";
+ Attributes attrs = new BasicAttributes( "voice", newValue );
+
+ try
+ {
+ ctx.modifyAttributes( RDN, DirContext.ADD_ATTRIBUTE, attrs );
+ }
+ catch (InvalidAttributeIdentifierException iaie )
+ {
+ // We have a failure : the attribute is unknown in the schema
+ assertTrue( true );
+ return;
+ }
+
+ fail( "Cannot reach this point" );
+ }
+}
Added: directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java?rev=375785&view=auto
==============================================================================
--- directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java (added)
+++ directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java Tue Feb 7 16:10:02 2006
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.directory.server;
+
+import java.util.Hashtable;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * Testcase with different modify DN operations on a person entry.
+ * Originally created to demonstrate DIREVE-173.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ModifyRdnTest extends AbstractServerTest
+{
+
+ private LdapContext ctx = null;
+
+ /**
+ * Create attributes for a person entry.
+ */
+ protected Attributes getPersonAttributes(String sn, String cn)
+ {
+ Attributes attributes = new BasicAttributes();
+ Attribute attribute = new BasicAttribute("objectClass");
+ attribute.add("top");
+ attribute.add("person");
+ attributes.put(attribute);
+ attributes.put("cn", cn);
+ attributes.put("sn", sn);
+ attributes.put("description", cn + " is a person.");
+
+ return attributes;
+ }
+
+ /**
+ * Create context
+ */
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ Hashtable env = new Hashtable();
+ env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+ env.put("java.naming.security.principal", "uid=admin,ou=system" );
+ env.put("java.naming.security.credentials", "secret" );
+ env.put("java.naming.security.authentication", "simple");
+ ctx = new InitialLdapContext(env, null);
+ assertNotNull(ctx);
+ }
+
+ /**
+ * Close context
+ */
+ public void tearDown() throws Exception
+ {
+ ctx.close();
+ ctx = null;
+
+ super.tearDown();
+ }
+
+ /**
+ * Just a little test to check wether opening the connection succeeds.
+ */
+ public void testSetUpTearDown() throws NamingException
+ {
+ assertNotNull(ctx);
+ }
+
+ /**
+ * Modify Rdn of an entry, delete its old rdn value.
+ *
+ * @throws NamingException
+ */
+ public void testModifyRdnAndDeleteOld() throws NamingException
+ {
+ // Create a person, cn value is rdn
+ String oldCn = "Myra Ellen Amos";
+ String oldRdn = "cn=" + oldCn;
+ Attributes attributes = this.getPersonAttributes("Amos", oldCn);
+ ctx.createSubcontext(oldRdn, attributes);
+
+ // modify Rdn
+ String newCn = "Tori Amos";
+ String newRdn = "cn=" + newCn;
+ ctx.addToEnvironment("java.naming.ldap.deleteRDN", "true");
+ ctx.rename(oldRdn, newRdn);
+
+ // Check, whether old Entry does not exists
+ try {
+ ctx.lookup(oldRdn);
+ fail("Entry must not exist");
+ } catch (NameNotFoundException ignored) {
+ // expected behaviour
+ assertTrue(true);
+ }
+
+ // Check, whether new Entry exists
+ DirContext tori = (DirContext) ctx.lookup(newRdn);
+ assertNotNull(tori);
+
+ // Check values of cn
+ Attribute cn = tori.getAttributes("").get("cn");
+ assertTrue(cn.contains(newCn));
+ assertTrue(!cn.contains(oldCn)); // old value is gone
+ assertEquals(1, cn.size());
+
+ // Remove entry (use new rdn)
+ ctx.unbind(newRdn);
+ }
+
+ /**
+ * Modify Rdn of an entry, keep its old rdn value.
+ *
+ * @throws NamingException
+ */
+ public void testModifyRdnAndKeepOld() throws NamingException
+ {
+ // Create a person, cn value is rdn
+ String oldCn = "Myra Ellen Amos";
+ String oldRdn = "cn=" + oldCn;
+ Attributes attributes = this.getPersonAttributes("Amos", oldCn);
+ ctx.createSubcontext(oldRdn, attributes);
+
+ // modify Rdn
+ String newCn = "Tori Amos";
+ String newRdn = "cn=" + newCn;
+ ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false");
+ ctx.rename(oldRdn, newRdn);
+
+ // Check, whether old entry does not exist
+ try {
+ ctx.lookup(oldRdn);
+ fail("Entry must not exist");
+ } catch (NameNotFoundException ignored) {
+ // expected behaviour
+ assertTrue(true);
+ }
+
+ // Check, whether new entry exists
+ DirContext tori = (DirContext) ctx.lookup(newRdn);
+ assertNotNull(tori);
+
+ // Check values of cn
+ Attribute cn = tori.getAttributes("").get("cn");
+ assertTrue(cn.contains(newCn));
+ assertTrue(cn.contains(oldCn)); // old value is still there
+ assertEquals(2, cn.size());
+
+ // Remove entry (use new rdn)
+ ctx.unbind(newRdn);
+ }
+
+ /**
+ * Modify Rdn of an entry, delete its old rdn value. Here, the rdn attribute
+ * cn has another value as well.
+ *
+ * @throws NamingException
+ */
+ public void testModifyRdnAndDeleteOldVariant() throws NamingException
+ {
+ // Create a person, cn value is rdn
+ String oldCn = "Myra Ellen Amos";
+ String oldRdn = "cn=" + oldCn;
+ Attributes attributes = this.getPersonAttributes("Amos", oldCn);
+
+ // add a second cn value
+ String alternateCn = "Myra E. Amos";
+ Attribute cn = attributes.get("cn");
+ cn.add(alternateCn);
+ assertEquals(2, cn.size());
+
+ ctx.createSubcontext(oldRdn, attributes);
+
+ // modify Rdn
+ String newCn = "Tori Amos";
+ String newRdn = "cn=" + newCn;
+ ctx.addToEnvironment("java.naming.ldap.deleteRDN", "true");
+ ctx.rename(oldRdn, newRdn);
+
+ // Check, whether old Entry does not exist anymore
+ try {
+ ctx.lookup(oldRdn);
+ fail("Entry must not exist");
+ } catch (NameNotFoundException ignored) {
+ // expected behaviour
+ assertTrue(true);
+ }
+
+ // Check, whether new Entry exists
+ DirContext tori = (DirContext) ctx.lookup(newRdn);
+ assertNotNull(tori);
+
+ // Check values of cn
+ cn = tori.getAttributes("").get("cn");
+ assertTrue(cn.contains(newCn));
+ assertTrue(!cn.contains(oldCn)); // old value is gone
+ assertTrue(cn.contains(alternateCn)); // alternate value is still available
+ assertEquals(2, cn.size());
+
+ // Remove entry (use new rdn)
+ ctx.unbind(newRdn);
+ }
+
+ /**
+ * Modify DN of an entry, changing RDN from cn to sn.
+ *
+ * @throws NamingException
+ */
+ public void testModifyRdnDifferentAttribute() throws NamingException {
+
+ // Create a person, cn value is rdn
+ String cnVal = "Tori Amos";
+ String snVal = "Amos";
+ String oldRdn = "cn=" + cnVal;
+ Attributes attributes = this.getPersonAttributes(snVal, cnVal);
+ ctx.createSubcontext(oldRdn, attributes);
+
+ // modify Rdn from cn=... to sn=...
+ String newRdn = "sn=" + snVal;
+ ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false");
+ ctx.rename(oldRdn, newRdn);
+
+ // Check, whether old Entry does not exists
+ try {
+ ctx.lookup(oldRdn);
+ fail("Entry must not exist");
+ } catch (NameNotFoundException ignored) {
+ // expected behaviour
+ }
+
+ // Check, whether new Entry exists
+ DirContext tori = (DirContext) ctx.lookup(newRdn);
+ assertNotNull(tori);
+
+ // Check values of cn and sn
+ // especially the number of cn and sn occurences
+ Attribute cn = tori.getAttributes("").get("cn");
+ assertTrue(cn.contains(cnVal));
+ assertEquals("Number of cn occurences", 1, cn.size());
+ Attribute sn = tori.getAttributes("").get("sn");
+ assertTrue(sn.contains(snVal));
+ assertEquals("Number of sn occurences", 1, sn.size());
+
+ // Remove entry (use new rdn)
+ ctx.unbind(newRdn);
+ }
+}
Added: directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRemoveTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRemoveTest.java?rev=375785&view=auto
==============================================================================
--- directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRemoveTest.java (added)
+++ directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/ModifyRemoveTest.java Tue Feb 7 16:10:02 2006
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2004 Solarsis Group LLC.
+ *
+ * Licensed under the Open Software License, Version 2.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/osl-2.1.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.directory.server;
+
+import java.util.Hashtable;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeIdentifierException;
+import javax.naming.directory.NoSuchAttributeException;
+import javax.naming.directory.SchemaViolationException;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * Testcase with different modify operations on a person entry. Each includes a
+ * single removal op only.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ModifyRemoveTest extends AbstractServerTest
+{
+
+ private LdapContext ctx = null;
+
+ public static final String RDN = "cn=Tori Amos";
+
+ /**
+ * Creation of required attributes of a person entry.
+ */
+ protected Attributes getPersonAttributes(String sn, String cn)
+ {
+ Attributes attributes = new BasicAttributes();
+ Attribute attribute = new BasicAttribute("objectClass");
+ attribute.add("top");
+ attribute.add("person");
+ attributes.put(attribute);
+ attributes.put("cn", cn);
+ attributes.put("sn", sn);
+
+ return attributes;
+ }
+
+ /**
+ * Create context and a person entry.
+ */
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ Hashtable env = new Hashtable();
+ env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("java.naming.provider.url", "ldap://localhost:" + port + "/ou=system");
+ env.put("java.naming.security.principal", "uid=admin,ou=system");
+ env.put("java.naming.security.credentials", "secret");
+ env.put("java.naming.security.authentication", "simple");
+
+ ctx = new InitialLdapContext(env, null);
+ assertNotNull(ctx);
+
+ // Create a person with description
+ Attributes attributes = this.getPersonAttributes("Amos", "Tori Amos");
+ attributes.put("description", "an American singer-songwriter");
+ ctx.createSubcontext(RDN, attributes);
+
+ }
+
+ /**
+ * Remove person entry and close context.
+ */
+ public void tearDown() throws Exception
+ {
+ ctx.unbind(RDN);
+ ctx.close();
+ ctx = null;
+ super.tearDown();
+ }
+
+ /**
+ * Just a little test to check wether opening the connection and creation of
+ * the person succeeds succeeds.
+ */
+ public void testSetUpTearDown() throws NamingException
+ {
+ assertNotNull(ctx);
+ DirContext tori = (DirContext) ctx.lookup(RDN);
+ assertNotNull(tori);
+ }
+
+ /**
+ * Remove an attribute, which is not required.
+ *
+ * Expected result: After successful deletion, attribute is not present in
+ * entry.
+ *
+ * @throws NamingException
+ */
+ public void testRemoveNotRequiredAttribute() throws NamingException
+ {
+ // Remove description Attribute
+ Attribute attr = new BasicAttribute("description");
+ Attributes attrs = new BasicAttributes();
+ attrs.put(attr);
+ ctx.modifyAttributes(RDN, DirContext.REMOVE_ATTRIBUTE, attrs);
+
+ // Verify, that attribute is deleted
+ attrs = ctx.getAttributes(RDN);
+ attr = attrs.get("description");
+ assertNull(attr);
+ }
+
+ /**
+ * Remove two not required attributes.
+ *
+ * Expected result: After successful deletion, both attributes ar not
+ * present in entry.
+ *
+ * @throws NamingException
+ */
+ public void testRemoveTwoNotRequiredAttributes() throws NamingException
+ {
+
+ // add telephoneNumber to entry
+ Attributes tn = new BasicAttributes("telephoneNumber", "12345678");
+ ctx.modifyAttributes(RDN, DirContext.ADD_ATTRIBUTE, tn);
+
+ // Remove description and telephoneNumber to Attribute
+ Attributes attrs = new BasicAttributes();
+ attrs.put(new BasicAttribute("description"));
+ attrs.put(new BasicAttribute("telephoneNumber"));
+ ctx.modifyAttributes(RDN, DirContext.REMOVE_ATTRIBUTE, attrs);
+
+ // Verify, that attributes are deleted
+ attrs = ctx.getAttributes(RDN);
+ assertNull(attrs.get("description"));
+ assertNull(attrs.get("telephoneNumber"));
+ assertNotNull(attrs.get("cn"));
+ assertNotNull(attrs.get("sn"));
+ }
+
+ /**
+ * Remove a required attribute. The sn attribute of the person entry is used
+ * here.
+ *
+ * Expected Result: Deletion fails with NamingException (Schema Violation).
+ *
+ * @throws NamingException
+ */
+ public void testRemoveRequiredAttribute() throws NamingException
+ {
+
+ // Remove sn attribute
+ Attribute attr = new BasicAttribute("sn");
+ Attributes attrs = new BasicAttributes();
+ attrs.put(attr);
+
+ try {
+ ctx.modifyAttributes(RDN, DirContext.REMOVE_ATTRIBUTE, attrs);
+ fail("Deletion of required attribute should fail.");
+ } catch (SchemaViolationException e) {
+ // expected behaviour
+ }
+ }
+
+ /**
+ * Remove a required attribute from RDN.
+ *
+ * Expected Result: Deletion fails with SchemaViolationException.
+ *
+ * @throws NamingException
+ */
+ public void testRemovePartOfRdn() throws NamingException
+ {
+
+ // Remove sn attribute
+ Attribute attr = new BasicAttribute("cn");
+ Attributes attrs = new BasicAttributes();
+ attrs.put(attr);
+
+ try {
+ ctx.modifyAttributes(RDN, DirContext.REMOVE_ATTRIBUTE, attrs);
+ fail("Deletion of RDN attribute should fail.");
+ } catch (SchemaViolationException e) {
+ // expected behaviour
+ }
+ }
+
+ /**
+ * Remove a not required attribute from RDN.
+ *
+ * Expected Result: Deletion fails with SchemaViolationException.
+ *
+ * @throws NamingException
+ */
+ public void testRemovePartOfRdnNotRequired() throws NamingException
+ {
+
+ // Change RDN to another attribute
+ String newRdn = "description=an American singer-songwriter";
+ ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false");
+ ctx.rename(RDN, newRdn);
+
+ // Remove description, which is now RDN attribute
+ Attribute attr = new BasicAttribute("description");
+ Attributes attrs = new BasicAttributes();
+ attrs.put(attr);
+
+ try {
+ ctx.modifyAttributes(newRdn, DirContext.REMOVE_ATTRIBUTE, attrs);
+ fail("Deletion of RDN attribute should fail.");
+ } catch (SchemaViolationException e) {
+ // expected behaviour
+ }
+
+ // Change RDN back to original
+ ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false");
+ ctx.rename(newRdn, RDN);
+ }
+
+ /**
+ * Remove a an attribute which is not present on the entry, but in the
+ * schema.
+ *
+ * Expected result: Deletion fails with NoSuchAttributeException
+ *
+ * @throws NamingException
+ */
+ public void testRemoveAttributeNotPresent() throws NamingException
+ {
+
+ // Remove telephoneNumber Attribute
+ Attribute attr = new BasicAttribute("telephoneNumber");
+ Attributes attrs = new BasicAttributes();
+ attrs.put(attr);
+
+ try {
+ ctx.modifyAttributes(RDN, DirContext.REMOVE_ATTRIBUTE, attrs);
+ fail("Deletion of attribute, which is not present in the entry, should fail.");
+ } catch (NoSuchAttributeException e) {
+ // expected behaviour
+ }
+ }
+
+ /**
+ * Remove a an attribute which is not present in the schema.
+ *
+ * Expected result: Deletion fails with NoSuchAttributeException
+ *
+ * @throws NamingException
+ */
+ public void testRemoveAttributeNotValid() throws NamingException
+ {
+
+ // Remove phantasy attribute
+ Attribute attr = new BasicAttribute("XXX");
+ Attributes attrs = new BasicAttributes();
+ attrs.put(attr);
+
+ try {
+ ctx.modifyAttributes(RDN, DirContext.REMOVE_ATTRIBUTE, attrs);
+ fail("Deletion of an invalid attribute should fail.");
+ } catch (NoSuchAttributeException e) {
+ // expected behaviour
+ } catch (InvalidAttributeIdentifierException e) {
+ // expected behaviour
+ }
+ }
+
+}
Added: directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/PersistentSearchTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/PersistentSearchTest.java?rev=375785&view=auto
==============================================================================
--- directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/PersistentSearchTest.java (added)
+++ directory/trunks/apacheds/server-unit/src/test/java/org/apache/directory/server/PersistentSearchTest.java Tue Feb 7 16:10:02 2006
@@ -0,0 +1,744 @@
+/*
+ * Copyright (c) 2004 Solarsis Group LLC.
+ *
+ * Licensed under the Open Software License, Version 2.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/osl-2.1.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.directory.server;
+
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchResult;
+import javax.naming.event.EventContext;
+import javax.naming.event.EventDirContext;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.ObjectChangeListener;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.HasControls;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
+import org.apache.directory.shared.ldap.codec.search.controls.EntryChangeControl;
+import org.apache.directory.shared.ldap.codec.search.controls.EntryChangeControlDecoder;
+import org.apache.directory.shared.ldap.message.PersistentSearchControl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Testcase which tests the correct operation of the persistent search control.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PersistentSearchTest extends AbstractServerTest
+{
+ public static final Logger log = LoggerFactory.getLogger( PersistentSearchTest.class );
+ public static final String PERSON_DESCRIPTION = "an American singer-songwriter";
+ public static final String RDN = "cn=Tori Amos";
+ private LdapContext ctx = null;
+
+
+
+ /**
+ * Creation of required attributes of a person entry.
+ */
+ protected Attributes getPersonAttributes( String sn, String cn )
+ {
+ Attributes attributes = new BasicAttributes();
+ Attribute attribute = new BasicAttribute( "objectClass" );
+ attribute.add( "top" );
+ attribute.add( "person" );
+ attributes.put( attribute );
+ attributes.put( "cn", cn );
+ attributes.put( "sn", sn );
+
+ return attributes;
+ }
+
+
+ /**
+ * Create context and a person entry.
+ */
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ Hashtable env = new Hashtable();
+ env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+ env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+ env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+ env.put( "java.naming.security.credentials", "secret" );
+ env.put( "java.naming.security.authentication", "simple" );
+
+ ctx = new InitialLdapContext( env, null );
+ assertNotNull( ctx );
+
+ // Create a person with description
+ Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" );
+ attributes.put( "description", PERSON_DESCRIPTION );
+ ctx.createSubcontext( RDN, attributes );
+ }
+
+
+ /**
+ * Remove person entry and close context.
+ */
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ ctx.unbind( RDN );
+ ctx.close();
+ ctx = null;
+ super.tearDown();
+ }
+ catch( Throwable t )
+ {
+ }
+ }
+
+
+ /**
+ * Shows correct notifications for modify(4) changes.
+ */
+ public void testPsearchModify() throws Exception
+ {
+ PSearchListener listener = new PSearchListener();
+ Thread t = new Thread( listener, "PSearchListener" );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ }
+
+
+ /**
+ * Shows correct notifications for moddn(8) changes.
+ */
+ public void testPsearchModifyDn() throws Exception
+ {
+ PSearchListener listener = new PSearchListener();
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.rename( RDN, "cn=Jack Black" );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ }
+
+
+ /**
+ * Shows correct notifications for delete(2) changes.
+ */
+ public void testPsearchDelete() throws Exception
+ {
+ PSearchListener listener = new PSearchListener();
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.destroySubcontext( RDN );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ }
+
+
+ /**
+ * Shows correct notifications for add(1) changes.
+ */
+ public void testPsearchAdd() throws Exception
+ {
+ PSearchListener listener = new PSearchListener();
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ }
+
+
+ /**
+ * Shows correct notifications for modify(4) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchModifyWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener, "PSearchListener" );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+ }
+
+
+ /**
+ * Shows correct notifications for moddn(8) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchModifyDnWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.rename( RDN, "cn=Jack Black" );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODDN );
+ assertEquals( ( RDN + ",ou=system" ).toLowerCase(), listener.result.control.getPreviousDn().toLowerCase() );
+ }
+
+
+ /**
+ * Shows correct notifications for delete(2) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchDeleteWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.destroySubcontext( RDN );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.DELETE );
+ }
+
+
+ /**
+ * Shows correct notifications for add(1) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchAddWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+ }
+
+
+ /**
+ * Shows correct notifications for only add(1) and modify(4) registered changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchAddModifyEnabledWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ control.setChangeTypes( ChangeType.ADD_VALUE );
+ control.enableNotification( ChangeType.MODIFY );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+ listener.result = null;
+ t = new Thread( listener );
+ t.start();
+
+ ctx.destroySubcontext( "cn=Jack Black" );
+
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNull( listener.result );
+
+ // thread is still waiting for notifications try a modify
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+ }
+
+
+ /**
+ * Shows correct notifications for add(1) changes with returned
+ * EntryChangeControl and changesOnly set to false so we return
+ * the first set of entries.
+ *
+ * This test is commented out because it exhibits some producer
+ * consumer lockups (server and client being in same process)
+ *
+ * PLUS ALL THIS GARBAGE IS TIME DEPENDENT!!!!!
+ */
+// public void testPsearchAddWithECAndFalseChangesOnly() throws Exception
+// {
+// PersistentSearchControl control = new PersistentSearchControl();
+// control.setReturnECs( true );
+// control.setChangesOnly( false );
+// PSearchListener listener = new PSearchListener( control );
+// Thread t = new Thread( listener );
+// t.start();
+//
+// Thread.sleep( 3000 );
+//
+// assertEquals( 5, listener.count );
+// ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+//
+// long start = System.currentTimeMillis();
+// while ( t.isAlive() )
+// {
+// Thread.sleep( 100 );
+// if ( System.currentTimeMillis() - start > 3000 )
+// {
+// break;
+// }
+// }
+//
+// assertEquals( 6, listener.count );
+// assertNotNull( listener.result );
+// // darn it getting normalized name back
+// assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+// assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+// }
+
+
+ /**
+ * Shows notifications functioning with the JNDI notification API of the SUN
+ * provider.
+ */
+ public void testPsearchUsingJndiNotifications() throws Exception
+ {
+ Hashtable env = new Hashtable();
+ env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+ env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+ env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+ env.put( "java.naming.security.credentials", "secret" );
+ env.put( "java.naming.security.authentication", "simple" );
+
+ JndiNotificationListener listener = new JndiNotificationListener();
+ InitialDirContext idc = new InitialDirContext( env );
+ EventDirContext edc = ( EventDirContext ) ( idc.lookup( "" ) );
+ edc.addNamingListener( "", EventContext.ONELEVEL_SCOPE, listener );
+
+ while ( listener.list.isEmpty() )
+ {
+ Thread.sleep( 250 );
+ String rdn = "cn=Jack Black";
+ ctx.createSubcontext( rdn, getPersonAttributes( "Black", "Jack Black" ) );
+ ctx.destroySubcontext( rdn );
+ }
+
+ NamingEvent event = ( NamingEvent ) listener.list.get( 0 );
+ assertEquals( edc, event.getSource() );
+ }
+
+
+ /**
+ * Shows notifications functioning with the JNDI notification API of the SUN
+ * provider.
+ */
+ public void testPsearchAbandon() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+ listener.result = null;
+ t = new Thread( listener );
+ t.start();
+
+ ctx.destroySubcontext( "cn=Jack Black" );
+
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ // there seems to be a race condition here
+ // assertNull( listener.result );
+
+ // thread is still waiting for notifications try a modify
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+ }
+
+
+ class JndiNotificationListener implements NamespaceChangeListener, ObjectChangeListener
+ {
+ ArrayList list = new ArrayList();
+
+ public void objectAdded(NamingEvent evt)
+ {
+ list.add( 0, evt );
+ }
+
+ public void objectRemoved(NamingEvent evt)
+ {
+ list.add( 0, evt );
+ }
+
+ public void objectRenamed(NamingEvent evt)
+ {
+ list.add( 0, evt );
+ }
+
+ public void namingExceptionThrown(NamingExceptionEvent evt)
+ {
+ list.add( 0, evt );
+ }
+
+ public void objectChanged(NamingEvent evt)
+ {
+ list.add( 0, evt );
+ }
+ }
+
+
+ class PSearchListener implements Runnable
+ {
+ boolean isReady = false;
+ PSearchNotification result;
+ int count = 0;
+ final PersistentSearchControl control;
+
+ PSearchListener() { control = new PersistentSearchControl(); }
+ PSearchListener( PersistentSearchControl control ) { this.control = control; }
+
+ public void run()
+ {
+ NamingEnumeration list = null;
+ control.setCritical( true );
+ Control[] ctxCtls = new Control[] { control };
+
+ try
+ {
+ ctx.setRequestControls( ctxCtls );
+ isReady = true;
+ list = ctx.search( "", "objectClass=*", null );
+ EntryChangeControl ecControl = null;
+
+ while( list.hasMore() )
+ {
+ Control[] controls = null;
+ SearchResult sresult = ( SearchResult ) list.next();
+ count++;
+ if ( sresult instanceof HasControls )
+ {
+ controls = ( ( HasControls ) sresult ).getControls();
+ if ( controls != null )
+ {
+ for ( int ii = 0; ii < controls.length; ii ++ )
+ {
+ if ( controls[ii].getID().equals(
+ org.apache.directory.shared.ldap.message.EntryChangeControl.CONTROL_OID ) )
+ {
+ EntryChangeControlDecoder decoder = new EntryChangeControlDecoder();
+ ecControl = ( EntryChangeControl ) decoder.decode( controls[ii].getEncodedValue() );
+ }
+ }
+ }
+ }
+ result = new PSearchNotification( sresult, ecControl );
+ break;
+ }
+ }
+ catch( Exception e )
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ if ( list != null )
+ {
+ try { list.close(); } catch ( Exception e ) { e.printStackTrace(); };
+ }
+ }
+ }
+ }
+
+
+ class PSearchNotification extends SearchResult
+ {
+ private static final long serialVersionUID = 1L;
+ final EntryChangeControl control;
+
+ public PSearchNotification( SearchResult result, EntryChangeControl control )
+ {
+ super( result.getName(), result.getClassName(), result.getObject(), result.getAttributes(), result.isRelative() );
+ this.control = control;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "DN: " ).append( getName() ).append( "\n" );
+ if ( control != null )
+ {
+ buf.append( " EntryChangeControl =\n" );
+ buf.append( " changeType : " ).append( control.getChangeType() ).append( "\n" );
+ buf.append( " previousDN : " ).append( control.getPreviousDn() ).append( "\n" );
+ buf.append( " changeNumber : " ).append( control.getChangeNumber() ).append( "\n" );
+ }
+ return buf.toString();
+ }
+ }
+}
|