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.
*