diff --git a/repository/org.testng/testng/5.0/testng-5.0.jar b/repository/org.testng/testng/5.0/testng-5.0.jar new file mode 100644 index 00000000..fa73e349 Binary files /dev/null and b/repository/org.testng/testng/5.0/testng-5.0.jar differ diff --git a/spring-ldap-odm/build-original.xml b/spring-ldap-odm/build-original.xml new file mode 100644 index 00000000..5c8174ca --- /dev/null +++ b/spring-ldap-odm/build-original.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-odm/build.xml b/spring-ldap-odm/build.xml new file mode 100644 index 00000000..280ce466 --- /dev/null +++ b/spring-ldap-odm/build.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Unit test report has been generated. +The HTML report is ${target.testresults.html.dir}/index.html + + + + + diff --git a/spring-ldap-odm/ivy.xml b/spring-ldap-odm/ivy.xml new file mode 100644 index 00000000..32b86cbe --- /dev/null +++ b/spring-ldap-odm/ivy.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-odm/project.properties b/spring-ldap-odm/project.properties new file mode 100644 index 00000000..2c371cea --- /dev/null +++ b/spring-ldap-odm/project.properties @@ -0,0 +1,14 @@ +# properties defined in this file are overridable by a local build.properties in this project dir + +# The location of the common build system +common.build.dir=${basedir}/../common-build + +project.base.version=1.2-dev +#project.version=${project.base.version} +#ivy.status=release + +javac.source=1.5 +javac.target=1.5 + +# extend common-build with source capability +main.build.configs=global,buildtime,test,source diff --git a/spring-ldap-odm/src/itest/java/conf/beans.xml b/spring-ldap-odm/src/itest/java/conf/beans.xml new file mode 100644 index 00000000..8f84cd19 --- /dev/null +++ b/spring-ldap-odm/src/itest/java/conf/beans.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/contextmapping/AnnotatedClassContextMapperTest.java b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/contextmapping/AnnotatedClassContextMapperTest.java new file mode 100644 index 00000000..16932b56 --- /dev/null +++ b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/contextmapping/AnnotatedClassContextMapperTest.java @@ -0,0 +1,349 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.contextmapping; + +import java.util.Date; +import java.util.Map; + +import javax.naming.Name; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.odm.annotations.DirAttribute; +import org.springframework.ldap.odm.annotations.NamingAttribute; +import org.springframework.ldap.odm.annotations.NamingSuffix; +import org.springframework.ldap.odm.annotations.ObjectClasses; +import org.springframework.ldap.odm.attributetypes.LdapTypeConverter; +import org.springframework.ldap.odm.attributetypes.ReferencedEntryEditorFactory; +import org.springframework.ldap.odm.contextmapping.exception.ContextMapperException; +import org.springframework.ldap.odm.entity.MockEntity; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class AnnotatedClassContextMapperTest extends + AbstractDependencyInjectionSpringContextTests { + private static final Log LOGGER = LogFactory + .getLog(AnnotatedClassContextMapperTest.class); + + private LdapTypeConverter typeConverter; + + private ReferencedEntryEditorFactory refEditorFactory; + + public void setRefEditorFactory( + ReferencedEntryEditorFactory refEditorFactory) { + this.refEditorFactory = refEditorFactory; + } + + public void setTypeConverter(LdapTypeConverter typeConverter) { + this.typeConverter = typeConverter; + } + + @Override + protected String[] getConfigLocations() { + return new String[] { "conf/beans.xml" }; + } + + public void testMapsDirectoryAttributesToBeanProperties() { + try { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockEntity.class, typeConverter, refEditorFactory); + ContextMap contextMap = contextMapper.getContextMap(); + Assert.assertEquals("cn", contextMap.attributeNameFor("Name"), + "Name mapping not as expected"); + Assert.assertEquals("mail", contextMap.attributeNameFor("Mail"), + "Mail mapping not as expected"); + Assert.assertEquals("addr", contextMap.attributeNameFor("Address"), + "Address mapping not as expected"); + Assert.assertEquals("userpassword", contextMap + .attributeNameFor("Password"), + "Password mapping not as expected"); + Assert.assertEquals("desc", contextMap + .attributeNameFor("Description"), + "Description mapping not as expected"); + } catch (ContextMapperException e) { + LOGGER.debug(e.getMessage()); + Assert.fail(e.getMessage(), e); + } + } + + public void testReturnsCorrectNamingAttribute() { + try { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockEntity.class, typeConverter, refEditorFactory); + Assert.assertEquals("cn", contextMapper.getNamingAttribute(), + "Context mapper naming attribute not as expected"); + + } catch (ContextMapperException e) { + LOGGER.debug(e.getMessage()); + Assert.fail(e.getMessage(), e); + } + } + + public void testReturnsCorrectObjectClasses() { + try { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockEntity.class, typeConverter, refEditorFactory); + Assert.assertEquals(new String[] { "top", "person", + "organizationalPerson", "inetorgperson" }, contextMapper + .getObjectClasses(), + "Context mapper naming attribute not as expected"); + + } catch (ContextMapperException e) { + LOGGER.debug(e.getMessage()); + Assert.fail(e.getMessage()); + } + } + + public void testBuildDn() { + try { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockEntity.class, typeConverter, refEditorFactory); + MockEntity entity = new MockEntity(); + entity.setName("Mr Bean"); + entity.setAddress("Somewhere in england"); + entity.setDescription(new String[] { "The quick brown fox jumped", + "over the lazy dog" }); + Name dn = contextMapper.buildDn(entity); + Assert.assertEquals("cn=Mr Bean, ou=people, dc=example, dc=com", dn + .toString(), "dn not as expected"); + + } catch (ContextMapperException e) { + LOGGER.debug(e.getMessage()); + Assert.fail(e.getMessage()); + } + } + + public void testMapToContext() { + try { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockEntity.class, typeConverter, refEditorFactory); + MockEntity entity = new MockEntity(); + entity.setName("Mr Bean"); + entity.setMail("bean@bean.com"); + entity.setAddress("Somewhere in england"); + entity.setPassword("fred".getBytes()); + entity.setDescription(new String[] { "The quick brown fox jumped", + "over the lazy dog" }); + entity.setAcceptEmails(false); + entity.setResetLogin(new Date(1L)); + entity.setCreator(new DistinguishedName( + "uid=amAdmin,ou=people,dc=myretsu,dc=com")); + entity.setFailedLogins(3); + DirContextAdapter ctxAdapter = new DirContextAdapter(); + contextMapper.mapToContext(entity, ctxAdapter); + + Assert.assertEquals("Mr Bean", ctxAdapter.getStringAttribute("cn"), + "Name mapping not as expected"); + Assert + .assertEquals("bean@bean.com", ctxAdapter + .getStringAttribute("mail"), + "Mail mapping not as expected"); + Assert.assertEquals("Somewhere in england", ctxAdapter + .getObjectAttribute("addr"), + "Address mapping not as expected"); + Assert.assertEquals("fred", new String((byte[]) ctxAdapter + .getObjectAttribute("userpassword")), + "Password mapping not as expected"); + Assert.assertEquals("The quick brown fox jumped", ctxAdapter + .getStringAttributes("desc")[0], + "Description mapping not as expected"); + Assert.assertEquals("false", ctxAdapter + .getStringAttribute("acceptemails"), + "AcceptEmails mapping not as expected"); + Assert.assertEquals("19700101100000.1", ctxAdapter + .getStringAttribute("loginresettime"), + "ResetLogin mapping not as expected"); + Assert.assertEquals("3", ctxAdapter + .getStringAttribute("failedlogins"), + "FailedLogins mapping not as expected"); + Assert.assertEquals("uid=amAdmin, ou=people, dc=myretsu, dc=com", + ctxAdapter.getStringAttribute("creatorname"), + "Creator mapping not as expected"); + } catch (ContextMapperException e) { + LOGGER.debug(e.getMessage()); + Assert.fail(e.getMessage(), e); + } + } + + public void testMapFromContext() throws ContextMapperException { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockEntity.class, typeConverter, refEditorFactory); + DirContextAdapter ctxAdapter = new DirContextAdapter(); + ctxAdapter.setAttributeValue("cn", "Mr Bean"); + ctxAdapter.setAttributeValue("mail", "bean@bean.com"); + ctxAdapter.setAttributeValue("addr", "Somewhere in england"); + ctxAdapter.setAttributeValue("userpassword", "fred".getBytes()); + ctxAdapter + .setAttributeValues("desc", new String[] { "elem1", "elem2" }); + ctxAdapter.setAttributeValue("userpassword", "fred".getBytes()); + ctxAdapter.setAttributeValue("acceptemails", "true"); + ctxAdapter.setAttributeValue("loginresettime", "19700101100000.1"); + ctxAdapter.setAttributeValue("failedlogins", "3"); + ctxAdapter.setAttributeValue("creatorname", + "uid=amAdmin,ou=people,dc=myretsu,dc=com"); + + MockEntity entity = (MockEntity) contextMapper + .mapFromContext(ctxAdapter); + Assert.assertEquals(entity.getMail(), "bean@bean.com", + "The mocked entity should match what has been mapped"); + Assert.assertEquals("Mr Bean", entity.getName(), + "Name mapping not as expected"); + Assert.assertEquals(entity.getMail(), "bean@bean.com", + "Mail mapping not as expected"); + Assert.assertEquals(entity.getAddress(), "Somewhere in england", + "Address mapping not as expected"); + Assert.assertEquals("fred", new String(entity.getPassword()), + "Password mapping not as expected"); + Assert.assertEquals("elem1", entity.getDescription()[0], + "Description mapping not as expected"); + Assert.assertEquals(Boolean.TRUE, entity.getAcceptEmails(), + "AcceptEmails mapping not as expected"); + Assert.assertEquals(new Date(1L), entity.getResetLogin(), + "ResetLogin mapping not as expected"); + Assert.assertEquals(new Integer(3), entity.getFailedLogins(), + "FailedLogin mapping not as expected"); + Assert.assertEquals(new DistinguishedName( + "uid=amAdmin,ou=people,dc=myretsu,dc=com"), + entity.getCreator(), "Creator mapping not as expected"); + + } + + @Test(groups = { "AnnotatedClassContextMapper" }, expectedExceptions = ContextMapperException.class) + public void throwsExceptionWhenNamingSuffixHasSyntaxError() + throws ContextMapperException { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockModelNamingSuffixSyntaxError.class, typeConverter, + refEditorFactory); + Assert + .fail("AnnotatedClassContextMapper should throw an exception when naming path annotation syntax is " + + "incorrect."); + } + + @Test(groups = { "AnnotatedClassContextMapper" }, expectedExceptions = ContextMapperException.class) + public void throwsExceptionWhenNamingSuffixNotInSeparateElements() + throws ContextMapperException { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockModelNamingSuffixNotInSeparateElements.class, + typeConverter, refEditorFactory); + Assert + .fail("AnnotatedClassContextMapper should throw an exception when naming path annotation syntax is " + + "incorrect."); + } + + @Test(groups = { "AnnotatedClassContextMapper" }, expectedExceptions = ContextMapperException.class) + public void throwsExceptionWhenModelObjectMissingGetterOrSetter() + throws ContextMapperException { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockModelMissingSetter.class, typeConverter, refEditorFactory); + Assert.fail("Should've thrown exception due to missing getter/setter"); + } + + @Test(groups = { "AnnotatedClassContextMapper" }) + public void throwsExceptionWhenBeanPropertyNotAValidTranslationType() { + try { + AnnotatedClassContextMapper contextMapper = new AnnotatedClassContextMapper( + MockModelInvalidTranslationType.class, typeConverter, + refEditorFactory); + Assert + .fail("Should've thrown exception due to missing getter/setter"); + } catch (ContextMapperException e) { + Assert + .assertTrue(e + .getMessage() + .contains( + "MockModelInvalidTranslationType.getMap() has invalid return type")); + } + + } + + @NamingAttribute("cn") + @NamingSuffix( { "ou=people", "dc=example", "dc=com" }) + @ObjectClasses( { "top", "person", "organizationalPerson", "inetorgperson" }) + public class MockModelMissingSetter { + @DirAttribute("cn") + private String name; + + @DirAttribute("addr") + private String address; + + @DirAttribute("mail") + private String mail; + + public String getName() { + return name; + } + + public String getAddress() { + return address; + } + + public String getMail() { + return mail; + } + + public void setName(String name) { + this.name = name; + } + + public void setAddress(String address) { + this.address = address; + } + } + + @NamingAttribute("cn") + @NamingSuffix( { "ou=people", "dc=example", "dc=com" }) + @ObjectClasses( { "top", "person", "organizationalPerson", "inetorgperson" }) + public class MockModelBadMethodName { + + @DirAttribute("cn") + private String name; + + @DirAttribute("addr") + private String address; + + public String zzzName() { + return name; + } + + public String getAddress() { + return address; + } + } + + @NamingAttribute("cn") + @NamingSuffix( { "ou=people,dc=example ,dc=com" }) + @ObjectClasses( { "top", "person", "organizationalPerson", "inetorgperson" }) + public class MockModelNamingSuffixNotInSeparateElements { + } + + @NamingAttribute("cn") + @NamingSuffix( { "ou=people", "dc 0); + } + + @Test(groups = "LdapDao") + public void filterByBeanProperty() + { + ldapDao.create(testPerson); + List results = ldapDao.filterByBeanProperty(testPerson.getEmailAddress(), "EmailAddress", TestPerson.class); + LOGGER.debug("filterByBeanProperty returned: " + results.size() + " results."); + Assert.assertTrue(results.size() > 0, "Filter should return collection containing more than one result. "); + } + + + @Test(groups = "LdapDao") + public void updateEntity() + { + try + { + ldapDao.create(testPerson); + String newEmail = "new@email.com"; + testPerson.setEmailAddress(newEmail); + ldapDao.update(testPerson); + TestPerson retrieved = (TestPerson) ldapDao.findByNamingAttribute(testPerson.getIdentifier(), + TestPerson.class); + Assert.assertEquals(retrieved.getEmailAddress(), newEmail, "Email name not updated."); + } + catch (Exception unexpected) + { + Assert.fail("Unable to update the details of an existing user: " + unexpected.getMessage()); + } + } + + + @Test(groups = "LdapDao") + public void updateEntityThrowsExceptionWhenNameNotBound() + { + try + { + String newEmail = "new@email.com"; + testPerson.setEmailAddress(newEmail); + ldapDao.update(testPerson); + Assert.fail("Should've failed since the entry doesn't exist"); + } + catch (EntryNotFoundException e) + { + Assert.assertTrue(e instanceof EntryNotFoundException); + Assert.assertTrue(e.getMessage().contains("Entry not found"), "Error message not as expected."); + } + } + + @Test(groups = "LdapDao") + public void deleteEntity() + { + ldapDao.create(testPerson); + LOGGER.debug(testPerson); + ldapDao.delete(testPerson); + TestPerson retrieved = (TestPerson) ldapDao.findByNamingAttribute(testPerson.getIdentifier(), TestPerson.class); + Assert.assertNull(retrieved, "Entity should've been deleted."); + } + + +} diff --git a/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/MockEntity.java b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/MockEntity.java new file mode 100644 index 00000000..968b7039 --- /dev/null +++ b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/MockEntity.java @@ -0,0 +1,138 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.entity; + +import java.util.Date; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.odm.annotations.DirAttribute; +import org.springframework.ldap.odm.annotations.NamingAttribute; +import org.springframework.ldap.odm.annotations.NamingSuffix; +import org.springframework.ldap.odm.annotations.ObjectClasses; + +@NamingAttribute("cn") +@NamingSuffix({"ou = people", "dc = example", "dc=com"}) +@ObjectClasses({"top", "person", "organizationalPerson", "inetorgperson"}) +public class MockEntity +{ + + @DirAttribute("cn") + private String name; + + @DirAttribute("addr") + private String address; + + @DirAttribute("mail") + private String mail; + + @DirAttribute("userpassword") + private byte[] password; + + @DirAttribute("desc") + private String[] description; + + @DirAttribute("acceptemails") + private Boolean acceptEmails; + + @DirAttribute("loginresettime") + private Date resetLogin; + + @DirAttribute("failedlogins") + private Integer failedLogins; + + @DirAttribute("creatorname") + private DistinguishedName creator; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getAddress() + { + return address; + } + + public void setAddress(String address) + { + this.address = address; + } + + public String getMail() + { + return mail; + } + + public void setMail(String mail) + { + this.mail = mail; + } + + public byte[] getPassword() + { + return password; + } + + public void setPassword(byte[] password) + { + this.password = password; + } + + public String[] getDescription() + { + return description; + } + + public void setDescription(String[] description) + { + this.description = description; + } + + public Boolean getAcceptEmails() + { + return acceptEmails; + } + + public void setAcceptEmails(Boolean acceptEmails) + { + this.acceptEmails = acceptEmails; + } + + public Date getResetLogin() + { + return resetLogin; + } + + public void setResetLogin(Date resetLogin) + { + this.resetLogin = resetLogin; + } + + public Integer getFailedLogins() + { + return failedLogins; + } + + public void setFailedLogins(Integer failedLogins) + { + this.failedLogins = failedLogins; + } + + public DistinguishedName getCreator() + { + return creator; + } + + public void setCreator(DistinguishedName creator) + { + this.creator = creator; + } +} diff --git a/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/TestPerson.java b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/TestPerson.java new file mode 100644 index 00000000..bdc066ae --- /dev/null +++ b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/TestPerson.java @@ -0,0 +1,110 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.entity; + +import org.springframework.ldap.odm.annotations.DirAttribute; +import org.springframework.ldap.odm.annotations.NamingAttribute; +import org.springframework.ldap.odm.annotations.NamingSuffix; +import org.springframework.ldap.odm.annotations.ObjectClasses; + +import com.thoughtworks.xstream.XStream; + + +@NamingAttribute("uid") +@NamingSuffix({"ou=people"}) +@ObjectClasses({"top", "person", "organizationalPerson", "inetorgperson"}) +public class TestPerson +{ + /*********************************** Directory mapped fields ************************************/ + @DirAttribute("uid") + private String identifier; + + @DirAttribute("sn") + private String lastName; + + @DirAttribute("givenName") + private String givenName; + + @DirAttribute("cn") + private String fullName; + + @DirAttribute("mail") + private String emailAddress; + + @DirAttribute("userpassword") + private byte[] password; + + /************************************************************************************************/ + + public String getIdentifier() + { + return identifier; + } + + public void setIdentifier(String identifier) + { + this.identifier = identifier; + } + + public String getLastName() + { + return lastName; + } + + public void setLastName(String lastName) + { + this.lastName = lastName; + } + + public String getGivenName() + { + return givenName; + } + + public void setGivenName(String givenName) + { + this.givenName = givenName; + } + + public String getFullName() + { + return fullName; + } + + public void setFullName(String fullName) + { + this.fullName = fullName; + } + + public String getEmailAddress() + { + return emailAddress; + } + + public void setEmailAddress(String emailAddress) + { + this.emailAddress = emailAddress; + } + + public byte[] getPassword() + { + return password; + } + + public void setPassword(byte[] password) + { + this.password = password; + } + + public String toString() + { + XStream xStream = new XStream(); + xStream.alias("TestPerson", TestPerson.class); + return "\n" + xStream.toXML(this); + } + + +} diff --git a/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/TestRole.java b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/TestRole.java new file mode 100644 index 00000000..be35d4fe --- /dev/null +++ b/spring-ldap-odm/src/itest/java/org/springframework/ldap/odm/entity/TestRole.java @@ -0,0 +1,71 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.entity; + +import org.springframework.ldap.odm.annotations.DirAttribute; +import org.springframework.ldap.odm.annotations.NamingAttribute; +import org.springframework.ldap.odm.annotations.NamingSuffix; +import org.springframework.ldap.odm.annotations.ObjectClasses; + +import com.thoughtworks.xstream.XStream; + +@NamingAttribute("cn") +@NamingSuffix({"ou=roles"}) +@ObjectClasses({"top", "organizationalRole"}) + +public class TestRole +{ + /*********************************** Directory mapped fields ************************************/ + @DirAttribute("cn") + private String roleName; + + @DirAttribute + private String description; + + @DirAttribute("roleOccupant") + private TestPerson[] members; + + /************************************************************************************************/ + + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public TestPerson[] getMembers() + { + return members; + } + + public void setMembers(TestPerson[] members) + { + this.members = members; + } + + public String toString() + { + XStream xStream = new XStream(); + xStream.alias("TestRole", TestRole.class); + xStream.alias("TestPerson", TestPerson.class); + return "\n" + xStream.toXML(this); + } + +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/DirAttribute.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/DirAttribute.java new file mode 100644 index 00000000..c046f617 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/DirAttribute.java @@ -0,0 +1,21 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.annotations; + +import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.FIELD; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Target; + +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +public @interface DirAttribute +{ + String value() default ""; +} + diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/NamingAttribute.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/NamingAttribute.java new file mode 100644 index 00000000..fed1545f --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/NamingAttribute.java @@ -0,0 +1,21 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.annotations; + +import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.TYPE; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Target; + + +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface NamingAttribute +{ + String value(); +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/NamingSuffix.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/NamingSuffix.java new file mode 100644 index 00000000..47b601c6 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/NamingSuffix.java @@ -0,0 +1,25 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.annotations; + +import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.TYPE; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Target; +/* + * Copyright 2006 by Sensis. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ + +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface NamingSuffix +{ + String[] value(); +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/ObjectClasses.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/ObjectClasses.java new file mode 100644 index 00000000..ea38aaee --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/annotations/ObjectClasses.java @@ -0,0 +1,20 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.annotations; + +import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.TYPE; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Target; + +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ObjectClasses +{ + String[] value(); +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/LdapTypeConverter.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/LdapTypeConverter.java new file mode 100644 index 00000000..4d9228e7 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/LdapTypeConverter.java @@ -0,0 +1,73 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.attributetypes; + +import java.beans.PropertyEditor; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.naming.ldap.LdapName; + +import org.springframework.beans.SimpleTypeConverter; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.ldap.core.DistinguishedName; + +public class LdapTypeConverter extends SimpleTypeConverter +{ + + public LdapTypeConverter() + { + super(); + registerCustomEditor(Date.class, + new CustomDateEditor(new SimpleDateFormat("yyyyMMddHHmmss.S"), true)); + registerCustomEditor(LdapName.class, new NameEditor(LdapName.class)); + registerCustomEditor(DistinguishedName.class, new NameEditor(DistinguishedName.class)); + } + + public String getAsText(Object value) + { + if (value instanceof String) + { + return (String) value; + } + PropertyEditor pe = propertyEditorFor(value.getClass()); + if (pe != null) + { + pe.setValue(value); + return pe.getAsText(); + } + else + { + return null; + } + } + + public String[] getAllAsText(Object[] values) + { + String[] textValues = new String[values.length]; + for (int i = 0; i < values.length; i++) + { + textValues[i] = getAsText(values[i]); + } + return textValues; + } + + private PropertyEditor propertyEditorFor(Class clazz) + { + PropertyEditor pe = findCustomEditor(clazz, null); + if (pe != null) + { + return pe; + } + else + { + pe = getDefaultEditor(clazz); + return pe; + } + } + + +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/NameEditor.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/NameEditor.java new file mode 100644 index 00000000..ae17ecdb --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/NameEditor.java @@ -0,0 +1,44 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.attributetypes; + +import java.beans.PropertyEditorSupport; + +import javax.naming.ldap.LdapName; + +import org.springframework.ldap.core.DistinguishedName; + +public class NameEditor extends PropertyEditorSupport +{ + private Class conversionClass; + + public NameEditor(Class conversionClass) + { + this.conversionClass = conversionClass; + if (!conversionClass.equals(DistinguishedName.class) && !conversionClass.equals(LdapName.class)) + { + throw new RuntimeException("NameEditor can only be created for LdapName or DistinguishedName"); + } + } + + public String getAsText() + { + Object value = conversionClass.cast(getValue()); + return value != null ? value.toString() : ""; + } + + public void setAsText(String text) throws IllegalArgumentException + { + try + { + setValue(conversionClass.getConstructor(String.class).newInstance(text)); + } + catch (Exception e) + { + throw new IllegalArgumentException(e.getMessage(), e); + } + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ReferencedEntryEditor.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ReferencedEntryEditor.java new file mode 100644 index 00000000..30996977 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ReferencedEntryEditor.java @@ -0,0 +1,51 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.attributetypes; + +import java.beans.PropertyEditorSupport; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.odm.contextmapping.AnnotatedClassContextMapper; +import org.springframework.ldap.odm.contextmapping.exception.ContextMapperException; + +public class ReferencedEntryEditor extends PropertyEditorSupport +{ + private LdapTemplate ldapTemplate; + private AnnotatedClassContextMapper contextMapper; + + public ReferencedEntryEditor(LdapTemplate ldapTemplate, AnnotatedClassContextMapper contextMapper) + { + this.ldapTemplate = ldapTemplate; + this.contextMapper = contextMapper; + } + + public String getAsText() + { + try + { + return contextMapper.buildDn(getValue()).toString(); + } + catch (ContextMapperException e) + { + throw new RuntimeException("No context mapper for class: " + getValue().getClass().getSimpleName()); + } + } + + public void setAsText(String text) throws IllegalArgumentException + { + try + { + setValue(ldapTemplate.lookup(new LdapName(text), contextMapper)); + } + catch (InvalidNameException e) + { + throw new IllegalArgumentException(e.getMessage(), e); + } + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ReferencedEntryEditorFactory.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ReferencedEntryEditorFactory.java new file mode 100644 index 00000000..50c3408a --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ReferencedEntryEditorFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.attributetypes; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.odm.attributetypes.exception.ReferencedEntryEditorCreationException; +import org.springframework.ldap.odm.contextmapping.AnnotatedClassContextMapper; +import org.springframework.ldap.odm.contextmapping.ContextMapperFactory; +import org.springframework.ldap.odm.contextmapping.exception.ContextMapperException; + +public class ReferencedEntryEditorFactory +{ + private static final Log LOGGER = LogFactory.getLog(ReferencedEntryEditorFactory.class); + private ContextMapperFactory contextMapperFactory; + private LdapTemplate ldapTemplate; + private Map referencedEntryEditors; + + public ReferencedEntryEditorFactory( + LdapTemplate ldapTemplate) + { + this.ldapTemplate = ldapTemplate; + this.referencedEntryEditors = new HashMap(); + } + + public ReferencedEntryEditor referencedEntryEditorForClass(Class clazz) + throws ReferencedEntryEditorCreationException + { + if (referencedEntryEditors.containsKey(clazz)) + { + LOGGER.debug("Returning cached referenced entry editor for class: " + clazz.getSimpleName()); + return referencedEntryEditors.get(clazz); + } + else + { + LOGGER.debug("Attempting to create a referenced entry editor for class: " + + clazz.getSimpleName()); + AnnotatedClassContextMapper contextMapper = null; + try + { + contextMapper = contextMapperFactory.contextMapperForClass(clazz); + ReferencedEntryEditor referencedEntryEditor = + new ReferencedEntryEditor(ldapTemplate, contextMapper); + referencedEntryEditors.put(clazz, referencedEntryEditor); + return referencedEntryEditor; + } + catch (ContextMapperException e) + { + throw new ReferencedEntryEditorCreationException(e.getMessage(), e); + } + } + } + + public void setContextMapperFactory(ContextMapperFactory contextMapperFactory) + { + this.contextMapperFactory = contextMapperFactory; + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ValidConversionType.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ValidConversionType.java new file mode 100644 index 00000000..3b835892 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/ValidConversionType.java @@ -0,0 +1,75 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.attributetypes; + +import java.util.Date; + +import javax.naming.ldap.LdapName; + +import org.springframework.ldap.core.DistinguishedName; + +/* Should be able to support any of Spring's conversion types. Add a test case if you add one */ +public enum ValidConversionType +{ + BYTE_ARRAY(byte[].class), + BOOLEAN(Boolean.class), + STRING(String.class), + STRING_ARRAY(String[].class), + DATE(Date.class), + DATE_ARRAY(Date[].class), + LDAP_NAME(LdapName.class), + LDAP_NAME_ARRAY(LdapName[].class), + DISTINGUISHED_NAME(DistinguishedName.class), + DISTINGUISHED_NAME_ARRAY(DistinguishedName[].class), + INTEGER(Integer.class), + INTEGER_ARRAY(Integer[].class), + LONG(Long.class), + LONG_ARRAY(Long[].class); + + private final Class clazz; + + + ValidConversionType(Class validType) + { + this.clazz = validType; + + } + + public Class getClazz() + { + return clazz; + } + + public static String listTypes() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < ValidConversionType.values().length; i++) + { + ValidConversionType validType = ValidConversionType.values()[i]; + sb.append("\n"); + sb.append(validType.getClazz().getSimpleName()); + if (i != ValidConversionType.values().length - 1) + { + sb.append(","); + } + } + return sb.toString(); + } + + public static boolean isValidConversionType(Class returnType) + { + for (ValidConversionType type : ValidConversionType.values()) + { + if (returnType.equals(type.getClazz())) + { + return true; + } + } + return false; + } + +} + diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/exception/ReferencedEntryEditorCreationException.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/exception/ReferencedEntryEditorCreationException.java new file mode 100644 index 00000000..8530bb14 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/attributetypes/exception/ReferencedEntryEditorCreationException.java @@ -0,0 +1,14 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.attributetypes.exception; + +public class ReferencedEntryEditorCreationException extends Exception +{ + public ReferencedEntryEditorCreationException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/AnnotatedClassContextMapper.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/AnnotatedClassContextMapper.java new file mode 100644 index 00000000..6bd84457 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/AnnotatedClassContextMapper.java @@ -0,0 +1,335 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.contextmapping; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.Name; +import javax.naming.directory.Attribute; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.TypeMismatchException; +import org.springframework.ldap.core.ContextAssembler; +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.odm.annotations.DirAttribute; +import org.springframework.ldap.odm.annotations.NamingAttribute; +import org.springframework.ldap.odm.annotations.NamingSuffix; +import org.springframework.ldap.odm.annotations.ObjectClasses; +import org.springframework.ldap.odm.attributetypes.LdapTypeConverter; +import org.springframework.ldap.odm.attributetypes.ReferencedEntryEditorFactory; +import org.springframework.ldap.odm.attributetypes.ValidConversionType; +import org.springframework.ldap.odm.attributetypes.exception.ReferencedEntryEditorCreationException; +import org.springframework.ldap.odm.contextmapping.exception.ContextMapperException; +import org.springframework.util.StringUtils; + +public class AnnotatedClassContextMapper implements ContextMapper, ContextAssembler +{ + private static final Log LOGGER = LogFactory.getLog(AnnotatedClassContextMapper.class); + + private LdapTypeConverter typeConverter; + private ReferencedEntryEditorFactory refEditorFactory; + + private Class clazz; + private String namingAttribute; + private String[] objectClasses; + private String[] namingSuffixElements; + private DistinguishedName namingSuffix; + + private ContextMap contextMap; + private Map propertyGetters; + private Map propertySetters; + + + public AnnotatedClassContextMapper(Class clazz, LdapTypeConverter typeConverter, + ReferencedEntryEditorFactory refEditorFactory) throws ContextMapperException + { + this.clazz = clazz; + this.typeConverter = typeConverter; + this.refEditorFactory = refEditorFactory; + checkConfigured(); + checkRequiredMetaData(); + buildNamingSuffix(); + mapAttributesToBeanProperties(); + cacheGettersAndSetters(); + registerReferencedEntries(); + } + + private void checkConfigured() throws ContextMapperException + { + if (clazz == null) + { + throw new ContextMapperException("Error creating context mapper." + + ". Class arugment is null"); + } + + if (this.typeConverter == null) + { + throw new ContextMapperException("Error creating context mapper for class: " + + clazz.getSimpleName() + + ". AttributeTypeTranslator arugment is null"); + } + + if (this.refEditorFactory == null) + { + throw new ContextMapperException("Error creating context mapper for class: " + + clazz.getSimpleName() + + ". ReferencedEntryEditorFactory arugment is null"); + } + } + + private void checkRequiredMetaData() throws ContextMapperException + { + NamingAttribute attnNamingAttr = clazz.getAnnotation(NamingAttribute.class); + ObjectClasses attnObjectClasses = clazz.getAnnotation(ObjectClasses.class); + NamingSuffix attnNamingSuffix = clazz.getAnnotation(NamingSuffix.class); + this.namingAttribute = attnNamingAttr != null ? attnNamingAttr.value() : null; + this.objectClasses = attnObjectClasses != null ? attnObjectClasses.value() : null; + this.namingSuffixElements = attnNamingSuffix != null ? attnNamingSuffix.value() : null; + + if (this.namingAttribute == null) + { + throw new ContextMapperException("Error creating context mapper for class: " + + clazz.getSimpleName() + + ". The @NamingAttribute annotation is required."); + } + + if (this.objectClasses == null) + { + throw new ContextMapperException("Error creating context mapper for class: " + + clazz.getSimpleName() + + ". The @ObjectClasses annotation is required."); + } + + if (namingSuffixElements == null || namingSuffixElements.length == 0) + { + throw new ContextMapperException("Error creating context mapper for class: " + + clazz.getSimpleName() + + ". The @NamingSuffix annotation is required."); + } + + if (namingSuffixElements[0].contains(",")) + { + throw new ContextMapperException("Error creating context mapper for class: " + + clazz.getSimpleName() + + ". Naming path nodes must be defined as separate elements, " + + "for example: @NamingSuffix({\"ou=people\", \"dc=example\", \"dc=com\"}), and not: " + + "@NamingSuffix({\"ou=people,dc=example,dc=com\"})"); + } + } + + private void buildNamingSuffix() throws ContextMapperException + { + namingSuffix = new DistinguishedName(); + for (int i = namingSuffixElements.length - 1; i >= 0; i--) + { + String[] nameValue = namingSuffixElements[i].split("="); + if (nameValue.length != 2) + { + throw new ContextMapperException("Error creating context mapper for class: " + clazz.getSimpleName() + + ". Syntax error in naming suffix element: " + namingSuffixElements[i]); + } + namingSuffix.add(nameValue[0].trim(), nameValue[1].trim()); + } + } + + private void mapAttributesToBeanProperties() + { + contextMap = new ContextMap(); + for (Field field : clazz.getDeclaredFields()) + { + DirAttribute dirAttribute = field.getAnnotation(DirAttribute.class); + if (dirAttribute != null) + { + String beanPropertyName = StringUtils.capitalize(field.getName()); + String attributeName = dirAttribute.value().equals("") ? field.getName() : dirAttribute.value(); + contextMap.map(beanPropertyName, attributeName); + } + } + } + + private void cacheGettersAndSetters() throws ContextMapperException + { + propertyGetters = new HashMap(); + propertySetters = new HashMap(); + + for (String beanPropertyName : contextMap.beanPropertyNames()) + { + try + { + Method getter = clazz.getMethod("get" + beanPropertyName); + propertyGetters.put(beanPropertyName, getter); + Method setter = clazz.getMethod("set" + beanPropertyName, getter.getReturnType()); + propertySetters.put(beanPropertyName, setter); + } + catch (NoSuchMethodException e) + { + throw new ContextMapperException("Error creating context mapper for class: " + + clazz.getSimpleName() + + ". " + clazz.getSimpleName() + " is missing a getter/setter for property: " + + beanPropertyName, e); + } + } + } + + private void registerReferencedEntries() throws ContextMapperException + { + for (Method getter : propertyGetters.values()) + { + Class returnType = getter.getReturnType(); + if (!ValidConversionType.isValidConversionType(returnType)) + { + try + { + Class componentType = returnType.isArray() ? returnType.getComponentType() : returnType; + typeConverter.registerCustomEditor(componentType, + refEditorFactory.referencedEntryEditorForClass(componentType)); + } + catch (ReferencedEntryEditorCreationException e) + { + throw new ContextMapperException(clazz.getSimpleName() + "." + + getter.getName() + + "() has invalid return type. Return type must be a context mapped " + + "directory object, or one of the following: " + + ValidConversionType.listTypes(), e); + } + } + } + } + + public Object mapFromContext(Object ctx) + { + DirContextAdapter ctxAdapter = (DirContextAdapter) ctx; + Object instance; + try + { + instance = clazz.newInstance(); + for (String beanPropertyName : contextMap.beanPropertyNames()) + { + Class attributeType = propertyGetters.get(beanPropertyName).getReturnType(); + Attribute attribute = + ctxAdapter.getAttributes().get(contextMap.attributeNameFor(beanPropertyName)); + if (attribute != null) + { + try + { + Object beanPropertyValue = typeConverter.convertIfNecessary( + new AttributeDecorator(attribute).getAllAsObject(), attributeType); + propertySetters.get(beanPropertyName).invoke(instance, beanPropertyValue); + } + catch (TypeMismatchException e) + { + throw new ContextMapperException("Cannot set bean property '" + beanPropertyName + + "' in class " + clazz.getSimpleName() + ", " + e.getMessage(), e); + } + } + } + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage(), e); + } + return instance; + } + + // TODO: Simplify this method: When bug with DirContextAdapter.setAttribute() (LDAP-15) is fixed, + // TODO: register a type converter to convert to type Attribute + public void mapToContext(Object beanInstance, Object ctx) + { + DirContextAdapter ctxAdapter = (DirContextAdapter) ctx; + ctxAdapter.setAttributeValues("objectclass", objectClasses); + for (String attributeName : contextMap.attributeNames()) + { + try + { + Object beanPropertyValue = propertyGetters.get( + contextMap.beanPropertyNameFor(attributeName)).invoke(beanInstance); + LOGGER.trace("mapToContext() attribute:" + attributeName + ", value: " + beanPropertyValue); + + if (beanPropertyValue != null) + { + if (beanPropertyValue instanceof byte[]) + { + ctxAdapter.setAttributeValue(attributeName, beanPropertyValue); + } + else if (beanPropertyValue instanceof Object[]) + { + ctxAdapter.setAttributeValues(attributeName, + typeConverter.getAllAsText((Object[]) beanPropertyValue)); + } + else + { + ctxAdapter.setAttributeValue(attributeName, + typeConverter.getAsText(beanPropertyValue)); + } + } + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage(), e); + } + } + } + + public Name buildDn(Object beanInstance) throws ContextMapperException + { + if (beanInstance == null) + { + throw new ContextMapperException("Can't build Dn from beanInstance, beanInstance is null"); + } + return buildDn(namingAttributeValue(beanInstance)); + } + + public Name buildDn(String namingAttributeValue) throws ContextMapperException + { + if (namingAttributeValue == null) + { + throw new ContextMapperException("Can't build Dn from namingAttributeValue, namingAttributeValue is null"); + } + DistinguishedName dn = (DistinguishedName) namingSuffix.clone(); + dn.add(namingAttribute, namingAttributeValue); + LOGGER.trace("buildDn(): " + dn); + return dn; + } + + public String namingAttributeValue(Object beanInstance) + { + Method method = propertyGetters.get(contextMap.beanPropertyNameFor(namingAttribute)); + try + { + return (String) method.invoke(clazz.cast(beanInstance)); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage(), e); + } + } + + public ContextMap getContextMap() + { + return contextMap; + } + + public String[] getObjectClasses() + { + return objectClasses; + } + + public String getNamingAttribute() + { + return namingAttribute; + } + + public DistinguishedName getNamingSuffix() + { + return namingSuffix; + } +} \ No newline at end of file diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/AttributeDecorator.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/AttributeDecorator.java new file mode 100644 index 00000000..ccd3ec96 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/AttributeDecorator.java @@ -0,0 +1,44 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.contextmapping; + +import javax.naming.NamingException; +import javax.naming.directory.Attribute; + +public class AttributeDecorator +{ + private Attribute attribute; + + public AttributeDecorator(Attribute attribute) + { + this.attribute = attribute; + } + + public Object getAllAsObject() throws NamingException + { + if (attribute != null) + { + if (attribute.size() == 1) + { + return attribute.get(0); + } + else + { + Object[] o = new Object[attribute.size()]; + for (int i = 0; i < attribute.size(); i++) + { + o[i] = attribute.get(i); + } + return o; + } + } + else + { + return null; + } + + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/ContextMap.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/ContextMap.java new file mode 100644 index 00000000..aae1d046 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/ContextMap.java @@ -0,0 +1,54 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.contextmapping; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** A bi-directional map of bean property names to attribute names */ +public class ContextMap +{ + private static final Log LOGGER = LogFactory.getLog(ContextMap.class); + private Map beanPropertyNameKeys; + private Map attributeNameKeys; + + public ContextMap() + { + beanPropertyNameKeys = new HashMap(); + attributeNameKeys = new HashMap(); + } + + public void map(String beanPropertyName, String toAttributeName) + { + LOGGER.info("Bean property '" + beanPropertyName + "' maps to ldap attribute '" + toAttributeName + "'"); + beanPropertyNameKeys.put(beanPropertyName, toAttributeName); + attributeNameKeys.put(toAttributeName, beanPropertyName); + } + + public String beanPropertyNameFor(String attributeName) + { + return attributeNameKeys.get(attributeName); + } + + public String attributeNameFor(String beanPropertyName) + { + return beanPropertyNameKeys.get(beanPropertyName); + } + + public Set beanPropertyNames() + { + return beanPropertyNameKeys.keySet(); + } + + public Set attributeNames() + { + return attributeNameKeys.keySet(); + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/ContextMapperFactory.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/ContextMapperFactory.java new file mode 100644 index 00000000..bf234a62 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/ContextMapperFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.contextmapping; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.odm.attributetypes.LdapTypeConverter; +import org.springframework.ldap.odm.attributetypes.ReferencedEntryEditorFactory; +import org.springframework.ldap.odm.contextmapping.exception.ContextMapperException; + +import java.util.HashMap; +import java.util.Map; + +public class ContextMapperFactory +{ + private static final Log LOGGER = LogFactory.getLog(ContextMapperFactory.class); + private Map contextMappers; + private LdapTypeConverter typeConverter; + private ReferencedEntryEditorFactory referencedEntryEditorFactory; + + public ContextMapperFactory(LdapTypeConverter typeConverter, + ReferencedEntryEditorFactory referencedEntryEditorFactory) + { + contextMappers = new HashMap(); + this.typeConverter = typeConverter; + this.referencedEntryEditorFactory = referencedEntryEditorFactory; + this.referencedEntryEditorFactory.setContextMapperFactory(this); + } + + public AnnotatedClassContextMapper contextMapperForClass(Class clazz) + throws ContextMapperException + { + if (contextMappers.containsKey(clazz)) + { + LOGGER.debug("Returning cached context mapper for class: " + clazz.getSimpleName()); + return contextMappers.get(clazz); + } + else + { + LOGGER.debug("Attempting to create a context mapper for class: " + clazz.getSimpleName()); + AnnotatedClassContextMapper contextMapper = + new AnnotatedClassContextMapper(clazz, typeConverter, referencedEntryEditorFactory); + contextMappers.put(clazz, contextMapper); + return contextMapper; + } + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/exception/ContextMapperException.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/exception/ContextMapperException.java new file mode 100644 index 00000000..66863897 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/contextmapping/exception/ContextMapperException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.contextmapping.exception; + +public class ContextMapperException extends Exception +{ + + + public ContextMapperException(String message) + { + super(message); + } + + public ContextMapperException(String message, Throwable cause) + { + super(message, cause); + } + + + + +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/LdapDao.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/LdapDao.java new file mode 100644 index 00000000..443400d1 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/LdapDao.java @@ -0,0 +1,29 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.dao; + +import java.util.List; + +import javax.naming.Name; + +public interface LdapDao +{ + void create(Object dirObject); + + void update(Object dirObject); + + void createOrUpdate(Object dirObject); + + void delete(Object dirObject); + + Object findByNamingAttribute(String namingValue, Class returnType); + + public Object findByDn(Name dn, Class returnType); + + List findAll(Class ofType); + + List filterByBeanProperty(String value, String beanPropertyName, Class returnType); +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/LdapDaoImpl.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/LdapDaoImpl.java new file mode 100644 index 00000000..fe478807 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/LdapDaoImpl.java @@ -0,0 +1,195 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.dao; + +import java.util.List; + +import javax.naming.Name; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.odm.contextmapping.AnnotatedClassContextMapper; +import org.springframework.ldap.odm.contextmapping.ContextMapperFactory; +import org.springframework.ldap.odm.contextmapping.exception.ContextMapperException; +import org.springframework.ldap.odm.dao.exception.DaoException; +import org.springframework.ldap.odm.dao.exception.DataIntegrityViolationException; +import org.springframework.ldap.odm.dao.exception.EntryNotFoundException; + +public class LdapDaoImpl implements LdapDao +{ + static final Log LOGGER = LogFactory.getLog(LdapDaoImpl.class); + LdapTemplate ldapTemplate; + ContextMapperFactory ctxMapperFactory; + + public LdapDaoImpl(LdapTemplate ldapTemplate, ContextMapperFactory ctxMapperFactory) + { + this.ldapTemplate = ldapTemplate; + this.ctxMapperFactory = ctxMapperFactory; + } + + public void create(Object dirObject) + { + DirContextAdapter context = new DirContextAdapter(); + try + { + AnnotatedClassContextMapper ctxMapper = ctxMapperFactory.contextMapperForClass(dirObject.getClass()); + ctxMapper.mapToContext(dirObject, context); + ldapTemplate.bind(ctxMapper.buildDn(dirObject), context, null); + } + catch (org.springframework.dao.DataIntegrityViolationException dive) + { + throw new DataIntegrityViolationException(dive.getMessage()); + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + } + + public void update(Object dirObject) + { + try + { + AnnotatedClassContextMapper ctxMapper = ctxMapperFactory.contextMapperForClass(dirObject.getClass()); + Name dn = ctxMapper.buildDn(dirObject); + DirContextAdapter contextAdapter = (DirContextAdapter) ldapTemplate.lookup(dn); + ctxMapper.mapToContext(dirObject, contextAdapter); + ldapTemplate.modifyAttributes(dn, contextAdapter.getModificationItems()); + } + catch (org.springframework.ldap.NameNotFoundException e) + { + //We need to rethrow a serializable exception if we are going to invoke + //this as a remote service. + throw new EntryNotFoundException(e.getMessage()); + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + } + + public void createOrUpdate(Object dirObject) + { + try + { + AnnotatedClassContextMapper ctxMapper = ctxMapperFactory.contextMapperForClass(dirObject.getClass()); + if (findByDn(ctxMapper.buildDn(dirObject), dirObject.getClass()) == null) + { + create(dirObject); + } + else + { + update(dirObject); + } + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + + } + + public void delete(Object dirObject) + { + try + { + AnnotatedClassContextMapper ctxMapper = ctxMapperFactory.contextMapperForClass(dirObject.getClass()); + ldapTemplate.unbind(ctxMapper.buildDn(dirObject)); + } + catch (ContextMapperException e) + { + e.printStackTrace(); + throw new DaoException(e.getMessage(), e); + } + } + + public Object findByNamingAttribute(String namingAttributeValue, Class returnType) + { + try + { + AnnotatedClassContextMapper ctxMapper = ctxMapperFactory.contextMapperForClass(returnType); + Name dn = ctxMapper.buildDn(namingAttributeValue); + return findByDn(dn, returnType); + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + } + + public Object findByDn(Name dn, Class returnType) + { + try + { + AnnotatedClassContextMapper ctxMapper = ctxMapperFactory.contextMapperForClass(returnType); + return ldapTemplate.lookup(dn, ctxMapper); + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + catch (RuntimeException e) + { + return null; + } + } + + + public List filterByBeanProperty(String value, String beanPropertyName, Class returnType) + { + try + { + LOGGER.debug("Filtering on property: " + beanPropertyName + ", for value: " + value); + AnnotatedClassContextMapper ctxMapper = + ctxMapperFactory.contextMapperForClass(returnType); + + try + { + String attributeName = ctxMapper.getContextMap().attributeNameFor(beanPropertyName); + String filter = attributeName + "=" + value; + List results = ldapTemplate.search(ctxMapper.getNamingSuffix().toString(), filter, + ctxMapperFactory.contextMapperForClass(returnType)); + LOGGER.debug(results.size() + " results found for filter: " + filter); + return results; + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + } + + public List findAll(Class ofType) + { + try + { + AnnotatedClassContextMapper ctxMapper = ctxMapperFactory.contextMapperForClass(ofType); + StringBuilder filterBuilder = new StringBuilder(); + filterBuilder.append("(&"); + for (String objectClass : ctxMapper.getObjectClasses()) + { + filterBuilder.append("(objectClass="); + filterBuilder.append(objectClass); + filterBuilder.append(")"); + } + filterBuilder.append(")"); + List results = ldapTemplate.search(DistinguishedName.EMPTY_PATH, filterBuilder.toString(), ctxMapper); + LOGGER.debug("Found " + results.size() + " entries of type " + ofType.getSimpleName()); + return results; + } + catch (ContextMapperException e) + { + throw new DaoException(e.getMessage(), e); + } + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/DaoException.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/DaoException.java new file mode 100644 index 00000000..9ca7abfd --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/DaoException.java @@ -0,0 +1,19 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.dao.exception; + +public class DaoException extends RuntimeException +{ + public DaoException(String message) + { + super(message); + } + + public DaoException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/DataIntegrityViolationException.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/DataIntegrityViolationException.java new file mode 100644 index 00000000..d2d015b7 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/DataIntegrityViolationException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.dao.exception; + +import java.util.List; + +/** + * A Serializable version of org.springframework.dao.DataIntegrityViolationException + */ +public class DataIntegrityViolationException extends DaoException +{ + + private List nonUniqueAttributes; + + public DataIntegrityViolationException(String message) + { + super(message); + } + + public DataIntegrityViolationException(String message, Throwable cause) + { + super(message, cause); + } + + + + +} diff --git a/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/EntryNotFoundException.java b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/EntryNotFoundException.java new file mode 100644 index 00000000..0274c8f0 --- /dev/null +++ b/spring-ldap-odm/src/main/java/org/springframework/ldap/odm/dao/exception/EntryNotFoundException.java @@ -0,0 +1,19 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.dao.exception; + +/** + * A Serializable version of org.springframework.dao.EntryNotFoundException + */ +public class EntryNotFoundException extends DaoException +{ + + public EntryNotFoundException(String message) + { + super(message); + } + +} diff --git a/spring-ldap-odm/src/test/java/org/springframework/ldap/odm/attributetypes/LdapTypeConverterTest.java b/spring-ldap-odm/src/test/java/org/springframework/ldap/odm/attributetypes/LdapTypeConverterTest.java new file mode 100644 index 00000000..88966e2e --- /dev/null +++ b/spring-ldap-odm/src/test/java/org/springframework/ldap/odm/attributetypes/LdapTypeConverterTest.java @@ -0,0 +1,264 @@ +/* + * Copyright 2006 by Majitek. All Rights Reserved. + * + * This software is the proprietary information of Majitek. Use is subject to license terms. + */ +package org.springframework.ldap.odm.attributetypes; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import junit.framework.TestCase; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.TypeMismatchException; +import org.springframework.ldap.core.DistinguishedName; +import org.testng.Assert; + +public class LdapTypeConverterTest extends TestCase { + private static final Log LOGGER = LogFactory + .getLog(LdapTypeConverterTest.class); + + private LdapTypeConverter typeConverter; + + @Override + protected void setUp() throws Exception { + super.setUp(); + typeConverter = new LdapTypeConverter(); + } + + public void testConvertToByteArray() { + byte[] objectToTranslate = "fred".getBytes(); + + try { + byte[] translated = (byte[]) typeConverter.convertIfNecessary( + objectToTranslate, byte[].class); + Assert.assertTrue(Arrays.equals(objectToTranslate, translated), + "byte[] attributes should pass through unchanged."); + } catch (TypeMismatchException e) { + Assert + .fail( + "Unexpected exception: byte[] attributes should pass through unchanged.", + e); + } + } + + public void testConvertToBoolean() { + String objectToTranslate = "true"; + try { + Object translated = typeConverter.convertIfNecessary( + objectToTranslate, Boolean.class); + Assert + .assertEquals(true, translated, + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertFromBoolean() { + boolean objectToTranslate = false; + try { + Object translated = typeConverter.getAsText(objectToTranslate); + Assert.assertEquals(translated, "false", + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToString() { + String objectToTranslate = "onetwothree"; + try { + Object translated = typeConverter.convertIfNecessary( + objectToTranslate, String.class); + Assert.assertEquals("onetwothree", translated, + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToStringArray() { + String object1 = "onetwothree"; + String object2 = "fourfivesix"; + String object3 = "seveneightnine"; + Object objectToTranslate = new String[] { object1, object2, object3 }; + + try { + Object[] translated = (Object[]) typeConverter.convertIfNecessary( + objectToTranslate, String[].class); + Assert.assertEquals("fourfivesix", translated[1], + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToGeneralizedTime() throws ParseException { + String object1 = "20061005093637.44"; + DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss.S"); + Date expected = df.parse("20061005093637.44"); + + try { + Object translated = typeConverter.convertIfNecessary(object1, + Date.class); + Assert.assertEquals(translated, expected, + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToGeneralizedTimeArray() throws ParseException { + String object1 = "20071105093655.44"; + String object2 = "20061005093637.44"; + Object objectToTranslate = new String[] { object1, object2 }; + + DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss.S"); + Date expected = df.parse("20061005093637.44"); + + try { + Object[] translated = (Object[]) typeConverter.convertIfNecessary( + objectToTranslate, Date[].class); + Assert.assertEquals(translated[1], expected, + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation"); + } + } + + public void testConvertToLdapName() { + String object1 = "uid=amAdmin, ou = people, dc = myretsu,dc=com"; + try { + Object translated = typeConverter.convertIfNecessary(object1, + LdapName.class); + Assert.assertEquals(translated, new LdapName(object1), + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } catch (InvalidNameException e) { + Assert.fail("Problem with test: Ldap name can't be parsed.", e); + } + } + + public void testConvertToLdapNameArray() { + String object1 = "uid=amAdmin, ou = people, dc = myretsu,dc=com"; + String object2 = "uid=fred, ou = people, dc = myretsu,dc=com"; + Object objectToTranslate = new String[] { object1, object2 }; + + try { + Object[] translated = (Object[]) typeConverter.convertIfNecessary( + objectToTranslate, LdapName[].class); + Assert.assertEquals(new LdapName(object2), translated[1], + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } catch (InvalidNameException e) { + Assert.fail("Problem with test: Ldap name can't be parsed.", e); + } + } + + public void testConvertToDistinguishedName() { + String object1 = "uid=amAdmin, ou = people, dc = myretsu,dc=com"; + try { + Object translated = typeConverter.convertIfNecessary(object1, + DistinguishedName.class); + Assert.assertEquals(new DistinguishedName(object1), translated, + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToDistinguishedNameArray() { + String object1 = "uid=amAdmin, ou = people, dc = myretsu,dc=com"; + String object2 = "uid=fred, ou = people, dc = myretsu,dc=com"; + Object objectToTranslate = new String[] { object1, object2 }; + + try { + Object[] translated = (Object[]) typeConverter.convertIfNecessary( + objectToTranslate, DistinguishedName[].class); + Assert.assertEquals(new DistinguishedName(object2), translated[1], + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToLong() { + String object1 = "9887342"; + try { + Object translated = typeConverter.convertIfNecessary(object1, + Long.class); + Assert.assertEquals(9887342L, translated, + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToLongArray() { + String object1 = "878787"; + String object2 = "23948787"; + Object objectToTranslate = new String[] { object1, object2 }; + + try { + Object[] translated = (Object[]) typeConverter.convertIfNecessary( + objectToTranslate, Long[].class); + Assert.assertEquals(23948787L, translated[1], + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToInteger() { + String object1 = "9887342"; + + try { + Object translated = typeConverter.convertIfNecessary(object1, + Integer.class); + Assert.assertEquals(9887342, translated, + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testConvertToIntegerArray() { + String object1 = "878787"; + String object2 = "23948787"; + Object objectToTranslate = new String[] { object1, object2 }; + + try { + Object[] translated = (Object[]) typeConverter.convertIfNecessary( + objectToTranslate, Integer[].class); + Assert.assertEquals(23948787, translated[1], + "Unexpected translated value"); + } catch (TypeMismatchException e) { + Assert.fail("Unexpected exception during translation", e); + } + } + + public void testThrowsTypeMismatchExceptionWhenTypeTranslationFails() { + String object1 = "asdlkjkalkjl"; + + Object[] translated; + try { + translated = (Object[]) typeConverter.convertIfNecessary(object1, + LdapName.class); + Assert.fail("Should've thrown exception"); + } catch (TypeMismatchException e) { + // Pass + } + } + +}