diff --git a/core/src/main/java/org/springframework/ldap/NoSuchAttributeException.java b/core/src/main/java/org/springframework/ldap/NoSuchAttributeException.java index b7739bb9..7fa42abb 100644 --- a/core/src/main/java/org/springframework/ldap/NoSuchAttributeException.java +++ b/core/src/main/java/org/springframework/ldap/NoSuchAttributeException.java @@ -25,8 +25,11 @@ package org.springframework.ldap; */ public class NoSuchAttributeException extends NamingException { - public NoSuchAttributeException( - javax.naming.directory.NoSuchAttributeException cause) { - super(cause); - } + public NoSuchAttributeException(String message) { + super(message); + } + + public NoSuchAttributeException(javax.naming.directory.NoSuchAttributeException cause) { + super(cause); + } } diff --git a/core/src/main/java/org/springframework/ldap/core/AttributesMapperCallbackHandler.java b/core/src/main/java/org/springframework/ldap/core/AttributesMapperCallbackHandler.java index dfbdd552..1ac45216 100644 --- a/core/src/main/java/org/springframework/ldap/core/AttributesMapperCallbackHandler.java +++ b/core/src/main/java/org/springframework/ldap/core/AttributesMapperCallbackHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2007 the original author or authors. + * Copyright 2005-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java b/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java index 2a112f74..f3f71d80 100644 --- a/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java +++ b/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java @@ -42,6 +42,7 @@ import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.NoSuchAttributeException; import org.springframework.ldap.support.LdapUtils; import org.springframework.util.StringUtils; @@ -709,25 +710,36 @@ public class DirContextAdapter implements DirContextOperations { * (java.lang.String) */ public String[] getStringAttributes(String name) { - String[] attributes; - - Attribute attribute = originalAttrs.get(name); - if (attribute != null && attribute.size() > 0) { - attributes = new String[attribute.size()]; - for (int i = 0; i < attribute.size(); i++) { - try { - attributes[i] = (String) attribute.get(i); - } - catch (NamingException e) { - throw LdapUtils.convertLdapException(e); - } - } + try { + return (String[]) collectAttributeValuesAsList(name).toArray(new String[0]); } - else { + catch (NoSuchAttributeException e) { + // The attribute does not exist - contract says to return null. return null; } + } - return attributes; + /* + * (non-Javadoc) + * + * @see + * org.springframework.ldap.core.DirContextOperations#getObjectAttributes + * (java.lang.String) + */ + public Object[] getObjectAttributes(String name) { + try { + return collectAttributeValuesAsList(name).toArray(new Object[0]); + } + catch (NoSuchAttributeException e) { + // The attribute does not exist - contract says to return null. + return null; + } + } + + private List collectAttributeValuesAsList(String name) { + List list = new LinkedList(); + LdapUtils.collectAttributeValues(originalAttrs, name, list); + return list; } /* @@ -735,24 +747,15 @@ public class DirContextAdapter implements DirContextOperations { * getAttributeSortedStringSet(java.lang.String) */ public SortedSet getAttributeSortedStringSet(String name) { - TreeSet attrSet = new TreeSet(); - - Attribute attribute = originalAttrs.get(name); - if (attribute != null) { - for (int i = 0; i < attribute.size(); i++) { - try { - attrSet.add(attribute.get(i)); - } - catch (NamingException e) { - throw LdapUtils.convertLdapException(e); - } - } + try { + TreeSet attrSet = new TreeSet(); + LdapUtils.collectAttributeValues(originalAttrs, name, attrSet); + return attrSet; } - else { + catch (NoSuchAttributeException e) { + // The attribute does not exist - contract says to return null. return null; } - - return attrSet; } /** @@ -1299,4 +1302,5 @@ public class DirContextAdapter implements DirContextOperations { public boolean isReferral() { return StringUtils.hasLength(referralUrl); } + } diff --git a/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java b/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java index 8d16db99..892a96c6 100644 --- a/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java +++ b/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java @@ -144,16 +144,30 @@ public interface DirContextOperations extends DirContext, AttributeModifications * Get all values of a String attribute. * * @param name name of the attribute. - * - * @return all registered values of the attribute. + * @return a (possibly empty) array containing all registered values of the + * attribute as Strings if the attribute is defined or null + * otherwise. + * @throws ArrayStoreException if any of the attribute values is not a + * String. */ String[] getStringAttributes(String name); + /** + * Get all values of an Object attribute. + * + * @param name name of the attribute. + * @return a (possibly empty) array containing all registered values of the + * attribute if the attribute is defined or null otherwise. + * @since 1.3 + */ + Object[] getObjectAttributes(String name); + /** * Get all String values of the attribute as a SortedSet. * * @param name name of the attribute. - * @return a SortedSet containing all values of the attribute. + * @return a SortedSet containing all values of the attribute, + * or null if the attribute does not exist. */ SortedSet getAttributeSortedStringSet(String name); diff --git a/core/src/main/java/org/springframework/ldap/support/AttributeValueCallbackHandler.java b/core/src/main/java/org/springframework/ldap/support/AttributeValueCallbackHandler.java new file mode 100644 index 00000000..44118eb3 --- /dev/null +++ b/core/src/main/java/org/springframework/ldap/support/AttributeValueCallbackHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2008 the original author or authors. + * + * 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.springframework.ldap.support; + +/** + * Callback interface for use when looping through Attribute values. + * + * @author Mattias Hellborg Arthursson + */ +public interface AttributeValueCallbackHandler { + /** + * Implement to take handle one of the Attribute values. + * + * @param attributeName the name of the Attribute. + * @param attributeValue the value. + * @param index the index of the value within the Attribute. + */ + void handleAttributeValue(String attributeName, Object attributeValue, int index); +} diff --git a/core/src/main/java/org/springframework/ldap/support/LdapUtils.java b/core/src/main/java/org/springframework/ldap/support/LdapUtils.java index 80c453e8..52d2d37a 100644 --- a/core/src/main/java/org/springframework/ldap/support/LdapUtils.java +++ b/core/src/main/java/org/springframework/ldap/support/LdapUtils.java @@ -16,12 +16,17 @@ package org.springframework.ldap.support; +import java.util.Collection; + +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.ldap.LdapContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.ldap.NamingException; +import org.springframework.ldap.NoSuchAttributeException; import org.springframework.util.Assert; /** @@ -65,9 +70,9 @@ public final class LdapUtils { } /** - * Convert the specified checked - * {@link javax.naming.NamingException NamingException} to a Spring LDAP - * runtime {@link org.springframework.ldap.NamingException NamingException} + * Convert the specified checked {@link javax.naming.NamingException + * NamingException} to a Spring LDAP runtime + * {@link org.springframework.ldap.NamingException NamingException} * equivalent. * * @param ex the original checked NamingException to convert @@ -127,8 +132,9 @@ public final class LdapUtils { return new org.springframework.ldap.InvalidSearchFilterException( (javax.naming.directory.InvalidSearchFilterException) ex); } - - // this class is abstract, so it can never be of exactly this class; using instanceof + + // this class is abstract, so it can never be of exactly this class; + // using instanceof if (ex instanceof javax.naming.ldap.LdapReferralException) { return new org.springframework.ldap.LdapReferralException((javax.naming.ldap.LdapReferralException) ex); } @@ -212,16 +218,74 @@ public final class LdapUtils { * Get the actual class of the supplied DirContext instance; LdapContext or * DirContext. * - * @param context - * the DirContext instance to check. + * @param context the DirContext instance to check. * @return LdapContext.class if context is an LdapContext, DirContext.class - * otherwise. + * otherwise. */ public static Class getActualTargetClass(DirContext context) { - if (context instanceof LdapContext) { - return LdapContext.class; - } - - return DirContext.class; + if (context instanceof LdapContext) { + return LdapContext.class; + } + + return DirContext.class; + } + + /** + * Collect all the values of a the specified attribute from the supplied + * Attributes. + * + * @param attributes The Attributes; not null. + * @param name The name of the Attribute to get values for. + * @param collection the collection to collect the values in. + * @throws NoSuchAttributeException if no attribute with the specified name + * exists. + */ + public static void collectAttributeValues(Attributes attributes, String name, Collection collection) { + Assert.notNull(attributes, "Attributes must not be null"); + Attribute attribute = attributes.get(name); + if (attribute == null) { + throw new NoSuchAttributeException("No attribute with name '" + name + "'"); + } + + iterateAttributeValues(attribute, new CollectingAttributeValueCallbackHandler(collection)); + } + + /** + * Iterate through all the values of the specified Attribute calling back to + * the specified callbackHandler. + * @param attribute the Attribute to work with; not null. + * @param callbackHandler the callbackHandler; not null. + */ + public static void iterateAttributeValues(Attribute attribute, AttributeValueCallbackHandler callbackHandler) { + Assert.notNull(attribute, "Attribute must not be null"); + Assert.notNull(callbackHandler, "callbackHandler must not be null"); + + for (int i = 0; i < attribute.size(); i++) { + try { + callbackHandler.handleAttributeValue(attribute.getID(), attribute.get(i), i); + } + catch (javax.naming.NamingException e) { + throw LdapUtils.convertLdapException(e); + } + } + } + + /** + * An {@link AttributeValueCallbackHandler} to collect values in a supplied + * collection. + * + * @author Mattias Hellborg Arthursson + */ + private final static class CollectingAttributeValueCallbackHandler implements AttributeValueCallbackHandler { + private final Collection collection; + + public CollectingAttributeValueCallbackHandler(Collection collection) { + Assert.notNull(collection, "Collection must not be null"); + this.collection = collection; + } + + public final void handleAttributeValue(String attributeName, Object attributeValue, int index) { + collection.add(attributeValue); + } } } diff --git a/core/src/test/java/org/springframework/ldap/core/DirContextAdapterTest.java b/core/src/test/java/org/springframework/ldap/core/DirContextAdapterTest.java index da6c4405..50975e25 100644 --- a/core/src/test/java/org/springframework/ldap/core/DirContextAdapterTest.java +++ b/core/src/test/java/org/springframework/ldap/core/DirContextAdapterTest.java @@ -16,6 +16,9 @@ package org.springframework.ldap.core; +import java.util.Iterator; +import java.util.SortedSet; + import javax.naming.Name; import javax.naming.NamingException; import javax.naming.directory.Attribute; @@ -107,11 +110,78 @@ public class DirContextAdapterTest extends TestCase { assertEquals(2, s.length); } + public void testGetStringAttributesExistsWithInvalidType() throws Exception { + final Attributes attrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add(new Object()); + attrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(attrs, null); + } + } + tested = new TestableDirContextAdapter(); + try { + tested.getStringAttributes("abc"); + fail("ClassCastException expected"); + } + catch (ArrayStoreException expected) { + assertTrue(true); + } + } + + public void testGetStringAttributesExistsEmpty() throws Exception { + final Attributes attrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + attrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(attrs, null); + } + } + tested = new TestableDirContextAdapter(); + String s[] = tested.getStringAttributes("abc"); + assertNotNull(s); + assertEquals(0, s.length); + } + public void testGetStringAttributesNotExists() throws Exception { String s[] = tested.getStringAttributes("abc"); assertNull(s); } + public void testGetAttributesSortedStringSetExists() throws Exception { + final Attributes attrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("234"); + attrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(attrs, null); + } + } + tested = new TestableDirContextAdapter(); + SortedSet s = tested.getAttributeSortedStringSet("abc"); + assertNotNull(s); + assertEquals(2, s.size()); + Iterator it = s.iterator(); + assertEquals("123", it.next()); + assertEquals("234", it.next()); + } + + public void testGetAttributesSortedStringSetNotExists() throws Exception { + final Attributes attrs = new BasicAttributes(); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(attrs, null); + } + } + tested = new TestableDirContextAdapter(); + SortedSet s = tested.getAttributeSortedStringSet("abc"); + assertNull(s); + } + public void testAddAttributeValue() throws NamingException { // Perform test tested.addAttributeValue("abc", "123"); @@ -653,7 +723,7 @@ public class DirContextAdapterTest extends TestCase { } tested = new TestableDirContextAdapter(); assertTrue(tested.isUpdateMode()); - tested.setAttributeValues("title", new String[] {"Jim", "George", "Juergen" }, true); + tested.setAttributeValues("title", new String[] { "Jim", "George", "Juergen" }, true); // change ModificationItem[] mods = tested.getModificationItems(); diff --git a/core/src/test/java/org/springframework/ldap/support/LdapUtilsTest.java b/core/src/test/java/org/springframework/ldap/support/LdapUtilsTest.java new file mode 100644 index 00000000..22794c2a --- /dev/null +++ b/core/src/test/java/org/springframework/ldap/support/LdapUtilsTest.java @@ -0,0 +1,92 @@ +package org.springframework.ldap.support; + +import java.util.LinkedList; + +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.NoSuchAttributeException; + +public class LdapUtilsTest extends TestCase { + + private MockControl handlerControl; + + private AttributeValueCallbackHandler handlerMock; + + protected void setUp() throws Exception { + super.setUp(); + + handlerControl = MockControl.createControl(AttributeValueCallbackHandler.class); + handlerMock = (AttributeValueCallbackHandler) handlerControl.getMock(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + handlerControl = null; + handlerMock = null; + } + + public void testCollectAttributeValues() { + String expectedAttributeName = "someAttribute"; + BasicAttribute expectedAttribute = new BasicAttribute(expectedAttributeName); + expectedAttribute.add("value1"); + expectedAttribute.add("value2"); + + BasicAttributes attributes = new BasicAttributes(); + attributes.put(expectedAttribute); + + LinkedList list = new LinkedList(); + LdapUtils.collectAttributeValues(attributes, expectedAttributeName, list); + + assertEquals(2, list.size()); + assertEquals("value1", list.get(0)); + assertEquals("value2", list.get(1)); + } + + public void testCollectAttributeValuesThrowsExceptionWhenAttributeNotPresent() { + String expectedAttributeName = "someAttribute"; + BasicAttributes attributes = new BasicAttributes(); + + LinkedList list = new LinkedList(); + try { + LdapUtils.collectAttributeValues(attributes, expectedAttributeName, list); + fail("NoSuchAttributeException expected"); + } + catch (NoSuchAttributeException expected) { + assertTrue(true); + } + } + + public void testIterateAttributeValues() { + String expectedAttributeName = "someAttribute"; + + BasicAttribute expectedAttribute = new BasicAttribute(expectedAttributeName); + expectedAttribute.add("value1"); + expectedAttribute.add("value2"); + + handlerMock.handleAttributeValue(expectedAttributeName, "value1", 0); + handlerMock.handleAttributeValue(expectedAttributeName, "value2", 1); + + handlerControl.replay(); + + LdapUtils.iterateAttributeValues(expectedAttribute, handlerMock); + + handlerControl.verify(); + } + + public void testIterateAttributeValuesWithEmptyAttribute() { + String expectedAttributeName = "someAttribute"; + + BasicAttribute expectedAttribute = new BasicAttribute(expectedAttributeName); + + handlerControl.replay(); + + LdapUtils.iterateAttributeValues(expectedAttribute, handlerMock); + + handlerControl.verify(); + } +}