diff --git a/spring-ldap/src/main/java/org/springframework/ldap/core/LdapOperations.java b/spring-ldap/src/main/java/org/springframework/ldap/core/LdapOperations.java index 495b2e21..323e5552 100644 --- a/spring-ldap/src/main/java/org/springframework/ldap/core/LdapOperations.java +++ b/spring-ldap/src/main/java/org/springframework/ldap/core/LdapOperations.java @@ -24,6 +24,7 @@ import javax.naming.NameClassPair; import javax.naming.directory.Attributes; import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; +import javax.naming.spi.DirObjectFactory; import org.springframework.ldap.ContextNotEmptyException; import org.springframework.ldap.NamingException; @@ -1017,34 +1018,30 @@ public interface LdapOperations { throws NamingException; /** - * Lookup the supplied DN and return the found object. WARNING: This - * method should only be used if a DirObjectFactory has been specified on - * the ContextFactory. If this is not the case, you will get a new instance - * of the actual DirContext, which is probably not what you want. If, - * however this is what you want, be careful to close the context - * after you finished working with it. + * Lookup the supplied DN and return the found object. This will typically + * be a {@link DirContextAdapter}, unless the DirObjectFactory has been + * modified in the ContextSource. * * @param dn * The distinguished name of the object to find. - * @return the found object. + * @return the found object, typically a {@link DirContextAdapter} instance. * @throws NamingException * if any error occurs. + * @see #lookupContext(Name) */ public Object lookup(Name dn) throws NamingException; /** - * Lookup the supplied DN and return the found object. WARNING: This - * method should only be used if a DirObjectFactory has been specified on - * the ContextFactory. If this is not the case, you will get a new instance - * of the actual DirContext, which is probably not what you want. If, - * however this is what you want, be careful to close the context - * after you finished working with it. + * Lookup the supplied DN and return the found object. This will typically + * be a {@link DirContextAdapter}, unless the DirObjectFactory has been + * modified in the ContextSource. * * @param dn * The distinguished name of the object to find. - * @return the found object. + * @return the found object, typically a {@link DirContextAdapter} instance. * @throws NamingException * if any error occurs. + * @see #lookupContext(Name) */ public Object lookup(String dn) throws NamingException; @@ -1366,4 +1363,77 @@ public interface LdapOperations { */ public void rename(final String oldDn, final String newDn) throws NamingException; + + /** + * Convenience method to lookup the supplied DN and automatically cast it to + * {@link DirContextOperations}. + * + * @param dn + * The distinguished name of the object to find. + * @return The found object, cast to {@link DirContextOperations}. + * @throws ClassCastException + * if an alternative DirObjectFactory has been registered woth + * the ContextSource, causing the actual class of the returned + * object to be something else than {@link DirContextOperations}. + * @throws NamingException + * if any other error occurs. + * @see #lookup(Name) + */ + public DirContextOperations lookupContext(Name dn) throws NamingException, + ClassCastException; + + /** + * Convenience method to lookup the supplied DN and automatically cast it to + * {@link DirContextOperations}. + * + * @param dn + * The distinguished name of the object to find. + * @return The found object, cast to {@link DirContextOperations}. + * @throws ClassCastException + * if an alternative DirObjectFactory has been registered woth + * the ContextSource, causing the actual class of the returned + * object to be something else than {@link DirContextOperations}. + * @throws NamingException + * if any other error occurs. + * @see #lookup(String) + */ + public DirContextOperations lookupContext(String dn) + throws NamingException, ClassCastException; + + /** + * Modify the attributes of the entry referenced by the supplied + * {@link DirContextOperations} instance. The DN to update will be the DN of + * the DirContextOperationsinstance, and the + * ModificationItem array is retrieved from the + * DirContextOperations instance using a call to + * {@link AttributeModificationsAware#getModificationItems()}. NB: + * The supplied instance needs to have been properly initialized; this means + * that if it hasn't been received from a lookup operation, + * its DN needs to be initialized and it must have been put in update mode ({@link DirContextAdapter#setUpdateMode(boolean)}). + *

+ * Typical use of this method would be as follows: + * + *

+     * public void update(Person person) {
+     *     DirContextOperations ctx = ldapOperations.lookupContext(person.getDn());
+     * 
+     *     ctx.setAttributeValue("description", person.getDescription());
+     *     ctx.setAttributeValue("telephoneNumber", person.getPhone());
+     *     // More modifications here
+     * 
+     *     ldapOperations.modifyAttributes(ctx);
+     * }
+     * 
+ * + * @param ctx + * the DirContextOperations instance to use in the update. + * @throws IllegalStateException + * if the supplied instance is not in update mode or has not + * been properly initialized. + * @throws NamingException + * if any other error occurs. + * + */ + public void modifyAttributes(DirContextOperations ctx) + throws IllegalStateException, NamingException; } diff --git a/spring-ldap/src/main/java/org/springframework/ldap/core/LdapTemplate.java b/spring-ldap/src/main/java/org/springframework/ldap/core/LdapTemplate.java index 0a2aab5a..b06f2da8 100644 --- a/spring-ldap/src/main/java/org/springframework/ldap/core/LdapTemplate.java +++ b/spring-ldap/src/main/java/org/springframework/ldap/core/LdapTemplate.java @@ -1294,4 +1294,23 @@ public class LdapTemplate implements LdapOperations, InitializingBean { } } } + + public DirContextOperations lookupContext(Name dn) { + return (DirContextOperations) lookup(dn); + } + + public DirContextOperations lookupContext(String dn) { + return (DirContextOperations) lookup(dn); + } + + public void modifyAttributes(DirContextOperations ctx) { + Name dn = ctx.getDn(); + if (dn != null && ctx.isUpdateMode()) { + modifyAttributes(dn, ctx.getModificationItems()); + } else { + throw new IllegalStateException( + "The DirContextOperations instance needs to be properly initialized."); + } + + } } diff --git a/spring-ldap/src/test/java/org/springframework/ldap/core/LdapTemplateTest.java b/spring-ldap/src/test/java/org/springframework/ldap/core/LdapTemplateTest.java index 5ec774a1..325e994c 100644 --- a/spring-ldap/src/test/java/org/springframework/ldap/core/LdapTemplateTest.java +++ b/spring-ldap/src/test/java/org/springframework/ldap/core/LdapTemplateTest.java @@ -82,6 +82,10 @@ public class LdapTemplateTest extends TestCase { private DirContextProcessor dirContextProcessorMock; + private MockControl dirContextOperationsConrol; + + private DirContextOperations dirContextOperationsMock; + protected void setUp() throws Exception { super.setUp(); @@ -129,6 +133,11 @@ public class LdapTemplateTest extends TestCase { dirContextProcessorMock = (DirContextProcessor) dirContextProcessorControl .getMock(); + dirContextOperationsConrol = MockControl + .createControl(DirContextOperations.class); + dirContextOperationsMock = (DirContextOperations) dirContextOperationsConrol + .getMock(); + tested = new LdapTemplate(contextSourceMock); } @@ -164,6 +173,10 @@ public class LdapTemplateTest extends TestCase { dirContextProcessorControl = null; dirContextProcessorMock = null; + + dirContextOperationsConrol = null; + dirContextOperationsMock = null; + } protected void replay() { @@ -177,6 +190,7 @@ public class LdapTemplateTest extends TestCase { contextExecutorControl.replay(); searchExecutorControl.replay(); dirContextProcessorControl.replay(); + dirContextOperationsConrol.replay(); } protected void verify() { @@ -190,6 +204,7 @@ public class LdapTemplateTest extends TestCase { contextExecutorControl.verify(); searchExecutorControl.verify(); dirContextProcessorControl.verify(); + dirContextOperationsConrol.verify(); } private void expectGetReadWriteContext() { @@ -1584,7 +1599,8 @@ public class LdapTemplateTest extends TestCase { expectGetReadOnlyContext(); searchExecutorControl.expectAndThrow(searchExecutorMock - .executeSearch(dirContextMock), new javax.naming.NameNotFoundException()); + .executeSearch(dirContextMock), + new javax.naming.NameNotFoundException()); dirContextMock.close(); replay(); @@ -1639,6 +1655,111 @@ public class LdapTemplateTest extends TestCase { verify(); } + public void testLookupContextWithName() { + final DirContextAdapter expectedResult = new DirContextAdapter(); + + LdapTemplate tested = new LdapTemplate() { + public Object lookup(Name dn) { + assertSame(DistinguishedName.EMPTY_PATH, dn); + return expectedResult; + } + }; + + DirContextOperations result = tested + .lookupContext(DistinguishedName.EMPTY_PATH); + assertSame(expectedResult, result); + + } + + public void testLookupContextWithString() { + final DirContextAdapter expectedResult = new DirContextAdapter(); + final String expectedName = "cn=John Doe"; + + LdapTemplate tested = new LdapTemplate() { + public Object lookup(String dn) { + assertSame(expectedName, dn); + return expectedResult; + } + }; + + DirContextOperations result = tested.lookupContext(expectedName); + assertSame(expectedResult, result); + } + + public void testModifyAttributesWithDirContextOperations() throws Exception { + final ModificationItem[] expectedModifications = new ModificationItem[0]; + + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getDn(), DistinguishedName.EMPTY_PATH); + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .isUpdateMode(), true); + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getModificationItems(), expectedModifications); + + LdapTemplate tested = new LdapTemplate() { + public void modifyAttributes(Name dn, ModificationItem[] mods) { + assertSame(DistinguishedName.EMPTY_PATH, dn); + assertSame(expectedModifications, mods); + } + }; + + replay(); + + tested.modifyAttributes(dirContextOperationsMock); + + verify(); + } + + public void testModifyAttributesWithDirContextOperationsNotInitializedDn() + throws Exception { + final ModificationItem[] expectedModifications = new ModificationItem[0]; + + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getDn(), DistinguishedName.EMPTY_PATH); + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .isUpdateMode(), false); + + LdapTemplate tested = new LdapTemplate() { + public void modifyAttributes(Name dn, ModificationItem[] mods) { + fail("The call to the base modifyAttributes should not have occured."); + } + }; + + replay(); + + try { + tested.modifyAttributes(dirContextOperationsMock); + fail("IllegalStateException expected"); + } catch (IllegalStateException expected) { + assertTrue(true); + } + verify(); + } + + public void testModifyAttributesWithDirContextOperationsNotInitializedInUpdateMode() + throws Exception { + final ModificationItem[] expectedModifications = new ModificationItem[0]; + + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getDn(), null); + + LdapTemplate tested = new LdapTemplate() { + public void modifyAttributes(Name dn, ModificationItem[] mods) { + fail("The call to the base modifyAttributes should not have occured."); + } + }; + + replay(); + + try { + tested.modifyAttributes(dirContextOperationsMock); + fail("IllegalStateException expected"); + } catch (IllegalStateException expected) { + assertTrue(true); + } + verify(); + } + /** * Needed to verify search control values. *