diff --git a/mvn-build/core/build-instructions.txt b/mvn-build/core/build-instructions.txt new file mode 100644 index 00000000..fddffa69 --- /dev/null +++ b/mvn-build/core/build-instructions.txt @@ -0,0 +1,41 @@ +In order to build the spring-ldap-person sample, you need to build spring-ldap first. + +% cd spring-ldap +% ant dist + +You will most likely get this error, if you haven't installed [url=https://javacc.dev.java.net/]JavaCC[/url] and pointed out to Ant where it is: + +[code] +BUILD FAILED +... JavaCC home must be a valid directory. +[/code] + +You install JavaCC say in /javacc-4.0 and put this in a spring-ldap/build.properties: + +[code] +javacc.home.dir=/javacc-4.0 +[/code] + +Try building spring-ldap again: + +% ant dist + +If that succeeds, you'll get a snapshot build installed in your integration repo: + +% ls ../integration-repo/artifacts/org.springframework/spring-ldap/ +1.2.1-SNAPSHOT-20071206091603 +ivy-1.2.1-SNAPSHOT-20071206091603.xml +ivy-1.2.1-SNAPSHOT-20071206091603.xml.md5 +ivy-1.2.1-SNAPSHOT-20071206091603.xml.sha1 + +Next, you can build spring-ldap-person: + +% cd ../spring-ldap-person +% ant + +If that succeeds, you'll get a war in target/artifacts: + +% ls target/artifacts/war +ldap-person.war + +Deploy that in Tomcat and see how it goes. \ No newline at end of file diff --git a/mvn-build/core/build.xml b/mvn-build/core/build.xml new file mode 100644 index 00000000..6ad8a44e --- /dev/null +++ b/mvn-build/core/build.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Unit test report has been generated. +The HTML report is ${target.testresults.html.dir}/index.html + + + + + + + + + + + diff --git a/mvn-build/core/changelog.txt b/mvn-build/core/changelog.txt new file mode 100644 index 00000000..005a464a --- /dev/null +++ b/mvn-build/core/changelog.txt @@ -0,0 +1,365 @@ +Spring LDAP CHANGELOG +===================== +http://www.springframework.org/ldap + +http://www.ietf.org/rfc/rfc1960.txt +http://www.ietf.org/rfc/rfc2251.txt +http://www.ietf.org/rfc/rfc2252.txt +http://www.ietf.org/rfc/rfc2253.txt +http://www.ietf.org/rfc/rfc2254.txt +http://www.ietf.org/rfc/rfc2255.txt +http://www.ietf.org/rfc/rfc2256.txt +http://www.ietf.org/rfc/rfc2696.txt + +Changes in version 1.2.2 (XXX 2008) +------------------------------------------- +* Upgraded Acegi to 1.0.6. + +* Upgraded commons-lang to 2.3. + +Changes in version 1.2.1 (Dec 2007) +------------------------------------------- +* Added pooling library which features flexible connection validation + and better configuration than the built-in pooling. (LDAP-85) + +* Fixed a problem in AbstractContextSource which led to an unnecessary + reference to the LDAP Booster Pack (ldapbp). (LDAP-88, LDAP-89) + +* Added missing package description and updated copyright. (LDAP-92) + +* Added build instructions. (LDAP-27) + +* Fixed bug in SimpleLdapTemplate where the wrong target method was being + called. (LDAP-93) + +* Made createContext in AbstractContextSource protected rather than + package private. (LDAP-94) + +Changes in version 1.2 (Oct 2007) +------------------------------------------- +A number of API-breaking changes have been made in the 1.2 release compared to version 1.1.2. +Consequently this is NOT a drop-in replacement for Spring LDAP 1.1.2, +though upgrading should not present all that much work. Please see +the supplied upgrade guide for details. + +* Added getContextSource on LdapTemplate. + +* The following getters on AbstractContextSource have been added: + getContextFactory + getDirObjectFactory + getAuthenticationSource + getUrls (was protected) + isPooled + isAnonymousReadOnly + +* Upgraded to Spring 2.0.6 internally. Spring 1.2.x is still supported. + +* Reviewed all javadoc. + +* Revised reference manual. + +* AbstractConnection source now actively removes pooling flag from context environment + if pooling is set to 'false'. (LDAP-83) + +* AcegiAuthenticationSource now supports anonymous authentication. (LDAP-67) + +* Added BaseLdapPathSource, BaseLdapPathAware and BaseLdapNameBeanPostProcessor to be used + if the ContextSource base path is needed by beans. There is now also a DistinguishedNameEditor + available for directly injecting DistinguishedName instances to beans. (LDAP-86) + +* Added immutableDistinguishedName() method in DistinguishedName to get an immutable + copy of the instance. (LDAP-87) + +* Added easier access methods for RDN attributes and values. (LDAP-71) + +* Added lookupContext() and alternate modifyAttributes() in LdapOperations/LdapTemplate + to simplify updates. (LDAP-78) + +* Modified append() method in DistinguishedName. This method now returns the instance, to + enable more fluent building of DistinguishedNames. (LDAP-74) + +* Added SimpleLdapTemplate providing Java 5 generics support. (LDAP-72) + +* Added AbstractContextMapper implementation. (LDAP-76) + +Changes in version 1.2-RC1 (5.12.2007) +------------------------------------------- +A number of API-breaking changes have been made in this release, +mainly package restructuring stuff (LDAP-33). Consequently this is +NOT a drop-in replacement for Spring LDAP 1.1.2, though upgrading +should not present all that much work. Please see the supplied +upgrade guide for details. + +* Restructured packages (LDAP-33): + - Moved the core classes into the package ldap.core. + - Moved subpackages of ldap.support to top level. + - Moved most classes in ldap.support to ldap.core or ldap.core.support. + - Moved classes in ldap.utils to ldap.support and removed ldap.utils. + +* Changed the exception hierarchy to be an unchecked mirror of the JNDI + NamingException hierarchy (LDAP-4). + +* Deprecated the userName property in AbstractContextSource and changed + it to the more correct userDn (LDAP-18). + +* Fixed bug causing PagedResultsRequestControl processing to fail against AD + in the case of a PartialResultsException (LDAP-32). + +* Fixed bug causing NullPointerException in some environments when performing + PagedResultsRequestControl search (LDAP-37). + +* Implemented client-side transaction support for Spring LDAP. See reference + documentation for further information (LDAP-29). + +* DirContextAdapter.setAttribute() now properly updates updatedAttrs in update mode + (resulting in ModificationItems if applicable) (LDAP-15). + +* Exceptions thrown by Spring LDAP are now always Serializable, regardless of whether + the wrapped NamingException is (which is not always the case) (LDAP-14). + +* LdapRdnComponent now implements Serializable (LDAP-45). + +* Rewrote LdapEncoder.nameDecode() to solve problem with national characters and + remove regular expression used in parsing, drastically improving Distinguished Name + parsing performance as a bonus (LDAP-30). + +* The DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY has been deprecated. If the base + property needs to be accessed from a subclass to AbstractContextSource, use the new + protected method getBase (LDAP-55). + +* LdapContextSource.getAnonymousEnv no longer sets a broken ResponseControlFactory in + the environment (LDAP-64). + +* Upgraded ApacheDS to 1.0.0 (LDAP-32). + +* Upgraded to Acegi Security 1.0.3 (LDAP-61). + +* Upgraded to ApacheDS 1.0.0 (LDAP-32). + +* Upgraded to Spring 2.0.4 internally. Spring 1.2.x is still supported (LDAP-35, LDAP-51). + +* Upgraded to Spring WebFlow 1.0.2 in the Person sample (LDAP-60). + +* Fixed a number of documentation errors and typos (LDAP-41, LDAP-40, LDAP-47, LDAP-48). + +* Extracted useful inner classes from LdapTemplate to top-level classes (LDAP-42). + +* Implemented addAttributeValue and removeAttributeValue in DirContextAdapter (LDAP-5). + +* Modified DirContextOperations interface so that getNameInNamespace doesn't throw NamingException. + +* Fixed NullPointerException when getting PagedResultsRequestControl response (LDAP-66). + +* Rewrote logic i DirContextAdapter.getModificationItems(). The JNDI provider's Attribute + comparison logic is now used internally. Also, REMOVE_ATTRIBUTE items are placed + first in the ModificationItem array, in order to prevent ATTRIBUTE_ALREADY_EXIST errors + (LDAP-13, LDAP-46). + +* In internal integration tests, each test now runs on a clean LDAP database (LDAP-43). + +* DirContextProcessor#postProcess() is now called in searches regardless of whether an + exception is thrown (LDAP-26). + +* Moved SortControlDirContextProcessor to spring-ldap from sandbox (LDAP-68). + + +Changes in version 1.1.2 (1.12.2006) +------------------------------------------- +* Fixed problem with DirContextAdapter.getNameInNameSpace. Now it really + returns the full DN. + +* There is now a wrapper class called PagedResult, which can be used to + wrap the resulting list and the PagedResultsCookie after using + PagedResultsRequestControl. + +* The actual cookie in PagedResultsCookie is now immutable. + +* The person sample is now a Spring WebFlow application, where the user + can manage persons and standard LDAP access groups. + +* Improved javadoc for DefaultDirObjectFactory and DistinguishedName. + +* Upgraded Spring to 2.0.1 internally. Spring 1.2.8 is still supported. + +Changes in version 1.1.1 (18.11.2006) +------------------------------------------- +* Added capability to use server-side controls in search by leveraging + DirContextProcessor. + +* Added generic DirContextProcessor that is run before and after a search. + +* Added paged search result functionality in PagedResultsRequestControl, + which is an implementation of DirContextProcessor. + +* Fixed problem with DirContextAdapter, where the wrong change was applied + when a change was followed by a reset to the original values. + +* DirContextAdapter.getNameInNamespace() now returns the full DN, as per the + documentation for javax.naming.Context. + +* The parsing of DistinguishedName has been changed from using regexp to + a JavaCC parser, which means improved performance (5-6 times faster) and + better error messages. + +* DistinguishedName now supports multi-valued RDNs separated by a '+' sign, + like "cn=Rod+sn=Johnson", for example. All according to spec 2253. + +* LdapRdn is now Serializable. + +* Added lookup methods that take an array of return attribute names. + +* Made DistinguishedName.EMPTY_NAME an immutable DistinguishedName instance. + +* Separated integration tests that require OpenLDAP from the other integration + tests that run the in-JVM Apache DS server. + +* Changed the spring-ldap-person sample application to authenticate and + authorize against groups rather than role attributes. + +* Upgraded Spring to 2.0 internally. Spring 1.2.8 is still supported. + +Changes in version 1.1 (28.8.2006) +------------------------------------------- +* Changed base package from net.sf.ldaptemplate to org.springframework.ldap + +* Changed package ...support.acegi to ...support.authentication + +* Replaced SearchResultCallbackHandler with NameClassCallbackHandler + to enable its use in all search operations (search, list, listBindings). + Changed all references and implementing classes accordingly, + including name changes where applicable: + ** CollectingSearchResultCallbackHandler was replaced by CollectingNameClassPairCallbackHandler. + ** CountSearchResultCallbackHandler was replaced by CountNameClassPairCallbackHandler + +* Added NameClassPairMapper for use with list operations + +* Added DefaultNameClassPairCallbackHandler. + +* Added DefaultValuesAuthenticationSourceDecorator to enable default authentication + information when AuthenticationSource returns empty values. + +* ContextSource implementations now creates authenticated Contexts by default, + since the previous behaviour has proven to be confusing. + The authenticatedReadOnly flag was replaced by an anonymousReadOnly flag. + +* AbstractContextSource now defaults the dirObjectFactory property to DefaultDirObjectFactory. + +* Added search methods to LdapTemplate for specifying return attributes. + +* Added list and listBindings methods to LdapTemplate. + +* Fixed bug in AbstractContextSource regarding java version number and base path. + +* Improved javadocs. + +* Upgraded Spring to 2.0-rc3. + +* Added full reference documentation. + +Changes in version 1.0.2 (LdapTemplate) (26.6.2006) +------------------------------------ +* Added methods in LdapTemplate to unbind recursively. + +* Added rename() method to LdapTemplate. + +* Fixed bug in getPrefix() of DistinguishedName. + +* Fixed bug in getModificationItems() of DirContextAdapter. + The method now correctly handles changes of individual vales in + multi-value attributes. + +* Introduced AbstractContextSource, moved the code from LdapContextSource there + and made LdapContextSource and DirContextSource inherit from this. + +* Upgraded to Acegi 1.0.0. Removed classes no longer needed: + LdapTemplateLdapAuthenticationProvider + LdapTemplateUserDetails + *NOTE*: + This means that configuration needs to be changed if using Acegi, + since the API and package structure has changed in Acegi between + versions 1.0.0-RC2 and 1.0.0 final. + + +Changes in version 1.0.1 (LdapTemplate) (5.5.2006) +----------------------------------- +* Now checking whether the first argument to getObjectInstance in + DefaultDirObjectFactory is a Context, and if so, closing it properly. + +* Fixed reference to non-existent ContextSourceImpl in ldaptemplate-person. + +* Upgraded Spring to 2.0-m4. + +* Upgraded DDSteps to 1.1-rc1 and EasyMock to 1.2. + +Changes in version 1.0 (LdapTemplate) (24.4.2006) +---------------------------------- +* Introduced the AuthenticationSource interface to enable other strategies for + retrieving principal and credentials than a hard-coded username/password. + +* Added the AcegiAuthenticationSource implementation for retrieving a previously + authenticated principal and credentials using Acegi. + +* Added a 'cacheEnvironmentProperties' property that enables the user to choose + whether to cache the environment HashMap or to re-create it each time a new + Context is created. + +* Added an ignorePartialResultException to LdapTemplate which causes all + PartialResultExceptions to be ignored during searches, for use when working + against Active Directory, which cannot handle referrals correctly. + +* Added search methods in LdapOperations (and LdapTemplate) that take a + parameter for SearchControls. + +* The methods getNamesOfModifiedAttributes, update, and getAttributeSortedStringSet + in DirContextOperations (and DirContextAdapter) no longer throw NamingException. + Instead, they throw unchecked DataAccessExceptions. + +* The method getModificationItems in AttributeModificationsAware no longer throws + NamingException. Instead, it throws unchecked DataAccessExceptions. + +* Removed the (unused) 'task' parameter to NamingExceptionTranslator.translate. + +* Removed the deprecated ContextSourceImpl class. + +* Integration tests now run using an in-memory version of Apache Directory Server, + rather than requiring a working installation of a directory server. + +* Integration tests now run together with pure unit tests under the 'tests' Ant target. + +* The ldaptemplate-person sample web application now uses an in-memory version of Apache + Directory Server, rather than requiring a working installation of a directory server. + +* Upgraded Spring to 2.0-m3. + +* The build now uses Ivy 1.3.1. + +Changes in version 1.0-RC2 (LdapTemplate) (22.2.2006) +-------------------------------------- +* LdapV2 is now supported. + ContextSourceImpl has been deprecated. In stead the new + LdapContextSource should be used for LdapV3 and a DirContextSource + has been added for LdapV2 compatibility. + +* Ability has been added in LdapContextSource and DirContextSource + to specify that an authenticated context should be used for + read-only operations as well. Use property 'authenticatedReadOnly' + in LdapContextSource/DirContextSource. + +* The Filter classes have been cleaned up. + +* DirContextAdapter has been cleaned up. Most important to note is that + in this operation the 'setStringAttribute/setStringAttributes' methods + have been removed and replaced by 'setAttributeValue/setAttributeValues'. + +* Base DNs are now correctly URL encoded when building the connection String. + This means that spaces and other 'unsafe' characters in base DNs should no + longer be manually URL encoded. + +* An AttributesIntegrityViolationException has been added and is thrown when + an InvalidAttributesException is encountered. + +* More unit tests have been added. + +Changes in version 1.0-RC1 (LdapTemplate) (27.1.2006) +-------------------------------------- +* First public release candidate. diff --git a/mvn-build/core/docs/quickstart/quickstart.pdf b/mvn-build/core/docs/quickstart/quickstart.pdf new file mode 100644 index 00000000..c9e37ade Binary files /dev/null and b/mvn-build/core/docs/quickstart/quickstart.pdf differ diff --git a/mvn-build/core/docs/reference/images/admons/blank.png b/mvn-build/core/docs/reference/images/admons/blank.png new file mode 100644 index 00000000..764bf4f0 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/blank.png differ diff --git a/mvn-build/core/docs/reference/images/admons/caution.gif b/mvn-build/core/docs/reference/images/admons/caution.gif new file mode 100644 index 00000000..d9f5e5b1 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/caution.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/caution.png b/mvn-build/core/docs/reference/images/admons/caution.png new file mode 100644 index 00000000..5b7809ca Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/caution.png differ diff --git a/mvn-build/core/docs/reference/images/admons/caution.tif b/mvn-build/core/docs/reference/images/admons/caution.tif new file mode 100644 index 00000000..4a282948 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/caution.tif differ diff --git a/mvn-build/core/docs/reference/images/admons/draft.png b/mvn-build/core/docs/reference/images/admons/draft.png new file mode 100644 index 00000000..0084708c Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/draft.png differ diff --git a/mvn-build/core/docs/reference/images/admons/home.gif b/mvn-build/core/docs/reference/images/admons/home.gif new file mode 100644 index 00000000..6784f5bb Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/home.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/home.png b/mvn-build/core/docs/reference/images/admons/home.png new file mode 100644 index 00000000..cbb711de Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/home.png differ diff --git a/mvn-build/core/docs/reference/images/admons/important.gif b/mvn-build/core/docs/reference/images/admons/important.gif new file mode 100644 index 00000000..6795d9a8 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/important.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/important.png b/mvn-build/core/docs/reference/images/admons/important.png new file mode 100644 index 00000000..12c90f60 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/important.png differ diff --git a/mvn-build/core/docs/reference/images/admons/important.tif b/mvn-build/core/docs/reference/images/admons/important.tif new file mode 100644 index 00000000..184de637 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/important.tif differ diff --git a/mvn-build/core/docs/reference/images/admons/next.gif b/mvn-build/core/docs/reference/images/admons/next.gif new file mode 100644 index 00000000..aa1516e6 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/next.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/next.png b/mvn-build/core/docs/reference/images/admons/next.png new file mode 100644 index 00000000..45835bf8 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/next.png differ diff --git a/mvn-build/core/docs/reference/images/admons/note.gif b/mvn-build/core/docs/reference/images/admons/note.gif new file mode 100644 index 00000000..f329d359 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/note.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/note.png b/mvn-build/core/docs/reference/images/admons/note.png new file mode 100644 index 00000000..d0c3c645 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/note.png differ diff --git a/mvn-build/core/docs/reference/images/admons/note.tif b/mvn-build/core/docs/reference/images/admons/note.tif new file mode 100644 index 00000000..08644d6b Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/note.tif differ diff --git a/mvn-build/core/docs/reference/images/admons/prev.gif b/mvn-build/core/docs/reference/images/admons/prev.gif new file mode 100644 index 00000000..64ca8f3c Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/prev.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/prev.png b/mvn-build/core/docs/reference/images/admons/prev.png new file mode 100644 index 00000000..cf24654f Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/prev.png differ diff --git a/mvn-build/core/docs/reference/images/admons/tip.gif b/mvn-build/core/docs/reference/images/admons/tip.gif new file mode 100644 index 00000000..823f2b41 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/tip.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/tip.png b/mvn-build/core/docs/reference/images/admons/tip.png new file mode 100644 index 00000000..5c4aab3b Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/tip.png differ diff --git a/mvn-build/core/docs/reference/images/admons/tip.tif b/mvn-build/core/docs/reference/images/admons/tip.tif new file mode 100644 index 00000000..4a3d8c75 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/tip.tif differ diff --git a/mvn-build/core/docs/reference/images/admons/toc-blank.png b/mvn-build/core/docs/reference/images/admons/toc-blank.png new file mode 100644 index 00000000..6ffad17a Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/toc-blank.png differ diff --git a/mvn-build/core/docs/reference/images/admons/toc-minus.png b/mvn-build/core/docs/reference/images/admons/toc-minus.png new file mode 100644 index 00000000..abbb020c Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/toc-minus.png differ diff --git a/mvn-build/core/docs/reference/images/admons/toc-plus.png b/mvn-build/core/docs/reference/images/admons/toc-plus.png new file mode 100644 index 00000000..941312ce Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/toc-plus.png differ diff --git a/mvn-build/core/docs/reference/images/admons/up.gif b/mvn-build/core/docs/reference/images/admons/up.gif new file mode 100644 index 00000000..aabc2d01 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/up.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/up.png b/mvn-build/core/docs/reference/images/admons/up.png new file mode 100644 index 00000000..07634de2 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/up.png differ diff --git a/mvn-build/core/docs/reference/images/admons/warning.gif b/mvn-build/core/docs/reference/images/admons/warning.gif new file mode 100644 index 00000000..3adf1912 Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/warning.gif differ diff --git a/mvn-build/core/docs/reference/images/admons/warning.png b/mvn-build/core/docs/reference/images/admons/warning.png new file mode 100644 index 00000000..1c33db8f Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/warning.png differ diff --git a/mvn-build/core/docs/reference/images/admons/warning.tif b/mvn-build/core/docs/reference/images/admons/warning.tif new file mode 100644 index 00000000..7b6611ec Binary files /dev/null and b/mvn-build/core/docs/reference/images/admons/warning.tif differ diff --git a/mvn-build/core/docs/reference/images/callouts/1.gif b/mvn-build/core/docs/reference/images/callouts/1.gif new file mode 100644 index 00000000..0d669771 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/1.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/1.png b/mvn-build/core/docs/reference/images/callouts/1.png new file mode 100644 index 00000000..7d473430 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/1.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/10.gif b/mvn-build/core/docs/reference/images/callouts/10.gif new file mode 100644 index 00000000..fb50b06d Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/10.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/10.png b/mvn-build/core/docs/reference/images/callouts/10.png new file mode 100644 index 00000000..997bbc82 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/10.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/11.gif b/mvn-build/core/docs/reference/images/callouts/11.gif new file mode 100644 index 00000000..9f5dba4f Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/11.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/11.png b/mvn-build/core/docs/reference/images/callouts/11.png new file mode 100644 index 00000000..ce47dac3 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/11.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/12.gif b/mvn-build/core/docs/reference/images/callouts/12.gif new file mode 100644 index 00000000..a373d0b4 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/12.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/12.png b/mvn-build/core/docs/reference/images/callouts/12.png new file mode 100644 index 00000000..31daf4e2 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/12.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/13.gif b/mvn-build/core/docs/reference/images/callouts/13.gif new file mode 100644 index 00000000..b00b1637 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/13.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/13.png b/mvn-build/core/docs/reference/images/callouts/13.png new file mode 100644 index 00000000..14021a89 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/13.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/14.gif b/mvn-build/core/docs/reference/images/callouts/14.gif new file mode 100644 index 00000000..6d6642ee Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/14.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/14.png b/mvn-build/core/docs/reference/images/callouts/14.png new file mode 100644 index 00000000..64014b75 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/14.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/15.gif b/mvn-build/core/docs/reference/images/callouts/15.gif new file mode 100644 index 00000000..cdd7072d Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/15.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/15.png b/mvn-build/core/docs/reference/images/callouts/15.png new file mode 100644 index 00000000..0d65765f Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/15.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/2.gif b/mvn-build/core/docs/reference/images/callouts/2.gif new file mode 100644 index 00000000..100ff79f Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/2.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/2.png b/mvn-build/core/docs/reference/images/callouts/2.png new file mode 100644 index 00000000..5d09341b Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/2.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/3.gif b/mvn-build/core/docs/reference/images/callouts/3.gif new file mode 100644 index 00000000..5008ca7d Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/3.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/3.png b/mvn-build/core/docs/reference/images/callouts/3.png new file mode 100644 index 00000000..ef7b7004 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/3.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/4.gif b/mvn-build/core/docs/reference/images/callouts/4.gif new file mode 100644 index 00000000..0e5617d2 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/4.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/4.png b/mvn-build/core/docs/reference/images/callouts/4.png new file mode 100644 index 00000000..adb8364e Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/4.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/5.gif b/mvn-build/core/docs/reference/images/callouts/5.gif new file mode 100644 index 00000000..9bc75ada Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/5.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/5.png b/mvn-build/core/docs/reference/images/callouts/5.png new file mode 100644 index 00000000..4d7eb460 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/5.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/6.gif b/mvn-build/core/docs/reference/images/callouts/6.gif new file mode 100644 index 00000000..d3964070 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/6.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/6.png b/mvn-build/core/docs/reference/images/callouts/6.png new file mode 100644 index 00000000..0ba694af Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/6.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/7.gif b/mvn-build/core/docs/reference/images/callouts/7.gif new file mode 100644 index 00000000..c90b2f3d Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/7.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/7.png b/mvn-build/core/docs/reference/images/callouts/7.png new file mode 100644 index 00000000..472e96f8 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/7.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/8.gif b/mvn-build/core/docs/reference/images/callouts/8.gif new file mode 100644 index 00000000..6fe3287d Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/8.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/8.png b/mvn-build/core/docs/reference/images/callouts/8.png new file mode 100644 index 00000000..5e60973c Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/8.png differ diff --git a/mvn-build/core/docs/reference/images/callouts/9.gif b/mvn-build/core/docs/reference/images/callouts/9.gif new file mode 100644 index 00000000..bc5c8125 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/9.gif differ diff --git a/mvn-build/core/docs/reference/images/callouts/9.png b/mvn-build/core/docs/reference/images/callouts/9.png new file mode 100644 index 00000000..a0676d26 Binary files /dev/null and b/mvn-build/core/docs/reference/images/callouts/9.png differ diff --git a/mvn-build/core/docs/reference/readme.txt b/mvn-build/core/docs/reference/readme.txt new file mode 100644 index 00000000..6280723d --- /dev/null +++ b/mvn-build/core/docs/reference/readme.txt @@ -0,0 +1,20 @@ +This project uses the 'DocBook XSL distribution' for HTML and PDF +generation of project reference documentation. + +This project's build.xml file contains targets to generate the +project reference documentation. + +To generate project documentation, execute one of the following +build targets: + +* doc-all - generate documentation in all supported formats +* doc-pdf - generate the PDF documentation +* doc-html - generate the HTML documentation +* doc-htmlsingle - generate single page HTML documentation +* doc-clean - clean any output directories for docs + +For generation to complete successfully, you must have first extracted +the .jar libraries contained in this archive: + - http://static.springframework.org/spring/files/docbook-reference-libs.zip +... to ${basedir}/docs/reference. If you have not yet done so, download this file +and unzip the contents of the archive into ${basedir}/docs/reference. \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/basic.xml b/mvn-build/core/docs/reference/src/basic.xml new file mode 100644 index 00000000..c6e0f552 --- /dev/null +++ b/mvn-build/core/docs/reference/src/basic.xml @@ -0,0 +1,385 @@ + + + Basic Operations + + + Search and Lookup Using AttributesMapper + + In this example we will use an AttributesMapper + to easily build a List of all common names of all person objects. + + + AttributesMapper that returns a single attribute + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public List getAllPersonNames() { + return ldapTemplate.search( + "", "(objectclass=person)", + new AttributesMapper() { + public Object mapFromAttributes(Attributes attrs) + throws NamingException { + return attrs.get("cn").get(); + } + }); + } +} + + + The inline implementation of AttributesMapper + just gets the desired attribute value from the + Attributes and returns it. Internally, + LdapTemplate iterates over all entries found, calling + the given AttributesMapper for each entry, and collects + the results in a list. The list is then returned by the + search method. + + Note that the AttributesMapper implementation + could easily be modified to return a full Person + object: + + + AttributesMapper that returns a Person object + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + private class PersonAttributesMapper implements AttributesMapper { + public Object mapFromAttributes(Attributes attrs) throws NamingException { + Person person = new Person(); + person.setFullName((String)attrs.get("cn").get()); + person.setLastName((String)attrs.get("sn").get()); + person.setDescription((String)attrs.get("description").get()); + return person; + } + } + + public List getAllPersons() { + return ldapTemplate.search("", "(objectclass=person)", new PersonAttributesMapper()); + } +} + + + If you have the distinguished name (dn) that + identifies an entry, you can retrieve the entry directly, without + searching for it. This is called a lookup in Java + LDAP. The following example shows how a lookup results in a Person + object: + + + A lookup resulting in a Person object + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public Person findPerson(String dn) { + return (Person) ldapTemplate.lookup(dn, new PersonAttributesMapper()); + } +} + + + This will look up the specified dn and pass the + found attributes to the supplied AttributesMapper, in + this case resulting in a Person object. + + + + Building Dynamic Filters + + We can build dynamic filters to use in searches, using the classes + from the org.springframework.ldap.filter + package. Let's say that we want the following filter: + (&(objectclass=person)(sn=?)), where we want the + ? to be replaced with the value of the parameter + lastName. This is how we do it using the filter support + classes: + + + Building a search filter dynamically + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public List getPersonNamesByLastName(String lastName) { + AndFilter filter = new AndFilter(); + filter.and(new EqualsFilter("objectclass", "person")); + filter.and(new EqualsFilter("sn", lastName)); + return ldapTemplate.search( + "", filter.encode(), + new AttributesMapper() { + public Object mapFromAttributes(Attributes attrs) + throws NamingException { + return attrs.get("cn").get(); + } + }); + } +} + + + To perform a wildcard search, it's possible to use the + WhitespaceWildcardsFilter: + + + Building a wildcard search filter + + AndFilter filter = new AndFilter(); +filter.and(new EqualsFilter("objectclass", "person")); +filter.and(new WhitespaceWildcardsFilter("cn", cn)); + + + + + In addition to simplifying building of complex search filters, + the Filter classes also provide proper escaping + of any unsafe characters. This prevents "ldap injection", + where a user might use such characters to inject unwanted operations + into your LDAP operations. + + + + + + Building Dynamic Distinguished Names + + The standard Name + interface represents a generic name, which is basically an ordered + sequence of components. The Name interface also + provides operations on that sequence; e.g., add or + remove. LdapTemplate provides an implementation of the + Name interface: DistinguishedName. + Using this class will greatly simplify building distinguished names, + especially considering the sometimes complex rules regarding escapings and + encodings. As with the Filter classes this helps preventing + potentially malicious data being injected into your LDAP operations. + + + The following example illustrates how + DistinguishedName can be used to dynamically construct + a distinguished name: + + + Building a distinguished name dynamically + + package com.example.dao; + +import org.springframework.ldap.core.support.DistinguishedName; +import javax.naming.Name; + +public class PersonDaoImpl implements PersonDao { + public static final String BASE_DN = "dc=example,dc=com"; + ... + protected Name buildDn(Person p) { + DistinguishedName dn = new DistinguishedName(BASE_DN); + dn.add("c", p.getCountry()); + dn.add("ou", p.getCompany()); + dn.add("cn", p.getFullname()); + return dn; + } +} + + + Assuming that a Person has the following attributes: + + + + + + country + + Sweden + + + + company + + Some Company + + + + fullname + + Some Person + + + + + + The code above would then result in the following distinguished + name: + + cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com + + In Java 5, there is an implementation of the Name interface: LdapName. + If you are in the Java 5 world, you might as well use + LdapName. However, you may still use + DistinguishedName if you so wish. + + + + Binding and Unbinding + + + Binding Data + + Inserting data in Java LDAP is called binding. In order to do + that, a distinguished name that uniquely identifies the new entry is + required. The following example shows how data is bound using + LdapTemplate: + + + Binding data using Attributes + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void create(Person p) { + Name dn = buildDn(p); + ldapTemplate.bind(dn, null, buildAttributes(p)); + } + + private Attributes buildAttributes(Person p) { + Attributes attrs = new BasicAttributes(); + BasicAttribute ocattr = new BasicAttribute("objectclass"); + ocattr.add("top"); + ocattr.add("person"); + attrs.put(ocattr); + attrs.put("cn", "Some Person"); + attrs.put("sn", "Person"); + return attrs; + } +} + + + The Attributes building is--while dull and verbose--sufficient for + many purposes. It is, however, possible to simplify the binding + operation further, which will be described in . + + + + Unbinding Data + + Removing data in Java LDAP is called unbinding. A distinguished + name (dn) is required to identify the entry, just as in the binding + operation. The following example shows how data is unbound using + LdapTemplate: + + + Unbinding data + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void delete(Person p) { + Name dn = buildDn(p); + ldapTemplate.unbind(dn); + } +} + + + + + + Modifying + + In Java LDAP, data can be modified in two ways: either using + rebind or + modifyAttributes. + + + Modifying using <literal>rebind</literal> + + A rebind is a very crude way to modify data. + It's basically an unbind followed by a + bind. It looks like this: + + + Modifying using rebind + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void update(Person p) { + Name dn = buildDn(p); + ldapTemplate.rebind(dn, null, buildAttributes(p)); + } +} + + + + + Modifying using <literal>modifyAttributes</literal> + + If only the modified attributes should be replaced, there is a + method called modifyAttributes that takes an array of + modifications: + + + Modifying using modifyAttributes + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void updateDescription(Person p) { + Name dn = buildDn(p); + Attribute attr = new BasicAttribute("description", p.getDescription()) + ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr); + ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item}); + } +} + + + Building Attributes and + ModificationItem arrays is a lot of work, but as you + will see in , the update operations + can be simplified. + + + + + Sample applications + + It is recommended that you review the Spring LDAP sample + applications included in the release distribution for best-practice + illustrations of the features of this library. A description of each + sample is provided below: + + + + spring-ldap-person - the sample demonstrating most + features. + + + + spring-ldap-article - the sample application that was written + to accompany a java.net + article about Spring LDAP. + + + + \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/configuration.xml b/mvn-build/core/docs/reference/src/configuration.xml new file mode 100644 index 00000000..9555a24f --- /dev/null +++ b/mvn-build/core/docs/reference/src/configuration.xml @@ -0,0 +1,259 @@ + + + Configuration + + + ContextSource Configuration + + There are several properties in AbstractContextSource + (superclass of DirContextSource and LdapContextSource) + that can be used to modify its behaviour. + + + LDAP Server URLs + + The URL of the LDAP server is specified using the url property. + The URL should be in the format ldap://myserver.example.com:389. + For SSL access, use the ldaps protocol and the appropriate port, e.g. + ldaps://myserver.example.com:636 + It is possible to configure multiple alternate LDAP servers using the + urls property. In this case, supply all server urls in a String + array to the urls property. + + + + Base LDAP path + + It is possible to specify the root context for all LDAP operations using the + base property of AbstractContextSource. + When a value has been specified to this property, all Distinguished Names supplied to and received from LDAP operations + will be relative to the LDAP path supplied. This can significantly simplify working against the LDAP + tree; however there are several occations when you will need to have access to the base path. + For more information on this, please refer to + + + + Authentication + + Authenticated contexts are created for both read-only and + read-write operations by default. You specify + userDn and password of the LDAP + user to be used for authentication on the + ContextSource. + + + The userDn needs to be the full + Distinguished Name (DN) of the user. + + + Some LDAP server setups allow anonymous read-only access. If you + want to use anonymous Contexts for read-only operations, set the + anonymousReadOnly property to + true. + + + Custom Authentication Using Acegi + + While the user name (i.e. user DN) and password used for + creating an authenticated Context are static by + default - the ones set on the ContextSource on + startup will be used throughout the lifetime of the + ContextSource - there are however several cases in + which this is not the desired behaviour. A common scenario is that the + principal and credentials of the current user should be used when + executing LDAP operations for that user. The default behaviour can be + modified by supplying a custom AuthenticationSource + implementation to the ContextSource on startup, + instead of explicitly specifying the userDn and + password. The + AuthenticationSource will be queried by the + ContextSource for principal and credentials each + time an authenticated Context is to be + created. + + To use the authentication information of the currently logged in + user using Acegi + Security, use the + AcegiAuthenticationSource: + + + The Spring bean definition for an + AcegiAuthenticationSource + + <beans> + ... + <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"> + <property name="url" value="ldap://localhost:389" /> + <property name="base" value="dc=example,dc=com" /> + <property name="authenticationSource" ref="acegiAuthenticationSource" /> + </bean> + + <bean id="acegiAuthenticationSource" + class="org.springframework.ldap.authentication.AcegiAuthenticationSource" /> + ... +</beans> + + + + We don't specify any userDn or + password to our ContextSource + when using an AuthenticationSource - these + properties are needed only when the default behaviour is + used. + + + + When using the AcegiAuthenticationSource + you need to use Acegi's + LdapAuthenticationProvider to authenticate the + users against LDAP. + + + + + Default Authentication + + When using AcegiAuthenticationSource, + authenticated contexts will only be possible to create once the user + is logged in using Acegi. To use default authentication information + when no user is logged in, use the + DefaultValuesAuthenticationSourceDecorator: + + + Configuring a + DefaultValuesAuthenticationSourceDecorator + + <beans> + ... + <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"> + <property name="url" value="ldap://localhost:389" /> + <property name="base" value="dc=example,dc=com" /> + <property name="authenticationSource" ref="authenticationSource" /> + </bean> + + <bean id="authenticationSource" + class="org.springframework.ldap.authentication.DefaultValuesAuthenticationSourceDecorator"> + <property name="target" ref="acegiAuthenticationSource" /> + <property name="defaultUser" value="cn=myDefaultUser" /> + <property name="defaultPassword" value="pass" /> + </bean> + + <bean id="acegiAuthenticationSource" + class="org.springframework.ldap.authentication.AcegiAuthenticationSource" /> + ... +</beans> + + + + + + Pooling + + LDAP connection pooling can be turned on/off using the + pooled flag. Default is true. The + configuration of LDAP connection pooling is managed using + System properties, so this needs to be handled + manually. Details of pooling configuration can be found here. + + + + Advanced ContextSource Configuration + + Alternate ContextFactory + + It is possible to configure the ContextFactory that the + ContextSource is to use when creating Contexts using the + contextFactory property. The default value is + com.sun.jndi.ldap.LdapCtxFactory. + + + Custom DirObjectFactory + + As described in , a DirObjectFactory + can be used to translate the Attributes of found Contexts + to a more useful DirContext implementation. This can be + configured using the dirObjectFactory property. You can use + this property if you have your own, custom DirObjectFactory implementation. + The default value is DefaultDirObjectFactory. + + + Custom DirContext Environment Properties + + In some cases the user might want to specify additional environment setup properties + in addition to the ones directly configurable from AbstractContextSource. + Such properties should be set in a Map and supplied to + the baseEnvironmentProperties property. + + + + + LdapTemplate Configuration + + + Ignoring PartialResultExceptions + + Some Active Directory (AD) servers are unable to automatically following + referrals, which often leads to a PartialResultException being + thrown in searches. You can specify that PartialResultException + is to be ignored by setting the ignorePartialResultException + property to true. + This causes all referrals to be ignored, and no notice will be given that + a PartialResultException has been encountered. + There is currently no way of manually following referrals using LdapTemplate. + + + + Obtaining a reference to the base LDAP path + As described above, a base LDAP path may be supplied to the ContextSource, + specifying the root in the LDAP tree to which all operations will be relative. This means that + you will only be working with relative distinguished names throughout your system, which is + typically rather handy. There are however some cases in which you will need to have access + to the base path in order to be able to construct full DNs, relative to the actual root of the LDAP tree. + One example would be when working with LDAP groups (e.g. groupOfNames objectclass), + in which case each group member attribute value will need to be the full DN of the referenced member. + For that reason, Spring LDAP has a mechanism by which any Spring controlled bean may be supplied + the base path on startup. For beans to be notified of the base path, two things need to be in place: + First of all, the bean that wants the base path reference needs to implement the + BaseLdapPathAware interface. Secondly, a BaseLdapPathBeanPostProcessor + needs to be defined in the application context + + Implementing <literal>BaseLdapPathAware</literal> + + package com.example.service; + +public class PersonService implements PersonService, BaseLdapPathAware { + ... + private DistinguishedName basePath; + + public void setBaseLdapPath(DistinguishedName basePath) { + this.basePath = basePath; + } + ... + private DistinguishedName getFullPersonDn(Person person) { + return new DistinguishedName(basePath).append(person.getDn()); + } + ... +} + + + Specifying a <literal>BaseLdapPathBeanPostProcessor</literal> in your <literal>ApplicationContext</literal> + + <beans> + ... + <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"> + <property name="url" value="ldap://localhost:389" /> + <property name="base" value="dc=example,dc=com" /> + <property name="acegiAuthenticationSource" ref="authenticationSource" /> + </bean> + ... + <bean class="org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor" /> +</beans> + + + The default behaviour of the BaseLdapPathBeanPostProcessor is to use the base path of the single + defined BaseLdapPathSource (AbstractContextSource )in the ApplicationContext. + If more than one BaseLdapPathSource is defined, you will need to specify which one to use with the + baseLdapPathSourceName property. + + \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/contextprocessor.xml b/mvn-build/core/docs/reference/src/contextprocessor.xml new file mode 100644 index 00000000..9f8bf51c --- /dev/null +++ b/mvn-build/core/docs/reference/src/contextprocessor.xml @@ -0,0 +1,200 @@ + + + Processing the DirContext + + + Custom DirContext Pre/Postprocessing + + In some situations, one would like to perform operations on the + DirContext before and after the search operation. The + interface that is used for this is called + DirContextProcessor: + + + public interface DirContextProcessor { + public void preProcess(DirContext ctx) throws NamingException; + public void postProcess(DirContext ctx) throws NamingException; +} + + + The LdapTemplate class has a search method that + takes a DirContextProcessor: + + + public void search(SearchExecutor se, NameClassPairCallbackHandler handler, + DirContextProcessor processor) throws DataAccessException; + + + Before the search operation, the preProcess + method is called on the given DirContextProcessor + instance. After the search has been executed and the resulting + NamingEnumeration has been processed, the + postProcess method is called. This enables a user to + perform operations on the DirContext to be used in the + search, and to check the DirContext when the search has + been performed. This can be very useful for example when handling request + and response controls. + + There are also a few convenience methods for those that don't need a + custom SearchExecutor: + + + public void search(Name base, String filter, + SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor) + +public void search(String base, String filter, + SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor) + +public void search(Name base, String filter, + SearchControls controls, AttributesMapper mapper, DirContextProcessor processor) + +public void search(String base, String filter, + SearchControls controls, AttributesMapper mapper, DirContextProcessor processor) + +public void search(Name base, String filter, + SearchControls controls, ContextMapper mapper, DirContextProcessor processor) + +public void search(String base, String filter, + SearchControls controls, ContextMapper mapper, DirContextProcessor processor) + + + + + Implementing a Request Control DirContextProcessor + + The LDAPv3 protocol uses Controls to send and receive additional + data to affect the behavior of predefined operations. In order to simplify + the implementation of a request control + DirContextProcessor, Spring LDAP provides the base + class AbstractRequestControlDirContextProcessor. This + class handles the retrieval of the current request controls from the + LdapContext, calls a template method for creating a + request control, and adds it to the LdapContext. All + you have to do in the subclass is to implement the template method + createRequestControl, and of course the + postProcess method for performing whatever you need to + do after the search. + + + public abstract class AbstractRequestControlDirContextProcessor implements + DirContextProcessor { + + public void preProcess(DirContext ctx) throws NamingException { + ... + } + + public abstract Control createRequestControl(); +} + + A typical DirContextProcessor will be similar to the following: + + + + A request control DirContextProcessor implementation + + package com.example.control; + +public class MyCoolRequestControl extends AbstractRequestControlDirContextProcessor { + private static final boolean CRITICAL_CONTROL = true; + private MyCoolCookie cookie; + ... + public MyCoolCookie getCookie() { + return cookie; + } + + public Control createRequestControl() { + return new SomeCoolControl(cookie.getCookie(), CRITICAL_CONTROL); + } + + public void postProcess(DirContext ctx) throws NamingException { + LdapContext ldapContext = (LdapContext) ctx; + Control[] responseControls = ldapContext.getResponseControls(); + + for (int i = 0; i < responseControls.length; i++) { + if (responseControls[i] instanceof SomeCoolResponseControl) { + SomeCoolResponseControl control = (SomeCoolResponseControl) responseControls[i]; + this.cookie = new MyCoolCookie(control.getCookie()); + } + } + } +} + + + + Make sure you use LdapContextSource when you + use Controls. The Control + interface is specific for LDAPv3 and requires that + LdapContext is used instead of + DirContext. If an + AbstractRequestControlDirContextProcessor subclass is + called with an argument that is not an LdapContext, + it will throw an IllegalArgumentException. + + + + + Paged Search Results + + Some searches may return large numbers of results. When there is no + easy way to filter out a smaller amount, it would be convenient to have + the server return only a certain number of results each time it is called. + This is known as paged search results. Each "page" of + the result could then be displayed at the time, with links to the next and + previous page. Without this functionality, the client must either manually + limit the search result into pages, or retrieve the whole result and then + chop it into pages of suitable size. The former would be rather + complicated, and the latter would be consuming unnecessary amounts of + memory. + + Some LDAP servers have support for the + PagedResultsControl, which requests that the results of + a search operation are returned by the LDAP server in pages of a specified + size. The user controls the rate at which the pages are returned, simply + by the rate at which the searches are called. However, the user must keep + track of a cookie between the calls. The server uses + this cookie to keep track of where it left off the previous time it was + called with a paged results request. + + Spring LDAP provides support for paged results by leveraging the + concept for pre- and postprocessing of an LdapContext that was discussed + in the previous sections. It does so by providing two classes: + PagedResultsRequestControl and + PagedResultsCookie. The + PagedResultsRequestControl class creates a + PagedResultsControl with the requested page size and + adds it to the LdapContext. After the search, it gets + the PagedResultsResponseControl and retrieves two + pieces of information from it: the estimated total result size and a + cookie. This cookie is a byte array containing information that the server + needs the next time it is called with a + PagedResultsControl. In order to make it easy to store + this cookie between searches, Spring LDAP provides the wrapper class + PagedResultsCookie. + + Below is an example of how the paged search results functionality may + be used: + + + Paged results using <literal>PagedResultsRequestControl</literal> + + public PagedResult getAllPersons(PagedResultsCookie cookie) { + PagedResultsRequestControl control = new PagedResultsRequestControl(PAGE_SIZE, cookie); + SearchControls searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + List persons = ldapTemplate.search("", "objectclass=person", searchControls, control); + + return new PagedResult(persons, control.getCookie()); + } + + + In the first call to this method, null will be supplied as + the cookie parameter. On subsequent calls the client will need to supply the cookie from + the last search (returned wrapped in the PagedResult) each time the + method is called. When the actual cookie is null (i.e. + pagedResult.getCookie().getCookie() returns null), + the last batch has been returned from the search. + + + \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/dirobjectfactory.xml b/mvn-build/core/docs/reference/src/dirobjectfactory.xml new file mode 100644 index 00000000..0892ccc9 --- /dev/null +++ b/mvn-build/core/docs/reference/src/dirobjectfactory.xml @@ -0,0 +1,341 @@ + + + Simpler Attribute Access and Manipulation with DirContextAdapter + + + Introduction + + A little-known--and probably underestimated--feature of the Java + LDAP API is the ability to register a DirObjectFactory + to automatically create objects from found contexts. One of the reasons + why it is seldom used is that you will need an implementation of + DirObjectFactory that creates instances of a meaningful + implementation of DirContext. The Spring LDAP library + provides the missing pieces: a default implementation of + DirContext called DirContextAdapter, + and a corresponding implementation of DirObjectFactory + called DefaultDirObjectFactory. Used together with + DefaultDirObjectFactory, the + DirContextAdapter can be a very powerful tool. + + + + Search and Lookup Using ContextMapper + + The DefaultDirObjectFactory is registered with + the ContextSource by default, which means that whenever + a context is found in the LDAP tree, its Attributes and + Distinguished Name (DN) will be used to construct a + DirContextAdapter. This enables us to use a + ContextMapper instead of an + AttributesMapper to transform found values: + + + Searching using a ContextMapper + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + ... + private static class PersonContextMapper implements ContextMapper { + public Object mapFromContext(Object ctx) { + DirContextAdapter context = (DirContextAdapter)ctx; + Person p = new Person(); + p.setFullName(context.getStringAttribute("cn")); + p.setLastName(context.getStringAttribute("sn")); + p.setDescription(context.getStringAttribute("description")); + return p; + } + } + + public Person findByPrimaryKey( + String name, String company, String country) { + Name dn = buildDn(name, company, country); + return ldapTemplate.lookup(dn, new PersonContextMapper()); + } +} + + + The above code shows that it is possible to retrieve the attributes + directly by name, without having to go through the + Attributes and BasicAttribute + classes. This is particularly useful when working with multi-value attributes. Extracting values from + multi-value attributes normally requires looping through a NamingEnumeration of + attribute values returned from the Attributes implementation. The + DirContextAdapter can do this for you, using the getStringAttributes() + or getObjectAttributes() methods: + + Getting multi-value attribute values using <literal>getStringAttributes()</literal> + + private static class PersonContextMapper implements ContextMapper { + public Object mapFromContext(Object ctx) { + DirContextAdapter context = (DirContextAdapter)ctx; + Person p = new Person(); + p.setFullName(context.getStringAttribute("cn")); + p.setLastName(context.getStringAttribute("sn")); + p.setDescription(context.getStringAttribute("description")); + // The roleNames property of Person is an String array + p.setRoleNames(context.getStringAttributes("roleNames")); + return p; + } +} + + + + + The AbstractContextMapper + Spring LDAP provides an abstract base implementation of ContextMapper, + AbstractContextMapper. This automatically takes care of the casting of the supplied + Object parameter to DirContexOperations. + The PersonContextMapper above can thus be re-written as follows: + + + Using an AbstractContextMapper + + + private static class PersonContextMapper extends AbstractContextMapper { + public Object doMapFromContext(DirContextOperations ctx) { + Person p = new Person(); + p.setFullName(context.getStringAttribute("cn")); + p.setLastName(context.getStringAttribute("sn")); + p.setDescription(context.getStringAttribute("description")); + return p; + } + } + + + + + + + Binding and Modifying Using DirContextAdapter + + While very useful when extracting attribute values, DirContextAdapter is even more + powerful for hiding attribute details when binding and modifying data. + + + Binding + + This is an example of an improved implementation of the create DAO + method. Compare it with the previous implementation in . + + + Binding using <literal>DirContextAdapter</literal> + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + ... + public void create(Person p) { + Name dn = buildDn(p); + DirContextAdapter context = new DirContextAdapter(dn); + + context.setAttributeValues("objectclass", new String[] {"top", "person"}); + context.setAttributeValue("cn", p.getFullname()); + context.setAttributeValue("sn", p.getLastname()); + context.setAttributeValue("description", p.getDescription()); + + ldapTemplate.bind(dn, context, null); + } +} + + + Note that we use the DirContextAdapter instance + as the second parameter to bind, which should be a Context. + The third parameter is null, since we're not using any + Attributes. + Also note the use of the setAttributeValues() method when setting the + objectclass attribute values. The objectclass attribute is + multi-value, and similar to the troubles of extracting muti-value attribute data, building multi-value + attributes is tedious and verbose work. Using the setAttributeValues() mehtod you can + have DirContextAdapter handle that work for you. + + + + Modifying + + The code for a rebind would be pretty much + identical to , except + that the method called would be rebind. As we saw in + a more correct approach would be to + build a ModificationItem array containing the actual + modifications you want to do. This would require you to determine the actual + modifications compared to the data present in the LDAP tree. Again, this + is something that DirContextAdapter can help you with; the + DirContextAdapter has the ability to keep track of + its modified attributes. The following example takes advantage of this + feature: + + + Modifying using <literal>DirContextAdapter</literal> + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + ... + public void update(Person p) { + Name dn = buildDn(p); + DirContextOperations context = ldapTemplate.lookupContext(dn); + + context.setAttributeValues("objectclass", new String[] {"top", "person"}); + context.setAttributeValue("cn", p.getFullname()); + context.setAttributeValue("sn", p.getLastname()); + context.setAttributeValue("description", p.getDescription()); + + ldapTemplate.modifyAttributes(context); + } +} + + When no mapper is passed to a ldapTemplate.lookup() operation, + the result will be a DirContextAdapter instance. + While the lookup method returns an Object, the convenience + method lookupContext method automatically casts the return value to + a DirContextOperations (the interface that DirContextAdapter implements. + The observant reader will see that we have duplicated code in the + create and update methods. This + code maps from a domain object to a context. It can be extracted to a + separate method: + + + Binding and modifying using DirContextAdapter + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + + ... + public void create(Person p) { + Name dn = buildDn(p); + DirContextAdapter context = new DirContextAdapter(dn); + mapToContext(p, context); + ldapTemplate.bind(dn, context, null); + } + + public void update(Person p) { + Name dn = buildDn(p); + DirContextOperations context = ldapTemplate.lookupContext(dn); + mapToContext(person, context); + ldapTemplate.modifyAttributes(context); + } + + protected void mapToContext (Person p, DirContextOperations context) { + context.setAttributeValues("objectclass", new String[] {"top", "person"}); + context.setAttributeValue("cn", p.getFullName()); + context.setAttributeValue("sn", p.getLastName()); + context.setAttributeValue("description", p.getDescription()); + } +} + + + + + + A Complete PersonDao Class + + To illustrate the power of Spring LDAP, here is a complete Person + DAO implementation for LDAP in just 68 lines: + + + A complete PersonDao class + + package com.example.dao; + +import java.util.List; + +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.support.DistinguishedName; +import org.springframework.ldap.filter.EqualsFilter; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public void create(Person person) { + DirContextAdapter context = new DirContextAdapter(); + mapToContext(person, context); + ldapTemplate.bind(buildDn(person), context, null); + } + + public void update(Person person) { + Name dn = buildDn(person); + DirContextOperations context = ldapTemplate.lookupContext(dn); + mapToContext(person, context); + ldapTemplate.modifyAttributes(context); + } + + public void delete(Person person) { + ldapTemplate.unbind(buildDn(person)); + } + + public Person findByPrimaryKey(String name, String company, String country) { + Name dn = buildDn(name, company, country); + return (Person) ldapTemplate.lookup(dn, getContextMapper()); + } + + public List findAll() { + EqualsFilter filter = new EqualsFilter("objectclass", "person"); + return ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(), getContextMapper()); + } + + protected ContextMapper getContextMapper() { + return new PersonContextMapper(); + } + + protected Name buildDn(Person person) { + return buildDn(person.getFullname(), person.getCompany(), person.getCountry()); + } + + protected Name buildDn(String fullname, String company, String country) { + DistinguishedName dn = new DistinguishedName(); + dn.add("c", country); + dn.add("ou", company); + dn.add("cn", fullname); + return dn; + } + + protected void mapToContext(Person person, DirContextOperations context) { + context.setAttributeValues("objectclass", new String[] {"top", "person"}); + context.setAttributeValue("cn", person.getFullName()); + context.setAttributeValue("sn", person.getLastName()); + context.setAttributeValue("description", person.getDescription()); + } + + private static class PersonContextMapper extends AbstractContextMapper { + public Object doMapFromContext(DirContextOperations context) { + Person person = new Person(); + person.setFullName(context.getStringAttribute("cn")); + person.setLastName(context.getStringAttribute("sn")); + person.setDescription(context.getStringAttribute("description")); + return person; + } + } +} + + + + In several cases the Distinguished Name (DN) of an object is + constructed using properties of the object. E.g. in the above example, + the country, company and full name of the Person are + used in the DN, which means that updating any of these properties will + actually require moving the entry in the LDAP tree using the + rename() operation in addition to updating the + Attribute values. Since this is highly implementation + specific this is something you'll need to keep track of yourself - + either by disallowing the user to change these properties or performing + the rename() operation in your + update() method if needed. + + + \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/executors.xml b/mvn-build/core/docs/reference/src/executors.xml new file mode 100644 index 00000000..cadc78e8 --- /dev/null +++ b/mvn-build/core/docs/reference/src/executors.xml @@ -0,0 +1,148 @@ + + + Adding Missing Overloaded API Methods + + + Implementing Custom Search Methods + + While LdapTemplate contains several overloaded + versions of the most common operations in DirContext, + we have not provided an alternative for each and every method signature, + mostly because there are so many of them. We have, however, provided a + means to call whichever DirContext method you want + and still get the benefits that LdapTemplate provides. + + Let's say that you want to call the following DirContext + method: + + NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls ctls) + + There is no corresponding overloaded method in LdapTemplate. The way to solve + this is to use a custom SearchExecutor implementation: + + + public interface SearchExecutor { + public NamingEnumeration executeSearch(DirContext ctx) throws NamingException; +} + + + In your custom executor, you have access to a DirContext + object, which you use to call the method you want. You then provide a handler + that is responsible for mapping attributes and collecting the results. You can + for example use one of the available implementations of + CollectingNameClassPairCallbackHandler, which will collect + the mapped results in an internal list. In order to + actually execute the search, you call the search + method in LdapTemplate that takes an executor and a handler as arguments. Finally, + you return whatever your handler has collected. + + + A custom search method using SearchExecutor and + AttributesMapper + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + ... + public List search(final Name base, final String filter, final String[] params, + final SearchControls ctls) { + SearchExecutor executor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) { + return ctx.search(base, filter, params, ctls); + } + }; + + CollectingNameClassPairCallbackHandler handler = + new AttributesMapperCallbackHandler(new PersonAttributesMapper()); + + ldapTemplate.search(executor, handler); + return handler.getList(); + } +} + + + If you prefer the ContextMapper to the + AttributesMapper, this is what it would look + like: + + + A custom search method using SearchExecutor and + ContextMapper + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + ... + public List search(final Name base, final String filter, final String[] params, + final SearchControls ctls) { + SearchExecutor executor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) { + return ctx.search(base, filter, params, ctls); + } + }; + + CollectingNameClassPairCallbackHandler handler = + new ContextMapperCallbackHandler(new PersonContextMapper()); + + ldapTemplate.search(executor, handler); + return handler.getList(); + } +} + + + + When using the + ContextMapperCallbackHandler you must + make sure that you have called + setReturningObjFlag(true) on your + SearchControls instance. + + + + + Implementing Other Custom Context Methods + + In the same manner as for custom search methods, + you can actually execute any method in DirContext by + using a ContextExecutor. + + + public interface ContextExecutor { + public Object executeWithContext(DirContext ctx) throws NamingException; +} + + When implementing a custom ContextExecutor, you + can choose between using the executeReadOnly() or the + executeReadWrite() method. Let's say that we want to + call this method: + + + Object lookupLink(Name name) + + It's available in DirContext, but there is no + matching method in LdapTemplate. It's a lookup method, + so it should be read-only. We can implement it like this: + + + A custom DirContext method using ContextExecutor + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + ... + public Object lookupLink(final Name name) { + ContextExecutor executor = new ContextExecutor() { + public Object executeWithContext(DirContext ctx) { + return ctx.lookupLink(name); + } + }; + + return ldapTemplate.executeReadOnly(executor); + } +} + + In the same manner you can execute a read-write operation using + the executeReadWrite() method. + + + \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/images/NamingException.png b/mvn-build/core/docs/reference/src/images/NamingException.png new file mode 100644 index 00000000..8042b84e Binary files /dev/null and b/mvn-build/core/docs/reference/src/images/NamingException.png differ diff --git a/mvn-build/core/docs/reference/src/images/banner4.jpg b/mvn-build/core/docs/reference/src/images/banner4.jpg new file mode 100644 index 00000000..ef33d2ae Binary files /dev/null and b/mvn-build/core/docs/reference/src/images/banner4.jpg differ diff --git a/mvn-build/core/docs/reference/src/images/bannerR.gif b/mvn-build/core/docs/reference/src/images/bannerR.gif new file mode 100644 index 00000000..548ab884 Binary files /dev/null and b/mvn-build/core/docs/reference/src/images/bannerR.gif differ diff --git a/mvn-build/core/docs/reference/src/images/logo.png b/mvn-build/core/docs/reference/src/images/logo.png new file mode 100644 index 00000000..31396b8a Binary files /dev/null and b/mvn-build/core/docs/reference/src/images/logo.png differ diff --git a/mvn-build/core/docs/reference/src/images/package-dependencies.png b/mvn-build/core/docs/reference/src/images/package-dependencies.png new file mode 100644 index 00000000..7a85cf53 Binary files /dev/null and b/mvn-build/core/docs/reference/src/images/package-dependencies.png differ diff --git a/mvn-build/core/docs/reference/src/images/package-structure.png b/mvn-build/core/docs/reference/src/images/package-structure.png new file mode 100644 index 00000000..253a0191 Binary files /dev/null and b/mvn-build/core/docs/reference/src/images/package-structure.png differ diff --git a/mvn-build/core/docs/reference/src/index.xml b/mvn-build/core/docs/reference/src/index.xml new file mode 100644 index 00000000..90f5a7b4 --- /dev/null +++ b/mvn-build/core/docs/reference/src/index.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + +]> + + + + + + Spring LDAP + Reference Documentation + Version 1.2.1 + December 2007 + + + Mattias + Arthursson + + + Ulrik + Sandberg + + + Eric + Dalquist + + + + + + Copies of this document may be made for your own use and + for distribution to others, provided that you do not + charge any fee for such copies and further provided that + each copy contains this Copyright Notice, whether + distributed in print or electronically. + + + + + + + &preface; + &overview; + &basic; + &dirobjectfactory; + &transactions; + &executors; + &contextprocessor; + &simple; + &configuration; + &pooling; + + \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/overview.xml b/mvn-build/core/docs/reference/src/overview.xml new file mode 100644 index 00000000..dbd53f14 --- /dev/null +++ b/mvn-build/core/docs/reference/src/overview.xml @@ -0,0 +1,417 @@ + + + Introduction + + + Overview + + Spring-LDAP (http://www.springframework.org/ldap) + is a library for simpler LDAP programming in Java, built on the same + principles as the JdbcTemplate + in Spring JDBC. It completely eliminates the need to worry about creating + and closing LdapContext and looping through + NamingEnumeration. It also provides a more + comprehensive unchecked Exception hierarchy, built on Spring's + DataAccessException. As a bonus, it also contains + classes for dynamically building LDAP filters and DNs (Distinguished + Names), LDAP attribute management, and client-side LDAP transaction management. + + Consider, for example, a method that should search some storage for + all persons and return their names in a list. Using JDBC, we would create + a connection and execute a query + using a statement. We would then loop over the + result set and retrieve the + column we want, adding it to a list. In contrast, + using Java LDAP, we would create a context and + perform a search using a search + filter. We would then loop over the resulting naming + enumeration and retrieve the attribute we + want, adding it to a list. + + The traditional way of implementing this person name search method + in Java LDAP looks like this, where the code marked as bold actually + performs tasks related to the business purpose of the method: + + + package com.example.dao; + +public class TraditionalPersonDaoImpl implements PersonDao { + public List getAllPersonNames() { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, "ldap://localhost:389/dc=example,dc=com"); + + DirContext ctx; + try { + ctx = new InitialDirContext(env); + } catch (NamingException e) { + throw new RuntimeException(e); + } + + LinkedList list = new LinkedList(); + NamingEnumeration results = null; + try { + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + results = ctx.search("", "(objectclass=person)", controls); + + while (results.hasMore()) { + SearchResult searchResult = (SearchResult) results.next(); + Attributes attributes = searchResult.getAttributes(); + Attribute attr = attributes.get("cn"); + String cn = (String) attr.get(); + list.add(cn); + } + } catch (NameNotFoundException e) { + // The base context was not found. + // Just clean up and exit. + } catch (NamingException e) { + throw new RuntimeException(e); + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception e) { + // Never mind this. + } + } + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + return list; + } +} + + + By using the Spring LDAP classes AttributesMapper + and LdapTemplate, we get the exact same functionality + with the following code: + + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public List getAllPersonNames() { + return ldapTemplate.search( + "", "(objectclass=person)", + new AttributesMapper() { + public Object mapFromAttributes(Attributes attrs) + throws NamingException { + return attrs.get("cn").get(); + } + }); + } +} + + + The amount of boiler-plate code is significantly less than in the + traditional example. The LdapTemplate version of the + search method performs the search, maps the attributes to a string using + the given AttributesMapper, collects the strings in an + internal list, and finally returns the list. + + Note that the PersonDaoImpl code simply assumes + that it has an LdapTemplate instance, rather than + looking one up somewhere. It provides a set method for this purpose. There + is nothing Spring-specific about this "Inversion of Control". Anyone that + can create an instance of PersonDaoImpl can also set + the LdapTemplate on it. However, Spring provides a very + flexible and easy way of achieving + this. The Spring container can be told to wire up an instance of + LdapTemplate with its required dependencies and inject + it into the PersonDao instance. This wiring can be + defined in various ways, but the most common is through XML: + + + <beans> + <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"> + <property name="url" value="ldap://localhost:389" /> + <property name="base" value="dc=example,dc=com" /> + <property name="userDn" value="cn=Manager" /> + <property name="password" value="secret" /> + </bean> + + <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate"> + <constructor-arg ref="contextSource" /> + </bean> + + <bean id="personDao" class="com.example.dao.PersonDaoImpl"> + <property name="ldapTemplate" ref="ldapTemplate" /> + </bean> +</beans> + + + + + Packaging overview + + At a minimum, to use Spring LDAP you need: + + + + spring-ldap (the Spring LDAP library) + + + + spring-core (miscellaneous utility classes used internally by + the framework) + + + + spring-beans (contains interfaces and classes for manipulating + Java beans) + + + + commons-logging (a simple logging facade, used + internally) + + + + commons-lang (misc utilities, used internally) + + + + + In addition to the required dependencies the following optional dependencies + are required for certain functionality: + + + + acegi-security (For Acegi security integration using AcegiAuthenticationSource) + + + spring-context (If your application is wired up using the Spring Application Context - + adds the ability for application objects to obtain resources using a consistent API. Definitely needed if you are + planning on using the BaseLdapPathBeanPostProcessor.) + + + spring-dao (If you are planning to use the client side compensating transaction support) + + + spring-jdbc (If you are planning to use the client side compensating transaction support) + + + ldapbp (Sun LDAP Booster Pack - if you will use the LDAP v3 Server controls integration) + + + commons-pool (If you are planning to use the pooling functionality) + + + + + + + Package structure + + This section provides an overview of the logical package structure + of the Spring LDAP codebase. The dependencies for each package are clearly + noted. A package dependency noted as (optional) means + that the dependency is needed to compile the package but is optionally + needed at runtime (depending on your use of the package). For example, use + of Spring LDAP together with Acegi Security entails use of the + org.acegisecurity package. + +
+ Spring LDAP package structure + + + + + + +
+ + + org.springframework.transaction.compensating + + The transaction.compensating package contains + the generic compensating transaction support. This is not LDAP-specific + or JNDI-specific in any way. + + + + Dependencies: commons-logging + + + + + + org.springframework.ldap + + The ldap package contains the exceptions of + the library. These exceptions form an unchecked hierarchy that mirrors + the NamingException hierarchy. + + + + Dependencies: spring-core + + + + + + org.springframework.ldap.core + + The ldap.core package contains the central + abstractions of the library. These abstractions include + AuthenticationSource, ContextSource, DirContextProcessor, and + NameClassPairCallbackHandler. This package also contains the central + class LdapTemplate, plus various mappers and executors. + + + + Dependencies: ldap, ldap.support, spring-beans, + commons-lang, commons-logging + + + + + + org.springframework.ldap.core.support + + The ldap.core.support package contains + supporting implementations of some of the core interfaces. + + + + Dependencies: ldap.core, ldap.support, spring-core, + spring-beans, spring-context (optional), commons-lang, commons-logging + + + + + + org.springframework.ldap.support + + The ldap.support package contains supporting + utilities, like the exception translation mechanism. + + + + Dependencies: ldap, spring-core, commons-logging + + + + + + org.springframework.ldap.authentication + + The ldap.authentication package contains an + implementation of the AuthenticationSource interface that can be used + with Acegi Security, + as well as related helper classes. + + + + Dependencies: ldap.core, acegi-security (optional), + spring-beans, commons-lang, commons-logging + + + + + + org.springframework.ldap.control + + The ldap.control package contains an abstract + implementation of the DirContextProcessor interface that can be used as + a basis for processing RequestControls and ResponseControls. There is + also a concrete implementation that handles paged search results. The + LDAP Booster + Pack is used to get support for controls. + + + + Dependencies: ldap, ldap.core, LDAP booster pack (optional), spring-core, + commons-lang, commons-logging + + + + + + org.springframework.ldap.filter + + The ldap.filter package contains the Filter + abstraction and several implementations of it. + + + + Dependencies: ldap.core, commons-lang + + + + + + org.springframework.ldap.transaction.compensating + + The ldap.transaction.compensating package contains the + core LDAP-specific implementation of compensating transactions. + + + + Dependencies: ldap, ldap.core, transaction.compensating, + spring-core, commons-lang, commons-logging + + + + + + org.springframework.ldap.transaction.compensating.manager + + The ldap.transaction.compensating.manager package contains the + core implementation classes for client-side compensating transactions. + + + + Dependencies: ldap, ldap.core, ldap.transaction.compensating, + ldap.transaction.compensating.support, transaction.compensating, + spring-dao (optional), spring-jdbc (optional), commons-logging + + + + + + org.springframework.ldap.transaction.compensating.support + + The ldap.transaction.compensating.support package contains + useful helper classes for client-side compensating transactions. + + + + Dependencies: ldap.core, ldap.transaction.compensating + + + + + For the exact list of jar dependencies, see the Spring LDAP Ivy dependency manager descriptor + located within the Spring LDAP distribution at + spring-ldap/ivy.xml +
+ + + Support + + Spring LDAP 1.2.1 is supported on Spring 1.2.8 or later, including + 2.0.x. + + The community support forum is located at http://forum.springframework.org, + and the project web page is http://www.springframework.org/ldap. + +
\ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/pooling.xml b/mvn-build/core/docs/reference/src/pooling.xml new file mode 100644 index 00000000..2df9a509 --- /dev/null +++ b/mvn-build/core/docs/reference/src/pooling.xml @@ -0,0 +1,550 @@ + + + + Pooling Support + + + Introduction + + + Pooling LDAP connections helps mitigate the overhead of + creating a new LDAP connection for each LDAP interaction. + While + + Java LDAP pooling support + + exists it is limited in its configuration options and + features, such as connection validation and pool + maintenance. Spring LDAP provides support for detailed pool + configuration on a per- + ContextSource + basis. + + + + Pooling support is provided by + PoolingContextSource + which can wrap any + ContextSource + and pool both read-only and read-write + DirContext + objects. + + Jakarta Commons-Pool + + is used to provide the underlying pool implementation. + + + + + DirContext Validation + + + Validation of pooled connections is the primary motivation + for using a custom pooling library versus the JDK provided + LDAP pooling functionality. Validation allows pooled + DirContext + connections to be checked to ensure they are still properly + connected and configured when checking them out of the pool, + in to the pool or while idle in the pool + + + + The + DirContextValidator + interface is used by the + PoolingContextSource + for validation and + DefaultDirContextValidator + is provided as the default validation implementation. + DefaultDirContextValidator + does a + + DirContext.search(String, String, SearchControls) + + , with an empty name, a filter of + "objectclass=*" + and + SearchControls + set to limit a single result with the only the objectclass + attribute and a 500ms timeout. If the returned + NamingEnumeration + has results the + DirContext + passes validation, if no results are returned or an + exception is thrown the + DirContext + fails validation. The + DefaultDirContextValidator + should work with no configuration changes on most LDAP + servers and provide the fastest way to validate the + DirContext + . + + + + + Pool Properties + + + The following properties are available on the + PoolingContextSource + for configuration of the DirContext pool. The + contextSource + property must be set and the + dirContextValidator + property must be set if validation is enabled, all other + properties are optional. + + + + Pooling Configuration Properties + + + + + + + + + + Parameter + + Default + + Description + + + + + + + contextSource + + + + null + + + + The + ContextSource + implementation to get + DirContext + s from to populate the pool. + + + + + + dirContextValidator + + + + null + + + + The + DirContextValidator + implementation to use when validating + connections. This is required if + testOnBorrow + , + testOnReturn + , or + testWhileIdle + options are set to + true + . + + + + + + maxActive + + + + 8 + + + + The maximum number of active connections of + each type (read-only|read-write) that can be + allocated from this pool at the same time, + or non-positive for no limit. + + + + + + maxTotal + + + + -1 + + + + The overall maximum number of active + connections (for all types) that can be + allocated from this pool at the same time, + or non-positive for no limit. + + + + + + maxIdle + + + + 8 + + + + The maximum number of active connections of + each type (read-only|read-write) that can + remain idle in the pool, without extra ones + being released, or non-positive for no + limit. + + + + + + minIdle + + + + 0 + + + + The minimum number of active connections of + each type (read-only|read-write) that can + remain idle in the pool, without extra ones + being created, or zero to create none. + + + + + + maxWait + + + + -1 + + + + The maximum number of milliseconds that the + pool will wait (when there are no available + connections) for a connection to be returned + before throwing an exception, or + non-positive to wait indefinitely. + + + + + + whenExhaustedAction + + + + 1 (BLOCK) + + + + Specifies the behaviour when the pool is + exhausted. + + + + The + FAIL (0) + option will throw a + + NoSuchElementException + + when the pool is exhausted. + + + + + + The + BLOCK (1) + option will wait until a new + object is available. If + maxWait + is positive a + + NoSuchElementException + + is thrown if no new object is + available after the + maxWait + time expires. + + + + + + The + GROW (2) + option will create and return a + new object (essentially making + maxActive + meaningless). + + + + + + + + + testOnBorrow + + + + false + + + + The indication of whether objects will be + validated before being borrowed from the + pool. If the object fails to validate, it + will be dropped from the pool, and an + attempt to borrow another will be made. + + + + + + testOnReturn + + + + false + + + + The indication of whether objects will be + validated before being returned to the pool. + + + + + + testWhileIdle + + + + false + + + + The indication of whether objects will be + validated by the idle object evictor (if + any). If an object fails to validate, it + will be dropped from the pool. + + + + + + + timeBetweenEvictionRunsMillis + + + + + -1 + + + + The number of milliseconds to sleep between + runs of the idle object evictor thread. When + non-positive, no idle object evictor thread + will be run. + + + + + + numTestsPerEvictionRun + + + + 3 + + + + The number of objects to examine during each + run of the idle object evictor thread (if + any). + + + + + + + minEvictableIdleTimeMillis + + + + + 1000 * 60 * 30 + + + + The minimum amount of time an object may sit + idle in the pool before it is eligible for + eviction by the idle object evictor (if + any). + + + + +
+
+ + + Configuration + + + Configuring pooling should look very familiar if you're used + to Jakarta Commons-Pool or Commons-DBCP. You will first + create a normal + ContextSource + then wrap it in a + PoolingContextSource + . + + + ... + + + + + + + + + + + + ... + +]]> + + In a real world example you would probably configure the + pool options and enable connection validation; the above + serves as an example to demonstrate the general idea. + + + Ensure that the + pooled + property is set to + false + on any + ContextSource + that will be wrapped in a + PoolingContextSource + . The + PoolingContextSource + must be able to create new connections when needed + and if + pooled + is set to + true + that may not be possible. + + + + + You'll notice that the actual + ContextSource + gets an id with a "Target" suffix. The bean you will + actually refer to is the + PoolingContextSource + that wraps the target + contextSource + + + + + + Validation Configuration + + + Adding validation and a few pool configuration tweaks to + the above example is straight forward. Inject a + DirContextValidator + and set when validation should occur and the pool is + ready to go. + + + ... + + + + + + + + + + + + + + + + + ... + +]]> + + The above example will test each + DirContext + before it is passed to the client application and test + DirContext + s that have been sitting idle in the pool. + + + + + + Known Issues + + + Custom Authentication + + + The PoolingContextSource assumes that all + DirContext objects retrieved from + ContextSource.getReadOnlyContext() will have + the same environment and likewise that all + DirContext objects retrieved from + ContextSource.getReadWriteContext() will + have the same environment. This means that wrapping a + LdapContextSource configured with an + AuthenticationSource in a + PoolingContextSource will not function + as expected. The pool would be populated using the credentials + of the first user and unless new connections were needed + subsequent context requests would not be filled for the user + specified by the AuthenticationSource for + the requesting thread. + + + +
\ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/preface.xml b/mvn-build/core/docs/reference/src/preface.xml new file mode 100644 index 00000000..b0a97605 --- /dev/null +++ b/mvn-build/core/docs/reference/src/preface.xml @@ -0,0 +1,32 @@ + + + Preface + + The Java Naming and Directory Interface (JNDI) is for LDAP programming + what Java Database Connectivity (JDBC) is for SQL programming. There are + several similarities between JDBC and JNDI/LDAP (Java LDAP). Despite + being two completely different APIs with different pros and cons, they + share a number of less flattering characteristics: + + + + They require extensive plumbing code, even to perform the simplest of tasks. + + + All resources need to be correctly closed, no matter what happens. + + + Exception handling is difficult. + + + + The above points often lead to massive code duplication in common usages + of the APIs. As we all know, code duplication is one of the worst code smells. + All in all, it boils down to this: JDBC and LDAP programming in Java are both + incredibly dull and repetitive. + + + Spring JDBC, a part of the Spring framework, provides excellent utilities for + simplifying SQL programming. We need a similar framework for Java LDAP programming. + + diff --git a/mvn-build/core/docs/reference/src/simple.xml b/mvn-build/core/docs/reference/src/simple.xml new file mode 100644 index 00000000..95056634 --- /dev/null +++ b/mvn-build/core/docs/reference/src/simple.xml @@ -0,0 +1,35 @@ + + + + Java 5 Support + + + SimpleLdapTemplate + + As of version 1.2 Spring LDAP includes the spring-ldap-tiger.jar distributable, which adds + a thin layer of Java 5 functionality on top of Spring LDAP. + + The SimpleLdapTemplate class adds search and lookup methods that take a + ParameterizedContextMapper, adding generics support to these methods. + + ParametrizedContextMapper is a typed version of ContextMapper, + which simplifies working with searches and lookups: + + Using <literal>ParameterizedContextMapper</literal> + public List<Person> getAllPersons(){ + return simpleLdapTemplate.search("", "(objectclass=person)", + new ParameterizedContextMapper<Person>() { + public Person mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + Person person = new Person(); + // Fill the domain object with data from the DirContextAdapter + + return person; + } + }; +} + + + + + \ No newline at end of file diff --git a/mvn-build/core/docs/reference/src/transactions.xml b/mvn-build/core/docs/reference/src/transactions.xml new file mode 100644 index 00000000..b456e277 --- /dev/null +++ b/mvn-build/core/docs/reference/src/transactions.xml @@ -0,0 +1,216 @@ + + + + Transaction Support + + + Introduction + + Programmers used to working with relational databases coming to the LDAP + world often express surprise to the fact that there is no notion of transactions. + It is not specified in the protocol, and thus no servers support it. + Recognizing that this may be a major problem, Spring LDAP provides support for client-side, + compensating transactions on LDAP resources. + + LDAP transaction support is provided by ContextSourceTransactionManager, a + PlatformTransactionManager implementation that manages Spring transaction + support for LDAP operations. Along with its collaborators it keeps track of the LDAP operations + performed in a transaction, making record of the state before each operation and taking steps to + restore the initial state should the transaction need to be rolled back. + In addition to the actual transaction management, Spring LDAP transaction support also + makes sure that the same DirContext instance will be used throughout the same transaction, + i.e. the DirContext will not actually be closed until the transaction is finished, + allowing for more efficient resources usage. + + It is important to note that while the approach used by Spring LDAP to provide transaction support + is sufficient for many cases it is by no means "real" transactions in the traditional sense. + The server is completely unaware of the transactions, so e.g. if the connection is broken there will + be no hope to rollback the transaction. While this should be carefully considered it should also be noted + that the alternative will be to operate without any transaction support whatsoever; this is pretty much + as good as it gets. + The client side transaction support will add some overhead in addition to the work required + by the original operations. While this overhead should not be something to worry about in most cases, + if your application will not perform several LDAP operations within the same + transaction (e.g. a modifyAttributes followed by a rebind), or + if transaction synchronization with a JDBC data source is not required (see below) there will be nothing to gain + by using the LDAP transaction support. + + + + + Configuration + + Configuring Spring LDAP transactions should look very familiar if you're used to configuring Spring transactions. + You will create a TransactionManager instance and wrap your target object using a + TransactionProxyFactoryBean. In addition to this, you will also need to wrap your + ContextSource in a TransactionAwareContextSourceProxy. + + <beans> + ... + <bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource"> + <property name="url" value="ldap://localhost:389" /> + <property name="base" value="dc=example,dc=com" /> + <property name="userDn" value="cn=Manager" /> + <property name="password" value="secret" /> + </bean> + + <bean id="contextSource" + class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy"> + <constructor-arg ref="contextSourceTarget" /> + </bean> + + <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate"> + <constructor-arg ref="contextSource" /> + </bean> + + <bean id="transactionManager" + class="org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager"> + <constructor-arg ref="contextSource" /> + </bean> + + <bean id="myDataAccessObjectTarget" class="com.example.MyDataAccessObject"> + <property name="ldapTemplate" ref="ldapTemplate" /> + </bean> + + <bean id="myDataAccessObject" + class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> + <property name="transactionManager" ref="transactionManager" /> + <property name="target" ref="myDataAccessObjectTarget" /> + <property name="transactionAttributes"> + <props> + <prop key="*">PROPAGATION_REQUIRES_NEW</prop> + </props> + </property> + </bean> + ... + + In a real world example you would probably apply the transactions on the service object level + rather than the DAO level; the above serves as an example to demonstrate the general idea. + You'll notice that the actual ContextSource and DAO instances get ids with a + "Target" suffix. The beans you will actually refer to are the Proxies that are created + around the targets; contextSource and myDataAccessObject + + + + JDBC Transaction Integration + A common use case when working against LDAP is that some of the data is stored in the LDAP tree, but + other data is stored in a relational database. In this case, transaction support becomes even more important, + since the update of the different resources should be synchronized. + While actual XA transactions is not supported, support is provided to conceptually wrap JDBC and LDAP + access within the same transaction using the ContextSourceAndDataSourceTransactionManager. + A DataSource and a ContextSource is supplied to the + ContextSourceAndDataSourceTransactionManager, which will then manage the two transactions, + virtually as if they were one. When performing a commit, the LDAP part of the operation will always + be performed first, allowing both transactions to be rolled back should the LDAP commit fail. The JDBC + part of the transaction is managed exactly as in DataSourceTransactionManager, except that + nested transactions is not supported. + Once again it should be noted that the provided support is all client side. The wrapped transaction is not + an XA transaction. No two-phase as such commit is performed, as the LDAP server will be unable to vote on its outcome. + Once again, however, for the majority of cases the supplied support will be sufficient. + + + LDAP Compensating Transactions Explained + Spring LDAP manages compensating transactions by making record of the state in the LDAP tree + before each modifying operation (bind, unbind, rebind, + modifyAttributes, and rename). + This enables the system + to perform compensating operations should the transaction need to be rolled back. In many cases the + compensating operation is pretty straightforward. E.g. the compensating rollback operation for a + bind operation will quite obviously be to unbind the entry. Other operations however require + a different, more complicated approach because of some particular characteristics of LDAP databases. Specifically, + it is not always possible to get the values of all Attributes of an entry, making the above + strategy insufficient for e.g. an unbind operation. + This is why each modifying operation performed within a Spring LDAP managed transaction is internally + split up in four distinct operations - a recording operation, a preparation operation, a commit operation, + and a rollback operation. The specifics for each LDAP operation is described in the table below: + + + + + + + + + + LDAP Operation + Recording + Preparation + Commit + Rollback + + + + + bind + Make record of the DN of the entry to bind. + Bind the entry. + No operation. + Unbind the entry using the recorded DN. + + + rename + Make record of the original and target DN. + Rename the entry. + No operation. + Rename the entry back to its original DN. + + + unbind + Make record of the original DN and calculate a temporary DN. + Rename the entry to the temporary location. + Unbind the temporary entry. + Rename the entry from the temporary location back to its original DN. + + + rebind + Make record of the original DN and the new Attributes, and calculate a temporary DN. + Rename the entry to a temporary location. + Bind the new Attributes at the original DN, and unbind the original entry + from its temporary location. + Rename the entry from the temporary location back to its original DN. + + + modifyAttributes + Make record of the DN of the entry to modify and calculate compensating ModificationItems + for the modifications to be done. + Perform the modifyAttributes operation. + No operation. + Perform a modifyAttributes operation using the calculated compensating + ModificationItems. + + + +
+ A more detailed description of the internal workings of the Spring LDAP transaction support is available in the + javadocs. + + Renaming Strategies + As described in the table above, the transaction management of some operations require the original entry affected + by the operation to be temporarily renamed before the actual modification can be made in the commit. + The manner in which the temporary DN of the entry is calculated is managed by a TempEntryRenamingStrategy + supplied to the ContextSourceTransactionManager. Two implementations are supplied with Spring LDAP, + but if specific behaviour is required a custom implementation can easily be implemented by the user. The + provided TempEntryRenamingStrategy implementations are: + + DefaultTempEntryRenamingStrategy (the default). Adds a suffix to the least significant + part of the entry DN. E.g. for the DN cn=john doe, ou=users, this strategy would return the + temporary DN cn=john doe_temp, ou=users. The suffix is configurable using the tempSuffix + property + DifferentSubtreeTempEntryRenamingStrategy. Takes the least significant part of the DN + and appends a subtree DN to this. This makes all temporary entries be placed at a specific location in the LDAP tree. + The temporary subtree DN is configured using the subtreeNode property. E.g., if + subtreeNode is ou=tempEntries and the original DN of the entry is + cn=john doe, ou=users, the temporary DN will be cn=john doe, ou=tempEntries. + Note that the configured subtree node needs to be present in the LDAP tree. + + + There are some situations where the DefaultTempEntryRenamingStrategy will not work. E.g. if your are planning + to do recursive deletes you'll need to use DifferentSubtreeTempEntryRenamingStrategy. This is because + the recursive delete operation actually consists of a depth-first delete of each node in the sub tree individually. + Since it is not allowed to rename an entry that has any children, and DefaultTempEntryRenamingStrategy would + leave each node in the same subtree (with a different name) in stead of actually removing it, this operation would fail. + When in doubt, use DifferentSubtreeTempEntryRenamingStrategy. + + +
+
\ No newline at end of file diff --git a/mvn-build/core/docs/reference/styles/fopdf.xsl b/mvn-build/core/docs/reference/styles/fopdf.xsl new file mode 100644 index 00000000..d47be44b --- /dev/null +++ b/mvn-build/core/docs/reference/styles/fopdf.xsl @@ -0,0 +1,475 @@ + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright ©right; 2005-2006 + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -5em + -5em + + + + + + + + + + + Spring LDAP () + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bold + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 0 + 1 + + 1 + + + + + + book toc + + + + 2 + + + + + + + + + + 0 + 0 + 0 + + + 5mm + 10mm + 10mm + + 15mm + 10mm + 0mm + + 18mm + 18mm + + + 0pc + + + + + justify + false + + + 11 + 8 + + + 1.4 + + + + + + + 0.8em + + + + + + 17.4cm + + + + 4pt + 4pt + 4pt + 4pt + + + + 0.1pt + 0.1pt + + + + + 1 + + + + + + + + left + bold + + + pt + + + + + + + + + + + + + + + 0.8em + 0.8em + 0.8em + + + pt + + 0.1em + 0.1em + 0.1em + + + 0.6em + 0.6em + 0.6em + + + pt + + 0.1em + 0.1em + 0.1em + + + 0.4em + 0.4em + 0.4em + + + pt + + 0.1em + 0.1em + 0.1em + + + + + bold + + + pt + + false + 0.4em + 0.6em + 0.8em + + + + + + + + + pt + + + + + 1em + 1em + 1em + #444444 + solid + 0.1pt + 0.5em + 0.5em + 0.5em + 0.5em + 0.5em + 0.5em + + + + 1 + + #F0F0F0 + + + + + + 0 + 1 + + + 90 + + + + + '1' + &admon_gfx_path; + + + + + + figure after + example before + equation before + table before + procedure before + + + + 1 + + + + 0.8em + 0.8em + 0.8em + 0.1em + 0.1em + 0.1em + + + + + + + + + + + + + + + + + diff --git a/mvn-build/core/docs/reference/styles/html.css b/mvn-build/core/docs/reference/styles/html.css new file mode 100644 index 00000000..c603be90 --- /dev/null +++ b/mvn-build/core/docs/reference/styles/html.css @@ -0,0 +1,270 @@ +BODY { + text-align: justify; + margin-right: 2em; + margin-left: 2em; +} + +A[accesskey^="p"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A[accesskey^="u"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A[accesskey^="h"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A[accesskey^="n"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A { + color: #003399; + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; +} + +A:active { + color: #003399; +} + +A:visited { + color: #888888; +} + +P { + font-family: Verdana, Arial; +} + +DT { + font-family: Verdana, Arial; + font-size: 12px; +} + +P, DL, DT, DD, BLOCKQUOTE { + color: #000000; + margin-bottom: 3px; + margin-top: 3px; + padding-top: 0px; +} + +OL, UL, P { + margin-top: 6px; + margin-bottom: 6px; +} + +P, BLOCKQUOTE { + font-size: 90%; +} + +P.releaseinfo { + font-size: 100%; + font-weight: bold; + font-family: Verdana, Arial, helvetica, sans-serif; + padding-top: 10px; +} + +P.pubdate { + font-size: 120%; font-weight: bold; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +td { + font-size: 80%; +} + +TD, TH, SPAN { + color: #000000; +} + +TD[width^="40%"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +TABLE[summary^="Navigation header"] tbody tr th[colspan^="3"] { + font-family: Verdana, Arial, helvetica, sans-serif; +} + +BLOCKQUOTE { + margin-right: 0px; +} + +H1, H2, H3, H4, H5, H6 { + color: #000000; + font-weight:500; + margin-top:0px; + padding-top:14px; + font-family: Verdana, Arial, helvetica, sans-serif; + margin-bottom: 0px; +} + +H2.title { + font-weight:800; + margin-bottom: 8px; +} + +H2.subtitle { + font-weight:800; + margin-bottom: 20px; +} + +.firstname, .surname { + font-size: 12px; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +TABLE { + border-collapse: collapse; + border-spacing:0; + border: 1px thin black; + empty-cells: hide; + margin: 10px 0px 30px 50px; + width: 90%; +} + +div.table { + margin: 30px 0 30 0; + border: 1px dashed gray; + padding: 10px; +} + +div.table > p.title { + padding-left: 10px; +} + +table[summary^="Navigation footer"] { + border-collapse: collapse; + border-spacing:0; + border: 1px thin black; + empty-cells: hide; + margin: 0px; + width: 100%; +} + +table[summary^="Note"], table[summary^="Warning"], table[summary^="Tip"] { + border-collapse: collapse; + border-spacing:0; + border: 1px thin black; + empty-cells: hide; + margin: 10px 0 10 -20; + width: 100%; +} + +TD { + padding: 4pt; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +div.warning TD { + text-align: justify; +} + +H1 { + font-size: 150%; +} +H2 { + font-size: 110%; +} +H3 { + font-size: 100%; font-weight: bold; +} +H4 { + font-size: 90%; font-weight: bold; +} +H5 { + font-size: 90%; font-style: italic; +} +H6 { + font-size: 100%; font-style: italic; +} + +TT { + font-size: 110%; + font-family: "Courier New", Courier, monospace; + color: #000000; +} + +.navheader, .navfooter { + background-color: #e4eff3; +} + +PRE { + font-size: 110%; + padding: 5px; + border-style: solid; + border-width: 1px; + border-color: #CCCCCC; + background-color: #F4F4F4; +} + +UL, OL, LI { + list-style: disc; +} + +HR { + width: 100%; + height: 1px; + background-color: #CCCCCC; + border-width: 0px; + padding: 0px; + color: #CCCCCC; +} + +.variablelist { + padding-top: 10; + padding-bottom:10; + margin:0; +} + +.term { + font-weight:bold; +} + +.mediaobject { + padding-top: 30px; + padding-bottom: 30px; +} + +.legalnotice { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + font-style: italic; +} + +.sidebar { + float: right; + margin: 10px 0px 10px 30px; + padding: 10px 20px 20px 20px; + width: 33%; + border: 1px solid black; + background-color: #F4F4F4; + font-size: 14px; +} + +.programlisting * .classname { + font-size: 95%; +} + +.programlisting * .interfacename { + font-size: 95%; +} + +.programlisting * .literal { + font-size: 95%; +} + +.property { + font-family: "Courier New", Courier, monospace; +} diff --git a/mvn-build/core/docs/reference/styles/html.xsl b/mvn-build/core/docs/reference/styles/html.xsl new file mode 100644 index 00000000..0b35acad --- /dev/null +++ b/mvn-build/core/docs/reference/styles/html.xsl @@ -0,0 +1,100 @@ + + + + + +]> + + + + + + + + ../styles/html.css + + + 1 + 0 + 1 + 0 + + + + + + book toc + + + + 3 + + + + + 1 + + + + + + + 1 + &callout_gfx_path; + + + 90 + + + + + '1' + &admon_gfx_path; + + + + + figure after + example before + equation before + table before + procedure before + + + + , + + + + + + + + +
+

Authors

+

+ +

+
+ +
diff --git a/mvn-build/core/docs/reference/styles/html_chunk.xsl b/mvn-build/core/docs/reference/styles/html_chunk.xsl new file mode 100644 index 00000000..258b97ce --- /dev/null +++ b/mvn-build/core/docs/reference/styles/html_chunk.xsl @@ -0,0 +1,103 @@ + + + + + +]> + + + + '5' + '1' + ../styles/html.css + + 1 + 0 + 1 + 0 + + + + book toc + + + 3 + + + 1 + + + + + 1 + &callout_gfx_path; + + 90 + + + '1' + &admon_gfx_path; + + + + figure after + example before + equation before + table before + procedure before + + + + , + + + + + + + + +
+

Authors

+

+ +

+
+ + + + + + + + + + 1 + +
diff --git a/mvn-build/core/docs/reference/styles/tld.to.docbook.xsl b/mvn-build/core/docs/reference/styles/tld.to.docbook.xsl new file mode 100644 index 00000000..b0823a14 --- /dev/null +++ b/mvn-build/core/docs/reference/styles/tld.to.docbook.xsl @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + -intro + + Introduction + + + + This appendix describes the + + + + tag library descriptor. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The + + + + tag + + + + + + + Attributes + + + + 4 + + + + center + + + + + center + + + + + center + + + + + center + + + + + + + center + + Attribute + + + + center + + Required? + + + + center + + Runtime Expression? + + + + center + + Description + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + + diff --git a/mvn-build/core/docs/reference/styles/xsd.to.docbook.xsl b/mvn-build/core/docs/reference/styles/xsd.to.docbook.xsl new file mode 100644 index 00000000..4a9bb86d --- /dev/null +++ b/mvn-build/core/docs/reference/styles/xsd.to.docbook.xsl @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + -intro + + + Introduction + + + + + This appendix describes the + + + + schema. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The + + + + element + + + [TODO : insert the description of the element here] + + + + + + + . + + + diff --git a/mvn-build/core/docs/upgrade/src/readme.txt b/mvn-build/core/docs/upgrade/src/readme.txt new file mode 100644 index 00000000..fecf1c5c --- /dev/null +++ b/mvn-build/core/docs/upgrade/src/readme.txt @@ -0,0 +1,19 @@ +This project uses the 'DocBook XSL distribution' for HTML and PDF +generation of project upgrade documentation. + +This project's build.xml file contains targets to generate the +project upgrade documentation. + +To generate project documentation, execute one of the following +build targets: + +* upgrade-all - generate documentation in all supported formats +* upgrade-pdf - generate the PDF documentation +* upgrade-html - generate the HTML documentation +* upgrade-clean - clean any output directories for docs + +For generation to complete successfully, you must have first extracted +the .jar libraries contained in this archive: + - http://static.springframework.org/spring/files/docbook-reference-libs.zip +... to ${basedir}/docs/reference. If you have not yet done so, download this file +and unzip the contents of the archive into ${basedir}/docs/reference. diff --git a/mvn-build/core/docs/upgrade/src/upgrade.xml b/mvn-build/core/docs/upgrade/src/upgrade.xml new file mode 100644 index 00000000..1e947dcf --- /dev/null +++ b/mvn-build/core/docs/upgrade/src/upgrade.xml @@ -0,0 +1,460 @@ + + +
+ Upgrade Guide + + The base package for Spring LDAP is + org.springframework.ldap. In this document, we will refer + to this package as ldap. + + + Upgrading from 1.1.x to 1.2 + + In 1.2, there are a couple of major changes: + + + + Transaction support added + + + + Exception hierarchy changed + + + + Package structure changed + + + + The transaction support is entirely new and should not affect users of + previous releases. + + The exception hierarchy was previously based on the Spring + DataAccessException hierarchy, but is now an unchecked + mirror of the JNDI NamingException hierarchy. The fact + that it is unchecked like DataAccessException makes + migration less troublesome. However, note that + ContextSource and its implementations now throw + org.springframework.ldap.NamingException, not + DataAccessException. + + In release 1.1.x, there were two cyclic dependencies. The first one + was between ldap.support and + ldap.support.parser, while the other one was between + ldap and ldap.support. + + The first cycle was easily solved by changing the generation of the + parser to generate code into ldap.support rather than + into a separate package. + + The second cycle was slightly more complicated. To sort it out, some major + restructuring was needed, as documented in the following sections. + + While the package restructuring is rather extensive, no classes have + changed names - this means that for the most cases a simple import resolve operation + (such as "Ctrl+Shift-O" in Eclipse) should sort things out. + You will still need to change the package names manually in your configuration files - + this should be a simple find/replace operation based on the information below. + + + Moved core classes from ldap to ldap.core + + The core classes listed below were moved to a subpackage called + ldap.core. + + + + + + + + Class + + Before + + After + + + + + + AttributesMapper + + ldap + + ldap.core + + + + AuthenticationSource + + ldap + + ldap.core + + + + CollectingNameClassPair- CallbackHandler + + ldap + + ldap.core + + + + ContextAssembler + + ldap + + ldap.core + + + + ContextExecutor + + ldap + + ldap.core + + + + ContextMapper + + ldap + + ldap.core + + + + ContextSource + + ldap + + ldap.core + + + + DefaultNameClassPairMapper + + ldap + + ldap.core + + + + DirContextProcessor + + ldap + + ldap.core + + + + LdapOperations + + ldap + + ldap.core + + + + LdapTemplate + + ldap + + ldap.core + + + + NameClassPairCallbackHandler + + ldap + + ldap.core + + + + NameClassPairMapper + + ldap + + ldap.core + + + + SearchExecutor + + ldap + + ldap.core + + + + + + + + Moved subpackages of ldap.support to ldap + + The various packages for authentication, controls and filters were + moved to the top level ldap package. + + + + + + + + Class + + Before + + After + + + + + + * + + ldap.support.authentication + + ldap.authentication + + + + * + + ldap.support.control + + ldap.control + + + + * + + ldap.support.filter + + ldap.filter + + + + + + + + Moved most classes in ldap.support to ldap.core or + ldap.core.support + + The classes in ldap.support that are directly + referenced from the core classes were moved to + ldap.core, while various support classes not directly + referenced were moved to ldap.core.support. + + + + + + + + Class + + Before + + After + + + + + + AbstractContextSource + + ldap.support + + ldap.core.support + + + + AggregateDirContextProcessor + + ldap.support + + ldap.core.support + + + + AttributeModificationsAware + + ldap.support + + ldap.core + + + + CountNameClassPair- CallbackHandler + + ldap.support + + ldap.core.support + + + + DefaultDirObjectFactory + + ldap.support + + ldap.core + + + + DefaultDnParserFactory + + ldap.support + + ldap.core + + + + DirContextAdapter + + ldap.support + + ldap.core + + + + DirContextOperations + + ldap.support + + ldap.core + + + + DirContextSource + + ldap.support + + ldap.core.support + + + + DistinguishedName + + ldap.support + + ldap.core + + + + DnParser + + ldap.support + + ldap.core + + + + LdapContextSource + + ldap.support + + ldap.core.support + + + + LdapEncoder + + ldap.support + + ldap.core + + + + LdapRdn + + ldap.support + + ldap.core + + + + LdapRdnComponent + + ldap.support + + ldap.core + + + + + + + + Moved classes in ldap and ldap.util to ldap.support + + The ldap.util package has been removed. The + classes in ldap.util were moved to + ldap.support. Also, the classes in + ldap related to exception translation were moved to + ldap.support. + + + The translation classes are deprecated. Instead, the static + method LdapUtils.convertLdapException should be + used. + + + + + + + + + Class + + Before + + After + + + + + + DefaultNamingExceptionTranslator + + ldap + + ldap.support + + + + LdapUtils + + - + + ldap.support + + + + ListComparator + + ldap.util + + ldap.support + + + + NamingExceptionTranslator + + ldap + + ldap.support + + + + + + +
diff --git a/mvn-build/core/itest-openldap-targets.xml b/mvn-build/core/itest-openldap-targets.xml new file mode 100644 index 00000000..09e00cbc --- /dev/null +++ b/mvn-build/core/itest-openldap-targets.xml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + compiling itest-openldap sources + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Integration test report has been generated. +The HTML report is ${target.itest.openldap.results.html.dir}/index.html + + + + + diff --git a/mvn-build/core/ivy.xml b/mvn-build/core/ivy.xml new file mode 100644 index 00000000..899e1aef --- /dev/null +++ b/mvn-build/core/ivy.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/core/license.txt b/mvn-build/core/license.txt new file mode 100644 index 00000000..0c8a028d --- /dev/null +++ b/mvn-build/core/license.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2006 Mattias Arthursson and Ulrik Sandberg + + 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. diff --git a/mvn-build/core/notice.txt b/mvn-build/core/notice.txt new file mode 100644 index 00000000..4e701033 --- /dev/null +++ b/mvn-build/core/notice.txt @@ -0,0 +1,16 @@ + ====================================================================== + == NOTICE file corresponding to section 4 d of the Apache License, == + == Version 2.0, for the LdapTemplate distribution. == + ====================================================================== + + This product includes software developed by + the Apache Software Foundation (http://www.apache.org). + + The end-user documentation included with a redistribution, if any, + must include the following acknowledgement: + + "This product includes software developed by the LdapTemplate + Project (http://ldaptemplate.sourceforge.net)." + + Alternately, this acknowledgement may appear in the software itself, + if and wherever such third-party acknowledgements normally appear. diff --git a/mvn-build/core/pom.xml b/mvn-build/core/pom.xml new file mode 100644 index 00000000..70aece06 --- /dev/null +++ b/mvn-build/core/pom.xml @@ -0,0 +1,116 @@ + + + 4.0.0 + org.springframework.ldap + spring-ldap + Spring LDAP Core + 1.2.2-SNAPSHOT + + 2.0.8 + + + + + + maven-compiler-plugin + + 1.4 + 1.4 + + + + org.codehaus.mojo + javacc-maven-plugin + 2.4 + + + + javacc + + + + + + + + + + + commons-logging + commons-logging + 1.0.4 + + + commons-lang + commons-lang + 2.1 + + + commons-pool + commons-pool + 1.3 + provided + + + com.sun + ldapbp + 1.0 + system + ${basedir}/../lib/ldapbp.jar + + + org.acegisecurity + acegi-security + 1.0.3 + provided + + + + org.springframework + spring-core + ${spring.version} + + + org.springframework + spring-beans + ${spring.version} + + + org.springframework + spring-context + ${spring.version} + provided + + + org.springframework + spring-dao + ${spring.version} + provided + + + org.springframework + spring-jdbc + ${spring.version} + provided + + + + junit + junit + 3.8.2 + test + + + easymock + easymock + 1.2_Java1.3 + test + + + gsbase + gsbase + 2.0.1 + test + + + diff --git a/mvn-build/core/project.properties b/mvn-build/core/project.properties new file mode 100644 index 00000000..1b09ab76 --- /dev/null +++ b/mvn-build/core/project.properties @@ -0,0 +1,17 @@ +# 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 + +src.javacc.dir=${basedir}/src/main/javacc +javacc.home.dir=c:/java/javacc-4.0 + +project.base.version=1.2.2-SNAPSHOT +#project.version=${project.base.version} +#ivy.status=release + +javac.source=1.3 +javac.target=1.3 + +# extend common-build with source capability +main.build.configs=global,buildtime,test,source diff --git a/mvn-build/core/project.xml b/mvn-build/core/project.xml new file mode 100644 index 00000000..683ca71c --- /dev/null +++ b/mvn-build/core/project.xml @@ -0,0 +1,240 @@ + + + 3 + + spring-ldap + Spring LDAP + org.springframework + spring-ldap + 1.1 + http://opensource.atlassian.com/projects/spring/browse/LDAP + 2006 + org.springframework.ldap + + <p> + Spring LDAP is a library to simplify LDAP programming in + Java, built on the same principles as Spring JDBC. + </p> + <p> + The LdapTemplate class encapsulates all the plumbing work + involved in traditional LDAP programming, such as creating + contexts, looping through NamingEnumerations, handling + Exceptions and cleaning up resources. This leaves the programmer + to handle the important stuff - where to find data (DNs and + Filters) and what to do with it (map to and from domain objects, + bind, modify, unbind, etc.), in the same way that JdbcTemplate + relieves the programmer from everything but the actual SQL and + how the data maps to the domain model. + </p> + <p> + In addition to this, LdapTemplate provides Exception translation + from NamingExceptions to DataAccessExceptions, as well as + several utilities for working with filters, LDAP paths and + Attributes. + </p> + + + Java framework for easier LDAP programming + + http://www.springframework.org/ldap + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + scm:svn:https://svn.sourceforge.net/svnroot/springframework/spring-projects/trunk/spring-ldap + + http://svn.sourceforge.net/viewvc/springframework/spring-projects/ + + + + Mattias Arthursson + marthursson + mattias-DOT-arthursson-AT-jayway-dot-se + Jayway AB + http://www.jayway.se + 1 + + Project Lead + SourceForge Admin + Committer + + + + Ulrik Sandberg + ulsa + ulrik_DOT_sandberg-AT-jayway_DOT_se + Jayway AB + http://www.jayway.se + 1 + + Project Lead + SourceForge Admin + Committer + + + + + + Gregor Middell + + + + Jayway AB + http://www.jayway.se + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + manual + + + + + junit + junit + 3.8.1 + jar + + + commons-logging + commons-logging + 1.0.4 + jar + + + commons-lang + commons-lang + 2.1 + jar + + + commons-collections + commons-collections + 3.1 + jar + + + easymock + easymock + 1.1 + jar + + + easymock + easymockclassextension + 1.1 + jar + + + springframework + spring-core + 1.2.6 + jar + + + springframework + spring-context + 1.2.6 + jar + + + springframework + spring-beans + 1.2.6 + jar + + + springframework + spring-dao + 1.2.6 + jar + + + cglib + cglib-full + 2.0.1 + jar + + + gsbase + gsbase + 2.0 + jar + + + acegisecurity + acegi-security + 1.0.0-RC2 + jar + + + + mattias.arthursson@jayway.se + src/java + src/test + + + **/*Test.java + + + + src/test + + **/*.dtd + **/*.xls + **/*.xml + **/*.properties + + false + + + + + + src/java + + **/*.dtd + **/*.xml + **/*.properties + **/*.xls + + false + + + src/test + + **/*.dtd + **/*.xml + **/*.properties + **/*.xls + + false + + + + + + maven-changes-plugin + + + maven-faq-plugin + + maven-javadoc-plugin + maven-jdepend-plugin + maven-jcoverage-plugin + maven-junit-report-plugin + maven-jxr-plugin + maven-license-plugin + + + + + + + \ No newline at end of file diff --git a/mvn-build/core/readme.txt b/mvn-build/core/readme.txt new file mode 100644 index 00000000..b58d4657 --- /dev/null +++ b/mvn-build/core/readme.txt @@ -0,0 +1,87 @@ +Spring-LDAP 1.2 (Oct 2007) +----------------------------- +http://www.springframework.org/ldap + +1. INTRODUCTION + +Spring-LDAP is a library to simplify LDAP programming in Java, built on the same +principles as Spring Jdbc. + +The LdapTemplate class encapsulates all the plumbing work involved in traditional LDAP +programming, such as creating, looping through NamingEnumerations, handling Exceptions +and cleaning up resources. This leaves the programmer to handle the important stuff - +where to find data (DNs and Filters) and what do do with it (map to and from domain +objects, bind, modify, unbind, etc.), in the same way that JdbcTemplate relieves the +programmer of all but the actual SQL and how the data maps to the domain model. + +In addition to this, Spring-LDAP provides Exception translation from NamingExceptions +to an unchecked exception hirearchy, as well as several utilities for working with filters, +LDAP paths and Attributes. + +As of version 1.2, support for client-side compensating transaction is provided, as well as +Java 5 generics support with the SimpleLdapTemplate. + +2. RELEASE INFO + +Spring LDAP requires J2SE 1.4 for running. +J2SE 1.4, and javacc version 4.0 is required for building the Spring LDAP distributables from source code. +J2EE 1.4 (Servlet 2.3, JSP 1.2) is required for running the example. + +The Spring-LDAP release comes in three different distributables: +* spring-ldap-bin-x.x.zip + ** readme.txt - this readme file. + ** license.txt - terms and conditions for use, reproduction and distribution + ** dist/spring-ldap-x.x.jar - the library + ** dist/spring-ldap-src-x.x.zip - the source code of the library + ** dist/ivys - ivy configuration + ** doc/api - api javadoc documentation + ** doc/reference - full project reference documentation + ** doc/upgrade - Upgrade guide from 1.1.2 + +* spring-ldap-bin-with-dependencies-x.x.zip + ** readme.txt - this readme file. + ** license.txt - terms and conditions for use, reproduction and distribution + ** dist/spring-ldap-x.x.jar - the library + ** dist/spring-ldap-src-x.x.zip - the source code of the library + ** dist/ivys - ivy configuration + ** lib - all referenced libraries necessary to use spring-LDAP + ** doc/api - api javadoc documentation + ** doc/reference - full project reference documentation + ** doc/upgrade - Upgrade guide from 1.1.2 + +The sources included in the distributables are for reference use only, however buildable +sources are available in svn on sourceforge on the following URLs: +Sources for this release (1.2 tag): +https://springframework.svn.sourceforge.net/svnroot/springframework/spring-ldap/tags/1.2 +Latest sources (trunk): +https://springframework.svn.sourceforge.net/svnroot/springframework/spring-ldap/trunk + +Nightly builds are available for download from: +http://static.springframework.org/downloads/nightly/snapshot-download.php?project=LDAP + +Spring-LDAP is released under the terms of the Apache Software License (see license.txt). + +3. DISTRIBUTION JAR FILES + +The following distinct jar files are included in the distribution. This list +specifies the respective contents and third-party dependencies. + +* spring-ldap-x.x.jar +- Contents: The Spring-LDAP library +- Dependencies: Commons Logging, Commons Lang, spring-beans, + spring-core, spring-context, spring-dao, ldapbp, commons-pool + +* spring-ldap-tiger-x.x.jar +- Contents: The Spring-LDAP Java 5 support library +- Dependencies: Commons Logging, Commons Lang, spring-beans, + spring-core, spring-context, spring-dao, ldapbp, commons-pool + +4. WHERE TO START + +The distribution contains extensive JavaDoc documentation as well as full reference +documentation and a sample application illustrating different ways to use Spring-LDAP. +The Spring-LDAP homepage can be found at the following URL: + +http://www.springframework.org/ldap + +There you will find resources related to the project. diff --git a/mvn-build/core/src/itest-openldap/java/conf/ldap-openldap.properties b/mvn-build/core/src/itest-openldap/java/conf/ldap-openldap.properties new file mode 100644 index 00000000..427661f7 --- /dev/null +++ b/mvn-build/core/src/itest-openldap/java/conf/ldap-openldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1 +userDn=cn=manager,dc=jayway,dc=se +password=secret +base=dc=jayway,dc=se diff --git a/mvn-build/core/src/itest-openldap/java/conf/ldap.conf b/mvn-build/core/src/itest-openldap/java/conf/ldap.conf new file mode 100644 index 00000000..b4c2f37d --- /dev/null +++ b/mvn-build/core/src/itest-openldap/java/conf/ldap.conf @@ -0,0 +1,28 @@ +# +# LDAP Defaults +# + +# See ldap.conf(5) for details +# This file should be world readable but not world writable. + +#BASE dc=example, dc=com +#URI ldap://ldap.example.com ldap://ldap-master.example.com:666 + +#SIZELIMIT 12 +#TIMELIMIT 15 +#DEREF never + +include "c:/Program/OpenLDAP/schema/core.schema" +include "c:/Program/OpenLDAP/schema/cosine.schema" +include "c:/Program/OpenLDAP/schema/inetorgperson.schema" +database bdb +suffix "dc=jayway,dc=se" +rootdn "cn=Manager,dc=jayway,dc=se" +rootpw secret +directory "c:/Program/OpenLDAP/data" + +TLSCipherSuite HIGH:MEDIUM:+SSLv2 +TLSCACertificateFile /tmp/cacert.pem +TLSCertificateFile /tmp/servercrt.pem +TLSCertificateKeyFile /tmp/serverkey.pem +TLSVerifyClient never diff --git a/mvn-build/core/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml b/mvn-build/core/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml new file mode 100644 index 00000000..fb18a69d --- /dev/null +++ b/mvn-build/core/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/LdapTemplateLookupOpenLdapITest.java b/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/LdapTemplateLookupOpenLdapITest.java new file mode 100644 index 00000000..11deccf1 --- /dev/null +++ b/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/LdapTemplateLookupOpenLdapITest.java @@ -0,0 +1,220 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests the lookup methods of LdapTemplate on OpenLdap. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapTemplateLookupOpenLdapITest extends + AbstractDependencyInjectionSpringContextTests { + + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext-openldap.xml" }; + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.core.support.DefaultDirObjectFactory}) + * being set in the ContextSource. + */ + public void testLookup_Plain() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person2, ou=company1,c=Sweden"); + + assertEquals("Some Person2", result.getStringAttribute("cn")); + assertEquals("Person2", result.getStringAttribute("sn")); + assertEquals("Sweden, Company1, Some Person2", result + .getStringAttribute("description")); + } + + public void testLookup_AttributesMapper() { + AttributesMapper mapper = new PersonAttributesMapper(); + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", mapper); + + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + public void testLookup_AttributesMapper_DistinguishedName() { + AttributesMapper mapper = new PersonAttributesMapper(); + Person person = (Person) tested.lookup(new DistinguishedName( + "cn=Some Person2, ou=company1,c=Sweden"), mapper); + + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + /** + * An {@link AttributesMapper} that only maps a subset of the full + * attributes list. Used in tests where the return attributes list has been + * limited. + * + * @author Ulrik Sandberg + */ + private final class SubsetPersonAttributesMapper implements + AttributesMapper { + /** + * Maps the cn attribute into a {@link Person} object. + * Also verifies that the other attributes haven't been set. + * + * @see org.springframework.ldap.core.AttributesMapper#mapFromAttributes(javax.naming.directory.Attributes) + */ + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + Person person = new Person(); + person.setFullname((String) attributes.get("cn").get()); + assertNull("sn should be null", attributes.get("sn")); + assertNull("description should be null", attributes + .get("description")); + return person; + } + } + + /** + * Verifies that only the subset is used when specifying a subset of the + * available attributes as return attributes. + */ + public void testLookup_ReturnAttributes_AttributesMapper() { + AttributesMapper mapper = new SubsetPersonAttributesMapper(); + + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", new String[] { "cn" }, + mapper); + + assertEquals("Some Person2", person.getFullname()); + assertNull("lastName should not be set", person.getLastname()); + assertNull("description should not be set", person.getDescription()); + } + + /** + * Verifies that only the subset is used when specifying a subset of the + * available attributes as return attributes. Uses DistinguishedName instead + * of plain string as name. + */ + public void testLookup_ReturnAttributes_AttributesMapper_DistinguishedName() { + AttributesMapper mapper = new SubsetPersonAttributesMapper(); + Person person = (Person) tested.lookup(new DistinguishedName( + "cn=Some Person2, ou=company1,c=Sweden"), + new String[] { "cn" }, mapper); + + assertEquals("Some Person2", person.getFullname()); + assertNull("lastName should not be set", person.getLastname()); + assertNull("description should not be set", person.getDescription()); + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.core.support.DefaultDirObjectFactory}) + * being set in the ContextSource. + */ + public void testLookup_ContextMapper() { + ContextMapper mapper = new PersonContextMapper(); + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", mapper); + + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + /** + * Verifies that only the subset is used when specifying a subset of the + * available attributes as return attributes. + */ + public void testLookup_ReturnAttributes_ContextMapper() { + ContextMapper mapper = new PersonContextMapper(); + + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", new String[] { "cn" }, + mapper); + + assertEquals("Some Person2", person.getFullname()); + assertNull("lastName should not be set", person.getLastname()); + assertNull("description should not be set", person.getDescription()); + } + + /** + * Verifies that we can lookup an entry that has a multi-valued rdn, which + * means more than one attribute is part of the relative DN for the entry. + */ + public void testLookup_MultiValuedRdn() { + AttributesMapper mapper = new PersonAttributesMapper(); + Person person = (Person) tested.lookup( + "cn=Some Person+sn=Person, ou=company1,c=Norway", mapper); + + assertEquals("Some Person", person.getFullname()); + assertEquals("Person", person.getLastname()); + assertEquals("Norway, Company1, Some Person+Person", person + .getDescription()); + } + + /** + * Verifies that we can lookup an entry that has a multi-valued rdn, which + * means more than one attribute is part of the relative DN for the entry. + */ + public void testLookup_MultiValuedRdn_DirContextAdapter() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person+sn=Person, ou=company1,c=Norway"); + + assertEquals("Some Person", result.getStringAttribute("cn")); + assertEquals("Person", result.getStringAttribute("sn")); + assertEquals("Norway, Company1, Some Person+Person", result + .getStringAttribute("description")); + } + + public void testLookup_GetNameInNamespace_Plain() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person2, ou=company1,c=Sweden"); + + assertEquals("cn=Some Person2, ou=company1, c=Sweden", result.getDn() + .toString()); + assertEquals( + "cn=Some Person2, ou=company1, c=Sweden, dc=jayway, dc=se", + result.getNameInNamespace()); + } + + public void testLookup_GetNameInNamespace_MultiRdn() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person+sn=Person, ou=company1,c=Norway"); + + assertEquals("cn=Some Person+sn=Person, ou=company1, c=Norway", result + .getDn().toString()); + assertEquals( + "cn=Some Person+sn=Person, ou=company1, c=Norway, dc=jayway, dc=se", + result.getNameInNamespace()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/control/LdapTemplatePagedSearchITest.java b/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/control/LdapTemplatePagedSearchITest.java new file mode 100644 index 00000000..61054045 --- /dev/null +++ b/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/control/LdapTemplatePagedSearchITest.java @@ -0,0 +1,157 @@ +/* + * Copyright 2005-2007 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.control; + +import java.util.List; + +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.Person; +import org.springframework.ldap.PersonAttributesMapper; +import org.springframework.ldap.core.AttributesMapperCallbackHandler; +import org.springframework.ldap.core.CollectingNameClassPairCallbackHandler; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.SearchExecutor; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests the paged search result capability of LdapTemplate. + *

+ * Note: Currently, ApacheDS does not support paged results controls, so this + * test must be run under another directory server, for example OpenLdap. This + * test will not run under ApacheDS, and the other integration tests assume + * ApacheDS and will probably not run under OpenLdap. + * + * @author Ulrik Sandberg + */ +public class LdapTemplatePagedSearchITest extends + AbstractDependencyInjectionSpringContextTests { + + private static final Name BASE = DistinguishedName.EMPTY_PATH; + + private static final String FILTER_STRING = "(&(objectclass=person))"; + + private LdapTemplate tested; + + private CollectingNameClassPairCallbackHandler callbackHandler; + + private SearchControls searchControls; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext-openldap.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + PersonAttributesMapper mapper = new PersonAttributesMapper(); + callbackHandler = new AttributesMapperCallbackHandler(mapper); + searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + } + + protected void onTearDown() throws Exception { + super.onTearDown(); + callbackHandler = null; + tested = null; + searchControls = null; + } + + public void testSearch_PagedResult() { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.search(BASE, FILTER_STRING, searchControls); + } + }; + Person person; + List list; + PagedResultsCookie cookie; + PagedResultsRequestControl requestControl; + + // Prepare for first search + requestControl = new PagedResultsRequestControl(3); + tested.search(searchExecutor, callbackHandler, requestControl); + cookie = requestControl.getCookie(); + assertNotNull("Cookie should not be null yet", cookie.getCookie()); + list = callbackHandler.getList(); + assertEquals(3, list.size()); + person = (Person) list.get(0); + assertEquals("Some Person", person.getFullname()); + assertEquals("+46 555-123456", person.getPhone()); + person = (Person) list.get(1); + assertEquals("Some Person2", person.getFullname()); + assertEquals("+46 555-654321", person.getPhone()); + person = (Person) list.get(2); + assertEquals("Some Person3", person.getFullname()); + assertEquals("+46 555-123654", person.getPhone()); + + // Prepare for second and last search + requestControl = new PagedResultsRequestControl(3, cookie); + tested.search(searchExecutor, callbackHandler, requestControl); + cookie = requestControl.getCookie(); + assertNull("Cookie should be null now", cookie.getCookie()); + assertEquals(5, list.size()); + person = (Person) list.get(3); + assertEquals("Some Person", person.getFullname()); + assertEquals("+46 555-456321", person.getPhone()); + person = (Person) list.get(4); + assertEquals("Some Person", person.getFullname()); + assertEquals("+45 555-654123", person.getPhone()); + } + + public void testSearch_PagedResult_ConvenienceMethod() { + Person person; + List list; + PagedResultsCookie cookie; + PagedResultsRequestControl requestControl; + + // Prepare for first search + requestControl = new PagedResultsRequestControl(3); + tested.search(BASE, FILTER_STRING, searchControls, + callbackHandler, requestControl); + cookie = requestControl.getCookie(); + assertNotNull("Cookie should not be null yet", cookie.getCookie()); + list = callbackHandler.getList(); + assertEquals(3, list.size()); + person = (Person) list.get(0); + assertEquals("Some Person", person.getFullname()); + person = (Person) list.get(1); + assertEquals("Some Person2", person.getFullname()); + person = (Person) list.get(2); + assertEquals("Some Person3", person.getFullname()); + + // Prepare for second and last search + requestControl = new PagedResultsRequestControl(3, cookie); + tested.search(BASE, FILTER_STRING, searchControls, + callbackHandler, requestControl); + cookie = requestControl.getCookie(); + assertNull("Cookie should be null now", cookie.getCookie()); + assertEquals(5, list.size()); + person = (Person) list.get(3); + assertEquals("Some Person", person.getFullname()); + person = (Person) list.get(4); + assertEquals("Some Person", person.getFullname()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/control/LdapTemplateSortedSearchITest.java b/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/control/LdapTemplateSortedSearchITest.java new file mode 100644 index 00000000..f9f646f9 --- /dev/null +++ b/mvn-build/core/src/itest-openldap/java/org/springframework/ldap/control/LdapTemplateSortedSearchITest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2005-2007 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.control; + +import java.util.List; + +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.Person; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.AttributesMapperCallbackHandler; +import org.springframework.ldap.core.CollectingNameClassPairCallbackHandler; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.SearchExecutor; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests the still experimental sorted search result capability of LdapTemplate. + * + * @author Ulrik Sandberg + */ +public class LdapTemplateSortedSearchITest extends + AbstractDependencyInjectionSpringContextTests { + + private static final Name BASE = DistinguishedName.EMPTY_PATH; + + private static final String FILTER_STRING = "(&(objectclass=ikeaperson)(cn=gor*))"; + + private LdapTemplate tested; + + private CollectingNameClassPairCallbackHandler callbackHandler; + + private SearchControls searchControls; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext-openldap.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + PersonAttributesMapper mapper = new PersonAttributesMapper(); + callbackHandler = new AttributesMapperCallbackHandler(mapper); + searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + } + + protected void onTearDown() throws Exception { + super.onTearDown(); + callbackHandler = null; + tested = null; + searchControls = null; + } + + public void testSearch_SortControl() { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.search(BASE, FILTER_STRING, searchControls); + } + }; + SortControlDirContextProcessor requestControl; + + // Prepare for first search + requestControl = new SortControlDirContextProcessor("cn"); + tested.search(searchExecutor, callbackHandler, requestControl); + int resultCode = requestControl.getResultCode(); + boolean sorted = requestControl.isSorted(); + assertTrue("Search result should have been sorted: " + resultCode, sorted); + List list = callbackHandler.getList(); + assertSortedList(list); + } + + public void testSearch_SortControl_ConvenienceMethod() { + SortControlDirContextProcessor requestControl; + + // Prepare for first search + requestControl = new SortControlDirContextProcessor("cn"); + tested.search(BASE, FILTER_STRING, searchControls, callbackHandler, + requestControl); + int resultCode = requestControl.getResultCode(); + boolean sorted = requestControl.isSorted(); + assertTrue("Search result should have been sorted: " + resultCode, sorted); + List list = callbackHandler.getList(); + assertSortedList(list); + } + + private void assertSortedList(List list) { + Person person; + assertEquals(6, list.size()); + person = (Person) list.get(0); + assertEquals("Goran Milenkovic", person.getFullname()); + person = (Person) list.get(1); + assertEquals("Goran Sundberg", person.getFullname()); + person = (Person) list.get(2); + assertEquals("Goran Westerberg", person.getFullname()); + person = (Person) list.get(3); + assertEquals("Gorana Milicevic", person.getFullname()); + person = (Person) list.get(4); + assertEquals("Gordana Canic", person.getFullname()); + person = (Person) list.get(5); + assertEquals("Gordana Russ", person.getFullname()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } + + private class PersonAttributesMapper implements AttributesMapper { + + /** + * Maps the given attributes into a {@link Person} object. + * + * @see org.springframework.ldap.core.AttributesMapper#mapFromAttributes(javax.naming.directory.Attributes) + */ + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + Person person = new Person(); + person.setFullname((String) attributes.get("cn").get()); + person.setLastname((String) attributes.get("sn").get()); + person.setDescription((String) attributes.get("givenName").get()); + return person; + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/AttributeInUseException.java b/mvn-build/core/src/main/java/org/springframework/ldap/AttributeInUseException.java new file mode 100644 index 00000000..d1c89b85 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/AttributeInUseException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI AttributeInUseException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.AttributeInUseException + */ +public class AttributeInUseException extends NamingException { + + public AttributeInUseException( + javax.naming.directory.AttributeInUseException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/AttributeModificationException.java b/mvn-build/core/src/main/java/org/springframework/ldap/AttributeModificationException.java new file mode 100644 index 00000000..f0f7479c --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/AttributeModificationException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI AttributeModificationException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.AttributeModificationException + */ +public class AttributeModificationException extends NamingException { + + public AttributeModificationException( + javax.naming.directory.AttributeModificationException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/AuthenticationException.java b/mvn-build/core/src/main/java/org/springframework/ldap/AuthenticationException.java new file mode 100644 index 00000000..53607134 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/AuthenticationException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI AuthenticationException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.AuthenticationException + */ +public class AuthenticationException extends NamingSecurityException { + + public AuthenticationException(javax.naming.AuthenticationException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/AuthenticationNotSupportedException.java b/mvn-build/core/src/main/java/org/springframework/ldap/AuthenticationNotSupportedException.java new file mode 100644 index 00000000..6036b5fc --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/AuthenticationNotSupportedException.java @@ -0,0 +1,33 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI AuthenticationNotSupportedException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.AuthenticationNotSupportedException + */ +public class AuthenticationNotSupportedException extends + NamingSecurityException { + + public AuthenticationNotSupportedException( + javax.naming.AuthenticationNotSupportedException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/BadLdapGrammarException.java b/mvn-build/core/src/main/java/org/springframework/ldap/BadLdapGrammarException.java new file mode 100644 index 00000000..1fa9d458 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/BadLdapGrammarException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2005-2007 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; + +/** + * Thrown to indicate that an invalid value has been supplied to an LDAP + * operation. This could be an invalid filter or dn. + * + * @author Mattias Arthursson + */ +public class BadLdapGrammarException extends NamingException { + + private static final long serialVersionUID = 961612585331409470L; + + public BadLdapGrammarException(String message) { + super(message); + } + + public BadLdapGrammarException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/CannotProceedException.java b/mvn-build/core/src/main/java/org/springframework/ldap/CannotProceedException.java new file mode 100644 index 00000000..aba33919 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/CannotProceedException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI CannotProceedException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.CannotProceedException + */ +public class CannotProceedException extends NamingException { + + public CannotProceedException(javax.naming.CannotProceedException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/CommunicationException.java b/mvn-build/core/src/main/java/org/springframework/ldap/CommunicationException.java new file mode 100644 index 00000000..5e6e4b90 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/CommunicationException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI CommunicationException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.CommunicationException + */ +public class CommunicationException extends NamingException { + + public CommunicationException(javax.naming.CommunicationException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/ConfigurationException.java b/mvn-build/core/src/main/java/org/springframework/ldap/ConfigurationException.java new file mode 100644 index 00000000..18da8c1b --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/ConfigurationException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI ConfigurationException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.ConfigurationException + */ +public class ConfigurationException extends NamingException { + + public ConfigurationException(javax.naming.ConfigurationException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/ContextNotEmptyException.java b/mvn-build/core/src/main/java/org/springframework/ldap/ContextNotEmptyException.java new file mode 100644 index 00000000..42de643d --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/ContextNotEmptyException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI ContextNotEmptyException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.ContextNotEmptyException + */ +public class ContextNotEmptyException extends NamingException { + + public ContextNotEmptyException(javax.naming.ContextNotEmptyException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InsufficientResourcesException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InsufficientResourcesException.java new file mode 100644 index 00000000..0506b867 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InsufficientResourcesException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InsufficientResourcesException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.InsufficientResourcesException + */ +public class InsufficientResourcesException extends NamingException { + + public InsufficientResourcesException( + javax.naming.InsufficientResourcesException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InterruptedNamingException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InterruptedNamingException.java new file mode 100644 index 00000000..6cfa8b67 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InterruptedNamingException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InterruptedNamingException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.InterruptedNamingException + */ +public class InterruptedNamingException extends NamingException { + + public InterruptedNamingException( + javax.naming.InterruptedNamingException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributeIdentifierException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributeIdentifierException.java new file mode 100644 index 00000000..7078fca1 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributeIdentifierException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InvalidAttributeIdentifierException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.InvalidAttributeIdentifierException + */ +public class InvalidAttributeIdentifierException extends NamingException { + + public InvalidAttributeIdentifierException( + javax.naming.directory.InvalidAttributeIdentifierException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributeValueException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributeValueException.java new file mode 100644 index 00000000..9bc5ae0e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributeValueException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InvalidAttributeValueException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.InvalidAttributeValueException + */ +public class InvalidAttributeValueException extends NamingException { + + public InvalidAttributeValueException( + javax.naming.directory.InvalidAttributeValueException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributesException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributesException.java new file mode 100644 index 00000000..92fc758a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidAttributesException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InvalidAttributesException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.InvalidAttributesException + */ +public class InvalidAttributesException extends NamingException { + + public InvalidAttributesException( + javax.naming.directory.InvalidAttributesException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InvalidNameException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidNameException.java new file mode 100644 index 00000000..8999d0cf --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidNameException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InvalidNameException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.InvalidNameException + */ +public class InvalidNameException extends NamingException { + + public InvalidNameException(javax.naming.InvalidNameException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InvalidSearchControlsException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidSearchControlsException.java new file mode 100644 index 00000000..b987ee5d --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidSearchControlsException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InvalidSearchControlsException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.InvalidSearchControlsException + */ +public class InvalidSearchControlsException extends NamingException { + + public InvalidSearchControlsException( + javax.naming.directory.InvalidSearchControlsException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/InvalidSearchFilterException.java b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidSearchFilterException.java new file mode 100644 index 00000000..b6ac1965 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/InvalidSearchFilterException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI InvalidSearchFilterException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.InvalidSearchFilterException + */ +public class InvalidSearchFilterException extends NamingException { + + public InvalidSearchFilterException( + javax.naming.directory.InvalidSearchFilterException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/LdapReferralException.java b/mvn-build/core/src/main/java/org/springframework/ldap/LdapReferralException.java new file mode 100644 index 00000000..bb072930 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/LdapReferralException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI LdapReferralException. + * + * This class is not abstract. We need to be able to instantiate it, should the + * caught exception be a provider-specific subclass of + * {@link javax.naming.ldap.LdapReferralException}. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.ldap.LdapReferralException + */ +public class LdapReferralException extends ReferralException { + + public LdapReferralException(javax.naming.ldap.LdapReferralException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/LimitExceededException.java b/mvn-build/core/src/main/java/org/springframework/ldap/LimitExceededException.java new file mode 100644 index 00000000..d89d8233 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/LimitExceededException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI LimitExceededException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.LimitExceededException + */ +public class LimitExceededException extends NamingException { + + public LimitExceededException(javax.naming.LimitExceededException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/LinkException.java b/mvn-build/core/src/main/java/org/springframework/ldap/LinkException.java new file mode 100644 index 00000000..ee85ad55 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/LinkException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI LinkException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.LinkException + */ +public class LinkException extends NamingException { + + public LinkException(javax.naming.LinkException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/LinkLoopException.java b/mvn-build/core/src/main/java/org/springframework/ldap/LinkLoopException.java new file mode 100644 index 00000000..00e5184c --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/LinkLoopException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI LinkLoopException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.LinkLoopException + */ +public class LinkLoopException extends LinkException { + + public LinkLoopException(javax.naming.LinkLoopException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/MalformedLinkException.java b/mvn-build/core/src/main/java/org/springframework/ldap/MalformedLinkException.java new file mode 100644 index 00000000..665e4ebc --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/MalformedLinkException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI MalformedLinkException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.MalformedLinkException + */ +public class MalformedLinkException extends LinkException { + + public MalformedLinkException(javax.naming.MalformedLinkException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NameAlreadyBoundException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NameAlreadyBoundException.java new file mode 100644 index 00000000..25c2f8e2 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NameAlreadyBoundException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI NameAlreadyBoundException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.NameAlreadyBoundException + */ +public class NameAlreadyBoundException extends NamingException { + + public NameAlreadyBoundException( + javax.naming.NameAlreadyBoundException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NameNotFoundException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NameNotFoundException.java new file mode 100644 index 00000000..ada8db7a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NameNotFoundException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI NameNotFoundException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.NameNotFoundException + */ +public class NameNotFoundException extends NamingException { + + public NameNotFoundException(String msg) { + super(msg); + } + + public NameNotFoundException(javax.naming.NameNotFoundException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NamingException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NamingException.java new file mode 100644 index 00000000..5bf22c1e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NamingException.java @@ -0,0 +1,186 @@ +/* + * Copyright 2005-2007 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; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import javax.naming.Name; + +import org.springframework.core.NestedRuntimeException; + +/** + * Base class for exception thrown by the framework whenever it encounters a + * problem related to LDAP. + * + * @author Ulrik Sandberg + * @since 1.2 + */ +public abstract class NamingException extends NestedRuntimeException { + + private Throwable cause; + + /** + * Overrides {@link NestedRuntimeException#getCause()} since serialization + * always tries to serialize the base class before the subclass. Our + * cause may have a resolvedObj that is not + * serializable. By storing the cause in this class, we get a chance at + * temporarily nulling the cause before serialization, thus in effect making + * the current instance serializable. + */ + public Throwable getCause() { + // Even if you cannot set the cause of this exception other than through + // the constructor, we check for the cause being "this" here, as the cause + // could still be set to "this" via reflection: for example, by a remoting + // deserializer like Hessian's. + return (this.cause == this ? null : this.cause); + } + + /** + * Constructor that takes a message. + * + * @param msg + * the detail message + */ + public NamingException(String msg) { + super(msg); + } + + /** + * Constructor that allows a message and a root cause. + * + * @param msg + * the detail message + * @param cause + * the cause of the exception. This argument is generally + * expected to be a proper subclass of + * {@link javax.naming.NamingException}. + */ + public NamingException(String msg, Throwable cause) { + super(msg); + this.cause = cause; + } + + /** + * Constructor that allows a plain root cause, intended for subclasses + * mirroring corresponding javax.naming exceptions. + * + * @param cause + * the cause of the exception. This argument is generally + * expected to be a proper subclass of + * {@link javax.naming.NamingException}. + */ + public NamingException(Throwable cause) { + this(cause != null ? cause.getMessage() : null, cause); + } + + /** + * Convenience method to get the explanation associated with this exception, + * if the root cause was an instance of {@link javax.naming.NamingException}. + * + * @return a detail string explaining more about this exception if the root + * cause is an instance of javax.naming.NamingException, or + * null if there is no detail message for this + * exception + */ + public String getExplanation() { + if (getCause() instanceof javax.naming.NamingException) { + return ((javax.naming.NamingException) getCause()).getExplanation(); + } + return null; + } + + /** + * Convenience method to get the unresolved part of the name associated with + * this exception, if the root cause was an instance of + * {@link javax.naming.NamingException}. + * + * @return a composite name describing the part of the name that has not + * been resolved if the root cause is an instance of + * javax.naming.NamingException, or null if the + * remaining name field has not been set + */ + public Name getRemainingName() { + if (getCause() instanceof javax.naming.NamingException) { + return ((javax.naming.NamingException) getCause()) + .getRemainingName(); + } + return null; + } + + /** + * Convenience method to get the leading portion of the resolved name + * associated with this exception, if the root cause was an instance of + * {@link javax.naming.NamingException}. + * + * @return a composite name describing the the leading portion of the name + * that was resolved successfully if the root cause is an instance + * of javax.naming.NamingException, or null if the + * resolved name field has not been set + */ + public Name getResolvedName() { + if (getCause() instanceof javax.naming.NamingException) { + return ((javax.naming.NamingException) getCause()) + .getResolvedName(); + } + return null; + } + + /** + * Convenience method to get the resolved object associated with this + * exception, if the root cause was an instance of + * {@link javax.naming.NamingException}. + * + * @return the object that was resolved so far if the root cause is an + * instance of javax.naming.NamingException, or null + * if the resolved object field has not been set + */ + public Object getResolvedObj() { + if (getCause() instanceof javax.naming.NamingException) { + return ((javax.naming.NamingException) getCause()).getResolvedObj(); + } + return null; + } + + /** + * Checks if the resolvedObj of the causing exception is + * suspected to be non-serializable, and if so temporarily nulls it before + * calling the default serialization mechanism. + * + * @param stream + * the stream onto which this object is serialized + * @throws IOException + * if there is an error writing this object to the stream + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + Object resolvedObj = getResolvedObj(); + boolean serializable = resolvedObj instanceof Serializable; + if (resolvedObj != null && !serializable) { + // the cause is of this type, since resolvedObj is not null + javax.naming.NamingException namingException = (javax.naming.NamingException) getCause(); + namingException.setResolvedObj(null); + try { + stream.defaultWriteObject(); + } finally { + namingException.setResolvedObj(resolvedObj); + } + } else { + stream.defaultWriteObject(); + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NamingSecurityException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NamingSecurityException.java new file mode 100644 index 00000000..6ae15225 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NamingSecurityException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI NamingSecurityException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.NamingSecurityException + */ +public abstract class NamingSecurityException extends NamingException { + + public NamingSecurityException(javax.naming.NamingSecurityException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NoInitialContextException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NoInitialContextException.java new file mode 100644 index 00000000..f54d5eee --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NoInitialContextException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI NoInitialContextException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.NoInitialContextException + */ +public class NoInitialContextException extends NamingException { + + public NoInitialContextException( + javax.naming.NoInitialContextException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NoPermissionException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NoPermissionException.java new file mode 100644 index 00000000..fce493fe --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NoPermissionException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI NoPermissionException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.NoPermissionException + */ +public class NoPermissionException extends NamingSecurityException { + + public NoPermissionException(javax.naming.NoPermissionException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NoSuchAttributeException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NoSuchAttributeException.java new file mode 100644 index 00000000..b7739bb9 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NoSuchAttributeException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI NoSuchAttributeException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.NoSuchAttributeException + */ +public class NoSuchAttributeException extends NamingException { + + public NoSuchAttributeException( + javax.naming.directory.NoSuchAttributeException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/NotContextException.java b/mvn-build/core/src/main/java/org/springframework/ldap/NotContextException.java new file mode 100644 index 00000000..5a3a6658 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/NotContextException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI NotContextException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.NotContextException + */ +public class NotContextException extends NamingException { + + public NotContextException(javax.naming.NotContextException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/OperationNotSupportedException.java b/mvn-build/core/src/main/java/org/springframework/ldap/OperationNotSupportedException.java new file mode 100644 index 00000000..7613d1a3 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/OperationNotSupportedException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI OperationNotSupportedException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.OperationNotSupportedException + */ +public class OperationNotSupportedException extends NamingException { + + public OperationNotSupportedException( + javax.naming.OperationNotSupportedException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/PartialResultException.java b/mvn-build/core/src/main/java/org/springframework/ldap/PartialResultException.java new file mode 100644 index 00000000..552a1d40 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/PartialResultException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI PartialResultException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.PartialResultException + */ +public class PartialResultException extends NamingException { + + public PartialResultException(javax.naming.PartialResultException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/ReferralException.java b/mvn-build/core/src/main/java/org/springframework/ldap/ReferralException.java new file mode 100644 index 00000000..3be8bf47 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/ReferralException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI ReferralException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.ReferralException + */ +public abstract class ReferralException extends NamingException { + + public ReferralException(javax.naming.ReferralException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/SchemaViolationException.java b/mvn-build/core/src/main/java/org/springframework/ldap/SchemaViolationException.java new file mode 100644 index 00000000..e9294aa4 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/SchemaViolationException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI SchemaViolationException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.directory.SchemaViolationException + */ +public class SchemaViolationException extends NamingException { + + public SchemaViolationException( + javax.naming.directory.SchemaViolationException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/ServiceUnavailableException.java b/mvn-build/core/src/main/java/org/springframework/ldap/ServiceUnavailableException.java new file mode 100644 index 00000000..1e8c09a9 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/ServiceUnavailableException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI ServiceUnavailableException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.ServiceUnavailableException + */ +public class ServiceUnavailableException extends NamingException { + + public ServiceUnavailableException( + javax.naming.ServiceUnavailableException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/SizeLimitExceededException.java b/mvn-build/core/src/main/java/org/springframework/ldap/SizeLimitExceededException.java new file mode 100644 index 00000000..52b9a99f --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/SizeLimitExceededException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI SizeLimitExceededException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.SizeLimitExceededException + */ +public class SizeLimitExceededException extends LimitExceededException { + + public SizeLimitExceededException( + javax.naming.SizeLimitExceededException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/TimeLimitExceededException.java b/mvn-build/core/src/main/java/org/springframework/ldap/TimeLimitExceededException.java new file mode 100644 index 00000000..42d55d89 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/TimeLimitExceededException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2007 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; + +/** + * Runtime exception mirroring the JNDI TimeLimitExceededException. + * + * @author Ulrik Sandberg + * @since 1.2 + * @see javax.naming.TimeLimitExceededException + */ +public class TimeLimitExceededException extends LimitExceededException { + + public TimeLimitExceededException( + javax.naming.TimeLimitExceededException cause) { + super(cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/UncategorizedLdapException.java b/mvn-build/core/src/main/java/org/springframework/ldap/UncategorizedLdapException.java new file mode 100644 index 00000000..47c8fd72 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/UncategorizedLdapException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2005-2007 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; + +/** + * NamingException to be thrown when no other matching subclass is found. + * + * @author Ulrik Sandberg + * @since 1.2 + */ +public class UncategorizedLdapException extends NamingException { + + public UncategorizedLdapException(String msg) { + super(msg); + } + + public UncategorizedLdapException(String msg, Throwable cause) { + super(msg, cause); + } + + public UncategorizedLdapException(Throwable cause) { + super("Uncategorized exception occured during LDAP processing", cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/authentication/AcegiAuthenticationSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/authentication/AcegiAuthenticationSource.java new file mode 100644 index 00000000..2a8fa91e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/authentication/AcegiAuthenticationSource.java @@ -0,0 +1,87 @@ +/* + * Copyright 2005-2007 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.authentication; + +import org.acegisecurity.Authentication; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; +import org.acegisecurity.userdetails.ldap.LdapUserDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.AuthenticationSource; + +/** + * An AuthenticationSource to retrieve authentication information stored in + * Acegi's SecurityContextHolder. Use Acegi's LdapAuthenticationProvider have a + * LdapUserDetails object placed in the authentication. + * + * @author Mattias Arthursson + * + */ +public class AcegiAuthenticationSource implements AuthenticationSource { + private static final Log log = LogFactory + .getLog(AcegiAuthenticationSource.class); + + /** + * Get the principals of the logged in user, in this case the distinguished + * name. + * + * @return the distinguished name of the logged in user. + */ + public String getPrincipal() { + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + if (authentication != null) { + Object principal = authentication.getPrincipal(); + if (principal instanceof LdapUserDetails) { + LdapUserDetails details = (LdapUserDetails) principal; + return details.getDn(); + } else if (authentication instanceof AnonymousAuthenticationToken) { + if (log.isDebugEnabled()) { + log + .debug("Anonymous Authentication, returning empty String as Principal"); + } + return ""; + } else { + throw new IllegalArgumentException( + "The principal property of the authentication object -" + + "needs to be a LdapUserDetails."); + } + } else { + log.warn("No Authentication object set in SecurityContext - " + + "returning empty String as Principal"); + return ""; + } + } + + /* + * @see org.springframework.ldap.core.AuthenticationSource#getCredentials() + */ + public String getCredentials() { + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + + if (authentication != null) { + return (String) authentication.getCredentials(); + } else { + log.warn("No Authentication object set in SecurityContext - " + + "returning empty String as Credentials"); + return ""; + } + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/authentication/DefaultValuesAuthenticationSourceDecorator.java b/mvn-build/core/src/main/java/org/springframework/ldap/authentication/DefaultValuesAuthenticationSourceDecorator.java new file mode 100644 index 00000000..7f5acc9a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/authentication/DefaultValuesAuthenticationSourceDecorator.java @@ -0,0 +1,157 @@ +/* + * Copyright 2005-2007 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.authentication; + +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.ldap.core.AuthenticationSource; + +/** + * Decorator on AuthenticationSource to have default authentication information + * be returned should the target return empty principal and credentials. Useful + * in combination with {@link AcegiAuthenticationSource} if users are to be + * allowed to read some information even though they are not logged in. + *

+ * Note: The defaultUser should be an non-privileged + * user. This is important as this is the one that will be used when no user is + * logged in (i.e. empty principal is returned from the target + * AuthenticationSource). + * + * @author Mattias Arthursson + * + */ +public class DefaultValuesAuthenticationSourceDecorator implements + AuthenticationSource, InitializingBean { + + private AuthenticationSource target; + + private String defaultUser; + + private String defaultPassword; + + /** + * Constructor for bean usage. + */ + public DefaultValuesAuthenticationSourceDecorator() { + } + + /** + * Constructor to setup instance directly. + * + * @param target + * the target AuthenticationSource. + * @param defaultUser + * dn of the user to use when the target returns an empty + * principal. + * @param defaultPassword + * password of the user to use when the target returns an empty + * principal. + */ + public DefaultValuesAuthenticationSourceDecorator( + AuthenticationSource target, String defaultUser, + String defaultPassword) { + this.target = target; + this.defaultUser = defaultUser; + this.defaultPassword = defaultPassword; + } + + /** + * Checks if the target's principal is not empty; if not, the credentials + * from the target is returned - otherwise return the + * defaultPassword. + * + * @return the target's password if the target's principal is not empty, the + * defaultPassword otherwise. + */ + public String getCredentials() { + if (StringUtils.isNotEmpty(target.getPrincipal())) { + return target.getCredentials(); + } else { + return defaultPassword; + } + } + + /** + * Checks if the target's principal is not empty; if not, this is returned - + * otherwise return the defaultPassword. + * + * @return the target's principal if it is not empty, the + * defaultPassword otherwise. + */ + public String getPrincipal() { + String principal = target.getPrincipal(); + if (StringUtils.isNotEmpty(principal)) { + return principal; + } else { + return defaultUser; + } + } + + /** + * Set the password of the default user. + * + * @param defaultPassword + * the password of the default user. + */ + public void setDefaultPassword(String defaultPassword) { + this.defaultPassword = defaultPassword; + } + + /** + * Set the default user DN. This should be a non-privileged user, since it + * will be used when no authentication information is returned from the + * target. + * + * @param defaultUser + * DN of the default user. + */ + public void setDefaultUser(String defaultUser) { + this.defaultUser = defaultUser; + } + + /** + * Set the target AuthenticationSource. + * + * @param target + * the target AuthenticationSource. + */ + public void setTarget(AuthenticationSource target) { + this.target = target; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception { + if (target == null) { + throw new IllegalArgumentException( + "Property 'target' must be set.'"); + } + + if (defaultUser == null) { + throw new IllegalArgumentException( + "Property 'defaultUser' must be set.'"); + } + + if (defaultPassword == null) { + throw new IllegalArgumentException( + "Property 'defaultPassword' must be set.'"); + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/authentication/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/authentication/package.html new file mode 100644 index 00000000..4e314da9 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/authentication/package.html @@ -0,0 +1,7 @@ + + + +Support classes for custom authentication. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/AbstractRequestControlDirContextProcessor.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/AbstractRequestControlDirContextProcessor.java new file mode 100644 index 00000000..5ecdee02 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/AbstractRequestControlDirContextProcessor.java @@ -0,0 +1,93 @@ +/* + * Copyright 2005-2007 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.control; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.DirContextProcessor; + +/** + * Abstract superclass with responsibility to apply a single RequestControl on + * an LdapContext, preserving any existing controls. Subclasses should implement + * {@link DirContextProcessor#postProcess(DirContext)} and template method + * {@link #createRequestControl()}. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public abstract class AbstractRequestControlDirContextProcessor implements + DirContextProcessor { + protected Log log = LogFactory + .getLog(AbstractRequestControlDirContextProcessor.class); + + /** + * Get the existing RequestControls from the LdapContext, call + * {@link #createRequestControl()} to get a new instance, build a new array + * of Controls and set it on the LdapContext. + *

+ * The {@link Control} feature is specific for LDAP v3 and thus applies only + * to {@link LdapContext}. However, the generic DirContextProcessor + * mechanism used for calling preProcess and + * postProcess uses DirContext, since it also works for LDAP + * v2. This is the reason that DirContext has to be cast to a LdapContext. + * + * @param ctx + * an LdapContext instance. + * @throws NamingException + * @throws IllegalArgumentException + * if the supplied DirContext is not an LdapContext. + */ + public void preProcess(DirContext ctx) throws NamingException { + LdapContext ldapContext; + if (ctx instanceof LdapContext) { + ldapContext = (LdapContext) ctx; + } else { + throw new IllegalArgumentException( + "Request Control operations require LDAPv3 - " + + "Context must be of type LdapContext"); + } + + Control[] requestControls = ldapContext.getRequestControls(); + if (requestControls == null) { + requestControls = new Control[0]; + } + Control newControl = createRequestControl(); + + Control[] newControls = new Control[requestControls.length + 1]; + for (int i = 0; i < requestControls.length; i++) { + newControls[i] = requestControls[i]; + } + + // Add the new Control at the end of the array. + newControls[newControls.length - 1] = newControl; + + ldapContext.setRequestControls(newControls); + } + + /** + * Create an instance of the appropriate RequestControl. + * + * @return the new instance. + */ + public abstract Control createRequestControl(); + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/CreateControlFailedException.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/CreateControlFailedException.java new file mode 100644 index 00000000..3a026645 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/CreateControlFailedException.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005-2007 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.control; + +import org.springframework.ldap.NamingException; + +/** + * Thrown by an AbstractRequestControlDirContextProcessor when it cannot create + * a request control. + * + * @author Ulrik Sandberg + * @since 1.2 + */ +public class CreateControlFailedException extends NamingException { + + /** + * Create a new CreateControlFailedException. + * + * @param msg + * the detail message + */ + public CreateControlFailedException(String msg) { + super(msg); + } + + /** + * Create a new CreateControlFailedException. + * + * @param msg + * the detail message + * @param cause + * the root cause (if any) + */ + public CreateControlFailedException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResult.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResult.java new file mode 100644 index 00000000..d2e898af --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResult.java @@ -0,0 +1,92 @@ +/* + * Copyright 2005-2007 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.control; + +import java.util.List; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +/** + * Bean to encapsulate a result List and a {@link PagedResultsCookie} to use for + * returning the results when using {@link PagedResultsRequestControl}. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PagedResult { + + private List resultList; + + private PagedResultsCookie cookie; + + /** + * Constructs a PagedResults using the supplied List and + * {@link PagedResultsCookie}. + * + * @param resultList + * the result list. + * @param cookie + * the cookie. + */ + public PagedResult(List resultList, PagedResultsCookie cookie) { + this.resultList = resultList; + this.cookie = cookie; + } + + /** + * Get the cookie. + * + * @return the cookie. + */ + public PagedResultsCookie getCookie() { + return cookie; + } + + /** + * Get the result list. + * + * @return the result list. + */ + public List getResultList() { + return resultList; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj != null && this.getClass().equals(obj.getClass())) { + PagedResult that = (PagedResult) obj; + return new EqualsBuilder().append(this.resultList, that.resultList) + .append(this.cookie, that.cookie).isEquals(); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return new HashCodeBuilder().append(this.resultList) + .append(this.cookie).toHashCode(); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResultsCookie.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResultsCookie.java new file mode 100644 index 00000000..1a3e9385 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResultsCookie.java @@ -0,0 +1,78 @@ +/* + * Copyright 2005-2007 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.control; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.sun.jndi.ldap.ctl.PagedResultsControl; + +/** + * Wrapper class for the cookie returned when using the + * {@link PagedResultsControl}. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PagedResultsCookie { + + private byte[] cookie; + + /** + * Constructor. + * + * @param cookie + * the cookie returned by a PagedResultsResponseControl. + */ + public PagedResultsCookie(byte[] cookie) { + this.cookie = ArrayUtils.clone(cookie); + } + + /** + * Get the cookie. + * + * @return the cookie. + */ + public byte[] getCookie() { + return ArrayUtils.clone(cookie); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj != null && this.getClass().equals(obj.getClass())) { + PagedResultsCookie that = (PagedResultsCookie) obj; + return new EqualsBuilder().append(this.cookie, that.cookie) + .isEquals(); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return new HashCodeBuilder().append(this.cookie).toHashCode(); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java new file mode 100644 index 00000000..dcc9dc6c --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java @@ -0,0 +1,238 @@ +/* + * Copyright 2005-2007 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.control; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; + +import org.springframework.util.ReflectionUtils; + +import com.sun.jndi.ldap.ctl.PagedResultsControl; +import com.sun.jndi.ldap.ctl.PagedResultsResponseControl; + +/** + * DirContextProcessor implementation for managing the paged results control. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PagedResultsRequestControl extends + AbstractRequestControlDirContextProcessor { + + private static final Class DEFAULT_RESPONSE_CONTROL = PagedResultsResponseControl.class; + + private static final boolean CRITICAL_CONTROL = true; + + private static final String JAVA5_RESPONSE_CONTROL = "javax.naming.ldap.PagedResultsResponseControl"; + + private int pageSize; + + private PagedResultsCookie cookie; + + private int resultSize; + + private Class responseControlClass = DEFAULT_RESPONSE_CONTROL; + + private Class fallbackResponseControlClass; + + private Class currentResponseControlClass; + + /** + * Constructs a new instance. This constructor should be used when + * performing the first paged search operation, when no other results have + * been retrieved. + * + * @param pageSize + * the page size. + */ + public PagedResultsRequestControl(int pageSize) { + this(pageSize, null); + } + + /** + * Constructs a new instance with the supplied page size and cookie. The + * cookie must be the exact same instance as received from a previous paged + * resullts search, or null if it is the first in an + * operation sequence. + * + * @param pageSize + * the page size. + * @param cookie + * the cookie, as received from a previous search. + */ + public PagedResultsRequestControl(int pageSize, PagedResultsCookie cookie) { + this.pageSize = pageSize; + this.cookie = cookie; + fallbackResponseControlClass = loadFallbackResponseControlClass(); + } + + /** + * Get the cookie. + * + * @return the cookie. + */ + public PagedResultsCookie getCookie() { + return cookie; + } + + /** + * Get the page size. + * + * @return the page size. + */ + public int getPageSize() { + return pageSize; + } + + /** + * Get the total estimated number of entries that matches the issued search. + * Note that this value is optional for the LDAP server to return, so it + * does not always contain any valid data. + * + * @return the estimated result size, if returned from the server. + */ + public int getResultSize() { + return resultSize; + } + + /** + * Set the class of the expected ResponseControl for the paged results + * response. The default is {@link PagedResultsResponseControl}. + * + * @param responseControlClass + * Class of the expected response control. + */ + public void setResponseControlClass(Class responseControlClass) { + this.responseControlClass = responseControlClass; + } + + /* + * @see org.springframework.ldap.control.AbstractRequestControlDirContextProcessor#createRequestControl() + */ + public Control createRequestControl() { + try { + if (cookie != null) { + return new PagedResultsControl(pageSize, cookie.getCookie(), + CRITICAL_CONTROL); + } else { + return new PagedResultsControl(pageSize); + } + } catch (IOException e) { + throw new CreateControlFailedException( + "Error creating PagedResultsControl", e); + } + } + + /* + * @see org.springframework.ldap.core.DirContextProcessor#postProcess(javax.naming.directory.DirContext) + */ + public void postProcess(DirContext ctx) throws NamingException { + // initialize from property + currentResponseControlClass = responseControlClass; + + LdapContext ldapContext = (LdapContext) ctx; + Control[] responseControls = ldapContext.getResponseControls(); + if (responseControls == null) { + responseControls = new Control[0]; + } + + // Go through response controls and get info, regardless of class + for (int i = 0; i < responseControls.length; i++) { + Control responseControl = responseControls[i]; + + // check for match, try fallback otherwise + if (isPagedResultsResponseControl(responseControl)) { + Object control = responseControl; + byte[] result = (byte[]) invokeMethod("getCookie", + currentResponseControlClass, control); + this.cookie = new PagedResultsCookie(result); + Integer wrapper = (Integer) invokeMethod("getResultSize", + currentResponseControlClass, control); + this.resultSize = wrapper.intValue(); + } + } + } + + /** + * Check if the given control matches a paged results response control. Try + * the fallback class from Java5 if there is no match. Set the + * {@link #currentResponseControlClass} to the fallback if it matches. + * + * @param responseControl + * the control to check for a match + * @return whether the control is a paged results response control + */ + private boolean isPagedResultsResponseControl(Control responseControl) { + if (responseControl.getClass().isAssignableFrom( + currentResponseControlClass)) { + return true; + } + if (fallbackResponseControlClass != null + && responseControl.getClass().isAssignableFrom( + fallbackResponseControlClass)) { + currentResponseControlClass = fallbackResponseControlClass; + return true; + } + return false; + } + + private Class loadFallbackResponseControlClass() { + Class fallbackResponseControlClass = null; + try { + fallbackResponseControlClass = Class + .forName(JAVA5_RESPONSE_CONTROL); + } catch (ClassNotFoundException e) { + log.debug("Could not load Java5 response control class " + + JAVA5_RESPONSE_CONTROL); + } + return fallbackResponseControlClass; + } + + private Object invokeMethod(String method, Class clazz, Object control) { + // For Spring 2.0 ReflectionUtils could be used for all of this, but + // since we still want to support the 1.2 branch we do it manually and + // only use the stuff present in 1.2.8. + Method actualMethod = null; + Object retval = null; + try { + actualMethod = clazz.getMethod(method, new Class[0]); + } catch (SecurityException e) { + ReflectionUtils.handleReflectionException(e); + } catch (NoSuchMethodException e) { + ReflectionUtils.handleReflectionException(e); + } + + try { + retval = actualMethod.invoke(control, new Object[0]); + } catch (IllegalArgumentException e) { + ReflectionUtils.handleReflectionException(e); + } catch (IllegalAccessException e) { + ReflectionUtils.handleReflectionException(e); + } catch (InvocationTargetException e) { + ReflectionUtils.handleReflectionException(e); + } + + // Retval will be set unless an exception has been thrown. + return retval; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/SortControlDirContextProcessor.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/SortControlDirContextProcessor.java new file mode 100644 index 00000000..94c6f92a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/SortControlDirContextProcessor.java @@ -0,0 +1,247 @@ +/* + * Copyright 2005-2007 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.control; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; + +import org.springframework.ldap.control.AbstractRequestControlDirContextProcessor; +import org.springframework.ldap.control.CreateControlFailedException; +import org.springframework.util.ReflectionUtils; + +import com.sun.jndi.ldap.ctl.SortControl; +import com.sun.jndi.ldap.ctl.SortResponseControl; + +/** + * DirContextProcessor implementation for managing the {@link SortControl}. + * Note that this class is stateful, so a new instance needs to be instantiated + * for each new search. + * + * @author Ulrik Sandberg + */ +public class SortControlDirContextProcessor extends + AbstractRequestControlDirContextProcessor { + + private static final Class DEFAULT_RESPONSE_CONTROL = SortResponseControl.class; + + private static final boolean CRITICAL_CONTROL = true; + + private static final String JAVA5_RESPONSE_CONTROL = "javax.naming.ldap.SortResponseControl"; + + /** + * What key to sort on. + */ + private String sortKey; + + /** + * Whether the search result actually was sorted. + */ + private boolean sorted; + + /** + * The result code of the supposedly sorted search. + */ + private int resultCode; + + private Class responseControlClass = DEFAULT_RESPONSE_CONTROL; + + private Class fallbackResponseControlClass; + + private Class currentResponseControlClass; + + /** + * Constructs a new instance using the supplied sort key. + * + * @param sortKey + * the sort key, i.e. the attribute name to sort on. + */ + public SortControlDirContextProcessor(String sortKey) { + this.sortKey = sortKey; + fallbackResponseControlClass = loadFallbackResponseControlClass(); + setSorted(false); + setResultCode(-1); + } + + /** + * Set the class of the expected ResponseControl for the sorted result + * response. The default is {@link SortResponseControl}. + * + * @param responseControlClass + * Class of the expected response control. + */ + public void setResponseControlClass(Class responseControlClass) { + this.responseControlClass = responseControlClass; + } + + /** + * Check whether the returned values were actually sorted by the server. + * + * @return true if the result was sorted, false + * otherwise. + */ + public boolean isSorted() { + return sorted; + } + + private void setSorted(boolean sorted) { + this.sorted = sorted; + } + + /** + * Get the result code returned by the control. + * + * @return result code. + */ + public int getResultCode() { + return resultCode; + } + + private void setResultCode(int sortResult) { + this.resultCode = sortResult; + } + + /** + * Get the sort key. + * + * @return the sort key. + */ + public String getSortKey() { + return sortKey; + } + + /** + * Set the sort key, i.e. the attribute on which to sort on. + * + * @param sortKey + * the sort key. + */ + public void setSortKey(String sortKey) { + this.sortKey = sortKey; + } + + /* + * @see org.springframework.ldap.control.AbstractRequestControlDirContextProcessor#createRequestControl() + */ + public Control createRequestControl() { + try { + return new SortControl(new String[] { sortKey }, CRITICAL_CONTROL); + } catch (IOException e) { + throw new CreateControlFailedException( + "Error creating SortControl", e); + } + } + + /* + * @see org.springframework.ldap.core.DirContextProcessor#postProcess(javax.naming.directory.DirContext) + */ + public void postProcess(DirContext ctx) throws NamingException { + // initialize from property + currentResponseControlClass = responseControlClass; + + LdapContext ldapContext = (LdapContext) ctx; + Control[] responseControls = ldapContext.getResponseControls(); + + if (responseControls == null) { + return; + } + + // Go through response controls and get info, regardless of class + for (int i = 0; i < responseControls.length; i++) { + Control responseControl = responseControls[i]; + + // check for match, try fallback otherwise + if (isSortResponseControl(responseControl)) { + Object control = responseControl; + Boolean result = (Boolean) invokeMethod("isSorted", + currentResponseControlClass, control); + setSorted(result.booleanValue()); + Integer code = (Integer) invokeMethod("getResultCode", + currentResponseControlClass, control); + setResultCode(code.intValue()); + } + } + } + + /** + * Check if the given control matches a sort response control. Try the + * fallback class from Java5 if there is no match. Set the + * {@link #currentResponseControlClass} to the fallback if it matches. + * + * @param responseControl + * the control to check for a match + * @return whether the control is a paged results response control + */ + private boolean isSortResponseControl(Control responseControl) { + if (responseControl.getClass().isAssignableFrom( + currentResponseControlClass)) { + return true; + } + if (fallbackResponseControlClass != null + && responseControl.getClass().isAssignableFrom( + fallbackResponseControlClass)) { + currentResponseControlClass = fallbackResponseControlClass; + return true; + } + return false; + } + + private Class loadFallbackResponseControlClass() { + Class fallbackResponseControlClass = null; + try { + fallbackResponseControlClass = Class + .forName(JAVA5_RESPONSE_CONTROL); + } catch (ClassNotFoundException e) { + log.debug("Could not load Java5 response control class " + + JAVA5_RESPONSE_CONTROL); + } + return fallbackResponseControlClass; + } + + private Object invokeMethod(String method, Class clazz, Object control) { + // For Spring 2.0 ReflectionUtils could be used for all of this, but + // since we still want to support the 1.2 branch we do it manually and + // only use the stuff present in 1.2.8. + Method actualMethod = null; + Object retval = null; + try { + actualMethod = clazz.getMethod(method, new Class[0]); + } catch (SecurityException e) { + ReflectionUtils.handleReflectionException(e); + } catch (NoSuchMethodException e) { + ReflectionUtils.handleReflectionException(e); + } + + try { + retval = actualMethod.invoke(control, new Object[0]); + } catch (IllegalArgumentException e) { + ReflectionUtils.handleReflectionException(e); + } catch (IllegalAccessException e) { + ReflectionUtils.handleReflectionException(e); + } catch (InvocationTargetException e) { + ReflectionUtils.handleReflectionException(e); + } + + // Retval will be set unless an exception has been thrown. + return retval; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/control/package.html new file mode 100644 index 00000000..af3c0892 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/package.html @@ -0,0 +1,7 @@ + + + +Support classes for custom request control context processors. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributeModificationsAware.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributeModificationsAware.java new file mode 100644 index 00000000..089ca336 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributeModificationsAware.java @@ -0,0 +1,37 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.directory.ModificationItem; + +/** + * Indicates that the implementing class is capable of keeping track of any + * attribute modifications and return them as ModificationItems. + * + * @author Mattias Arthursson + * + */ +public interface AttributeModificationsAware { + + /** + * Creates an array of which attributes have been changed, added or removed + * since the initialization of this object. + * + * @return an array of modification items. + */ + public ModificationItem[] getModificationItems(); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributesMapper.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributesMapper.java new file mode 100644 index 00000000..b090bf1f --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributesMapper.java @@ -0,0 +1,55 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +/** + * An interface used by LdapTemplate for mapping LDAP Attributes to beans. + * Implementions of this interface perform the actual work of extracting + * results, but need not worry about exception handling. NamingExceptions will + * be caught and handled correctly by the {@link LdapTemplate} class. + *

+ * Typically used in search methods of {@link LdapTemplate}. + * AttributeMapper objects are normally stateless and thus + * reusable; they are ideal for implementing attribute-mapping logic in one + * place. + *

+ * Alternatively, consider using a {@link ContextMapper} in stead. + * + * @see LdapTemplate#search(Name, String, AttributesMapper) + * @see LdapTemplate#lookup(Name, AttributesMapper) + * @see ContextMapper + * + * @author Mattias Arthursson + */ +public interface AttributesMapper { + /** + * Map Attributes to an object. The supplied attributes are the attributes + * from a single SearchResult. + * + * @param attributes + * attributes from a SearchResult. + * @return an object built from the attributes. + * @throws NamingException + * if any error occurs mapping the attributes + */ + public Object mapFromAttributes(Attributes attributes) + throws NamingException; +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributesMapperCallbackHandler.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributesMapperCallbackHandler.java new file mode 100644 index 00000000..dfbdd552 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/AttributesMapperCallbackHandler.java @@ -0,0 +1,66 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.NameClassPair; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.support.LdapUtils; + +/** + * A CollectingNameClassPairCallbackHandler to wrap an {@link AttributesMapper}. + * That is, the found object is extracted from the {@link Attributes} of each + * {@link SearchResult}, and then passed to the specified + * {@link AttributesMapper} for translation. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + * @since 1.2 + */ +public class AttributesMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private AttributesMapper mapper; + + /** + * Constructs a new instance around the specified {@link AttributesMapper}. + * + * @param mapper + * the target mapper. + */ + public AttributesMapperCallbackHandler(AttributesMapper mapper) { + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a SearchResult and pass its attributes to the + * {@link AttributesMapper}. + * + * @param nameClassPair + * a SearchResult instance. + * @return the Object returned from the mapper. + */ + public Object getObjectFromNameClassPair(NameClassPair nameClassPair) { + SearchResult searchResult = (SearchResult) nameClassPair; + Attributes attributes = searchResult.getAttributes(); + try { + return mapper.mapFromAttributes(attributes); + } catch (javax.naming.NamingException e) { + throw LdapUtils.convertLdapException(e); + } + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/AuthenticationSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/AuthenticationSource.java new file mode 100644 index 00000000..4f368e09 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/AuthenticationSource.java @@ -0,0 +1,40 @@ +/* + * Copyright 2005-2007 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.core; + +/** + * An AuthenticationSource is responsible for providing the + * principal (user DN) and credentials to be used when creating a new context. + * + * @author Mattias Arthursson + * + */ +public interface AuthenticationSource { + /** + * Get the principal to use when creating an authenticated context. + * + * @return the principal (userDn). + */ + public String getPrincipal(); + + /** + * Get the credentials to use when creating an authenticated context. + * + * @return the credentials (password). + */ + public String getCredentials(); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/CollectingNameClassPairCallbackHandler.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/CollectingNameClassPairCallbackHandler.java new file mode 100644 index 00000000..ebb3a5dd --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/CollectingNameClassPairCallbackHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.LinkedList; +import java.util.List; + +import javax.naming.NameClassPair; + +/** + * A NameClassPairCallbackHandler to collect all results in an internal List. + * + * @see LdapTemplate + * + * @author Mattias Arthursson + */ +public abstract class CollectingNameClassPairCallbackHandler implements + NameClassPairCallbackHandler { + + private List list = new LinkedList(); + + /** + * Get the assembled list. + * + * @return the list of all assembled objects. + */ + public List getList() { + return list; + } + + /** + * Pass on the supplied NameClassPair to + * {@link #getObjectFromNameClassPair(NameClassPair)} and add the result to + * the internal list. + */ + public void handleNameClassPair(NameClassPair nameClassPair) { + list.add(getObjectFromNameClassPair(nameClassPair)); + } + + /** + * Handle a NameClassPair and transform it to an Object of the desired type + * and with data from the NameClassPair. + * + * @param nameClassPair + * a NameClassPair from a search operation. + * @return an object constructed from the data in the NameClassPair. + */ + public abstract Object getObjectFromNameClassPair( + NameClassPair nameClassPair); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextAssembler.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextAssembler.java new file mode 100644 index 00000000..33403622 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextAssembler.java @@ -0,0 +1,36 @@ +/* + * Copyright 2005-2007 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.core; + +/** + * Helper interface to be used by Dao implementations for assembling to and from + * context. Useful if we have assembler classes responsible for mapping to and + * from a specific entry. + * + * @author Mattias Arthursson + */ +public interface ContextAssembler extends ContextMapper { + /** + * Map the supplied object to the specified context. + * + * @param obj + * the object to read data from. + * @param ctx + * the context to map to. + */ + public void mapToContext(Object obj, Object ctx); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextExecutor.java new file mode 100644 index 00000000..3feb41bf --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextExecutor.java @@ -0,0 +1,50 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +/** + * Interface for delegating an actual operation to be performed on a + * DirContext. For searches, use {@link SearchExecutor} in + * stead. A typical usage of this interface could be e.g.: + * + *

+ * ContextExecutor executor = new ContextExecutor() {
+ *     public Object executeWithContext(DirContext ctx) throws NamingException {
+ *         return ctx.lookup(dn);
+ *     }
+ * };
+ * 
+ * + * @see LdapTemplate#executeReadOnly(ContextExecutor) + * @see LdapTemplate#executeReadWrite(ContextExecutor) + * + * @author Mattias Arthursson + */ +public interface ContextExecutor { + /** + * Perform any operation on the context. + * + * @param ctx + * the DirContext to perform the operation on. + * @return any object resulting from the operation - might be null. + * @throws NamingException + * if the operation resulted in one. + */ + public Object executeWithContext(DirContext ctx) throws NamingException; +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextMapper.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextMapper.java new file mode 100644 index 00000000..2d82c243 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextMapper.java @@ -0,0 +1,65 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Binding; +import javax.naming.Name; +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.core.support.AbstractContextMapper; +import org.springframework.ldap.core.support.DefaultDirObjectFactory; + +/** + * An interface used by LdapTemplate to map LDAP Contexts to beans. When a + * DirObjectFactory is set on the ContextSource, the objects returned from + * search and listBindings operations are + * automatically transformed to DirContext objects (when using the + * {@link DefaultDirObjectFactory} - which is typically the case, unless + * something else has been explicitly specified - you get a + * {@link DirContextAdapter} object). This object will then be passed to the + * ContextMapper implementation for transformation to the desired bean. + *

+ * ContextMapper implementations are typically stateless and thus reusable; they + * are ideal for implementing mapping logic in one place. + *

+ * Alternatively, consider using an {@link AttributesMapper} in stead. + * + * @see LdapTemplate#search(Name, String, ContextMapper) + * @see LdapTemplate#listBindings(Name, ContextMapper) + * @see LdapTemplate#lookup(Name, ContextMapper) + * @see AttributesMapper + * @see DefaultDirObjectFactory + * @see DirContextAdapter + * @see AbstractContextMapper + * + * @author Mattias Arthursson + */ +public interface ContextMapper { + /** + * Map a single LDAP Context to an object. The supplied Object + * ctx is the object from a single {@link SearchResult}, + * {@link Binding}, or a lookup operation. + * + * @param ctx + * the context to map to an object. Typically this will be a + * {@link DirContextAdapter} instance, unless a project specific + * DirObjectFactory has been specified on the + * ContextSource. + * @return an object built from the data in the context. + */ + public Object mapFromContext(Object ctx); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextMapperCallbackHandler.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextMapperCallbackHandler.java new file mode 100644 index 00000000..fccccb2a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextMapperCallbackHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Binding; +import javax.naming.NameClassPair; + +import org.apache.commons.lang.Validate; + +/** + * A CollectingNameClassPairCallbackHandler to wrap a ContextMapper. That is, + * the found object is extracted from each {@link Binding}, and then passed to + * the specified ContextMapper for translation. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + * @since 1.2 + */ +public class ContextMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private ContextMapper mapper; + + /** + * Constructs a new instance wrapping the supplied {@link ContextMapper}. + * + * @param mapper + * the mapper to be called for each entry. + */ + public ContextMapperCallbackHandler(ContextMapper mapper) { + Validate.notNull(mapper, "Mapper must not be empty"); + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a {@link Binding} and pass its object to + * the ContextMapper. + * + * @param nameClassPair + * a Binding instance. + * @return the Object returned from the mapper. + */ + public Object getObjectFromNameClassPair(NameClassPair nameClassPair) { + Binding binding = (Binding) nameClassPair; + Object object = binding.getObject(); + if (object == null) { + throw new ObjectRetrievalException( + "Binding did not contain any object."); + } + return mapper.mapFromContext(object); + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextSource.java new file mode 100644 index 00000000..87656e44 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/ContextSource.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.directory.DirContext; + +import org.springframework.ldap.NamingException; + +/** + * Interface used by {@link LdapTemplate} to create LDAP contexts. + * + * @see org.springframework.ldap.core.LdapTemplate + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public interface ContextSource { + + /** + * Gets a read-only DirContext. The returned DirContext must be possible to + * perform read-only operations on. + * + * @return A DirContext instance, never null. + * @throws NamingException + * if some error occurs creating an DirContext. + */ + public DirContext getReadOnlyContext() throws NamingException; + + /** + * Gets a read-write DirContext. + * + * @return A DirContext instance, never null. + * @throws NamingException + * if some error occurs creating an DirContext. + */ + public DirContext getReadWriteContext() throws NamingException; +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DefaultDnParserFactory.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DefaultDnParserFactory.java new file mode 100644 index 00000000..3d685972 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DefaultDnParserFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright 2005-2007 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.core; + +import java.io.StringReader; + +import org.springframework.ldap.core.DnParserImpl; + +/** + * A factory for creating DnParser instances. The actual implementation of + * DnParser is generated using javacc and should not be constructed directly. + * + * @author Mattias Arthursson + * + */ +public class DefaultDnParserFactory { + /** + * Create a new DnParser instance. + * + * @param string + * the DN String to be parsed. + * @return a new DnParser instance for parsing the supplied DN string. + */ + public static DnParser createDnParser(String string) { + return new DnParserImpl(new StringReader(string)); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DefaultNameClassPairMapper.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DefaultNameClassPairMapper.java new file mode 100644 index 00000000..9e61d85d --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DefaultNameClassPairMapper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.NameClassPair; +import javax.naming.NamingException; + +/** + * The default NameClassPairMapper implementation. This implementation simply + * takes the Name string from the supplied NameClassPair and returns it as + * result. + * + * @author Mattias Arthursson + * + */ +public class DefaultNameClassPairMapper implements NameClassPairMapper { + + /** + * Gets the Name from the supplied NameClassPair and returns it as the + * result. + * + * @param nameClassPair + * the NameClassPair to transform. + * @return the Name string from the NameClassPair. + */ + public Object mapFromNameClassPair(NameClassPair nameClassPair) + throws NamingException { + + return nameClassPair.getName(); + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java new file mode 100644 index 00000000..3bd4b8ae --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java @@ -0,0 +1,1254 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameNotFoundException; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SearchControls; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +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.support.LdapUtils; + +/** + * Adapter that implements the interesting methods of the DirContext interface. + * In particular it contains utility methods for getting and setting attributes. + * Using the + * {@link org.springframework.ldap.core.support.DefaultDirObjectFactory} in your + * ContextSource (which is the default) you will receive + * instances of this class from searches and lookups. This can be particularly + * useful when updating data, since this class implements + * {@link AttributeModificationsAware}, providing a + * {@link #getModificationItems()} method. When in update mode, an object of + * this class keeps track of the changes made to its attributes, making them + * available as an array of ModificationItem objects, suitable as + * input to {@link LdapTemplate#modifyAttributes(DirContextOperations)}. + * + * @see #setAttributeValue(String, Object) + * @see #setAttributeValues(String, Object[]) + * @see #getStringAttribute(String) + * @see #getStringAttributes(String) + * @see #getObjectAttribute(String) + * @see #addAttributeValue(String, Object) + * @see #removeAttributeValue(String, Object) + * @see #setUpdateMode(boolean) + * @see #isUpdateMode() + * + * @author Magnus Robertsson + * @author Andreas Ronge + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class DirContextAdapter implements DirContextOperations { + + private static final boolean ORDER_DOESNT_MATTER = false; + + private static Log log = LogFactory.getLog(DirContextAdapter.class); + + private final Attributes originalAttrs; + + private DistinguishedName dn; + + private DistinguishedName base; + + private boolean updateMode = false; + + private Attributes updatedAttrs; + + /** + * Default constructor. + */ + public DirContextAdapter() { + this(null, null, null); + } + + /** + * Create a new adapter from the supplied dn. + * + * @param dn + * the dn. + */ + public DirContextAdapter(Name dn) { + this(null, dn); + } + + /** + * Create a new adapter from the supplied attributes and dn. + * + * @param attrs + * the attributes. + * @param dn + * the dn. + */ + public DirContextAdapter(Attributes attrs, Name dn) { + this(attrs, dn, null); + } + + /** + * Create a new adapter from the supplied attributes, dn, and base. + * + * @param attrs + * the attributes. + * @param dn + * the dn. + * @param base + * the base name. + */ + public DirContextAdapter(Attributes attrs, Name dn, Name base) { + if (attrs != null) { + this.originalAttrs = attrs; + } else { + this.originalAttrs = new BasicAttributes(true); + } + if (dn != null) { + this.dn = new DistinguishedName(dn.toString()); + } else { + this.dn = new DistinguishedName(); + } + if (base != null) { + this.base = new DistinguishedName(base.toString()); + } else { + this.base = new DistinguishedName(); + } + } + + /** + * Constructor for cloning an existing adapter. + * + * @param master + * The adapter to be copied. + */ + protected DirContextAdapter(DirContextAdapter master) { + this.originalAttrs = (Attributes) master.originalAttrs.clone(); + this.dn = master.dn; + this.updatedAttrs = (Attributes) master.updatedAttrs.clone(); + this.updateMode = master.updateMode; + } + + /** + * Sets the update mode. The update mode should be false for + * a new entry and true for an existing entry that is being + * updated. + * + * @param mode + * Update mode. + */ + public void setUpdateMode(boolean mode) { + this.updateMode = mode; + if (updateMode) { + updatedAttrs = new BasicAttributes(true); + } + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#isUpdateMode() + */ + public boolean isUpdateMode() { + return updateMode; + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#getNamesOfModifiedAttributes() + */ + public String[] getNamesOfModifiedAttributes() { + + List tmpList = new ArrayList(); + + NamingEnumeration attributesEnumeration; + if (isUpdateMode()) { + attributesEnumeration = updatedAttrs.getAll(); + } else { + attributesEnumeration = originalAttrs.getAll(); + } + + try { + while (attributesEnumeration.hasMore()) { + Attribute oneAttribute = (Attribute) attributesEnumeration + .next(); + tmpList.add(oneAttribute.getID()); + } + } catch (NamingException e) { + throw LdapUtils.convertLdapException(e); + } finally { + closeNamingEnumeration(attributesEnumeration); + } + + return (String[]) tmpList.toArray(new String[0]); + } + + private void closeNamingEnumeration(NamingEnumeration enumeration) { + try { + if (enumeration != null) { + enumeration.close(); + } + } catch (NamingException e) { + // Never mind this + } + } + + /* + * @see org.springframework.ldap.support.AttributeModificationsAware#getModificationItems() + */ + public ModificationItem[] getModificationItems() { + if (!updateMode) { + return new ModificationItem[0]; + } + + List tmpList = new LinkedList(); + NamingEnumeration attributesEnumeration = null; + try { + attributesEnumeration = updatedAttrs.getAll(); + + // find attributes that have been changed, removed or added + while (attributesEnumeration.hasMore()) { + Attribute oneAttr = (Attribute) attributesEnumeration.next(); + + collectModifications(oneAttr, tmpList); + } + } catch (NamingException e) { + throw LdapUtils.convertLdapException(e); + } finally { + closeNamingEnumeration(attributesEnumeration); + } + + if (log.isDebugEnabled()) { + log.debug("Number of modifications:" + tmpList.size()); + } + + return (ModificationItem[]) tmpList + .toArray(new ModificationItem[tmpList.size()]); + } + + /** + * Collect all modifications for the changed attribute. If no changes have + * been made, return immediately. If modifications have been made, and the + * original size as well as the updated size of the attribute is 1, replace + * the attribute. If the size of the updated attribute is 0, remove the + * attribute. Otherwise, the attribute is a multi-value attribute, in which + * case all modifications to the original value (removals and additions) + * will be collected individually. + * + * @param changedAttr + * the value of the changed attribute. + * @param modificationList + * the list in which to add the modifications. + * @throws NamingException + * if thrown by called Attribute methods. + */ + private void collectModifications(Attribute changedAttr, + List modificationList) throws NamingException { + Attribute currentAttribute = originalAttrs.get(changedAttr.getID()); + + if (changedAttr.equals(currentAttribute)) { + // No changes + return; + } else if (currentAttribute != null && currentAttribute.size() == 1 + && changedAttr.size() == 1) { + // Replace single-vale attribute. + modificationList.add(new ModificationItem( + DirContext.REPLACE_ATTRIBUTE, changedAttr)); + } else if (changedAttr.size() == 0 && currentAttribute != null) { + // Attribute has been removed. + modificationList.add(new ModificationItem( + DirContext.REMOVE_ATTRIBUTE, changedAttr)); + } else if ((currentAttribute == null || currentAttribute.size() == 0) + && changedAttr.size() > 0) { + // Attribute has been added. + modificationList.add(new ModificationItem(DirContext.ADD_ATTRIBUTE, + changedAttr)); + } else if (changedAttr.size() > 0) { + // Change of multivalue Attribute. Collect additions and removals + // individually. + List myModifications = new LinkedList(); + collectModifications(currentAttribute, changedAttr, myModifications); + + if (myModifications.isEmpty()) { + // This means that the attributes are not equal, but the + // actual values are the same - thus the order must have + // changed. This should result in a REPLACE_ATTRIBUTE operation. + myModifications.add(new ModificationItem( + DirContext.REPLACE_ATTRIBUTE, changedAttr)); + } + + modificationList.addAll(myModifications); + } + } + + private void collectModifications(Attribute originalAttr, + Attribute changedAttr, List modificationList) + throws NamingException { + + Attribute originalClone = (Attribute) originalAttr.clone(); + Attribute addedValuesAttribute = new BasicAttribute(originalAttr + .getID()); + + for (int i = 0; i < changedAttr.size(); i++) { + Object attributeValue = changedAttr.get(i); + if (!originalClone.remove(attributeValue)) { + addedValuesAttribute.add(attributeValue); + } + } + + // We have now traversed and removed all values from the original that + // were also present in the new values. The remaining values in the + // original must be the ones that were removed. + if (originalClone.size() > 0) { + modificationList.add(new ModificationItem( + DirContext.REMOVE_ATTRIBUTE, originalClone)); + } + + if (addedValuesAttribute.size() > 0) { + modificationList.add(new ModificationItem(DirContext.ADD_ATTRIBUTE, + addedValuesAttribute)); + } + } + + /** + * returns true if the attribute is empty. It is empty if a == null, size == + * 0 or get() == null or an exception if thrown when accessing the get + * method + */ + private boolean isEmptyAttribute(Attribute a) { + try { + return (a == null || a.size() == 0 || a.get() == null); + } catch (NamingException e) { + return true; + } + } + + /** + * Compare the existing attribute name with the values on the + * array values. The order of the array must be the same + * order as the existing multivalued attribute. + *

+ * Also handles the case where the values have been reset to the original + * values after a previous change. For example, changing + * [a,b,c] to [a,b] and then back to + * [a,b,c] again must result in this method returning + * true so the first change can be overwritten with the + * latest change. + * + * @param name + * Name of the original multi-valued attribute. + * @param values + * Array of values to check if they have been changed. + * @return true if there has been a change compared to original attribute, + * or a previous update + */ + private boolean isChanged(String name, Object[] values, boolean orderMatters) { + + Attribute orig = originalAttrs.get(name); + Attribute prev = updatedAttrs.get(name); + + // values == null and values.length == 0 is treated the same way + boolean emptyNewValue = (values == null || values.length == 0); + + // Setting to empty --------------------- + if (emptyNewValue) { + // FALSE: if both are null, it is not changed (both don't exist) + // TRUE: if new value is null and old value exists (should be + // removed) + // TODO Also include prev in null check + // TODO Also check if there is a single null element + if (orig != null) { + return true; + } + return false; + } + + // NOT setting to empty ------------------- + + // TRUE if existing value is null + if (orig == null) { + return true; + } + + // TRUE if different length compared to original attributes + if (orig.size() != values.length) { + return true; + } + + // TRUE if different length compared to previously updated attributes + if (prev != null && prev.size() != values.length) { + return true; + } + + // Check contents of arrays + + // Order DOES matter, e.g. first names + try { + for (int i = 0; i < orig.size(); i++) { + Object obj = orig.get(i); + // TRUE if one value is not equal + if (!(obj instanceof String)) { + return true; + } + if (orderMatters) { + // check only the string with same index + if (!values[i].equals(obj)) { + return true; + } + } else { + // check all strings + if (!ArrayUtils.contains(values, obj)) { + return true; + } + } + } + + } catch (NamingException e) { + // TRUE if we can't access the value + return true; + } + + if (prev != null) { + // Also check against updatedAttrs, since there might have been + // a previous update + try { + for (int i = 0; i < prev.size(); i++) { + Object obj = prev.get(i); + // TRUE if one value is not equal + if (!(obj instanceof String)) { + return true; + } + if (orderMatters) { + // check only the string with same index + if (!values[i].equals(obj)) { + return true; + } + } else { + // check all strings + if (!ArrayUtils.contains(values, obj)) { + return true; + } + } + } + + } catch (NamingException e) { + // TRUE if we can't access the value + return true; + } + } + // FALSE since we have compared all values + return false; + } + + /** + * Checks if an entry has a specific attribute. + * + * This method simply calls exists(String) with the attribute name. + * + * @param attr + * the attribute to check. + * @return true if attribute exists in entry. + */ + protected final boolean exists(Attribute attr) { + return exists(attr.getID()); + } + + /** + * Checks if the attribute exists in this entry, either it was read or it + * has been added and update() has been called. + * + * @param attrId + * id of the attribute to check. + * @return true if the attribute exists in the entry. + */ + protected final boolean exists(String attrId) { + return originalAttrs.get(attrId) != null; + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#getStringAttribute(java.lang.String) + */ + public String getStringAttribute(String name) { + return (String) getObjectAttribute(name); + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#getObjectAttribute(java.lang.String) + */ + public Object getObjectAttribute(String name) { + Attribute oneAttr = originalAttrs.get(name); + if (oneAttr == null) { + return null; + } + try { + return oneAttr.get(); + } catch (NamingException e) { + throw LdapUtils.convertLdapException(e); + } + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#setAttributeValue(java.lang.String, + * java.lang.Object) + */ + public void setAttributeValue(String name, Object value) { + // new entry + if (!updateMode && value != null) { + originalAttrs.put(name, value); + } + + // updating entry + if (updateMode) { + BasicAttribute attribute = new BasicAttribute(name); + if (value != null) { + attribute.add(value); + } + updatedAttrs.put(attribute); + } + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.DirContextOperations#addAttributeValue(java.lang.String, + * java.lang.Object) + */ + public void addAttributeValue(String name, Object value) { + if (!updateMode && value != null) { + Attribute attr = originalAttrs.get(name); + if (attr == null) { + originalAttrs.put(name, value); + } else { + attr.add(value); + } + } else if (updateMode) { + Attribute attr = updatedAttrs.get(name); + if (attr == null) { + if (originalAttrs.get(name) == null) { + // No match in the original attributes - + // add a new Attribute to updatedAttrs + updatedAttrs.put(name, value); + } else { + // The attribute exists in the original attributes - clone + // that and add the new entry to it + attr = (Attribute) originalAttrs.get(name).clone(); + attr.add(value); + updatedAttrs.put(attr); + } + } else { + attr.add(value); + } + } + + // Null values will not be added + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.DirContextOperations#removeAttributeValue(java.lang.String, + * java.lang.Object) + */ + public void removeAttributeValue(String name, Object value) { + if (!updateMode && value != null) { + Attribute attr = originalAttrs.get(name); + if (attr != null) { + attr.remove(value); + if (attr.size() == 0) { + originalAttrs.remove(name); + } + } + } else if (updateMode) { + Attribute attr = updatedAttrs.get(name); + if (attr == null) { + if (originalAttrs.get(name) != null) { + attr = (Attribute) originalAttrs.get(name).clone(); + attr.remove(value); + updatedAttrs.put(attr); + } + } else { + attr.remove(value); + } + } + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#setAttributeValues(java.lang.String, + * java.lang.Object[]) + */ + public void setAttributeValues(String name, Object[] values) { + setAttributeValues(name, values, ORDER_DOESNT_MATTER); + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#setAttributeValues(java.lang.String, + * java.lang.Object[], boolean) + */ + public void setAttributeValues(String name, Object[] values, + boolean orderMatters) { + Attribute a = new BasicAttribute(name, orderMatters); + + for (int i = 0; values != null && i < values.length; i++) { + a.add(values[i]); + } + + // only change the original attribute if not in update mode + if (!updateMode && values != null && values.length > 0) { + // don't save empty arrays + originalAttrs.put(a); + } + + // possible to set an already existing attribute to an empty array + if (updateMode && isChanged(name, values, orderMatters)) { + updatedAttrs.put(a); + } + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#update() + */ + public void update() { + NamingEnumeration attributesEnumeration = null; + + try { + attributesEnumeration = updatedAttrs.getAll(); + + // find what to update + while (attributesEnumeration.hasMore()) { + Attribute a = (Attribute) attributesEnumeration.next(); + + // if it does not exist it should be added + if (isEmptyAttribute(a)) { + originalAttrs.remove(a.getID()); + } else { + // Otherwise it should be set. + originalAttrs.put(a); + } + } + } catch (NamingException e) { + throw LdapUtils.convertLdapException(e); + } finally { + closeNamingEnumeration(attributesEnumeration); + } + + // Reset the attributes to be updated + updatedAttrs = new BasicAttributes(true); + } + + /* + * @see org.springframework.ldap.support.DirContextOperations#getStringAttributes(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); + } + } + } else { + return null; + } + + return attributes; + } + + /* + * @see org.springframework.ldap.support.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); + } + } + } else { + return null; + } + + return attrSet; + } + + /** + * Set the supplied attribute. + * + * @param attribute + * the attribute to set. + */ + public void setAttribute(Attribute attribute) { + if (!updateMode) { + originalAttrs.put(attribute); + } else { + updatedAttrs.put(attribute); + } + } + + /** + * Get all attributes. + * + * @return all attributes. + */ + public Attributes getAttributes() { + return originalAttrs; + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(Name) + */ + public Attributes getAttributes(Name name) throws NamingException { + return getAttributes(name.toString()); + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(String) + */ + public Attributes getAttributes(String name) throws NamingException { + if (!StringUtils.isEmpty(name)) { + throw new NameNotFoundException(); + } + return (Attributes) originalAttrs.clone(); + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(Name, String[]) + */ + public Attributes getAttributes(Name name, String[] attrIds) + throws NamingException { + return getAttributes(name.toString(), attrIds); + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(String, String[]) + */ + public Attributes getAttributes(String name, String[] attrIds) + throws NamingException { + if (!StringUtils.isEmpty(name)) { + throw new NameNotFoundException(); + } + + Attributes a = new BasicAttributes(true); + Attribute target; + for (int i = 0; i < attrIds.length; i++) { + target = originalAttrs.get(attrIds[i]); + if (target != null) { + a.put(target); + } + } + + return a; + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(javax.naming.Name, + * int, javax.naming.directory.Attributes) + */ + public void modifyAttributes(Name name, int modOp, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(String, int, + * Attributes) + */ + public void modifyAttributes(String name, int modOp, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(Name, + * ModificationItem[]) + */ + public void modifyAttributes(Name name, ModificationItem[] mods) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(String, + * ModificationItem[]) + */ + public void modifyAttributes(String name, ModificationItem[] mods) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#bind(Name, Object, Attributes) + */ + public void bind(Name name, Object obj, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#bind(String, Object, Attributes) + */ + public void bind(String name, Object obj, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#rebind(Name, Object, Attributes) + */ + public void rebind(Name name, Object obj, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#rebind(String, Object, Attributes) + */ + public void rebind(String name, Object obj, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#createSubcontext(Name, Attributes) + */ + public DirContext createSubcontext(Name name, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#createSubcontext(String, + * Attributes) + */ + public DirContext createSubcontext(String name, Attributes attrs) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#getSchema(Name) + */ + public DirContext getSchema(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#getSchema(String) + */ + public DirContext getSchema(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#getSchemaClassDefinition(Name) + */ + public DirContext getSchemaClassDefinition(Name name) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#getSchemaClassDefinition(String) + */ + public DirContext getSchemaClassDefinition(String name) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(Name, Attributes, String[]) + */ + public NamingEnumeration search(Name name, Attributes matchingAttributes, + String[] attributesToReturn) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(String, Attributes, + * String[]) + */ + public NamingEnumeration search(String name, Attributes matchingAttributes, + String[] attributesToReturn) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(Name, Attributes) + */ + public NamingEnumeration search(Name name, Attributes matchingAttributes) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(String, Attributes) + */ + public NamingEnumeration search(String name, Attributes matchingAttributes) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(Name, String, + * SearchControls) + */ + public NamingEnumeration search(Name name, String filter, + SearchControls cons) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(String, String, + * SearchControls) + */ + public NamingEnumeration search(String name, String filter, + SearchControls cons) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(Name, String, Object[], + * SearchControls) + */ + public NamingEnumeration search(Name name, String filterExpr, + Object[] filterArgs, SearchControls cons) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.directory.DirContext#search(String, String, Object[], + * SearchControls) + */ + public NamingEnumeration search(String name, String filterExpr, + Object[] filterArgs, SearchControls cons) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#lookup(Name) + */ + public Object lookup(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#lookup(String) + */ + public Object lookup(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#bind(Name, Object) + */ + public void bind(Name name, Object obj) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#bind(String, Object) + */ + public void bind(String name, Object obj) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#rebind(Name, Object) + */ + public void rebind(Name name, Object obj) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#rebind(String, Object) + */ + public void rebind(String name, Object obj) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#unbind(Name) + */ + public void unbind(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#unbind(String) + */ + public void unbind(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#rename(Name, Name) + */ + public void rename(Name oldName, Name newName) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#rename(String, String) + */ + public void rename(String oldName, String newName) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#list(Name) + */ + public NamingEnumeration list(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#list(String) + */ + public NamingEnumeration list(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#listBindings(Name) + */ + public NamingEnumeration listBindings(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#listBindings(String) + */ + public NamingEnumeration listBindings(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#destroySubcontext(Name) + */ + public void destroySubcontext(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#destroySubcontext(String) + */ + public void destroySubcontext(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#createSubcontext(Name) + */ + public Context createSubcontext(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#createSubcontext(String) + */ + public Context createSubcontext(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#lookupLink(Name) + */ + public Object lookupLink(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#lookupLink(String) + */ + public Object lookupLink(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#getNameParser(Name) + */ + public NameParser getNameParser(Name name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#getNameParser(String) + */ + public NameParser getNameParser(String name) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#composeName(Name, Name) + */ + public Name composeName(Name name, Name prefix) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#composeName(String, String) + */ + public String composeName(String name, String prefix) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#addToEnvironment(String, Object) + */ + public Object addToEnvironment(String propName, Object propVal) + throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#removeFromEnvironment(String) + */ + public Object removeFromEnvironment(String propName) throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#getEnvironment() + */ + public Hashtable getEnvironment() throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#close() + */ + public void close() throws NamingException { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * @see javax.naming.Context#getNameInNamespace() + */ + public String getNameInNamespace() { + DistinguishedName result = new DistinguishedName(dn); + result.prepend(base); + return result.toString(); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#getDn() + */ + public Name getDn() { + return dn; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#setDn(javax.naming.Name) + */ + public final void setDn(Name dn) { + if (!updateMode) { + this.dn = new DistinguishedName(dn.toString()); + } + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + // A subclass with identical values should NOT be considered equal. + // EqualsBuilder in commons-lang cannot handle subclasses correctly. + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + return EqualsBuilder.reflectionEquals(this, obj); + } + + /** + * @see Object#hashCode() + */ + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getClass().getName()); + buf.append(":"); + if (dn != null) { + buf.append(" dn=" + dn); + } + buf.append(" {"); + + try { + for (NamingEnumeration i = originalAttrs.getAll(); i.hasMore();) { + Attribute attribute = (Attribute) i.next(); + if (attribute.size() == 1) { + buf.append(attribute.getID()); + buf.append('='); + buf.append(attribute.get()); + } else { + for (int j = 0; j < attribute.size(); j++) { + if (j > 0) { + buf.append(", "); + } + buf.append(attribute.getID()); + buf.append('['); + buf.append(j); + buf.append("]="); + buf.append(attribute.get(j)); + } + } + + if (i.hasMore()) { + buf.append(", "); + } + } + } catch (NamingException e) { + log.warn("Error in toString()"); + } + buf.append('}'); + + return buf.toString(); + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java new file mode 100644 index 00000000..9a2cfceb --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java @@ -0,0 +1,202 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.SortedSet; + +import javax.naming.Name; +import javax.naming.directory.DirContext; + +/** + * Interface for DirContextAdapter. + * + * @author Mattias Arthursson + * @see DirContextAdapter + */ +public interface DirContextOperations extends DirContext, + AttributeModificationsAware { + + /** + * Gets the update mode. An entry in update mode will keep track of its + * modifications so that they can be retrieved using + * {@link AttributeModificationsAware#getModificationItems()}. The update + * mode should be true for a new entry and true + * for an existing entry that is being updated. + * + * @return update mode. + */ + public boolean isUpdateMode(); + + /** + * Creates a String array of the names of the attributes which have been + * changed. + * + * If this is a new entry, all set entries will be in the list. If this is + * an updated entry, only changed and removed entries will be in the array. + * + * @return Array of String + */ + public String[] getNamesOfModifiedAttributes(); + + /** + * Get the value of a String attribute. If more than one attribute value + * exists for the specified attribute, only the first one will be returned. + * + * @param name + * name of the attribute. + * @return the value of the attribute. + * @throws ClassCastException + * if the value of the entry is not a String. + */ + public String getStringAttribute(String name); + + /** + * Get the value of an Object attribute. If more than one attribute value + * exists for the specified attribute, only the first one will be returned. + * + * @param name + * name of the attribute. + * @return the attribute value as an object if it exists, or + * null otherwise. + */ + public Object getObjectAttribute(String name); + + /** + * Set the with the name name to the value. + * + * @param name + * name of the attribute. + * @param value + * value to set the attribute to. + */ + public void setAttributeValue(String name, Object value); + + /** + * Sets a multivalue attribute, disregarding the order of the values. + * + * If value is null or value.length == 0 then the attribute will be removed. + * + * If update mode, changes will be made only if the array has more or less + * objects or if one or more object has changed. Reordering the objects will + * not cause an update. + * + * @param name + * The id of the attribute. + * @param values + * Attribute values. + */ + public void setAttributeValues(String name, Object[] values); + + /** + * Sets a multivalue attribute. + * + * If value is null or value.length == 0 then the attribute will be removed. + * + * If update mode, changes will be made if the array has more or less + * objects or if one or more string has changed. + * + * Reordering the objects will only cause an update if orderMatters is set + * to true. + * + * @param name + * The id of the attribute. + * @param values + * Attribute values. + * @param orderMatters + * If true, it will be changed even if data was + * just reordered. + */ + public void setAttributeValues(String name, Object[] values, + boolean orderMatters); + + /** + * Add a value to the Attribute with the specified name. If the Attribute + * doesn't exist it will be created. + * + * @param name + * the name of the Attribute to which the specified value should + * be added. + * @param value + * the Attribute value to add. + */ + public void addAttributeValue(String name, Object value); + + /** + * Remove a value from the Attribute with the specified name. If the + * Attribute doesn't exist, do nothing. + * + * @param name + * the name of the Attribute from which the specified value + * should be removed. + * @param value + * the value to remove. + */ + public void removeAttributeValue(String name, Object value); + + /** + * Update the attributes.This will mean that the getters (getStringAttribute + * methods) will return the updated values, and the modifications will be + * forgotten (i.e. + * {@link AttributeModificationsAware#getModificationItems()} will return an + * empty array. + */ + public void update(); + + /** + * Get all values of a String attribute. + * + * @param name + * name of the attribute. + * + * @return all registered values of the attribute. + */ + public String[] getStringAttributes(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. + */ + public SortedSet getAttributeSortedStringSet(String name); + + /** + * Returns the DN relative to the base path. + * + * @return The distinguished name of the current context. + * + * @see DirContextAdapter#getNameInNamespace() + */ + public Name getDn(); + + /** + * Set the dn of this entry. + * + * @param dn + * the dn. + */ + public void setDn(Name dn); + + /* + * (non-Javadoc) + * + * @see javax.naming.Context#getNameInNamespace() + */ + public String getNameInNamespace(); +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextProcessor.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextProcessor.java new file mode 100644 index 00000000..907a682e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextProcessor.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +/** + * Interface to be called in search by {@link LdapTemplate} before and after the + * actual search and enumeration traversal. Implementations may be used to apply + * search controls on the Context and retrieve the results of + * such controls afterwards. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public interface DirContextProcessor { + /** + * Perform pre-processing on the supplied DirContext. + * + * @param ctx + * the DirContext instance. + * @throws NamingException + * if thrown by the underlying operation. + */ + public void preProcess(DirContext ctx) throws NamingException; + + /** + * Perform post-processing on the supplied DirContext. + * + * @param ctx + * the DirContext instance. + * @throws NamingException + * if thrown by the underlying operation. + */ + public void postProcess(DirContext ctx) throws NamingException; +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextProxy.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextProxy.java new file mode 100644 index 00000000..7e2a997d --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DirContextProxy.java @@ -0,0 +1,34 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.directory.DirContext; + +/** + * Helper interface to be able to get hold of the target DirContext + * from proxies created by ContextSource proxies. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public interface DirContextProxy { + /** + * Get the target DirContext of the proxy. + * + * @return the target DirContext. + */ + DirContext getTargetContext(); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java new file mode 100644 index 00000000..94bbff8e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java @@ -0,0 +1,738 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import javax.naming.CompositeName; +import javax.naming.InvalidNameException; +import javax.naming.Name; +import javax.naming.ldap.Rdn; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.support.ListComparator; + +/** + * Default implementation of a {@link Name} corresponding to an LDAP path. A + * Distinguished Name manipulation implementation is included in JDK1.5 + * (LdapName), but not in prior releases. + * + * A DistinguishedName is particularly useful when building or + * modifying an LDAP path dynamically, as escaping will be taken care of. + * + * A path is split into several names. The {@link Name} interface specifies that + * the most significant part be in position 0. + *

+ * Example: + * + *

+ *
The path
+ *
uid=adam.skogman, ou=People, ou=EU
+ *
Name[0]
+ *
ou=EU
+ *
Name[1]
+ *
ou=People
+ *
Name[2]
+ *
uid=adam.skogman
+ *
+ * + * Example: + * + *
+ * DistinguishedName path = new DistinguishedName("dc=jayway,dc=se");
+ * path.add("ou", "People");
+ * path.add("uid", "adam.skogman");
+ * String dn = path.toString();
+ * 
+ * + * will render uid=adam.skogman, ou=People, dc=jayway, dc=se + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class DistinguishedName implements Name { + private static final boolean COMPACT = true; + + private static final boolean NON_COMPACT = false; + + private static final long serialVersionUID = 3514344371999042586L; + + /** + * An empty, unmodifiable DistinguishedName. + */ + public static final DistinguishedName EMPTY_PATH = new DistinguishedName(Collections.EMPTY_LIST); + + private List names; + + /** + * Construct a new DistinguishedName with no components. + */ + public DistinguishedName() { + names = new LinkedList(); + } + + /** + * Construct a new DistinguishedName from a String. + * + * @param path a String corresponding to a (syntactically) valid LDAP path. + */ + public DistinguishedName(String path) { + if (StringUtils.isBlank(path)) { + names = new LinkedList(); + } + else { + parse(path); + } + } + + /** + * Construct a new DistinguishedName from the supplied + * List of {@link LdapRdn} objects. + * + * @param list the components that this instance will consist of. + */ + public DistinguishedName(List list) { + this.names = list; + } + + /** + * Construct a new DistinguishedName from the supplied + * {@link Name}. The parts of the supplied {@link Name} must be + * syntactically correct {@link LdapRdn}s. + * + * @param name the {@link Name} to construct a new + * DistinguishedName from. + */ + public DistinguishedName(Name name) { + names = new LinkedList(); + for (int i = 0; i < name.size(); i++) { + names.add(new LdapRdn(name.get(i))); + } + } + + /** + * Parse the supplied String and make this instance represent the + * corresponding distinguished name. + * + * @param path the LDAP path to parse. + */ + protected void parse(String path) { + DnParser parser = DefaultDnParserFactory.createDnParser(unmangleCompositeName(path)); + DistinguishedName dn; + try { + dn = parser.dn(); + } + catch (ParseException e) { + throw new BadLdapGrammarException("Failed to parse DN", e); + } + catch (TokenMgrError e) { + throw new BadLdapGrammarException("Failed to parse DN", e); + } + this.names = dn.names; + } + + /** + * If path is surrounded by quotes, strip them. JNDI considers forward slash + * ('/') special, but LDAP doesn't. {@link CompositeName#toString()} tends + * to mangle a {@link Name} with a slash by surrounding it with quotes + * ('"'). + * + * @param path Path to check and possibly strip. + * @return A String with the possibly stripped path. + */ + private String unmangleCompositeName(String path) { + String tempPath; + // Check if CompositeName has mangled the name with quotes + if (path.startsWith("\"") && path.endsWith("\"")) { + tempPath = path.substring(1, path.length() - 1); + } + else { + tempPath = path; + } + return tempPath; + } + + /** + * Get the {@link LdapRdn} at a specified position. + * + * @param index the {@link LdapRdn} to retrieve. + * @return the {@link LdapRdn} at the requested position. + */ + public LdapRdn getLdapRdn(int index) { + return (LdapRdn) names.get(index); + } + + /** + * Get the {@link LdapRdn} with the specified key. If there are several + * {@link Rdn}s with the same key, the first one found (in order of + * significance) will be returned. + * + * @param key Attribute name of the {@link LdapRdn} to retrieve. + * @return the {@link LdapRdn} with the requested key. + * @throws IllegalArgumentException if no Rdn matches the given key. + */ + public LdapRdn getLdapRdn(String key) { + for (Iterator iter = names.iterator(); iter.hasNext();) { + LdapRdn rdn = (LdapRdn) iter.next(); + if (StringUtils.equals(rdn.getKey(), key)) { + return rdn; + } + } + + throw new IllegalArgumentException("No Rdn with the requested key: '" + key + "'"); + } + + /** + * Get the value of the {@link LdapRdnComponent} with the specified key + * (Attribute value). If there are several Rdns with the same key, the value + * of the first one found (in order of significance) will be returned. + * + * @param key Attribute name of the {@link LdapRdn} to retrieve. + * @return the value. + * @throws IllegalArgumentException if no Rdn matches the given key. + */ + public String getValue(String key) { + return getLdapRdn(key).getValue(); + } + + /** + * Get the name List. + * + * @return the list of {@link LdapRdn}s that this + * DistinguishedName consists of. + */ + public List getNames() { + return names; + } + + /** + * Get the String representation of this DistinguishedName. + * Add a space after each comma, to make it readable. + * + * @return a syntactically correct, properly escaped, nicely formatted + * String representation of the DistinguishedName. + */ + public String toString() { + return format(NON_COMPACT); + } + + /** + * Get the compact String representation of this + * DistinguishedName. Add no space after each comma, to make + * it compact. + * + * @return a syntactically correct, properly escaped String representation + * of the DistinguishedName. + */ + public String toCompactString() { + return format(COMPACT); + } + + /** + * Builds a complete LDAP path, ldap encoded, useful as a DN. + * + * Always uses lowercase, always separates with ", " i.e. comma and a space. + * + * @return the LDAP path. + */ + public String encode() { + return format(NON_COMPACT); + } + + private String format(boolean compact) { + // empty path + if (names.size() == 0) + return ""; + + StringBuffer buffer = new StringBuffer(256); + + ListIterator i = names.listIterator(names.size()); + while (i.hasPrevious()) { + LdapRdn rdn = (LdapRdn) i.previous(); + buffer.append(rdn.getLdapEncoded()); + + // add comma, except in last iteration + if (i.hasPrevious()) { + if (compact) { + buffer.append(","); + } + else { + buffer.append(", "); + + } + } + } + + return buffer.toString(); + } + + /** + * Builds a complete LDAP path, ldap and url encoded. Separates only with + * ",". + * + * @return the LDAP path, for use in an url. + */ + public String toUrl() { + StringBuffer buffer = new StringBuffer(256); + + for (int i = names.size() - 1; i >= 0; i--) { + LdapRdn n = (LdapRdn) names.get(i); + buffer.append(n.encodeUrl()); + if (i > 0) { + buffer.append(","); + } + } + return buffer.toString(); + } + + /** + * Determines if this DistinguishedName path contains another + * path. + * + * @param path the path to check. + * @return true if the supplied path is conained in this + * instance, false otherwise. + */ + public boolean contains(DistinguishedName path) { + + List shortlist = path.getNames(); + + // this path must be at least as long + if (getNames().size() < shortlist.size()) + return false; + + // must have names + if (shortlist.size() == 0) + return false; + + Iterator longiter = getNames().iterator(); + Iterator shortiter = shortlist.iterator(); + + LdapRdn longname = (LdapRdn) longiter.next(); + LdapRdn shortname = (LdapRdn) shortiter.next(); + + // find first match + while (!longname.equals(shortname) && longiter.hasNext()) { + longname = (LdapRdn) longiter.next(); + } + + // Done? + if (!shortiter.hasNext() && longname.equals(shortname)) + return true; + if (!longiter.hasNext()) + return false; + + // compare + while (longname.equals(shortname) && longiter.hasNext() && shortiter.hasNext()) { + longname = (LdapRdn) longiter.next(); + shortname = (LdapRdn) shortiter.next(); + } + + // Done + if (!shortiter.hasNext() && longname.equals(shortname)) + return true; + else + return false; + + } + + /** + * Add an LDAP path last in this DistinguishedName. E.g.: + * + *
+	 * DistinguishedName name1 = new DistinguishedName("c=SE, dc=jayway, dc=se");
+	 * DistinguishedName name2 = new DistinguishedName("ou=people");
+	 * name1.append(name2);
+	 * 
+ * + * will result in ou=people, c=SE, dc=jayway, dc=se + * + * @param path the path to append. + * @return this instance. + */ + public DistinguishedName append(DistinguishedName path) { + getNames().addAll(path.getNames()); + return this; + } + + /** + * Append a new {@link LdapRdn} using the supplied key and value. + * + * @param key the key of the {@link LdapRdn}. + * @param value the value of the {@link LdapRdn}. + * @return this instance. + */ + public DistinguishedName append(String key, String value) { + add(key, value); + return this; + } + + /** + * Add an LDAP path first in this DistinguishedName. E.g.: + * + *
+	 * DistinguishedName name1 = new DistinguishedName("ou=people");
+	 * DistinguishedName name2 = new DistinguishedName("c=SE, dc=jayway, dc=se");
+	 * name1.prepend(name2);
+	 * 
+ * + * will result in ou=people, c=SE, dc=jayway, dc=se + * + * @param path the path to prepend. + */ + public void prepend(DistinguishedName path) { + ListIterator i = path.getNames().listIterator(path.getNames().size()); + while (i.hasPrevious()) { + names.add(0, i.previous()); + } + } + + /** + * Remove the first part of this DistinguishedName. + * + * @return the removed entry. + */ + public LdapRdn removeFirst() { + return (LdapRdn) names.remove(0); + } + + /** + * Remove the supplied path from the beginning of this + * DistinguishedName if this instance starts with + * path. Useful for stripping base path suffix from a + * DistinguishedName. + * + * @param path the path to remove from the beginning of this instance. + */ + public void removeFirst(Name path) { + if (path != null && this.startsWith(path)) { + for (int i = 0; i < path.size(); i++) + this.removeFirst(); + } + } + + /** + * @see java.lang.Object#clone() + */ + public Object clone() { + + // just duplicate the list, the rdns are immutable. + LinkedList list = new LinkedList(getNames()); + + return new DistinguishedName(list); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + // A subclass with identical values should NOT be considered equal. + // EqualsBuilder in commons-lang cannot handle subclasses correctly. + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + + DistinguishedName name = (DistinguishedName) obj; + + // compare the lists + return getNames().equals(name.getNames()); + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return this.getClass().hashCode() ^ getNames().hashCode(); + } + + /** + * Compare this instance to another object. Note that the comparison is done + * in order of significance, so the most significant Rdn is compared first, + * then the second and so on. + * + * @see javax.naming.Name#compareTo(java.lang.Object) + */ + public int compareTo(Object obj) { + DistinguishedName that = (DistinguishedName) obj; + ListComparator comparator = new ListComparator(); + return comparator.compare(this.names, that.names); + } + + public int size() { + return names.size(); + } + + public boolean isEmpty() { + return names.size() == 0; + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#getAll() + */ + public Enumeration getAll() { + LinkedList strings = new LinkedList(); + for (Iterator iter = names.iterator(); iter.hasNext();) { + LdapRdn rdn = (LdapRdn) iter.next(); + strings.add(rdn.getLdapEncoded()); + } + + return Collections.enumeration(strings); + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#get(int) + */ + public String get(int index) { + LdapRdn rdn = (LdapRdn) names.get(index); + return rdn.getLdapEncoded(); + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#getPrefix(int) + */ + public Name getPrefix(int index) { + LinkedList newNames = new LinkedList(); + for (int i = 0; i < index; i++) { + newNames.add(names.get(i)); + } + + return new DistinguishedName(newNames); + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#getSuffix(int) + */ + public Name getSuffix(int index) { + if (index > names.size()) { + throw new ArrayIndexOutOfBoundsException(); + } + + LinkedList newNames = new LinkedList(); + for (int i = index; i < names.size(); i++) { + newNames.add(names.get(i)); + } + + return new DistinguishedName(newNames); + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#startsWith(javax.naming.Name) + */ + public boolean startsWith(Name name) { + if (name.size() == 0) { + return false; + } + + DistinguishedName start = null; + if (name instanceof DistinguishedName) { + start = (DistinguishedName) name; + } + else { + return false; + } + + if (start.size() > this.size()) { + return false; + } + + Iterator longiter = names.iterator(); + Iterator shortiter = start.getNames().iterator(); + + while (shortiter.hasNext()) { + Object longname = longiter.next(); + Object shortname = shortiter.next(); + + if (!longname.equals(shortname)) { + return false; + } + } + + // All names in shortiter matched. + return true; + } + + /** + * Determines if this DistinguishedName ends with a certian + * path. + * + * If the argument path is empty (no names in path) this method will return + * false. + * + * @param name The suffix to check for. + * + */ + public boolean endsWith(Name name) { + DistinguishedName path = null; + if (name instanceof DistinguishedName) { + path = (DistinguishedName) name; + } + else { + return false; + } + + List shortlist = path.getNames(); + + // this path must be at least as long + if (getNames().size() < shortlist.size()) + return false; + + // must have names + if (shortlist.size() == 0) + return false; + + ListIterator longiter = getNames().listIterator(getNames().size()); + ListIterator shortiter = shortlist.listIterator(shortlist.size()); + + while (shortiter.hasPrevious()) { + LdapRdn longname = (LdapRdn) longiter.previous(); + LdapRdn shortname = (LdapRdn) shortiter.previous(); + + if (!longname.equals(shortname)) + return false; + } + + // if short list ended, all were equal + return true; + + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#addAll(javax.naming.Name) + */ + public Name addAll(Name name) throws InvalidNameException { + return addAll(names.size(), name); + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#addAll(int, javax.naming.Name) + */ + public Name addAll(int arg0, Name name) throws InvalidNameException { + DistinguishedName distinguishedName = null; + try { + distinguishedName = (DistinguishedName) name; + } + catch (ClassCastException e) { + throw new InvalidNameException("Invalid name type"); + } + + names.addAll(arg0, distinguishedName.getNames()); + return this; + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#add(java.lang.String) + */ + public Name add(String string) throws InvalidNameException { + return add(names.size(), string); + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#add(int, java.lang.String) + */ + public Name add(int index, String string) throws InvalidNameException { + try { + names.add(index, new LdapRdn(string)); + } + catch (BadLdapGrammarException e) { + throw new InvalidNameException("Failed to parse rdn '" + string + "'"); + } + return this; + } + + /* + * (non-Javadoc) + * + * @see javax.naming.Name#remove(int) + */ + public Object remove(int arg0) throws InvalidNameException { + LdapRdn rdn = (LdapRdn) names.remove(arg0); + return rdn.getLdapEncoded(); + } + + /** + * Remove the last part of this DistinguishedName. + * + * @return the removed {@link LdapRdn}. + */ + public LdapRdn removeLast() { + return (LdapRdn) names.remove(names.size() - 1); + } + + /** + * Add a new {@link LdapRdn} using the supplied key and value. + * + * @param key the key of the {@link LdapRdn}. + * @param value the value of the {@link LdapRdn}. + */ + public void add(String key, String value) { + names.add(new LdapRdn(key, value)); + } + + /** + * Add the supplied {@link LdapRdn} last in the list of Rdns. + * + * @param rdn the {@link LdapRdn} to add. + */ + public void add(LdapRdn rdn) { + names.add(rdn); + } + + /** + * Add the supplied {@link LdapRdn} att the specified index. + * + * @param idx the index at which to add the LdapRdn. + * @param rdn the LdapRdn to add. + */ + public void add(int idx, LdapRdn rdn) { + names.add(idx, rdn); + } + + /** + * Return an unmodifialbe copy of this instance. Note that the individual + * Rdns will still be possible to modify. + * + * @return a copy of this instance backed by an immutable list. + * @since 1.2 + */ + public DistinguishedName immutableDistinguishedName() { + return new DistinguishedName(Collections.unmodifiableList(getNames())); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DistinguishedNameEditor.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DistinguishedNameEditor.java new file mode 100644 index 00000000..051a4452 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DistinguishedNameEditor.java @@ -0,0 +1,57 @@ +/* + * Copyright 2005-2007 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.core; + +import java.beans.PropertyEditorSupport; + +/** + * Property editor for use with {@link DistinguishedName} instances. The + * {@link #setAsText(String)} method sets the value as an immutable + * instance of a DistinguishedName. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class DistinguishedNameEditor extends PropertyEditorSupport { + + /* + * (non-Javadoc) + * @see java.beans.PropertyEditorSupport#setAsText(java.lang.String) + */ + public void setAsText(String text) throws IllegalArgumentException { + if (text == null) { + setValue(null); + } + else { + setValue(new DistinguishedName(text).immutableDistinguishedName()); + } + } + + /* + * (non-Javadoc) + * @see java.beans.PropertyEditorSupport#getAsText() + */ + public String getAsText() { + Object theValue = getValue(); + if (theValue == null) { + return null; + } + else { + return ((DistinguishedName) theValue).toString(); + } + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/DnParser.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/DnParser.java new file mode 100644 index 00000000..b4cbb81d --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/DnParser.java @@ -0,0 +1,39 @@ +/* + * Copyright 2005-2007 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.core; + +/** + * A parser for RFC2253-compliant Distinguished Names. + * + * @author Mattias Arthursson + * + */ +public interface DnParser { + /** + * Parse a full Distinguished Name. + * + * @return the DistinguishedName corresponding to the parsed + * stream. + */ + public DistinguishedName dn() throws ParseException; + + /** + * Parse a Relative Distinguished Name. + * + * @return the next rdn on the stream. + */ + public LdapRdn rdn() throws ParseException; +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapEncoder.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapEncoder.java new file mode 100644 index 00000000..8c54209f --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapEncoder.java @@ -0,0 +1,230 @@ +/* + * Copyright 2005-2007 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.core; + +import org.springframework.ldap.BadLdapGrammarException; + +/** + * Helper class to encode and decode ldap names and values. + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class LdapEncoder { + + private static String[] nameEscapeTable = new String[96]; + + private static String[] filterEscapeTable = new String['\\' + 1]; + + static { + + // Name encoding table ------------------------------------- + + // all below 0x20 (control chars) + for (char c = 0; c < ' '; c++) { + nameEscapeTable[c] = "\\" + toTwoCharHex(c); + } + + nameEscapeTable['#'] = "\\#"; + nameEscapeTable[','] = "\\,"; + nameEscapeTable[';'] = "\\;"; + nameEscapeTable['='] = "\\="; + nameEscapeTable['+'] = "\\+"; + nameEscapeTable['<'] = "\\<"; + nameEscapeTable['>'] = "\\>"; + nameEscapeTable['\"'] = "\\\""; + nameEscapeTable['\\'] = "\\\\"; + + // Filter encoding table ------------------------------------- + + // fill with char itself + for (char c = 0; c < filterEscapeTable.length; c++) { + filterEscapeTable[c] = String.valueOf(c); + } + + // escapes (RFC2254) + filterEscapeTable['*'] = "\\2a"; + filterEscapeTable['('] = "\\28"; + filterEscapeTable[')'] = "\\29"; + filterEscapeTable['\\'] = "\\5c"; + filterEscapeTable[0] = "\\00"; + + } + + /** + * All static methods - not to be instantiated. + */ + private LdapEncoder() { + } + + protected static String toTwoCharHex(char c) { + + String raw = Integer.toHexString(c).toUpperCase(); + + if (raw.length() > 1) + return raw; + else + return "0" + raw; + } + + /** + * Escape a value for use in a filter. + * + * @param value + * the value to escape. + * @return a properly escaped representation of the supplied value. + */ + public static String filterEncode(String value) { + + if (value == null) + return null; + + // make buffer roomy + StringBuffer encodedValue = new StringBuffer(value.length() * 2); + + int length = value.length(); + + for (int i = 0; i < length; i++) { + + char c = value.charAt(i); + + if (c < filterEscapeTable.length) { + encodedValue.append(filterEscapeTable[c]); + } else { + // default: add the char + encodedValue.append(c); + } + } + + return encodedValue.toString(); + } + + /** + * LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI! + * + *
Escapes:
' ' [space] - "\ " [if first or last]
'#' + * [hash] - "\#"
',' [comma] - "\,"
';' [semicolon] - "\;"
'= + * [equals] - "\="
'+' [plus] - "\+"
'<' [less than] - + * "\<"
'>' [greater than] - "\>"
'"' [double quote] - + * "\""
'\' [backslash] - "\\"
+ * + * @param value + * the value to escape. + * @return The escaped value. + */ + static public String nameEncode(String value) { + + if (value == null) + return null; + + // make buffer roomy + StringBuffer encodedValue = new StringBuffer(value.length() * 2); + + int length = value.length(); + int last = length - 1; + + for (int i = 0; i < length; i++) { + + char c = value.charAt(i); + + // space first or last + if (c == ' ' && (i == 0 || i == last)) { + encodedValue.append("\\ "); + continue; + } + + if (c < nameEscapeTable.length) { + // check in table for escapes + String esc = nameEscapeTable[c]; + + if (esc != null) { + encodedValue.append(esc); + continue; + } + } + + // default: add the char + encodedValue.append(c); + } + + return encodedValue.toString(); + + } + + /** + * Decodes a value. Converts escaped chars to ordinary chars. + * + * @param value + * Trimmed value, so no leading an trailing blanks, except an + * escaped space last. + * @return The decoded value as a string. + * @throws BadLdapGrammarException + */ + static public String nameDecode(String value) + throws BadLdapGrammarException { + + if (value == null) + return null; + + // make buffer same size + StringBuffer decoded = new StringBuffer(value.length()); + + int i = 0; + while (i < value.length()) { + char currentChar = value.charAt(i); + if (currentChar == '\\') { + if (value.length() <= i + 1) { + // Ending with a single backslash is not allowed + throw new BadLdapGrammarException( + "Unexpected end of value " + "unterminated '\\'"); + } else { + char nextChar = value.charAt(i + 1); + if (nextChar == ',' || nextChar == '=' || nextChar == '+' + || nextChar == '<' || nextChar == '>' + || nextChar == '#' || nextChar == ';' + || nextChar == '\\' || nextChar == '\"' + || nextChar == ' ') { + // Normal backslash escape + decoded.append(nextChar); + i += 2; + } else { + if (value.length() <= i + 2) { + throw new BadLdapGrammarException( + "Unexpected end of value " + + "expected special or hex, found '" + + nextChar + "'"); + } else { + // This should be a hex value + String hexString = "" + nextChar + + value.charAt(i + 2); + decoded.append((char) Integer.parseInt(hexString, + 16)); + i += 3; + } + } + } + } else { + // This character wasn't escaped - just append it + decoded.append(currentChar); + i++; + } + } + + return decoded.toString(); + + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapOperations.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapOperations.java new file mode 100644 index 00000000..04dce7d2 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapOperations.java @@ -0,0 +1,1575 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.List; + +import javax.naming.Binding; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.directory.Attributes; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.ContextNotEmptyException; +import org.springframework.ldap.NamingException; +import org.springframework.ldap.core.support.AbstractContextSource; +import org.springframework.ldap.support.LdapUtils; + +/** + * Interface that specifies a basic set of LDAP operations. Implemented by + * LdapTemplate, but it might be a useful option to use this interface in order + * to enhance testability. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public interface LdapOperations { + /** + * Perform a search using a particular {@link SearchExecutor} and context + * processor. Use this method only if especially needed - for the most cases + * there is an overloaded convenience method which calls this one with + * suitable argments. This method handles all the plumbing; getting a + * readonly context; looping through the NamingEnumeration + * and closing the context and enumeration. The actual search is delegated + * to the SearchExecutor and each found NameClassPair is + * passed to the CallbackHandler. Any encountered + * NamingException will be translated using + * {@link LdapUtils#convertLdapException(javax.naming.NamingException)}. + * + * @param se + * The SearchExecutor to use for performing the + * actual search. + * @param handler + * The NameClassPairCallbackHandler to which each + * found entry will be passed. + * @param processor + * DirContextProcessor for custom pre- and + * post-processing. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted as no entries being found. + */ + public void search(SearchExecutor se, NameClassPairCallbackHandler handler, + DirContextProcessor processor) throws NamingException; + + /** + * Perform a search using a particular {@link SearchExecutor}. Use this + * method only if especially needed - for the most cases there is an + * overloaded convenience method which calls this one with suitable + * argments. This method handles all the plumbing; getting a readonly + * context; looping through the NamingEnumeration and closing + * the context and enumeration. The actual search is delegated to the + * SearchExecutor and each found NameClassPair + * is passed to the CallbackHandler. Any encountered + * NamingException will be translated using the + * {@link LdapUtils#convertLdapException(javax.naming.NamingException)}. + * + * @param se + * The SearchExecutor to use for performing the + * actual search. + * @param handler + * The NameClassPairCallbackHandler to which each + * found entry will be passed. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted as no entries being found. + * @see #search(Name, String, AttributesMapper) + * @see #search(Name, String, ContextMapper) + */ + public void search(SearchExecutor se, NameClassPairCallbackHandler handler) + throws NamingException; + + /** + * Perform an operation (or series of operations) on a read-only context. + * This method handles the plumbing - getting a DirContext, + * translating any Exceptions and closing the context afterwards. This + * method is not intended for searches; use + * {@link #search(SearchExecutor, NameClassPairCallbackHandler)} or any of + * the overloaded search methods for this. + * + * @param ce + * The ContextExecutor to which the actual + * operation on the DirContext will be delegated. + * @return the result from the ContextExecutor's operation. + * @throws NamingException + * if the operation resulted in a NamingException. + * + * @see #search(SearchExecutor, NameClassPairCallbackHandler) + * @see #search(Name, String, AttributesMapper) + * @see #search(Name, String, ContextMapper) + */ + public Object executeReadOnly(ContextExecutor ce) throws NamingException; + + /** + * Perform an operation (or series of operations) on a read-write context. + * This method handles the plumbing - getting a DirContext, + * translating any exceptions and closing the context afterwards. This + * method is intended only for very particular cases, where there is no + * suitable method in this interface to use. + * + * @param ce + * The ContextExecutor to which the actual + * operation on the DirContext will be delegated. + * @return the result from the ContextExecutor's operation. + * @throws NamingException + * if the operation resulted in a NamingException. + * @see #bind(Name, Object, Attributes) + * @see #unbind(Name) + * @see #rebind(Name, Object, Attributes) + * @see #rename(Name, Name) + * @see #modifyAttributes(Name, ModificationItem[]) + */ + public Object executeReadWrite(ContextExecutor ce) throws NamingException; + + /** + * Search for all objects matching the supplied filter. Each + * SearchResult is supplied to the specified + * NameClassPairCallbackHandler. The + * SearchScope specified in the supplied + * SearchControls will be used in the search. Note that if + * you are using a ContextMapper, the returningObjFlag needs + * to be set to true in the SearchControls. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResult to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(Name base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler) throws NamingException; + + /** + * Search for all objects matching the supplied filter. See + * {@link #search(Name, String, SearchControls, NameClassPairCallbackHandler)} + * for details. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResult to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(String base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler) throws NamingException; + + /** + * Search for all objects matching the supplied filter. Each + * SearchResult is supplied to the specified + * NameClassPairCallbackHandler. The + * SearchScope specified in the supplied + * SearchControls will be used in the search. Note that if + * you are using a ContextMapper, the returningObjFlag needs + * to be set to true in the SearchControls. The given + * DirContextProcessor will be called before and after the + * search. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResult to. + * @param processor + * The DirContextProcessor to use before and after + * the search. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(Name base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified + * AttributesMapper. The SearchScope + * specified in the supplied SearchControls will be used in + * the search. The given DirContextProcessor will be called + * before and after the search. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @param processor + * The DirContextProcessor to use before and after + * the search. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, SearchControls controls, + AttributesMapper mapper, DirContextProcessor processor) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified + * AttributesMapper. The SearchScope + * specified in the supplied SearchControls will be used in + * the search. The given DirContextProcessor will be called + * before and after the search. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @param processor + * The DirContextProcessor to use before and after + * the search. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, SearchControls controls, + AttributesMapper mapper, DirContextProcessor processor) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified + * ContextMapper. The SearchScope specified + * in the supplied SearchControls will be used in the search. + * The given DirContextProcessor will be called before and + * after the search. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. If the + * returnObjFlag is not set in the SearchControls, + * this method will set it automatically, as this is required for + * the ContextMapper to work. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @param processor + * The DirContextProcessor to use before and after + * the search. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, SearchControls controls, + ContextMapper mapper, DirContextProcessor processor) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified + * ContextMapper. The SearchScope specified + * in the supplied SearchControls will be used in the search. + * The given DirContextProcessor will be called before and + * after the search. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. If the + * returnObjFlag is not set in the SearchControls, + * this method will set it automatically, as this is required for + * the ContextMapper to work. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @param processor + * The DirContextProcessor to use before and after + * the search. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, SearchControls controls, + ContextMapper mapper, DirContextProcessor processor) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. See + * {@link #search(Name, String, SearchControls, NameClassPairCallbackHandler, DirContextProcessor)} + * for details. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResults to. + * @param processor + * The DirContextProcessor to use before and after + * the search. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(String base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. Each + * SearchResult is supplied to the specified + * NameClassPairCallbackHandler. Use the specified values + * for search scope and return objects flag. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param returningObjFlag + * Whether the bound object should be returned in search results. + * Must be set to true if a + * ContextMapper is used. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResults to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(Name base, String filter, int searchScope, + boolean returningObjFlag, NameClassPairCallbackHandler handler) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. Each + * SearchResult is supplied to the specified + * NameClassPairCallbackHandler. Use the specified values + * for search scope and return objects flag. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param returningObjFlag + * Whether the bound object should be returned in search results. + * Must be set to true if a + * ContextMapper is used. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResults to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(String base, String filter, int searchScope, + boolean returningObjFlag, NameClassPairCallbackHandler handler) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. Each + * SearchResult is supplied to the specified + * NameClassPairCallbackHandler. The default Search scope (SearchControls.SUBTREE_SCOPE) + * will be used and the returnObjects flag will be set to false. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResults to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(Name base, String filter, + NameClassPairCallbackHandler handler) throws NamingException; + + /** + * Search for all objects matching the supplied filter. Each + * SearchResult is supplied to the specified + * NameClassPairCallbackHandler. The default Search scope (SearchControls.SUBTREE_SCOPE) + * will be used and the returnObjects flag will be set to false. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param handler + * The NameClassPairCallbackHandler to supply the + * SearchResults to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void search(String base, String filter, + NameClassPairCallbackHandler handler) throws NamingException; + + /** + * Search for all objects matching the supplied filter. Only return any + * attributes mathing the specified attribute names. The Attributes in each + * SearchResult is supplied to the specified + * AttributesMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param attrs + * The attributes to return, null means returning + * all attributes. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. Only return any + * attributes mathing the specified attribute names. The Attributes in each + * SearchResult is supplied to the specified + * AttributesMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param attrs + * The attributes to return, null means returning + * all attributes. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified + * AttributesMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, int searchScope, + AttributesMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified + * AttributesMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, int searchScope, + AttributesMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified + * AttributesMapper. The default search scope will be used. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, AttributesMapper mapper) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified + * AttributesMapper. The default search scope will be used. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * AttributesMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, AttributesMapper mapper) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The + * Object returned in each SearchResult is + * supplied to the specified ContextMapper. Only return the + * supplied attributes. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param attrs + * The attributes to return, null means all + * attributes. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The + * Object returned in each SearchResult is + * supplied to the specified ContextMapper. Only return the + * supplied attributes. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param attrs + * The attributes to return, null means all + * attributes. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The + * Object returned in each SearchResult is + * supplied to the specified ContextMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, int searchScope, + ContextMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The + * Object returned in each SearchResult is + * supplied to the specified ContextMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param searchScope + * The search scope to set in SearchControls. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, int searchScope, + ContextMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The + * Object returned in each SearchResult is + * supplied to the specified ContextMapper. The default + * search scope (SearchControls.SUBTREE_SCOPE) will be + * used. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, ContextMapper mapper) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The + * Object returned in each SearchResult is + * supplied to the specified ContextMapper. The default + * search scope (SearchControls.SUBTREE_SCOPE) will be + * used. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, ContextMapper mapper) + throws NamingException; + + /** + * Search for all objects matching the supplied filter. The + * Object returned in each SearchResult is + * supplied to the specified ContextMapper. The default + * search scope (SearchControls.SUBTREE_SCOPE) will be + * used. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, SearchControls controls, + ContextMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified + * ContextMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. If the + * returnObjFlag is not set in the SearchControls, + * this method will set it automatically, as this is required for + * the ContextMapper to work. + * @param mapper + * The ContextMapper to use for translating each + * entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, SearchControls controls, + ContextMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes + * returned in each SearchResult is supplied to the specified + * AttributesMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(String base, String filter, SearchControls controls, + AttributesMapper mapper) throws NamingException; + + /** + * Search for all objects matching the supplied filter. The Attributes + * returned in each SearchResult is supplied to the specified + * AttributesMapper. + * + * @param base + * The base DN where the search should begin. + * @param filter + * The filter to use in the search. + * @param controls + * The SearchControls to use in the search. + * @param mapper + * The AttributesMapper to use for translating + * each entry. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List search(Name base, String filter, SearchControls controls, + AttributesMapper mapper) throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Each resulting NameClassPair is + * supplied to the specified NameClassPairCallbackHandler. + * + * @param base + * The base DN where the list should be performed. + * @param handler + * The NameClassPairCallbackHandler to supply each + * {@link NameClassPair} to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void list(String base, NameClassPairCallbackHandler handler) + throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Each resulting NameClassPair is + * supplied to the specified NameClassPairCallbackHandler. + * + * @param base + * The base DN where the list should be performed. + * @param handler + * The NameClassPairCallbackHandler to supply each + * {@link NameClassPair} to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void list(Name base, NameClassPairCallbackHandler handler) + throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Pass all the found NameClassPair + * objects to the supplied NameClassPairMapper and return all + * the returned values as a List. + * + * @param base + * The base DN where the list should be performed. + * @param mapper + * The NameClassPairMapper to supply each + * {@link NameClassPair} to. + * @return a List containing the Objects returned from the + * Mapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List list(String base, NameClassPairMapper mapper) + throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Pass all the found NameClassPair + * objects to the supplied NameClassPairMapper and return all + * the returned values as a List. + * + * @param base + * The base DN where the list should be performed. + * @param mapper + * The NameClassPairMapper to supply each + * {@link NameClassPair} to. + * @return a List containing the Objects returned from the + * Mapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List list(Name base, NameClassPairMapper mapper) + throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. + * + * @param base + * The base DN where the list should be performed. + * @return a List containing the names of all the contexts bound to + * base. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List list(String base) throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. + * + * @param base + * The base DN where the list should be performed. + * @return a List containing the names of all the contexts bound to + * base. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List list(Name base) throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Each resulting Binding is supplied + * to the specified NameClassPairCallbackHandler. + * + * @param base + * The base DN where the list should be performed. + * @param handler + * The NameClassPairCallbackHandler to supply each + * {@link Binding} to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void listBindings(final String base, + NameClassPairCallbackHandler handler) throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Each resulting Binding is supplied + * to the specified NameClassPairCallbackHandler. + * + * @param base + * The base DN where the list should be performed. + * @param handler + * The NameClassPairCallbackHandler to supply each + * {@link Binding} to. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public void listBindings(final Name base, + NameClassPairCallbackHandler handler) throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Pass all the found Binding objects + * to the supplied NameClassPairMapper and return all the + * returned values as a List. + * + * @param base + * The base DN where the list should be performed. + * @param mapper + * The NameClassPairMapper to supply each + * {@link Binding} to. + * @return a List containing the Objects returned from the + * Mapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List listBindings(String base, NameClassPairMapper mapper) + throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. Pass all the found Binding objects + * to the supplied NameClassPairMapper and return all the + * returned values as a List. + * + * @param base + * The base DN where the list should be performed. + * @param mapper + * The NameClassPairMapper to supply each + * {@link Binding} to. + * @return a List containing the Objects returned from the + * Mapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List listBindings(Name base, NameClassPairMapper mapper) + throws NamingException; + + /** + * Perform a non-recursive listing of children of the given + * base. + * + * @param base + * The base DN where the list should be performed. + * @return a List containing the names of all the contexts + * bound to base. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List listBindings(final String base) throws NamingException; + + /** + * Perform a non-recursive listing of children of the given + * base. + * + * @param base + * The base DN where the list should be performed. + * @return a List containing the names of all the contexts + * bound to base. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List listBindings(final Name base) throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. The Object returned in each {@link Binding} is + * supplied to the specified ContextMapper. + * + * @param base + * The base DN where the list should be performed. + * @param mapper + * The ContextMapper to use for mapping the found + * object. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List listBindings(String base, ContextMapper mapper) + throws NamingException; + + /** + * Perform a non-recursive listing of the children of the given + * base. The Object returned in each {@link Binding} is + * supplied to the specified ContextMapper. + * + * @param base + * The base DN where the list should be performed. + * @param mapper + * The ContextMapper to use for mapping the found + * object. + * @return a List containing all entries received from the + * ContextMapper. + * @throws NamingException + * if any error occurs. Note that a + * NameNotFoundException will be ignored. Instead + * this is interpreted that no entries were found. + */ + public List listBindings(Name base, ContextMapper mapper) + throws NamingException; + + /** + * Lookup the supplied DN and return the found object. This will typically + * be a {@link DirContextAdapter}, unless the DirObjectFactory + * has been modified in the ContextSource. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object, typically a {@link DirContextAdapter} instance. + * @throws NamingException + * if any error occurs. + * @see #lookupContext(Name) + * @see AbstractContextSource#setDirObjectFactory(Class) + */ + public Object lookup(Name dn) throws NamingException; + + /** + * Lookup the supplied DN and return the found object. This will typically + * be a {@link DirContextAdapter}, unless the DirObjectFactory + * has been modified in the ContextSource. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object, typically a {@link DirContextAdapter} instance. + * @throws NamingException + * if any error occurs. + * @see #lookupContext(String) + * @see AbstractContextSource#setDirObjectFactory(Class) + */ + public Object lookup(String dn) throws NamingException; + + /** + * Convenience method to get the attributes of a specified DN and + * automatically pass them to an AttributesMapper. + * + * @param dn + * The distinguished name to find. + * @param mapper + * The AttributesMapper to use for mapping the + * found object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(Name dn, AttributesMapper mapper) + throws NamingException; + + /** + * Convenience method to get the attributes of a specified DN and + * automatically pass them to an AttributesMapper. + * + * @param dn + * The distinguished name to find. + * @param mapper + * The AttributesMapper to use for mapping the + * found object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(String dn, AttributesMapper mapper) + throws NamingException; + + /** + * Convenience method to lookup a specified DN and automatically pass the + * found object to a ContextMapper. + * + * @param dn + * The distinguished name to find. + * @param mapper + * The ContextMapper to use for mapping the found + * object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(Name dn, ContextMapper mapper) throws NamingException; + + /** + * Convenience method to lookup a specified DN and automatically pass the + * found object to a ContextMapper. + * + * @param dn + * The distinguished name to find. + * @param mapper + * The ContextMapper to use for mapping the found + * object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(String dn, ContextMapper mapper) + throws NamingException; + + /** + * Convenience method to get the specified attributes of a specified DN and + * automatically pass them to an AttributesMapper. + * + * @param dn + * The distinguished name to find. + * @param attributes + * The names of the attributes to pass to the mapper. + * @param mapper + * The AttributesMapper to use for mapping the + * found object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(Name dn, String[] attributes, AttributesMapper mapper) + throws NamingException; + + /** + * Convenience method to get the specified attributes of a specified DN and + * automatically pass them to an AttributesMapper. + * + * @param dn + * The distinguished name to find. + * @param attributes + * The names of the attributes to pass to the mapper. + * @param mapper + * The AttributesMapper to use for mapping the + * found object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(String dn, String[] attributes, AttributesMapper mapper) + throws NamingException; + + /** + * Convenience method to get the specified attributes of a specified DN and + * automatically pass them to a ContextMapper. + * + * @param dn + * The distinguished name to find. + * @param attributes + * The names of the attributes to pass to the mapper. + * @param mapper + * The ContextMapper to use for mapping the found + * object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(Name dn, String[] attributes, ContextMapper mapper) + throws NamingException; + + /** + * Convenience method to get the specified attributes of a specified DN and + * automatically pass them to a ContextMapper. + * + * @param dn + * The distinguished name to find. + * @param attributes + * The names of the attributes to pass to the mapper. + * @param mapper + * The ContextMapper to use for mapping the found + * object. + * @return the object returned from the mapper. + * @throws NamingException + * if any error occurs. + */ + public Object lookup(String dn, String[] attributes, ContextMapper mapper) + throws NamingException; + + /** + * Modify an entry in the LDAP tree using the supplied + * ModificationItems. + * + * @param dn + * The distinguished name of the node to modify. + * @param mods + * The modifications to perform. + * @throws NamingException + * if any error occurs. + * @see #modifyAttributes(DirContextOperations) + */ + public void modifyAttributes(Name dn, ModificationItem[] mods) + throws NamingException; + + /** + * Modify an entry in the LDAP tree using the supplied + * ModificationItems. + * + * @param dn + * The distinguished name of the node to modify. + * @param mods + * The modifications to perform. + * @throws NamingException + * if any error occurs. + * @see #modifyAttributes(DirContextOperations) + */ + public void modifyAttributes(String dn, ModificationItem[] mods) + throws NamingException; + + /** + * Create an entry in the LDAP tree. The attributes used to create the entry + * are either retrieved from the obj parameter or the + * attributes parameter (or both). One of these parameters + * may be null but not both. + * + * @param dn + * The distinguished name to bind the object and attributes to. + * @param obj + * The object to bind, may be null. Typically a + * DirContext implementation. + * @param attributes + * The attributes to bind, may be null. + * @throws NamingException + * if any error occurs. + * @see DirContextAdapter + */ + public void bind(Name dn, Object obj, Attributes attributes) + throws NamingException; + + /** + * Create an entry in the LDAP tree. The attributes used to create the entry + * are either retrieved from the obj parameter or the + * attributes parameter (or both). One of these parameters + * may be null but not both. + * + * @param dn + * The distinguished name to bind the object and attributes to. + * @param obj + * The object to bind, may be null. Typically a + * DirContext implementation. + * @param attributes + * The attributes to bind, may be null. + * @throws NamingException + * if any error occurs. + * @see DirContextAdapter + */ + public void bind(String dn, Object obj, Attributes attributes) + throws NamingException; + + /** + * Remove an entry from the LDAP tree. The entry must not have any children - + * if you suspect that the entry might have descendants, use + * {@link #unbind(Name, boolean)} in stead. + * + * @param dn + * The distinguished name of the entry to remove. + * @throws NamingException + * if any error occurs. + */ + public void unbind(Name dn) throws NamingException; + + /** + * Remove an entry from the LDAP tree. The entry must not have any children - + * if you suspect that the entry might have descendants, use + * {@link #unbind(Name, boolean)} in stead. + * + * @param dn + * The distinguished name to unbind. + * @throws NamingException + * if any error occurs. + */ + public void unbind(String dn) throws NamingException; + + /** + * Remove an entry from the LDAP tree, optionally removing all descendants + * in the process. + * + * @param dn + * The distinguished name to unbind. + * @param recursive + * Whether to unbind all subcontexts as well. If this parameter + * is false and the entry has children, the + * operation will fail. + * @throws NamingException + * if any error occurs. + */ + public void unbind(Name dn, boolean recursive) throws NamingException; + + /** + * Remove an entry from the LDAP tree, optionally removing all descendants + * in the process. + * + * @param dn + * The distinguished name to unbind. + * @param recursive + * Whether to unbind all subcontexts as well. If this parameter + * is false and the entry has children, the + * operation will fail. + * @throws NamingException + * if any error occurs. + */ + public void unbind(String dn, boolean recursive) throws NamingException; + + /** + * Remove an entry and replace it with a new one. The attributes used to + * create the entry are either retrieved from the obj + * parameter or the attributes parameter (or both). One of + * these parameters may be null but not both. This method + * assumes that the specified context already exists - if not it will fail. + * + * @param dn + * The distinguished name to rebind. + * @param obj + * The object to bind to the DN, may be null. + * Typically a DirContext implementation. + * @param attributes + * The attributes to bind, may be null. + * @throws NamingException + * if any error occurs. + * @see DirContextAdapter + */ + public void rebind(Name dn, Object obj, Attributes attributes) + throws NamingException; + + /** + * Remove an entry and replace it with a new one. The attributes used to + * create the entry are either retrieved from the obj + * parameter or the attributes parameter (or both). One of + * these parameters may be null but not both. This method + * assumes that the specified context already exists - if not it will fail. + * + * @param dn + * The distinguished name to rebind. + * @param obj + * The object to bind to the DN, may be null. + * Typically a DirContext implementation. + * @param attributes + * The attributes to bind, may be null. + * @throws NamingException + * if any error occurs. + * @see DirContextAdapter + */ + public void rebind(String dn, Object obj, Attributes attributes) + throws NamingException; + + /** + * Move an entry in the LDAP tree to a new location. + * + * @param oldDn + * The distinguished name of the entry to move; may not be + * null or empty. + * @param newDn + * The distinguished name where the entry should be moved; may + * not be null or empty. + * @throws ContextNotEmptyException + * if newDn is already bound + * @throws NamingException + * if any other error occurs. + */ + public void rename(final Name oldDn, final Name newDn) + throws NamingException; + + /** + * Move an entry in the LDAP tree to a new location. + * + * @param oldDn + * The distinguished name of the entry to move; may not be + * null or empty. + * @param newDn + * The distinguished name where the entry should be moved; may + * not be null or empty. + * @throws ContextNotEmptyException + * if newDn is already bound + * @throws NamingException + * if any other error occurs. + */ + public void rename(final String oldDn, final String newDn) + throws NamingException; + + /** + * Convenience method to lookup the supplied DN and automatically cast it to + * {@link DirContextOperations}. + * + * @param dn + * The distinguished name of the object to find. + * @return The found object, cast to {@link DirContextOperations}. + * @throws ClassCastException + * if an alternative DirObjectFactory has been + * registered with the ContextSource, causing + * the actual class of the returned object to be something else + * than {@link DirContextOperations}. + * @throws NamingException + * if any other error occurs. + * @see #lookup(Name) + * @see #modifyAttributes(DirContextOperations) + * @since 1.2 + */ + public DirContextOperations lookupContext(Name dn) throws NamingException, + ClassCastException; + + /** + * Convenience method to lookup the supplied DN and automatically cast it to + * {@link DirContextOperations}. + * + * @param dn + * The distinguished name of the object to find. + * @return The found object, cast to {@link DirContextOperations}. + * @throws ClassCastException + * if an alternative DirObjectFactory has been + * registered with the ContextSource, causing + * the actual class of the returned object to be something else + * than {@link DirContextOperations}. + * @throws NamingException + * if any other error occurs. + * @see #lookup(String) + * @see #modifyAttributes(DirContextOperations) + * @since 1.2 + */ + public DirContextOperations lookupContext(String dn) + throws NamingException, ClassCastException; + + /** + * Modify the attributes of the entry referenced by the supplied + * {@link DirContextOperations} instance. The DN to update will be the DN of + * the DirContextOperationsinstance, and the + * ModificationItem array is retrieved from the + * DirContextOperations instance using a call to + * {@link AttributeModificationsAware#getModificationItems()}. NB: + * The supplied instance needs to have been properly initialized; this means + * that if it hasn't been received from a lookup operation, + * its DN needs to be initialized and it must have been put in update mode ({@link DirContextAdapter#setUpdateMode(boolean)}). + *

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

+     * public void update(Person person) {
+     *     DirContextOperations ctx = ldapOperations.lookupContext(person.getDn());
+     * 
+     *     ctx.setAttributeValue("description", person.getDescription());
+     *     ctx.setAttributeValue("telephoneNumber", person.getPhone());
+     *     // More modifications here
+     * 
+     *     ldapOperations.modifyAttributes(ctx);
+     * }
+     * 
+ * + * @param ctx + * the DirContextOperations instance to use in the update. + * @throws IllegalStateException + * if the supplied instance is not in update mode or has not + * been properly initialized. + * @throws NamingException + * if any other error occurs. + * @since 1.2 + * @see #lookupContext(Name) + * @see DirContextAdapter + */ + public void modifyAttributes(DirContextOperations ctx) + throws IllegalStateException, NamingException; +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapRdn.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapRdn.java new file mode 100644 index 00000000..4186ce09 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapRdn.java @@ -0,0 +1,258 @@ +/* + * Copyright 2005-2007 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.core; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.support.ListComparator; + +/** + * Datatype for a LDAP name, a part of a path. + * + * The name: uid=adam.skogman Key: uid Value: adam.skogman + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class LdapRdn implements Serializable, Comparable { + private static final long serialVersionUID = 5681397547245228750L; + + private List components = new LinkedList(); + + /** + * Default constructor. Create an empty, uninitialized LdapRdn. + */ + public LdapRdn() { + } + + /** + * Parse the supplied string and construct this instance accordingly. + * + * @param string + * the string to parse. + */ + public LdapRdn(String string) { + DnParser parser = DefaultDnParserFactory.createDnParser(string); + LdapRdn rdn; + try { + rdn = parser.rdn(); + } catch (ParseException e) { + throw new BadLdapGrammarException("Failed to parse Rdn", e); + } catch (TokenMgrError e) { + throw new BadLdapGrammarException("Failed to parse Rdn", e); + } + this.components = rdn.components; + } + + /** + * Construct an LdapRdn using the supplied key and value. + * + * @param key + * the attribute name. + * @param value + * the attribute value. + */ + public LdapRdn(String key, String value) { + components.add(new LdapRdnComponent(key, value)); + } + + /** + * Add an LdapRdnComponent to this LdapRdn. + * + * @param rdnComponent + * the LdapRdnComponent to add.s + */ + public void addComponent(LdapRdnComponent rdnComponent) { + components.add(rdnComponent); + } + + /** + * Gets all components in this LdapRdn. + * + * @return the List of all LdapRdnComponents composing this LdapRdn. + */ + public List getComponents() { + return components; + } + + /** + * Gets the first LdapRdnComponent of this LdapRdn. + * + * @return The first LdapRdnComponent of this LdapRdn. + * @throws IndexOutOfBoundsException + * if there are no components in this Rdn. + */ + public LdapRdnComponent getComponent() { + return (LdapRdnComponent) components.get(0); + } + + /** + * Get the LdapRdnComponent at index idx. + * + * @param idx + * the 0-based index of the component to get. + * @return the LdapRdnComponent at index idx. + * @throws IndexOutOfBoundsException + * if there are no components in this Rdn. + */ + public LdapRdnComponent getComponent(int idx) { + return (LdapRdnComponent) components.get(idx); + } + + /** + * Get a properly rfc2253-encoded String representation of this LdapRdn. + * + * @return an escaped String corresponding to this LdapRdn. + * @throws IndexOutOfBoundsException + * if there are no components in this Rdn. + */ + public String getLdapEncoded() { + if (components.size() == 0) { + throw new IndexOutOfBoundsException("No components in Rdn."); + } + StringBuffer sb = new StringBuffer(100); + for (Iterator iter = components.iterator(); iter.hasNext();) { + LdapRdnComponent component = (LdapRdnComponent) iter.next(); + sb.append(component.encodeLdap()); + if (iter.hasNext()) { + sb.append("+"); + } + } + + return sb.toString(); + } + + /** + * Get a String representation of this LdapRdn for use in urls. + * + * @return a String representation of this LdapRdn for use in urls. + */ + public String encodeUrl() { + StringBuffer sb = new StringBuffer(100); + for (Iterator iter = components.iterator(); iter.hasNext();) { + LdapRdnComponent component = (LdapRdnComponent) iter.next(); + sb.append(component.encodeUrl()); + if (iter.hasNext()) { + sb.append("+"); + } + } + + return sb.toString(); + } + + /** + * Compare this LdapRdn to another object. + * + * @param obj + * the object to compare to. + * @throws ClassCastException + * if the supplied object is not an LdapRdn instance. + */ + public int compareTo(Object obj) { + LdapRdn that = (LdapRdn) obj; + Comparator comparator = new ListComparator(); + return comparator.compare(this.components, that.components); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + + LdapRdn that = (LdapRdn) obj; + return this.getComponents().equals(that.getComponents()); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return this.getClass().hashCode() ^ getComponents().hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + return getLdapEncoded(); + } + + /** + * Get the value of this LdapRdn. Note that if this Rdn is multi-value the + * first value will be returned. E.g. for the Rdn + * cn=john doe+sn=doe, the return value would be + * john doe. + * + * @return the (first) value of this LdapRdn. + * @throws IndexOutOfBoundsException + * if there are no components in this Rdn. + */ + public String getValue() { + return getComponent().getValue(); + } + + /** + * Get the key of this LdapRdn. Note that if this Rdn is multi-value the + * first key will be returned. E.g. for the Rdn + * cn=john doe+sn=doe, the return value would be + * cn. + * + * @return the (first) key of this LdapRdn. + * @throws IndexOutOfBoundsException + * if there are no components in this Rdn. + */ + public String getKey() { + return getComponent().getKey(); + } + + /** + * Get the value of the LdapComponent with the specified key (Attribute + * name). + * + * @param key + * the key + * @return the value. + * @throws IllegalArgumentException + * if there is no component with the specified key. + */ + public String getValue(String key) { + for (Iterator iter = components.iterator(); iter.hasNext();) { + LdapRdnComponent component = (LdapRdnComponent) iter.next(); + if (StringUtils.equals(component.getKey(), key)) { + return component.getValue(); + } + } + + throw new IllegalArgumentException("No RdnComponent with the key " + + key); + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java new file mode 100644 index 00000000..85cb324c --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java @@ -0,0 +1,195 @@ +/* + * Copyright 2005-2007 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.core; + +import java.io.Serializable; +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; + +/** + * Represents part of an LdapRdn. As specified in RFC2253 an LdapRdn may be + * composed of several attributes, separated by "+". An + * LdapRdnComponent represents one of these attributes. + * + * @author Mattias Arthursson + * + */ +public class LdapRdnComponent implements Comparable, Serializable { + private static final long serialVersionUID = -3296747972616243038L; + + public static final boolean DONT_DECODE_VALUE = false; + + private String key; + + private String value; + + /** + * Constructs an LdapRdnComponent without decoding the value. + * + * @param key the Attribute name. + * @param value the Attribute value. + */ + public LdapRdnComponent(String key, String value) { + this(key, value, DONT_DECODE_VALUE); + } + + /** + * Constructs an LdapRdnComponent, optionally decoding the value. + * + * @param key the Atttribute name. + * @param value the Attribute value. + * @param decodeValue if true the value is decoded (typically + * used when a DN is parsed from a String), otherwise the value is used as + * specified. + */ + public LdapRdnComponent(String key, String value, boolean decodeValue) { + Validate.notEmpty(key, "Key must not be empty"); + Validate.notEmpty(value, "Value must not be empty"); + + this.key = StringUtils.lowerCase(key); + if (decodeValue) { + this.value = LdapEncoder.nameDecode(value); + } + else { + this.value = value; + } + } + + /** + * Get the key (Attribute name) of this component. + * + * @return the key. + */ + public String getKey() { + return key; + } + + /** + * Set the key (Attribute name) of this component. + * + * @param key the key. + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Get the (Attribute) value of this component. + * + * @return the value. + */ + public String getValue() { + return value; + } + + /** + * Set the (Attribute) value of this component. + * + * @param value the value. + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Encode key and value to ldap. + * + * @return Properly ldap escaped rdn. + */ + protected String encodeLdap() { + StringBuffer buff = new StringBuffer(key.length() + value.length() * 2); + + buff.append(key); + buff.append('='); + buff.append(LdapEncoder.nameEncode(value)); + + return buff.toString(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + return getLdapEncoded(); + } + + /** + * @return The LdapRdn as a string where the value is LDAP-encoded. + */ + public String getLdapEncoded() { + return encodeLdap(); + } + + /** + * Get a String representation of this instance for use in URLs. + * + * @return a properly URL encoded representation of this instancs. + */ + public String encodeUrl() { + // Use the URI class to properly URL encode the value. + try { + URI valueUri = new URI(null, null, value, null); + return key + "=" + valueUri.toString(); + } + catch (URISyntaxException e) { + // This should really never happen... + return key + "=" + "value"; + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj != null && obj.getClass() == LdapRdnComponent.class) { + LdapRdnComponent that = (LdapRdnComponent) obj; + return StringUtils.equalsIgnoreCase(this.key, that.key) + && StringUtils.equalsIgnoreCase(this.value, that.value); + + } + else { + return false; + } + } + + /** + * Compare this instance to the supplied object. + * + * @param obj the object to compare to. + * @throws ClassCastException if the object is not possible to cast to an + * LdapRdnComponent. + */ + public int compareTo(Object obj) { + LdapRdnComponent that = (LdapRdnComponent) obj; + return this.toString().compareTo(that.toString()); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java new file mode 100644 index 00000000..6769c249 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java @@ -0,0 +1,1339 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.List; + +import javax.naming.Binding; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NameNotFoundException; +import javax.naming.NamingEnumeration; +import javax.naming.PartialResultException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SearchControls; + +import org.apache.commons.lang.Validate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.ldap.NamingException; +import org.springframework.ldap.support.LdapUtils; + +/** + * Executes core LDAP functionality and helps to avoid common errors, relieving + * the user of the burden of looking up contexts, looping through + * NamingEnumerations and closing contexts. + *

+ * Note for Active Directory (AD) users: AD servers are apparently + * unable to handle referrals automatically, which causes a + * PartialResultException to be thrown whenever a referral is + * encountered in a search. To avoid this, set the + * ignorePartialResultException property to true. + * There is currently no way of manually handling these referrals in the form of + * ReferralException, i.e. either you get the exception (and + * your results are lost) or all referrals are ignored (if the server is unable + * to handle them properly. Neither is there any simple way to get notified that + * a PartialResultException has been ignored (other than in the + * log). + * + * @see org.springframework.ldap.core.ContextSource + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapTemplate implements LdapOperations, InitializingBean { + + private static final Log log = LogFactory.getLog(LdapTemplate.class); + + private static final int DEFAULT_SEARCH_SCOPE = SearchControls.SUBTREE_SCOPE; + + private static final boolean DONT_RETURN_OBJ_FLAG = false; + + private static final boolean RETURN_OBJ_FLAG = true; + + private static final String[] ALL_ATTRIBUTES = null; + + private ContextSource contextSource; + + private boolean ignorePartialResultException = false; + + /** + * Constructor for bean usage. + */ + public LdapTemplate() { + } + + /** + * Constructor to setup instance directly. + * + * @param contextSource + * the ContextSource to use. + */ + public LdapTemplate(ContextSource contextSource) { + this.contextSource = contextSource; + } + + /** + * Set the ContextSource. Call this method when the default constructor has + * been used. + * + * @param contextSource + * the ContextSource. + */ + public void setContextSource(ContextSource contextSource) { + this.contextSource = contextSource; + } + + /** + * Get the ContextSource. + * + * @return the ContextSource. + */ + public ContextSource getContextSource() { + return contextSource; + } + + /** + * Specify whether PartialResultException should be ignored + * in searches. AD servers typically have a problem with referrals. Normally + * a referral should be followed automatically, but this does not seem to + * work with AD servers. The problem manifests itself with a a + * PartialResultException being thrown when a referral is + * encountered by the server. Setting this property to true + * presents a workaround to this problem by causing + * PartialResultException to be ignored, so that the search + * method returns normally. Default value of this parameter is + * false. + * + * @param ignore + * true if PartialResultException + * should be ignored in searches, false otherwise. + * Default is false. + */ + public void setIgnorePartialResultException(boolean ignore) { + this.ignorePartialResultException = ignore; + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, boolean, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void search(Name base, String filter, int searchScope, + boolean returningObjFlag, NameClassPairCallbackHandler handler) { + + search(base, filter, getDefaultSearchControls(searchScope, + returningObjFlag, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, int, boolean, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, int searchScope, + boolean returningObjFlag, NameClassPairCallbackHandler handler) { + + search(base, filter, getDefaultSearchControls(searchScope, + returningObjFlag, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void search(final Name base, final String filter, + final SearchControls controls, NameClassPairCallbackHandler handler) { + + // Create a SearchExecutor to perform the search. + SearchExecutor se = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void search(final String base, final String filter, + final SearchControls controls, NameClassPairCallbackHandler handler) { + + // Create a SearchExecutor to perform the search. + SearchExecutor se = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.NameClassPairCallbackHandler, + * org.springframework.ldap.core.DirContextProcessor) + */ + public void search(final Name base, final String filter, + final SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor) { + + // Create a SearchExecutor to perform the search. + SearchExecutor se = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler, processor); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.NameClassPairCallbackHandler, + * org.springframework.ldap.core.DirContextProcessor) + */ + public void search(final String base, final String filter, + final SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor) { + + // Create a SearchExecutor to perform the search. + SearchExecutor se = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler, processor); + } + + /** + * Perform a search operation, such as a search(), list() or listBindings(). + * This method handles all the plumbing; getting a readonly context; looping + * through the NamingEnumeration and closing the context and enumeration. It + * also calls the supplied DirContextProcessor before and after the search, + * respectively. This enables custom pre-processing and post-processing, + * like for example when handling paged results or other search controls. + *

+ * The actual list is delegated to the {@link SearchExecutor} and each + * {@link NameClassPair} (this might be a NameClassPair or a subclass + * thereof) is passed to the CallbackHandler. Any encountered + * NamingException will be translated using the NamingExceptionTranslator. + * + * @param se + * the SearchExecutor to use for performing the actual list. + * @param handler + * the NameClassPairCallbackHandler to which each found entry + * will be passed. + * @param processor + * DirContextProcessor for custom pre- and post-processing. May + * be null if no custom processing should take + * place. + * @throws NamingException + * if any error occurs. Note that a NameNotFoundException will + * be ignored. Instead this is interpreted that no entries were + * found. + */ + public void search(SearchExecutor se, NameClassPairCallbackHandler handler, + DirContextProcessor processor) { + DirContext ctx = contextSource.getReadOnlyContext(); + + NamingEnumeration results = null; + RuntimeException ex = null; + try { + processor.preProcess(ctx); + results = se.executeSearch(ctx); + + while (results.hasMore()) { + NameClassPair result = (NameClassPair) results.next(); + handler.handleNameClassPair(result); + } + } catch (NameNotFoundException e) { + // The base context was not found, which basically means + // that the search did not return any results. Just clean up and + // exit. + // Note that this may present problems if a DirContextProcessor was + // supplied - there's no guarantee that the postProcess() operation + // will go well after a NamingException has been thrown. It is + // however quite possible that information will be available for + // retrieval either way. + } catch (PartialResultException e) { + // Workaround for AD servers not handling referrals correctly. + if (ignorePartialResultException) { + log.debug("PartialResultException encountered and ignored", e); + } else { + ex = LdapUtils.convertLdapException(e); + } + } catch (javax.naming.NamingException e) { + ex = LdapUtils.convertLdapException(e); + } finally { + try { + processor.postProcess(ctx); + } catch (javax.naming.NamingException e) { + if (ex == null) { + ex = LdapUtils.convertLdapException(e); + } else { + // We already had an exception from above and should ignore + // this one. + log.debug("Ignoring Exception from postProcess, " + + "main exception thrown instead", e); + } + } + closeContextAndNamingEnumeration(ctx, results); + // If we got an exception it should be thrown. + if (ex != null) { + throw ex; + } + } + } + + /** + * Perform a search operation, such as a search(), list() or listBindings(). + * This method handles all the plumbing; getting a readonly context; looping + * through the NamingEnumeration and closing the context and enumeration. + *

+ * The actual list is delegated to the {@link SearchExecutor} and each + * {@link NameClassPair} (this might be a NameClassPair or a subclass + * thereof) is passed to the CallbackHandler. Any encountered + * NamingException will be translated using the NamingExceptionTranslator. + * + * @param se + * the SearchExecutor to use for performing the actual list. + * @param handler + * the NameClassPairCallbackHandler to which each found entry + * will be passed. + * @throws NamingException + * if any error occurs. Note that a NameNotFoundException will + * be ignored. Instead this is interpreted that no entries were + * found. + */ + public void search(SearchExecutor se, NameClassPairCallbackHandler handler) { + search(se, handler, new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void search(Name base, String filter, + NameClassPairCallbackHandler handler) { + + SearchControls controls = getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES); + if (handler instanceof ContextMapperCallbackHandler) { + assureReturnObjFlagSet(controls); + } + search(base, filter, controls, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, + NameClassPairCallbackHandler handler) { + + SearchControls controls = getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES); + if (handler instanceof ContextMapperCallbackHandler) { + assureReturnObjFlagSet(controls); + } + search(base, filter, controls, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.core.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.core.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, + * org.springframework.ldap.core.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + AttributesMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, int, + * org.springframework.ldap.core.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + AttributesMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.core.AttributesMapper) + */ + public List search(Name base, String filter, AttributesMapper mapper) { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.core.AttributesMapper) + */ + public List search(String base, String filter, AttributesMapper mapper) { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.core.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.core.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, org.springframework.ldap.core.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + ContextMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, int, org.springframework.ldap.core.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + ContextMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.core.ContextMapper) + */ + public List search(Name base, String filter, ContextMapper mapper) { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.core.ContextMapper) + */ + public List search(String base, String filter, ContextMapper mapper) { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.ContextMapper) + */ + public List search(String base, String filter, SearchControls controls, + ContextMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.ContextMapper) + */ + public List search(Name base, String filter, SearchControls controls, + ContextMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.AttributesMapper) + */ + public List search(Name base, String filter, SearchControls controls, + AttributesMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.AttributesMapper) + */ + public List search(String base, String filter, SearchControls controls, + AttributesMapper mapper) { + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.AttributesMapper, + * org.springframework.ldap.core.DirContextProcessor) + */ + public List search(String base, String filter, SearchControls controls, + AttributesMapper mapper, DirContextProcessor processor) { + AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler( + mapper); + search(base, filter, controls, handler, processor); + + return handler.getList(); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.AttributesMapper, + * org.springframework.ldap.core.DirContextProcessor) + */ + public List search(Name base, String filter, SearchControls controls, + AttributesMapper mapper, DirContextProcessor processor) { + AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler( + mapper); + search(base, filter, controls, handler, processor); + + return handler.getList(); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.ContextMapper, + * org.springframework.ldap.core.DirContextProcessor) + */ + public List search(String base, String filter, SearchControls controls, + ContextMapper mapper, DirContextProcessor processor) { + assureReturnObjFlagSet(controls); + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + search(base, filter, controls, handler, processor); + + return handler.getList(); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.ContextMapper, + * org.springframework.ldap.core.DirContextProcessor) + */ + public List search(Name base, String filter, SearchControls controls, + ContextMapper mapper, DirContextProcessor processor) { + assureReturnObjFlagSet(controls); + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + search(base, filter, controls, handler, processor); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#list(java.lang.String, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void list(final String base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void list(final Name base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#list(java.lang.String, + * org.springframework.ldap.core.NameClassPairMapper) + */ + public List list(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.core.NameClassPairMapper) + */ + public List list(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#list(javax.naming.Name) + */ + public List list(final Name base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#list(java.lang.String) + */ + public List list(final String base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void listBindings(final String base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.core.NameClassPairCallbackHandler) + */ + public void listBindings(final Name base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws javax.naming.NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.core.NameClassPairMapper) + */ + public List listBindings(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.core.NameClassPairMapper) + */ + public List listBindings(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String) + */ + public List listBindings(final String base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(javax.naming.Name) + */ + public List listBindings(final Name base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.core.ContextMapper) + */ + public List listBindings(String base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.core.ContextMapper) + */ + public List listBindings(Name base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#executeReadOnly(org.springframework.ldap.core.DirContextProcessor) + */ + public Object executeReadOnly(ContextExecutor ce) { + DirContext ctx = contextSource.getReadOnlyContext(); + return executeWithContext(ce, ctx); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#executeReadWrite(org.springframework.ldap.core.DirContextProcessor) + */ + public Object executeReadWrite(ContextExecutor ce) { + DirContext ctx = contextSource.getReadWriteContext(); + return executeWithContext(ce, ctx); + } + + private Object executeWithContext(ContextExecutor ce, DirContext ctx) { + try { + return ce.executeWithContext(ctx); + } catch (javax.naming.NamingException e) { + throw LdapUtils.convertLdapException(e); + } finally { + closeContext(ctx); + } + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name) + */ + public Object lookup(final Name dn) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(java.lang.String) + */ + public Object lookup(final String dn) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.core.AttributesMapper) + */ + public Object lookup(final Name dn, final AttributesMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.core.AttributesMapper) + */ + public Object lookup(final String dn, final AttributesMapper mapper) { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.core.ContextMapper) + */ + public Object lookup(final Name dn, final ContextMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.core.ContextMapper) + */ + public Object lookup(final String dn, final ContextMapper mapper) { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name, + * java.lang.String[], org.springframework.ldap.core.AttributesMapper) + */ + public Object lookup(final Name dn, final String[] attributes, + final AttributesMapper mapper) { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + return mapper.mapFromAttributes(filteredAttributes); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(java.lang.String, + * java.lang.String[], org.springframework.ldap.core.AttributesMapper) + */ + public Object lookup(final String dn, final String[] attributes, + final AttributesMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + return mapper.mapFromAttributes(filteredAttributes); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name, + * java.lang.String[], org.springframework.ldap.core.ContextMapper) + */ + public Object lookup(final Name dn, final String[] attributes, + final ContextMapper mapper) { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + DirContextAdapter contextAdapter = new DirContextAdapter( + filteredAttributes, dn); + return mapper.mapFromContext(contextAdapter); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#lookup(java.lang.String, + * java.lang.String[], org.springframework.ldap.core.ContextMapper) + */ + public Object lookup(final String dn, final String[] attributes, + final ContextMapper mapper) { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + DistinguishedName name = new DistinguishedName(dn); + DirContextAdapter contextAdapter = new DirContextAdapter( + filteredAttributes, name); + return mapper.mapFromContext(contextAdapter); + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#modifyAttributes(javax.naming.Name, + * javax.naming.directory.ModificationItem[]) + */ + public void modifyAttributes(final Name dn, final ModificationItem[] mods) { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#modifyAttributes(java.lang.String, + * javax.naming.directory.ModificationItem[]) + */ + public void modifyAttributes(final String dn, final ModificationItem[] mods) { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#bind(javax.naming.Name, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(final Name dn, final Object obj, + final Attributes attributes) { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#bind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(final String dn, final Object obj, + final Attributes attributes) { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#unbind(javax.naming.Name) + */ + public void unbind(final Name dn) { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#unbind(java.lang.String) + */ + public void unbind(final String dn) { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#unbind(javax.naming.Name, + * boolean) + */ + public void unbind(final Name dn, boolean recursive) { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + /* + * @see org.springframework.ldap.core.LdapOperations#unbind(java.lang.String, + * boolean) + */ + public void unbind(final String dn, boolean recursive) { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + private void doUnbind(final Name dn) { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbind(final String dn) { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbindRecursively(final Name dn) { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) { + deleteRecursively(ctx, new DistinguishedName(dn)); + return null; + } + }); + } + + private void doUnbindRecursively(final String dn) { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + deleteRecursively(ctx, new DistinguishedName(dn)); + return null; + } + }); + } + + /** + * Delete all subcontexts including the current one recursively. + * + * @param ctx + * The context to use for deleting. + * @param name + * The starting point to delete recursively. + * @throws NamingException + * if any error occurs + */ + protected void deleteRecursively(DirContext ctx, DistinguishedName name) { + + NamingEnumeration enumeration = null; + try { + enumeration = ctx.listBindings(name); + while (enumeration.hasMore()) { + Binding binding = (Binding) enumeration.next(); + DistinguishedName childName = new DistinguishedName(binding + .getName()); + childName.prepend((DistinguishedName) name); + deleteRecursively(ctx, childName); + } + ctx.unbind(name); + if (log.isDebugEnabled()) { + log.debug("Entry " + name + " deleted"); + } + } catch (javax.naming.NamingException e) { + throw LdapUtils.convertLdapException(e); + } finally { + try { + enumeration.close(); + } catch (Exception e) { + // Never mind this + } + } + } + + /* + * @see org.springframework.ldap.core.LdapOperations#rebind(javax.naming.Name, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final Name dn, final Object obj, + final Attributes attributes) { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#rebind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final String dn, final Object obj, + final Attributes attributes) { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#rename(javax.naming.Name, + * javax.naming.Name) + */ + public void rename(final Name oldDn, final Name newDn) { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.rename(oldDn, newDn); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.core.LdapOperations#rename(java.lang.String, + * java.lang.String) + */ + public void rename(final String oldDn, final String newDn) { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws javax.naming.NamingException { + ctx.rename(oldDn, newDn); + return null; + } + }); + } + + /* + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception { + if (contextSource == null) { + throw new IllegalArgumentException( + "Property 'contextSource' must be set."); + } + } + + private void closeContextAndNamingEnumeration(DirContext ctx, + NamingEnumeration results) { + + closeNamingEnumeration(results); + closeContext(ctx); + } + + /** + * Close the supplied DirContext if it is not null. Swallow any exceptions, + * as this is only for cleanup. + * + * @param ctx + * the context to close. + */ + private void closeContext(DirContext ctx) { + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + + /** + * Close the supplied NamingEnumeration if it is not null. Swallow any + * exceptions, as this is only for cleanup. + * + * @param results + * the NamingEnumeration to close. + */ + private void closeNamingEnumeration(NamingEnumeration results) { + if (results != null) { + try { + results.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + + private SearchControls getDefaultSearchControls(int searchScope, + boolean returningObjFlag, String[] attrs) { + + SearchControls controls = new SearchControls(); + controls.setSearchScope(searchScope); + controls.setReturningObjFlag(returningObjFlag); + controls.setReturningAttributes(attrs); + return controls; + } + + /** + * Make sure the returnObjFlag is set in the supplied SearchControls. Set it + * and log if it's not set. + * + * @param controls + * the SearchControls to check. + */ + private void assureReturnObjFlagSet(SearchControls controls) { + Validate.notNull(controls); + if (!controls.getReturningObjFlag()) { + log.info("The returnObjFlag of supplied SearchControls is not set" + + " but a ContextMapper is used - setting flag to true"); + controls.setReturningObjFlag(true); + } + } + + private final class NullDirContextProcessor implements DirContextProcessor { + public void postProcess(DirContext ctx) throws NamingException { + // Do nothing + } + + public void preProcess(DirContext ctx) throws NamingException { + // Do nothing + } + } + + /** + * A {@link NameClassPairCallbackHandler} that passes the NameClassPairs + * found to a NameClassPairMapper and collects the results in a list. + * + * @author Mattias Arthursson + */ + public class MappingCollectingNameClassPairCallbackHandler extends + CollectingNameClassPairCallbackHandler { + + private NameClassPairMapper mapper; + + public MappingCollectingNameClassPairCallbackHandler( + NameClassPairMapper mapper) { + this.mapper = mapper; + } + + /* + * @see org.springframework.ldap.CollectingNameClassPairCallbackHandler#getObjectFromNameClassPair(javax.naming.NameClassPair) + */ + public Object getObjectFromNameClassPair(NameClassPair nameClassPair) { + try { + return mapper.mapFromNameClassPair(nameClassPair); + } catch (javax.naming.NamingException e) { + throw LdapUtils.convertLdapException(e); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.LdapOperations#lookupContext(javax.naming.Name) + */ + public DirContextOperations lookupContext(Name dn) { + return (DirContextOperations) lookup(dn); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.LdapOperations#lookupContext(java.lang.String) + */ + public DirContextOperations lookupContext(String dn) { + return (DirContextOperations) lookup(dn); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.LdapOperations#modifyAttributes(org.springframework.ldap.core.DirContextOperations) + */ + public void modifyAttributes(DirContextOperations ctx) { + Name dn = ctx.getDn(); + if (dn != null && ctx.isUpdateMode()) { + modifyAttributes(dn, ctx.getModificationItems()); + } else { + throw new IllegalStateException( + "The DirContextOperations instance needs to be properly initialized."); + } + + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/NameClassPairCallbackHandler.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/NameClassPairCallbackHandler.java new file mode 100644 index 00000000..5913c4de --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/NameClassPairCallbackHandler.java @@ -0,0 +1,41 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.NameClassPair; + +/** + * Callback interface used by {@link LdapTemplate} search, list and listBindings + * methods. Implementations of this interface perform the actual work of + * extracting results from a single NameClassPair (a + * NameClassPair, Binding or + * SearchResult depending on the search operation) returned by an + * LDAP seach operation, such as search(), list(), and listBindings(). + * + * @author Mattias Arthursson + */ +public interface NameClassPairCallbackHandler { + /** + * Handle one entry. This method will be called once for each entry returned + * by a search or list. + * + * @param nameClassPair + * the NameClassPair returned from the + * NamingEnumeration. + */ + public void handleNameClassPair(NameClassPair nameClassPair); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/NameClassPairMapper.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/NameClassPairMapper.java new file mode 100644 index 00000000..8282368a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/NameClassPairMapper.java @@ -0,0 +1,44 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.NameClassPair; +import javax.naming.NamingException; + +/** + * Responsible for mapping NameClassPair objects to beans. + * + * @author Mattias Arthursson + */ +public interface NameClassPairMapper { + /** + * Map NameClassPair to an Object. The supplied + * NameClassPair is one of the results from a search + * operation (search, list or listBindings). Depending on which search + * operation is being performed, the NameClassPair might be a + * SearchResult, Binding or + * NameClassPair. + * + * @param nameClassPair + * NameClassPair from a search operation. + * @return and Object built from the NameClassPair. + * @throws NamingException + * if one is encountered in the operation. + */ + public Object mapFromNameClassPair(NameClassPair nameClassPair) + throws NamingException; +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/ObjectRetrievalException.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/ObjectRetrievalException.java new file mode 100644 index 00000000..94a4235f --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/ObjectRetrievalException.java @@ -0,0 +1,53 @@ +/* + * Copyright 2005-2007 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.core; + +import org.springframework.ldap.NamingException; + +/** + * Thrown by a {@link ContextMapperCallbackHandler} when it cannot retrieve an + * object from the given Binding. + * + * @author Ulrik Sandberg + * @since 1.2 + */ +public class ObjectRetrievalException extends NamingException { + + /** + * Create a new ObjectRetrievalException. + * + * @param msg + * the detail message + * + */ + public ObjectRetrievalException(String msg) { + super(msg); + } + + /** + * Create a new ObjectRetrievalException. + * + * @param msg + * the detail message + * @param cause + * the root cause (if any) + */ + public ObjectRetrievalException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/SearchExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/SearchExecutor.java new file mode 100644 index 00000000..36f3d2d2 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/SearchExecutor.java @@ -0,0 +1,52 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +/** + * Interface for delegating an actual search operation. The typical + * implementation of executeSearch would be something like: + * + *

+ * SearchExecutor executor = new SearchExecutor(){
+ *   public NamingEnumeration executeSearch(DirContext ctx) throws NamingException{
+ *     return ctx.search(dn, filter, searchControls);
+ *   }
+ * }
+ * 
+ * + * @see org.springframework.ldap.core.LdapTemplate#search(SearchExecutor, + * NameClassPairCallbackHandler) + * + * @author Mattias Arthursson + */ +public interface SearchExecutor { + /** + * Execute the actual search. + * + * @param ctx + * the DirContext on which to work. + * @return the NamingEnumeration resulting from the search + * operation. + * @throws NamingException + * if the search results in one. + */ + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException; +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/core/package.html new file mode 100644 index 00000000..2cb85563 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/package.html @@ -0,0 +1,8 @@ + + + +Core package of the JNDI/LDAP support. +Provides a LdapTemplate class and various callback interfaces. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractContextMapper.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractContextMapper.java new file mode 100644 index 00000000..003a2438 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractContextMapper.java @@ -0,0 +1,58 @@ +/* + * Copyright 2005-2007 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.core.support; + +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.DirContextOperations; + +/** + * Abstract superclass that may be used instead of implementing + * {@link ContextMapper} directly. Subclassing from this superclass, the + * supplied context will be automatically cast to + * DirContextOperations. Note that if you use your own + * DirObjectFactory, this implementation will fail with a + * ClassCastException. + * + * @author Mattias Arthursson + * + */ +public abstract class AbstractContextMapper implements ContextMapper { + + /** + * {@inheritDoc} + * + * @throws ClassCastException + * if a custom DirObjectFactory implementation is + * used, causing the objects passed in be anything else than + * {@link DirContextOperations} instances. + */ + public final Object mapFromContext(Object ctx) { + return doMapFromContext((DirContextOperations) ctx); + } + + /** + * Map a single DirContextOperation to an object. The + * supplied instance is the object supplied to + * {@link #mapFromContext(Object)} cast to a + * DirContextOperations. + * + * @param ctx + * the context to map to an object. + * @return an object built from the data in the context. + */ + protected abstract Object doMapFromContext(DirContextOperations ctx); + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractContextSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractContextSource.java new file mode 100644 index 00000000..617fceda --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractContextSource.java @@ -0,0 +1,532 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.Hashtable; +import java.util.Map; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.JdkVersion; +import org.springframework.ldap.core.AuthenticationSource; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.support.LdapUtils; + +/** + * Abstract implementation of the {@link ContextSource} interface. By default, + * returns an authenticated + * DirContext implementation for both read-only and + * read-write operations. To have an anonymous environment created for read-only + * operations, set the anonymousReadOnly property to true. + *

+ * Implementing classes need to implement + * {@link #getDirContextInstance(Hashtable)} to create a DirContext instance of + * the desired type. + *

+ * If an {@link AuthenticationSource} is set, this will be used for getting user principal + * and password for each new connection, otherwise a default one will be created + * using the specified userDn and password. + *

+ * Note: When using implementations of this class outside of a Spring + * Context it is necessary to call {@link #afterPropertiesSet()} when all + * properties are set, in order to finish up initialization. + * + * @see org.springframework.ldap.core.LdapTemplate + * @see org.springframework.ldap.core.support.DefaultDirObjectFactory + * @see org.springframework.ldap.core.support.LdapContextSource + * @see org.springframework.ldap.core.support.DirContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public abstract class AbstractContextSource implements BaseLdapPathContextSource, InitializingBean { + + private static final Class DEFAULT_CONTEXT_FACTORY = com.sun.jndi.ldap.LdapCtxFactory.class; + + private static final Class DEFAULT_DIR_OBJECT_FACTORY = DefaultDirObjectFactory.class; + + private Class dirObjectFactory = DEFAULT_DIR_OBJECT_FACTORY; + + private Class contextFactory = DEFAULT_CONTEXT_FACTORY; + + private DistinguishedName base = DistinguishedName.EMPTY_PATH; + + protected String userDn = ""; + + protected String password = ""; + + private String[] urls; + + private boolean pooled = true; + + private Hashtable baseEnv = new Hashtable(); + + private Hashtable anonymousEnv; + + private AuthenticationSource authenticationSource; + + private boolean cacheEnvironmentProperties = true; + + private boolean anonymousReadOnly = false; + + private static final Log log = LogFactory.getLog(AbstractContextSource.class); + + public static final String SUN_LDAP_POOLING_FLAG = "com.sun.jndi.ldap.connect.pool"; + + private static final String JDK_142 = "1.4.2"; + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.ContextSource#getReadOnlyContext() + */ + public DirContext getReadOnlyContext() { + if (!anonymousReadOnly) { + return createContext(getAuthenticatedEnv()); + } + else { + return createContext(getAnonymousEnv()); + } + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.ContextSource#getReadWriteContext() + */ + public DirContext getReadWriteContext() { + return createContext(getAuthenticatedEnv()); + } + + /** + * Default implementation of setting the environment up to be authenticated. + * Override in subclass if necessary. + * + * @param env the environment to modify. + */ + protected void setupAuthenticatedEnvironment(Hashtable env) { + String principal = authenticationSource.getPrincipal(); + env.put(Context.SECURITY_PRINCIPAL, principal); + log.debug("Principal: '" + principal + "'"); + env.put(Context.SECURITY_CREDENTIALS, authenticationSource.getCredentials()); + } + + /** + * Close the context and swallow any exceptions. + * + * @param ctx the DirContext to close. + */ + private void closeContext(DirContext ctx) { + if (ctx != null) { + try { + ctx.close(); + } + catch (Exception e) { + } + } + } + + /** + * Assemble a valid url String from all registered urls to add as + * PROVIDER_URL to the environment. + * + * @param ldapUrls all individual url Strings. + * @return the full url String + */ + protected String assembleProviderUrlString(String[] ldapUrls) { + StringBuffer providerUrlBuffer = new StringBuffer(1024); + for (int i = 0; i < ldapUrls.length; i++) { + providerUrlBuffer.append(ldapUrls[i]); + if (!DistinguishedName.EMPTY_PATH.equals(base)) { + if (!ldapUrls[i].endsWith("/")) { + providerUrlBuffer.append("/"); + } + } + providerUrlBuffer.append(base.toUrl()); + providerUrlBuffer.append(' '); + } + return providerUrlBuffer.toString().trim(); + } + + /** + * Set the base suffix from which all operations should origin. If a base + * suffix is set, you will not have to (and, indeed, must not) specify the + * full distinguished names in any operations performed. + * + * @param base the base suffix. + */ + public void setBase(String base) { + this.base = new DistinguishedName(base); + } + + /** + * Get the base suffix from which all operations should originate. If a base + * suffix is set, you will not have to (and, indeed, must not) specify the + * full distinguished names in any operations performed. + * + * @return the base suffix + */ + protected DistinguishedName getBase() { + return base; + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.support.BaseLdapPathSource#getBaseLdapPath() + */ + public DistinguishedName getBaseLdapPath() { + return getBase().immutableDistinguishedName(); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.support.BaseLdapPathSource#getBaseLdapPathAsString() + */ + public String getBaseLdapPathAsString() { + return getBaseLdapPath().toString(); + } + + /** + * Create a DirContext using the supplied environment. + * + * @param environment the Ldap environment to use when creating the + * DirContext. + * @return a new DirContext implpementation initialized with the supplied + * environment. + */ + protected DirContext createContext(Hashtable environment) { + DirContext ctx = null; + + try { + ctx = getDirContextInstance(environment); + + if (log.isInfoEnabled()) { + Hashtable ctxEnv = ctx.getEnvironment(); + String ldapUrl = (String) ctxEnv.get(Context.PROVIDER_URL); + log.debug("Got Ldap context on server '" + ldapUrl + "'"); + } + + return ctx; + } + catch (NamingException e) { + closeContext(ctx); + throw LdapUtils.convertLdapException(e); + } + } + + /** + * Set the context factory. Default is com.sun.jndi.ldap.LdapCtxFactory. + * + * @param contextFactory the context factory used when creating Contexts. + */ + public void setContextFactory(Class contextFactory) { + this.contextFactory = contextFactory; + } + + /** + * Get the context factory. + * + * @return the context factory used when creating Contexts. + */ + public Class getContextFactory() { + return contextFactory; + } + + /** + * Set the DirObjectFactory to use. Default is + * {@link DefaultDirObjectFactory}. The specified class needs to be an + * implementation of javax.naming.spi.DirObjectFactory. Note: Setting + * this value to null may have cause connection leaks when using + * ContextMapper methods in LdapTemplate. + * + * @param dirObjectFactory the DirObjectFactory to be used. Null means that + * no DirObjectFactory will be used. + */ + public void setDirObjectFactory(Class dirObjectFactory) { + this.dirObjectFactory = dirObjectFactory; + } + + /** + * Get the DirObjectFactory to use. + * + * @return the DirObjectFactory to be used. null means that + * no DirObjectFactory will be used. + */ + public Class getDirObjectFactory() { + return dirObjectFactory; + } + + /** + * Checks that all necessary data is set and that there is no compatibility + * issues, after which the instance is initialized. Note that you need to + * call this method explicitly after setting all desired properties if using + * the class outside of a Spring Context. + */ + public void afterPropertiesSet() throws Exception { + if (ArrayUtils.isEmpty(urls)) { + throw new IllegalArgumentException("At least one server url must be set"); + } + + if (!DistinguishedName.EMPTY_PATH.equals(base) && getJdkVersion().compareTo(JDK_142) < 0) { + throw new IllegalArgumentException("Base path is not supported for JDK versions < 1.4.2"); + } + + if (authenticationSource == null) { + log.debug("AuthenticationSource not set - " + "using default implementation"); + if (StringUtils.isBlank(userDn)) { + log.info("Property 'userDn' not set - " + "anonymous context will be used for read-write operations"); + } + else if (StringUtils.isBlank(password)) { + log.info("Property 'password' not set - " + "blank password will be used"); + } + authenticationSource = new SimpleAuthenticationSource(); + } + + if (cacheEnvironmentProperties) { + anonymousEnv = setupAnonymousEnv(); + } + } + + private Hashtable setupAnonymousEnv() { + if (pooled) { + baseEnv.put(SUN_LDAP_POOLING_FLAG, "true"); + log.debug("Using LDAP pooling."); + } + else { + baseEnv.remove(SUN_LDAP_POOLING_FLAG); + log.debug("Not using LDAP pooling"); + } + + Hashtable env = new Hashtable(baseEnv); + + env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory.getName()); + env.put(Context.PROVIDER_URL, assembleProviderUrlString(urls)); + + if (dirObjectFactory != null) { + env.put(Context.OBJECT_FACTORIES, dirObjectFactory.getName()); + } + + if (!DistinguishedName.EMPTY_PATH.equals(base)) { + // Save the base path for use in the DefaultDirObjectFactory. + env.put(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY, base); + } + + log.debug("Trying provider Urls: " + assembleProviderUrlString(urls)); + + return env; + } + + /** + * Set the password (credentials) to use for getting authenticated contexts. + * + * @param password the password. + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Set the user distinguished name (principal) to use for getting + * authenticated contexts. + * + * @param userDn the user distinguished name. + */ + public void setUserDn(String userDn) { + this.userDn = userDn; + } + + /** + * Set the user distinguished name (principal) to use for getting + * authenticated contexts. + * + * @param userName the user distinguished name. + * @deprecated Use {@link #setUserDn(String)} instead. + */ + public void setUserName(String userName) { + setUserDn(userName); + } + + /** + * Set the urls of the LDAP servers. Use this method if several servers are + * required. + * + * @param urls the urls of all servers. + */ + public void setUrls(String[] urls) { + this.urls = urls; + } + + /** + * Get the urls of the LDAP servers. + * + * @return the urls of all servers. + */ + public String[] getUrls() { + return urls; + } + + /** + * Set the url of the LDAP server. Utility method if only one server is + * used. + * + * @param url the url of the LDAP server. + */ + public void setUrl(String url) { + this.urls = new String[] { url }; + } + + /** + * Set whether the pooling flag should be set. Default is true. Note that + * since LDAP pooling is system wide, full configuration of this needs be + * done using system parameters as specified in the LDAP/JNDI documentation. + * Also note, that pooling is done on user dn basis, i.e. each individually + * authenticated connection will be pooled separately. This means that LDAP + * pooling will be most efficient using anonymous connections or connections + * authenticated using one single system user. + * + * @param pooled whether Contexts should be pooled. + */ + public void setPooled(boolean pooled) { + this.pooled = pooled; + } + + /** + * Get whether the pooling flag should be set. + * + * @return whether Contexts should be pooled. + */ + public boolean isPooled() { + return pooled; + } + + /** + * If any custom environment properties are needed, these can be set using + * this method. + * + * @param baseEnvironmentProperties + */ + public void setBaseEnvironmentProperties(Map baseEnvironmentProperties) { + this.baseEnv = new Hashtable(baseEnvironmentProperties); + } + + String getJdkVersion() { + return JdkVersion.getJavaVersion(); + } + + protected Hashtable getAnonymousEnv() { + if (cacheEnvironmentProperties) { + return anonymousEnv; + } + else { + return setupAnonymousEnv(); + } + } + + protected Hashtable getAuthenticatedEnv() { + // The authenticated environment should always be rebuilt. + Hashtable env = new Hashtable(getAnonymousEnv()); + setupAuthenticatedEnvironment(env); + return env; + } + + /** + * Set the authentication source to use when retrieving user principal and + * credentials. + * + * @param authenticationSource the {@link AuthenticationSource} that will + * provide user info. + */ + public void setAuthenticationSource(AuthenticationSource authenticationSource) { + this.authenticationSource = authenticationSource; + } + + /** + * Get the authentication source. + * + * @return the {@link AuthenticationSource} that will provide user info. + */ + public AuthenticationSource getAuthenticationSource() { + return authenticationSource; + } + + /** + * Set whether environment properties should be cached between requsts for + * anonymous environment. Default is true; setting this + * property to false causes the environment Hashmap to be + * rebuilt from the current property settings of this instance between each + * request for an anonymous environment. + * + * @param cacheEnvironmentProperties true causes that the + * anonymous environment properties should be cached, false + * causes the Hashmap to be rebuilt for each request. + */ + public void setCacheEnvironmentProperties(boolean cacheEnvironmentProperties) { + this.cacheEnvironmentProperties = cacheEnvironmentProperties; + } + + /** + * Set whether an anonymous environment should be used for read-only + * operations. Default is false. + * + * @param anonymousReadOnly true if an anonymous environment + * should be used for read-only operations, false otherwise. + */ + public void setAnonymousReadOnly(boolean anonymousReadOnly) { + this.anonymousReadOnly = anonymousReadOnly; + } + + /** + * Get whether an anonymous environment should be used for read-only + * operations. + * + * @return true if an anonymous environment should be used + * for read-only operations, false otherwise. + */ + public boolean isAnonymousReadOnly() { + return anonymousReadOnly; + } + + /** + * Implement in subclass to create a DirContext of the desired type (e.g. + * InitialDirContext or InitialLdapContext). + * + * @param environment the environment to use when creating the instance. + * @return a new DirContext instance. + * @throws NamingException if one is encountered when creating the instance. + */ + protected abstract DirContext getDirContextInstance(Hashtable environment) throws NamingException; + + class SimpleAuthenticationSource implements AuthenticationSource { + + public String getPrincipal() { + return userDn; + } + + public String getCredentials() { + return password; + } + + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AggregateDirContextProcessor.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AggregateDirContextProcessor.java new file mode 100644 index 00000000..5f4cd33d --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AggregateDirContextProcessor.java @@ -0,0 +1,88 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.springframework.ldap.core.DirContextProcessor; + +/** + * Manages a sequence of {@link DirContextProcessor} instances. Applies + * {@link #preProcess(DirContext)} and {@link #postProcess(DirContext)} + * respectively in sequence on the managed objects. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class AggregateDirContextProcessor implements DirContextProcessor { + + private List dirContextProcessors = new LinkedList(); + + /** + * Add the supplied DirContextProcessor to the list of managed objects. + * + * @param processor + * the DirContextpProcessor to add. + */ + public void addDirContextProcessor(DirContextProcessor processor) { + dirContextProcessors.add(processor); + } + + /** + * Get the list of managed {@link DirContextProcessor} instances. + * + * @return the managed list of {@link DirContextProcessor} instances. + */ + public List getDirContextProcessors() { + return dirContextProcessors; + } + + /** + * Set the list of managed {@link DirContextProcessor} instances. + * + * @param dirContextProcessors + * the list of {@link DirContextProcessor} instances to set. + */ + public void setDirContextProcessors(List dirContextProcessors) { + this.dirContextProcessors = dirContextProcessors; + } + + /* + * @see org.springframework.ldap.core.DirContextProcessor#preProcess(javax.naming.directory.DirContext) + */ + public void preProcess(DirContext ctx) throws NamingException { + for (Iterator iter = dirContextProcessors.iterator(); iter.hasNext();) { + DirContextProcessor processor = (DirContextProcessor) iter.next(); + processor.preProcess(ctx); + } + } + + /* + * @see org.springframework.ldap.core.DirContextProcessor#postProcess(javax.naming.directory.DirContext) + */ + public void postProcess(DirContext ctx) throws NamingException { + for (Iterator iter = dirContextProcessors.iterator(); iter.hasNext();) { + DirContextProcessor processor = (DirContextProcessor) iter.next(); + processor.postProcess(ctx); + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathAware.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathAware.java new file mode 100644 index 00000000..45ebf5de --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathAware.java @@ -0,0 +1,49 @@ +/* + * Copyright 2005-2007 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.core.support; + +import org.springframework.ldap.core.DistinguishedName; + +/** + * Interface to be implemented by classes that want to have access to the base + * context used in the active ContextSource. There are several + * cases in which services may want to have access to the base context, e.g. + * when working with groups (groupOfNames objectclass), in which + * case the full DN of each group member needs to be specified in the attribute + * value. + *

+ * If a class implements this interface and a + * {@link BaseLdapPathBeanPostProcessor} is defined in the + * ApplicationContext, the default base path will automatically + * passed to the {@link #setBaseLdapPath(DistinguishedName)} method on + * initialization. + *

+ * NB:The ContextSource needs to be a subclass of + * {@link AbstractContextSource} for this mechanism to work. + * + * + * @author Mattias Arthursson + * @since 1.2 + */ +public interface BaseLdapPathAware { + + /** + * Set the base LDAP path specified in the current + * ApplicationContext. + * @param baseLdapPath the base path used in the ContextSource + */ + public void setBaseLdapPath(DistinguishedName baseLdapPath); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostProcessor.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostProcessor.java new file mode 100644 index 00000000..d7037e60 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostProcessor.java @@ -0,0 +1,126 @@ +/* + * Copyright 2005-2007 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.core.support; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.util.StringUtils; + +/** + * This BeanPostProcessor checks each bean if it implements + * {@link BaseLdapPathAware}. If it does, the default context base LDAP path + * will be determined, and that value will be injected to the + * {@link BaseLdapPathAware#setBaseLdapPath(DistinguishedName)} method of the + * processed bean. + *

+ * If the baseLdapPath property of this + * BeanPostProcessor is set, that value will be used. Otherwise, + * in order to determine which base LDAP path to supply to the instance the + * ApplicationContext is searched for any beans that are + * implementations of {@link BaseLdapPathSource}. If one single occurrence + * is found, that instance is queried for its base path, and that is what will + * be injected. If more than one {@link BaseLdapPathSource} instance is + * configured in the ApplicationContext, the name of the one to + * use will need to be specified to the baseLdapPathSourceName + * property; otherwise the post processing will fail. If no + * {@link BaseLdapPathSource} implementing bean is found in the context and + * the basePath property is not set, post processing will also + * fail. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class BaseLdapPathBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware { + + private ApplicationContext applicationContext; + + private DistinguishedName basePath; + + private String baseLdapPathSourceName; + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof BaseLdapPathAware) { + BaseLdapPathAware baseLdapPathAware = (BaseLdapPathAware) bean; + + if (basePath != null) { + baseLdapPathAware.setBaseLdapPath(basePath); + } + else { + BaseLdapPathSource ldapPathSource = getBaseLdapPathSourceFromApplicationContext(); + baseLdapPathAware.setBaseLdapPath(ldapPathSource.getBaseLdapPath()); + } + } + return bean; + } + + BaseLdapPathSource getBaseLdapPathSourceFromApplicationContext() { + if (StringUtils.hasLength(baseLdapPathSourceName)) { + return (BaseLdapPathSource) applicationContext.getBean(baseLdapPathSourceName); + } + String[] definedContextSources = applicationContext.getBeanNamesForType(BaseLdapPathSource.class); + if (definedContextSources.length < 1) { + throw new NoSuchBeanDefinitionException("No BaseLdapPathSource implementation definition found"); + } + else if (definedContextSources.length > 1) { + throw new NoSuchBeanDefinitionException( + "More than BaseLdapPathSource implementation definition found in current ApplicationContext"); + } + return (BaseLdapPathSource) applicationContext.getBean(definedContextSources[0]); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, + * java.lang.String) + */ + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + // Do nothing for this implementation + return bean; + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + /** + * Set the base path to be injected in all {@link BaseLdapPathAware} beans. + * If this property is not set, the default base path will be determined + * from any defined {@link BaseLdapPathSource} instances available in the + * ApplicationContext. + * + * @param basePath the base path. + */ + public void setBasePath(DistinguishedName basePath) { + this.basePath = basePath; + } + + /** + * Set the name of the ContextSource bean to use for getting + * the base path. This method is typically useful if several ContextSource + * instances have been configured. + * + * @param contextSourceName the name of the ContextSource + * bean to use for determining the base path. + */ + public void setBaseLdapPathSourceName(String contextSourceName) { + this.baseLdapPathSourceName = contextSourceName; + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathContextSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathContextSource.java new file mode 100644 index 00000000..f0d607bd --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathContextSource.java @@ -0,0 +1,28 @@ +/* + * Copyright 2005-2007 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.core.support; + +import org.springframework.ldap.core.ContextSource; + +/** + * Interface to be implemented by ContextSources that are capable + * of providing the base LDAP path. + * + * @author Mattias Arthursson + */ +public interface BaseLdapPathContextSource extends ContextSource, BaseLdapPathSource { + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathSource.java new file mode 100644 index 00000000..c26a914e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/BaseLdapPathSource.java @@ -0,0 +1,46 @@ +/* + * Copyright 2005-2007 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.core.support; + +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DistinguishedName; + +/** + * Implementations of this interface are capable of providing a base LDAP path. + * The base LDAP path is the root path to which all LDAP operations performed on + * a particular context are relative. + * + * @see ContextSource + * + * @author Mattias Arthursson + */ +public interface BaseLdapPathSource { + /** + * Get the base LDAP path as a {@link DistinguishedName}. + * + * @return the base LDAP path as a {@link DistinguishedName}. The path will + * be empty if no base path is specified. + */ + DistinguishedName getBaseLdapPath(); + + /** + * Get the base LDAP path as a String. + * + * @return the base LDAP path as a An empty String will be returned if no + * base path is specified. + */ + String getBaseLdapPathAsString(); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/CountNameClassPairCallbackHandler.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/CountNameClassPairCallbackHandler.java new file mode 100644 index 00000000..ce51601a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/CountNameClassPairCallbackHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005-2007 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.core.support; + +import javax.naming.NameClassPair; + +import org.springframework.ldap.core.NameClassPairCallbackHandler; + +/** + * A {@link NameClassPairCallbackHandler} for counting all returned entries. + * + * @author Mattias Arthursson + * + */ +public class CountNameClassPairCallbackHandler implements + NameClassPairCallbackHandler { + + private int noOfRows = 0; + + /** + * Get the number of rows that was returned by the search. + * + * @return the number of entries that have been handled. + */ + public int getNoOfRows() { + return noOfRows; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.SearchResultCallbackHandler#handleSearchResult(javax.naming.directory.SearchResult) + */ + public void handleNameClassPair(NameClassPair nameClassPair) { + noOfRows++; + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DefaultDirObjectFactory.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DefaultDirObjectFactory.java new file mode 100644 index 00000000..5f823c92 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DefaultDirObjectFactory.java @@ -0,0 +1,97 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.directory.Attributes; +import javax.naming.spi.DirObjectFactory; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; + +/** + * Default implementation of the DirObjectFactory interface. Creates a + * {@link DirContextAdapter} from the supplied arguments. + * + * @author Mattias Arthursson + */ +public class DefaultDirObjectFactory implements DirObjectFactory { + /** + * Key to use in the ContextSource implementation to store the value of the + * base path suffix, if any, in the Ldap Environment. + * + * @deprecated Use {@link BaseLdapPathAware} and {@link BaseLdapPathBeanPostProcessor} instead. + */ + public static final String JNDI_ENV_BASE_PATH_KEY = "org.springframework.ldap.base.path"; + + /* + * (non-Javadoc) + * + * @see javax.naming.spi.DirObjectFactory#getObjectInstance(java.lang.Object, + * javax.naming.Name, javax.naming.Context, java.util.Hashtable, + * javax.naming.directory.Attributes) + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment, Attributes attrs) throws Exception { + + try { + String nameInNamespace = null; + if (nameCtx != null) { + nameInNamespace = nameCtx.getNameInNamespace(); + } else { + nameInNamespace = ""; + } + DirContextAdapter dirContextAdapter = new DirContextAdapter(attrs, + name, new DistinguishedName(nameInNamespace)); + dirContextAdapter.setUpdateMode(true); + + return dirContextAdapter; + } finally { + // It seems that the object supplied to the obj parameter is a + // DirContext instance with reference to the same Ldap connection as + // the original context. Since it is not the same instance (that's + // the nameCtx parameter) this one really needs to be closed in + // order to correctly clean up and return the connection to the pool + // when we're finished with the surrounding operation. + if (obj instanceof Context) { + + Context ctx = (Context) obj; + try { + ctx.close(); + } catch (Exception e) { + // Never mind this + } + + } + } + } + + /* + * (non-Javadoc) + * + * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, + * javax.naming.Name, javax.naming.Context, java.util.Hashtable) + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment) throws Exception { + return null; + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DirContextSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DirContextSource.java new file mode 100644 index 00000000..8f973b26 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DirContextSource.java @@ -0,0 +1,48 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.Hashtable; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + + +/** + * ContextSource implementation which creates InitialDirContext instances, for + * LDAPv2 compatibility. For configuration information, see + * {@link org.springframework.ldap.core.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.core.support.AbstractContextSource + * + * @author Mattias Arthursson + */ +public class DirContextSource extends AbstractContextSource { + + /** + * Create a new InitialDirContext instance. + * + * @param environment + * the environment to use when creating the context. + * @return a new InitialDirContext implementation. + */ + protected DirContext getDirContextInstance(Hashtable environment) + throws NamingException { + return new InitialDirContext(environment); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/LdapContextSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/LdapContextSource.java new file mode 100644 index 00000000..df1f6760 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/LdapContextSource.java @@ -0,0 +1,45 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.Hashtable; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.InitialLdapContext; + +/** + * ContextSource implementation which creates an InitialLdapContext + * instance. For configuration information, see + * {@link org.springframework.ldap.core.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.core.support.AbstractContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public class LdapContextSource extends AbstractContextSource { + + /* + * @see org.springframework.ldap.support.AbstractContextSource#getDirContextInstance(java.util.Hashtable) + */ + protected DirContext getDirContextInstance(Hashtable environment) + throws NamingException { + return new InitialLdapContext(environment, null); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/package.html new file mode 100644 index 00000000..30162db2 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/package.html @@ -0,0 +1,7 @@ + + + +Support classes the core Spring LDAP package. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/AbstractFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/AbstractFilter.java new file mode 100644 index 00000000..889abef4 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/AbstractFilter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005-2007 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.filter; + +/** + * Convenience class that implements most of the methods in the Filter + * interface. + * + * @author Adam Skogman + */ +public abstract class AbstractFilter implements Filter { + + protected AbstractFilter() { + super(); + } + + /* + * @see org.springframework.ldap.filter.Filter#encode(java.lang.StringBuffer) + */ + public abstract StringBuffer encode(StringBuffer buff); + + /* + * @see org.springframework.ldap.filter.Filter#encode() + */ + public String encode() { + StringBuffer buf = new StringBuffer(256); + buf = encode(buf); + return buf.toString(); + } + + /* + * @see java.lang.Object#toString() + */ + public String toString() { + return encode(); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/AndFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/AndFilter.java new file mode 100644 index 00000000..222a049e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/AndFilter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2005-2007 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.filter; + +/** + * A filter for a logical AND. Example: + * + *

+ *     AndFilter filter = new AndFilter();
+ *     filter.and(new EqualsFilter("objectclass", "person");
+ *     filter.and(new EqualsFilter("cn", "Some CN");
+ *     System.out.println(filter.ecode());    
+ * 
+ * + * would result in: (&(objectclass=person)(cn=Some CN)) + * + * @see org.springframework.ldap.filter.EqualsFilter + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class AndFilter extends BinaryLogicalFilter { + + private static final String AMPERSAND = "&"; + + /* + * @see org.springframework.ldap.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return AMPERSAND; + } + + /** + * Add a query to the AND expression. + * + * @param query The expression to AND with the rest of the AND:ed + * expressions. + * @return This LdapAndQuery + */ + public AndFilter and(Filter query) { + queryList.add(query); + return this; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/BinaryLogicalFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/BinaryLogicalFilter.java new file mode 100644 index 00000000..7b10f706 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/BinaryLogicalFilter.java @@ -0,0 +1,97 @@ +/* + * Copyright 2005-2007 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.filter; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +/** + * Abstract superclass for binary logical operations, that is "AND" + * and "OR" operations. + * + * @author Mattias Arthursson + */ +public abstract class BinaryLogicalFilter extends AbstractFilter { + + protected List queryList = new LinkedList(); + + /* + * @see org.springframework.ldap.filter.AbstractFilter#encode(java.lang.StringBuffer) + */ + public StringBuffer encode(StringBuffer buff) { + if (queryList.size() <= 0) { + + // only output query if contains anything + return buff; + + } + else if (queryList.size() == 1) { + + // don't add the & + Filter query = (Filter) queryList.get(0); + return query.encode(buff); + + } + else { + buff.append("(" + getLogicalOperator()); + + for (Iterator i = queryList.iterator(); i.hasNext();) { + Filter query = (Filter) i.next(); + buff = query.encode(buff); + } + + buff.append(")"); + + return buff; + } + } + + /** + * Implement this in subclass to return the logical operator, for example + * &qout;&&qout;. + * + * @return the logical operator. + */ + protected abstract String getLogicalOperator(); + + /** + * Compares each filter in turn. + * + * @see org.springframework.ldap.filter.Filter#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj instanceof BinaryLogicalFilter && this.getClass() == obj.getClass()) { + return EqualsBuilder.reflectionEquals(this, obj); + } + else { + return false; + } + } + + /** + * Hashes all contained data. + * + * @see org.springframework.ldap.filter.Filter#hashCode() + */ + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/CompareFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/CompareFilter.java new file mode 100644 index 00000000..1529d4c3 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/CompareFilter.java @@ -0,0 +1,123 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.springframework.ldap.core.LdapEncoder; + +/** + * Abstract superclass for filters that compare values. + * + * @author Mattias Arthursson + */ +public abstract class CompareFilter extends AbstractFilter { + + private final String attribute; + + private final String value; + + private final String encodedValue; + + public CompareFilter(String attribute, String value) { + this.attribute = attribute; + this.value = value; + this.encodedValue = encodeValue(value); + } + + /** + * For testing purposes. + * + * @return the encoded value. + */ + String getEncodedValue() { + return encodedValue; + } + + /** + * Override to perform special encoding in subclass. + * + * @param value the value to encode. + * @return properly escaped value. + */ + protected String encodeValue(String value) { + return LdapEncoder.filterEncode(value); + } + + /** + * Convenience constructor for int values. + * + * @param attribute Name of attribute in filter. + * @param value The value of the attribute in the filter. + */ + public CompareFilter(String attribute, int value) { + this.attribute = attribute; + this.value = String.valueOf(value); + this.encodedValue = LdapEncoder.filterEncode(this.value); + } + + /* + * @see org.springframework.ldap.filter.AbstractFilter#encode(java.lang.StringBuffer) + */ + public StringBuffer encode(StringBuffer buff) { + buff.append('('); + buff.append(attribute).append(getCompareString()).append(encodedValue); + buff.append(')'); + + return buff; + } + + /** + * Compares key and value before encoding. + * + * @see org.springframework.ldap.filter.Filter#equals(java.lang.Object) + */ + public boolean equals(Object o) { + if (o instanceof CompareFilter && o.getClass() == this.getClass()) { + CompareFilter that = (CompareFilter) o; + EqualsBuilder builder = new EqualsBuilder(); + builder.append(this.attribute, that.attribute); + builder.append(this.value, that.value); + return builder.isEquals(); + } + else { + return false; + } + } + + /** + * Calculate the hash code for the attribute and the value. + * + * @see org.springframework.ldap.filter.Filter#hashCode() + */ + public int hashCode() { + HashCodeBuilder builder = new HashCodeBuilder(); + builder.append(attribute); + builder.append(value); + return builder.toHashCode(); + } + + /** + * Implement this method in subclass to return a String representing the + * operator. The {@link EqualsFilter#getCompareString()} would for example + * return an equals sign, "=". + * + * @return the String to use as operator in the comparison for the specific + * subclass. + */ + protected abstract String getCompareString(); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/EqualsFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/EqualsFilter.java new file mode 100644 index 00000000..e452be35 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/EqualsFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2005-2007 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.filter; + +/** + * A filter for 'equals'. The following code: + * + *
+ * EqualsFilter filter = new EqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn=Some CN)
+ * 
+ * + * @author Adam Skogman + */ +public class EqualsFilter extends CompareFilter { + + private static final String EQUALS_SIGN = "="; + + public EqualsFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Convenience constructor for int values. + * + * @param attribute Name of attribute in filter. + * @param value The value of the attribute in the filter. + */ + public EqualsFilter(String attribute, int value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.filter.CompareFilter#getCompareString() + */ + protected String getCompareString() { + return EQUALS_SIGN; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/Filter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/Filter.java new file mode 100644 index 00000000..06cb9c8e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/Filter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2005-2007 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.filter; + +/** + * Common interface for LDAP filters. + * + * @author Adam Skogman + * @see RFC 1960: A String Representation of LDAP Search Filters + */ +public interface Filter { + + /** + * Encodes the filter to a String. + * + * @return The encoded filter in the standard String format + */ + public String encode(); + + /** + * Encodes the filter to a StringBuffer. + * + * @param buf + * The StringBuffer to encode the filter to + * @return The same StringBuffer as was given + */ + public StringBuffer encode(StringBuffer buf); + + /** + * All filters must implement equals. + * + * @param o + * @return true if the objects are equal. + */ + public boolean equals(Object o); + + /** + * All filters must implement hashCode. + * + * @return the hash code according to the contract in + * {@link Object#hashCode()} + */ + public int hashCode(); + +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/GreaterThanOrEqualsFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/GreaterThanOrEqualsFilter.java new file mode 100644 index 00000000..c46be2ac --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/GreaterThanOrEqualsFilter.java @@ -0,0 +1,54 @@ +/* + * Copyright 2005-2007 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.filter; + +/** + * A filter to compare >=. LDAP RFC does not allow > comparison. The following + * code: + * + *
+ * GreaterThanOrEqualsFilter filter = new GreaterThanOrEqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn>=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class GreaterThanOrEqualsFilter extends CompareFilter { + + private static final String GREATER_THAN_OR_EQUALS = ">="; + + public GreaterThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public GreaterThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.filter.CompareFilter#getCompareString() + */ + protected String getCompareString() { + return GREATER_THAN_OR_EQUALS; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/LessThanOrEqualsFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/LessThanOrEqualsFilter.java new file mode 100644 index 00000000..389a654b --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/LessThanOrEqualsFilter.java @@ -0,0 +1,54 @@ +/* + * Copyright 2005-2007 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.filter; + +/** + * A filter to compare <=. LDAP RFC does not allow < comparison. The following + * code: + * + *
+ * LessThanOrEqualsFilter filter = new LessThanOrEqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn<=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class LessThanOrEqualsFilter extends CompareFilter { + + private static final String LESS_THAN_OR_EQUALS = "<="; + + public LessThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public LessThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.filter.CompareFilter#getCompareString() + */ + protected String getCompareString() { + return LESS_THAN_OR_EQUALS; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/LikeFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/LikeFilter.java new file mode 100644 index 00000000..5ad694e9 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/LikeFilter.java @@ -0,0 +1,75 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.core.LdapEncoder; + +/** + * This filter allows the user to specify wildcards (*) by not escaping them in + * the filter. The following code: + * + *
+ * LikeFilter filter = new LikeFilter("cn", "foo*");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ *  (cn=foo*)
+ * 
+ * + * @author Anders Henja + * @author Mattias Arthursson + */ +public class LikeFilter extends EqualsFilter { + + public LikeFilter(String attribute, String value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.filter.CompareFilter#encodeValue(java.lang.String) + */ + protected String encodeValue(String value) { + // just return if blank string + if (value == null) { + return ""; + } + + String[] substrings = value.split("\\*", -2); + + if (substrings.length == 1) { + return LdapEncoder.filterEncode(substrings[0]); + } + + StringBuffer buff = new StringBuffer(); + for (int i = 0; i < substrings.length; i++) { + buff.append(LdapEncoder.filterEncode(substrings[i])); + if (i < substrings.length - 1) { + buff.append("*"); + } + else { + if (substrings[i].equals("")) { + continue; + } + } + } + + return buff.toString(); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/NotFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/NotFilter.java new file mode 100644 index 00000000..8584ad00 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/NotFilter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.apache.commons.lang.Validate; + +/** + * A filter for 'not'. The following code: + * + *
+ * Filter filter = new NotFilter(new EqualsFilter("cn", "foo");
+ * System.out.println(filter.encode());
+ * 
+ * + * would result in: + * + *
+ * (!(cn = foo))
+ * 
+ * + * @author Adam Skogman + */ +public class NotFilter extends AbstractFilter { + + private final Filter filter; + + static private final int HASH = "!".hashCode(); + + /** + * Create a filter that negates the outcome of the given filter. + * + * @param filter The filter that should be negated. + */ + public NotFilter(Filter filter) { + Validate.notNull(filter); + this.filter = filter; + } + + /* + * @see org.springframework.ldap.filter.AbstractFilter#encode(java.lang.StringBuffer) + */ + public StringBuffer encode(StringBuffer buff) { + + buff.append("(!"); + filter.encode(buff); + buff.append(')'); + + return buff; + } + + /* + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + + if (o instanceof NotFilter && o.getClass() == this.getClass()) { + NotFilter f = (NotFilter) o; + return this.filter.equals(f.filter); + } + + return false; + } + + /* + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return HASH ^ filter.hashCode(); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/OrFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/OrFilter.java new file mode 100644 index 00000000..b0369a70 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/OrFilter.java @@ -0,0 +1,56 @@ +/* + * Copyright 2005-2007 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.filter; + +/** + * Filter for logical OR. + * + *
+ *        AndFilter filter = new AndFilter();
+ *        filter.or(new EqualsFilter("objectclass", "person");
+ *        filter.or(new EqualsFilter("objectclass", "organizationalUnit");
+ *        System.out.println(filter.ecode());    
+ * 
+ * + * would result in: + * (|(objectclass=person)(objectclass=organizationalUnit)) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class OrFilter extends BinaryLogicalFilter { + + private static final String PIPE_SIGN = "|"; + + /** + * Add a query to the OR expression + * + * @param query The query to or with the rest of the or:ed queries. + * @return This LdapOrQuery + */ + public OrFilter or(Filter query) { + queryList.add(query); + return this; + } + + /* + * @see org.springframework.ldap.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return PIPE_SIGN; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/WhitespaceWildcardsFilter.java b/mvn-build/core/src/main/java/org/springframework/ldap/filter/WhitespaceWildcardsFilter.java new file mode 100644 index 00000000..73c02f8c --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/WhitespaceWildcardsFilter.java @@ -0,0 +1,79 @@ +/* + * Copyright 2005-2007 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.filter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.core.LdapEncoder; + +/** + * This filter automatically converts all whitespace to wildcards (*). The + * following code: + * + *
+ * WhitespaceWildcardsFilter filter = new WhitespaceWildcardsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: (cn=*Some*CN*) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class WhitespaceWildcardsFilter extends EqualsFilter { + private static Pattern starReplacePattern = Pattern.compile("\\s+"); + + public WhitespaceWildcardsFilter(String attribute, String value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.filter.CompareFilter#encodeValue(java.lang.String) + */ + protected String encodeValue(String value) { + + // blank string means just ONE star + if (StringUtils.isBlank(value)) { + return "*"; + } + + // trim value, we will add in stars first and last anywhay + value = value.trim(); + + // filter encode so that any stars etc. are preserved + String filterEncoded = LdapEncoder.filterEncode(value); + + // Now replace all whitespace with stars + Matcher m = starReplacePattern.matcher(filterEncoded); + + // possibly 2 longer (stars at ends) + StringBuffer buff = new StringBuffer(value.length() + 2); + + buff.append('*'); + + while (m.find()) { + m.appendReplacement(buff, "*"); + } + m.appendTail(buff); + + buff.append('*'); + + return buff.toString(); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/filter/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/filter/package.html new file mode 100644 index 00000000..5b26891b --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/filter/package.html @@ -0,0 +1,20 @@ + + + +Utility classes for dynamically building LDAP +filters. Filters can be nested and wrapped around each other: +
+AndFilter andFilter = new AndFilter();
+andFilter.and(new EqualsFilter("objectclass", "person");
+andFilter.and(new EqualsFilter("cn", "Some CN");
+OrFilter orFilter = new OrFilter();
+orFilter.or(andFilter);
+orFilter.or(new EqualsFilter("objectclass", "organizationalUnit));
+System.out.println(orFilter.encode());
+
+would result in: + +
(|(&(objectclass=person)(cn=Some CN))(objectclass=organizationalUnit))
+ + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/package.html new file mode 100644 index 00000000..f3059be9 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/package.html @@ -0,0 +1,7 @@ + + + +Base package of Spring LDAP, containing an unchecked mirror of the JNDI NamingException hierarchy. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingContext.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingContext.java new file mode 100644 index 00000000..1d51315e --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingContext.java @@ -0,0 +1,381 @@ +/* + * Copyright 2005-2007 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.pool; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; + +import org.apache.commons.lang.Validate; +import org.apache.commons.pool.KeyedObjectPool; +import org.springframework.ldap.pool.factory.PoolingContextSource; + +/** + * Used by {@link PoolingContextSource} to wrap a {@link Context}, delegating most methods + * to the underlying context, retains a reference to the pool the context was checked out + * from and returns itself to the pool when {@link #close()} is called. + * + * @author Eric Dalquist + */ +public class DelegatingContext implements Context { + private KeyedObjectPool keyedObjectPool; + private Context delegateContext; + private final DirContextType dirContextType; + + + /** + * Create a new delegating context for the specified pool, context and context type. + * + * @param keyedObjectPool The pool the delegate context was checked out from. + * @param delegateContext The context to delegate operations to. + * @param dirContextType The type of context, used as a key for the pool. + * @throws IllegalArgumentException if any of the arguments are null + */ + public DelegatingContext(KeyedObjectPool keyedObjectPool, Context delegateContext, DirContextType dirContextType) { + Validate.notNull(keyedObjectPool, "keyedObjectPool may not be null"); + Validate.notNull(delegateContext, "delegateContext may not be null"); + Validate.notNull(dirContextType, "dirContextType may not be null"); + + this.keyedObjectPool = keyedObjectPool; + this.delegateContext = delegateContext; + this.dirContextType = dirContextType; + } + + + //***** Helper Methods *****// + + /** + * @return The direct delegate for this context proxy + */ + public Context getDelegateContext() { + return this.delegateContext; + } + + /** + * Recursivley inspect delegates until a non-delegating context is found. + * + * @return The innermost (real) Context that is being delegated to. + */ + public Context getInnermostDelegateContext() { + final Context delegateContext = this.getDelegateContext(); + + if (delegateContext instanceof DelegatingContext) { + return ((DelegatingContext)delegateContext).getInnermostDelegateContext(); + } + + return delegateContext; + } + + /** + * @throws NamingException If the delegate is null, {@link #close()} has been called. + */ + protected void assertOpen() throws NamingException { + if (this.delegateContext == null) { + throw new NamingException("Context is closed."); + } + } + + + //***** Object methods *****// + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Context)) { + return false; + } + + final Context thisContext = this.getInnermostDelegateContext(); + Context otherContext = (Context)obj; + if (otherContext instanceof DelegatingContext) { + otherContext = ((DelegatingContext)otherContext).getInnermostDelegateContext(); + } + + return thisContext == otherContext || (thisContext != null && thisContext.equals(otherContext)); + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + final Context context = this.getInnermostDelegateContext(); + return (context != null ? context.hashCode() : 0); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + final Context context = this.getInnermostDelegateContext(); + return (context != null ? context.toString() : "Context is closed"); + } + + + //***** Context Interface Delegates *****// + + /** + * @see javax.naming.Context#addToEnvironment(java.lang.String, java.lang.Object) + */ + public Object addToEnvironment(String propName, Object propVal) throws NamingException { + throw new UnsupportedOperationException("Cannot call addToEnvironment on a pooled context"); + } + + /** + * @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object) + */ + public void bind(Name name, Object obj) throws NamingException { + this.assertOpen(); + this.getDelegateContext().bind(name, obj); + } + + /** + * @see javax.naming.Context#bind(java.lang.String, java.lang.Object) + */ + public void bind(String name, Object obj) throws NamingException { + this.assertOpen(); + this.getDelegateContext().bind(name, obj); + } + + /** + * @see javax.naming.Context#close() + */ + public void close() throws NamingException { + final Context context = this.getInnermostDelegateContext(); + if (context == null) { + return; + } + + //Get a local reference so the member can be nulled earlier + this.delegateContext = null; + + //Return the object to the Pool and then null the pool reference + try { + this.keyedObjectPool.returnObject(this.dirContextType, context); + } + catch (Exception e) { + final NamingException namingException = new NamingException("Failed to return delegate Context to pool."); + namingException.setRootCause(e); + throw namingException; + } + finally { + this.keyedObjectPool = null; + } + } + + /** + * @see javax.naming.Context#composeName(javax.naming.Name, javax.naming.Name) + */ + public Name composeName(Name name, Name prefix) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().composeName(name, prefix); + } + + /** + * @see javax.naming.Context#composeName(java.lang.String, java.lang.String) + */ + public String composeName(String name, String prefix) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().composeName(name, prefix); + } + + /** + * @see javax.naming.Context#createSubcontext(javax.naming.Name) + */ + public Context createSubcontext(Name name) throws NamingException { + throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context"); + } + + /** + * @see javax.naming.Context#createSubcontext(java.lang.String) + */ + public Context createSubcontext(String name) throws NamingException { + throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context"); + } + + /** + * @see javax.naming.Context#destroySubcontext(javax.naming.Name) + */ + public void destroySubcontext(Name name) throws NamingException { + throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context"); + } + + /** + * @see javax.naming.Context#destroySubcontext(java.lang.String) + */ + public void destroySubcontext(String name) throws NamingException { + throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context"); + } + + /** + * @see javax.naming.Context#getEnvironment() + */ + public Hashtable getEnvironment() throws NamingException { + this.assertOpen(); + return this.getDelegateContext().getEnvironment(); + } + + /** + * @see javax.naming.Context#getNameInNamespace() + */ + public String getNameInNamespace() throws NamingException { + this.assertOpen(); + return this.getDelegateContext().getNameInNamespace(); + } + + /** + * @see javax.naming.Context#getNameParser(javax.naming.Name) + */ + public NameParser getNameParser(Name name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().getNameParser(name); + } + + /** + * @see javax.naming.Context#getNameParser(java.lang.String) + */ + public NameParser getNameParser(String name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().getNameParser(name); + } + + /** + * @see javax.naming.Context#list(javax.naming.Name) + */ + public NamingEnumeration list(Name name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().list(name); + } + + /** + * @see javax.naming.Context#list(java.lang.String) + */ + public NamingEnumeration list(String name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().list(name); + } + + /** + * @see javax.naming.Context#listBindings(javax.naming.Name) + */ + public NamingEnumeration listBindings(Name name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().listBindings(name); + } + + /** + * @see javax.naming.Context#listBindings(java.lang.String) + */ + public NamingEnumeration listBindings(String name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().listBindings(name); + } + + /** + * @see javax.naming.Context#lookup(javax.naming.Name) + */ + public Object lookup(Name name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().lookup(name); + } + + /** + * @see javax.naming.Context#lookup(java.lang.String) + */ + public Object lookup(String name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().lookup(name); + } + + /** + * @see javax.naming.Context#lookupLink(javax.naming.Name) + */ + public Object lookupLink(Name name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().lookupLink(name); + } + + /** + * @see javax.naming.Context#lookupLink(java.lang.String) + */ + public Object lookupLink(String name) throws NamingException { + this.assertOpen(); + return this.getDelegateContext().lookupLink(name); + } + + /** + * @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object) + */ + public void rebind(Name name, Object obj) throws NamingException { + this.assertOpen(); + this.getDelegateContext().rebind(name, obj); + } + + /** + * @see javax.naming.Context#rebind(java.lang.String, java.lang.Object) + */ + public void rebind(String name, Object obj) throws NamingException { + this.assertOpen(); + this.getDelegateContext().rebind(name, obj); + } + + /** + * @see javax.naming.Context#removeFromEnvironment(java.lang.String) + */ + public Object removeFromEnvironment(String propName) throws NamingException { + throw new UnsupportedOperationException("Cannot call removeFromEnvironment on a pooled context"); + } + + /** + * @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name) + */ + public void rename(Name oldName, Name newName) throws NamingException { + this.assertOpen(); + this.getDelegateContext().rename(oldName, newName); + } + + /** + * @see javax.naming.Context#rename(java.lang.String, java.lang.String) + */ + public void rename(String oldName, String newName) throws NamingException { + this.assertOpen(); + this.getDelegateContext().rename(oldName, newName); + } + + /** + * @see javax.naming.Context#unbind(javax.naming.Name) + */ + public void unbind(Name name) throws NamingException { + this.assertOpen(); + this.getDelegateContext().unbind(name); + } + + /** + * @see javax.naming.Context#unbind(java.lang.String) + */ + public void unbind(String name) throws NamingException { + this.assertOpen(); + this.getDelegateContext().unbind(name); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingDirContext.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingDirContext.java new file mode 100644 index 00000000..defa0d00 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingDirContext.java @@ -0,0 +1,361 @@ +/* + * Copyright 2005-2007 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.pool; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SearchControls; + +import org.apache.commons.lang.Validate; +import org.apache.commons.pool.KeyedObjectPool; +import org.springframework.ldap.core.DirContextProxy; +import org.springframework.ldap.pool.factory.PoolingContextSource; + + +/** + * Used by {@link PoolingContextSource} to wrap a {@link DirContext}, delegating most methods + * to the underlying context. This class extends {@link DelegatingContext} which handles returning + * the context to the pool on a call to {@link #close()} + * + * @author Eric Dalquist + */ +public class DelegatingDirContext extends DelegatingContext implements DirContext, DirContextProxy { + private DirContext delegateDirContext; + + /** + * Create a new delegating dir context for the specified pool, context and context type. + * + * @param keyedObjectPool The pool the delegate context was checked out from. + * @param delegateDirContext The dir context to delegate operations to. + * @param dirContextType The type of context, used as a key for the pool. + * @throws IllegalArgumentException if any of the arguments are null + */ + public DelegatingDirContext(KeyedObjectPool keyedObjectPool, DirContext delegateDirContext, DirContextType dirContextType) { + super(keyedObjectPool, delegateDirContext, dirContextType); + Validate.notNull(delegateDirContext, "delegateDirContext may not be null"); + + this.delegateDirContext = delegateDirContext; + } + + + //***** Helper Methods *****// + + /** + * @return The direct delegate for this dir context proxy + */ + public DirContext getDelegateDirContext() { + return this.delegateDirContext; + } + + public Context getDelegateContext() { + return this.getDelegateDirContext(); + } + + /** + * Recursivley inspect delegates until a non-delegating dir context is found. + * + * @return The innermost (real) DirContext that is being delegated to. + */ + public DirContext getInnermostDelegateDirContext() { + final DirContext delegateDirContext = this.getDelegateDirContext(); + + if (delegateDirContext instanceof DelegatingDirContext) { + return ((DelegatingDirContext)delegateDirContext).getInnermostDelegateDirContext(); + } + + return delegateDirContext; + } + + protected void assertOpen() throws NamingException { + if (this.delegateDirContext == null) { + throw new NamingException("DirContext is closed."); + } + + super.assertOpen(); + } + + + //***** Object methods *****// + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof DirContext)) { + return false; + } + + final DirContext thisDirContext = this.getInnermostDelegateDirContext(); + DirContext otherDirContext = (DirContext)obj; + if (otherDirContext instanceof DelegatingDirContext) { + otherDirContext = ((DelegatingDirContext)otherDirContext).getInnermostDelegateDirContext(); + } + + return thisDirContext == otherDirContext || (thisDirContext != null && thisDirContext.equals(otherDirContext)); + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + final DirContext context = this.getInnermostDelegateDirContext(); + return (context != null ? context.hashCode() : 0); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + final DirContext context = this.getInnermostDelegateDirContext(); + return (context != null ? context.toString() : "DirContext is closed"); + } + + + //***** DirContextProxy Interface Methods *****// + + /* (non-Javadoc) + * @see org.springframework.ldap.core.DirContextProxy#getTargetContext() + */ + public DirContext getTargetContext() { + return this.getInnermostDelegateDirContext(); + } + + + //***** DirContext Interface Delegates *****// + + /** + * @see javax.naming.directory.DirContext#bind(javax.naming.Name, java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(Name name, Object obj, Attributes attrs) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().bind(name, obj, attrs); + } + + /** + * @see javax.naming.directory.DirContext#bind(java.lang.String, java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(String name, Object obj, Attributes attrs) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().bind(name, obj, attrs); + } + + /** + * @see javax.naming.directory.DirContext#createSubcontext(javax.naming.Name, javax.naming.directory.Attributes) + */ + public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException { + throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context"); + } + + /** + * @see javax.naming.directory.DirContext#createSubcontext(java.lang.String, javax.naming.directory.Attributes) + */ + public DirContext createSubcontext(String name, Attributes attrs) throws NamingException { + throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context"); + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name, java.lang.String[]) + */ + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().getAttributes(name, attrIds); + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name) + */ + public Attributes getAttributes(Name name) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().getAttributes(name); + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(java.lang.String, java.lang.String[]) + */ + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().getAttributes(name, attrIds); + } + + /** + * @see javax.naming.directory.DirContext#getAttributes(java.lang.String) + */ + public Attributes getAttributes(String name) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().getAttributes(name); + } + + /** + * @see javax.naming.directory.DirContext#getSchema(javax.naming.Name) + */ + public DirContext getSchema(Name name) throws NamingException { + throw new UnsupportedOperationException("Cannot call getSchema on a pooled context"); + } + + /** + * @see javax.naming.directory.DirContext#getSchema(java.lang.String) + */ + public DirContext getSchema(String name) throws NamingException { + throw new UnsupportedOperationException("Cannot call getSchema on a pooled context"); + } + + /** + * @see javax.naming.directory.DirContext#getSchemaClassDefinition(javax.naming.Name) + */ + public DirContext getSchemaClassDefinition(Name name) throws NamingException { + throw new UnsupportedOperationException("Cannot call getSchemaClassDefinition on a pooled context"); + } + + /** + * @see javax.naming.directory.DirContext#getSchemaClassDefinition(java.lang.String) + */ + public DirContext getSchemaClassDefinition(String name) throws NamingException { + throw new UnsupportedOperationException("Cannot call getSchemaClassDefinition on a pooled context"); + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(javax.naming.Name, int, javax.naming.directory.Attributes) + */ + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().modifyAttributes(name, mod_op, attrs); + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(javax.naming.Name, javax.naming.directory.ModificationItem[]) + */ + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().modifyAttributes(name, mods); + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String, int, javax.naming.directory.Attributes) + */ + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().modifyAttributes(name, mod_op, attrs); + } + + /** + * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String, javax.naming.directory.ModificationItem[]) + */ + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().modifyAttributes(name, mods); + } + + /** + * @see javax.naming.directory.DirContext#rebind(javax.naming.Name, java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(Name name, Object obj, Attributes attrs) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().rebind(name, obj, attrs); + } + + /** + * @see javax.naming.directory.DirContext#rebind(java.lang.String, java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(String name, Object obj, Attributes attrs) throws NamingException { + this.assertOpen(); + this.getDelegateDirContext().rebind(name, obj, attrs); + } + + /** + * @see javax.naming.directory.DirContext#search(javax.naming.Name, javax.naming.directory.Attributes, java.lang.String[]) + */ + public NamingEnumeration search(Name name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, matchingAttributes, attributesToReturn); + } + + /** + * @see javax.naming.directory.DirContext#search(javax.naming.Name, javax.naming.directory.Attributes) + */ + public NamingEnumeration search(Name name, Attributes matchingAttributes) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, matchingAttributes); + } + + /** + * @see javax.naming.directory.DirContext#search(javax.naming.Name, java.lang.String, java.lang.Object[], javax.naming.directory.SearchControls) + */ + public NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, filterExpr, filterArgs, cons); + } + + /** + * @see javax.naming.directory.DirContext#search(javax.naming.Name, java.lang.String, javax.naming.directory.SearchControls) + */ + public NamingEnumeration search(Name name, String filter, SearchControls cons) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, filter, cons); + } + + /** + * @see javax.naming.directory.DirContext#search(java.lang.String, javax.naming.directory.Attributes, java.lang.String[]) + */ + public NamingEnumeration search(String name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, matchingAttributes, attributesToReturn); + } + + /** + * @see javax.naming.directory.DirContext#search(java.lang.String, javax.naming.directory.Attributes) + */ + public NamingEnumeration search(String name, Attributes matchingAttributes) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, matchingAttributes); + } + + /** + * @see javax.naming.directory.DirContext#search(java.lang.String, java.lang.String, java.lang.Object[], javax.naming.directory.SearchControls) + */ + public NamingEnumeration search(String name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, filterExpr, filterArgs, cons); + } + + /** + * @see javax.naming.directory.DirContext#search(java.lang.String, java.lang.String, javax.naming.directory.SearchControls) + */ + public NamingEnumeration search(String name, String filter, SearchControls cons) throws NamingException { + this.assertOpen(); + return this.getDelegateDirContext().search(name, filter, cons); + } + + /** + * @see DelegatingContext#close() + */ + public void close() throws NamingException { + if (this.delegateDirContext == null) { + return; + } + + super.close(); + this.delegateDirContext = null; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingLdapContext.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingLdapContext.java new file mode 100644 index 00000000..1c64a16a --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DelegatingLdapContext.java @@ -0,0 +1,199 @@ +/* + * Copyright 2005-2007 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.pool; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.Control; +import javax.naming.ldap.ExtendedRequest; +import javax.naming.ldap.ExtendedResponse; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.lang.Validate; +import org.apache.commons.pool.KeyedObjectPool; +import org.springframework.ldap.pool.factory.PoolingContextSource; + +/** + * Used by {@link PoolingContextSource} to wrap a {@link LdapContext}, delegating most methods + * to the underlying context. This class extends {@link DelegatingDirContext} which handles returning + * the context to the pool on a call to {@link #close()} + * + * @author Eric Dalquist + */ +public class DelegatingLdapContext extends DelegatingDirContext implements LdapContext { + private LdapContext delegateLdapContext; + + /** + * Create a new delegating ldap context for the specified pool, context and context type. + * + * @param keyedObjectPool The pool the delegate context was checked out from. + * @param delegateLdapContext The ldap context to delegate operations to. + * @param dirContextType The type of context, used as a key for the pool. + * @throws IllegalArgumentException if any of the arguments are null + */ + public DelegatingLdapContext(KeyedObjectPool keyedObjectPool, LdapContext delegateLdapContext, DirContextType dirContextType) { + super(keyedObjectPool, delegateLdapContext, dirContextType); + Validate.notNull(delegateLdapContext, "delegateLdapContext may not be null"); + + this.delegateLdapContext = delegateLdapContext; + } + + + //***** Helper Methods *****// + + /** + * @return The direct delegate for this ldap context proxy + */ + public LdapContext getDelegateLdapContext() { + return this.delegateLdapContext; + } + + // cannot return subtype in overriden method unless Java5 + public DirContext getDelegateDirContext() { + return this.getDelegateLdapContext(); + } + + /** + * Recursivley inspect delegates until a non-delegating ldap context is found. + * + * @return The innermost (real) DirContext that is being delegated to. + */ + public LdapContext getInnermostDelegateLdapContext() { + final LdapContext delegateLdapContext = this.getDelegateLdapContext(); + + if (delegateLdapContext instanceof DelegatingLdapContext) { + return ((DelegatingLdapContext)delegateLdapContext).getInnermostDelegateLdapContext(); + } + + return delegateLdapContext; + } + + protected void assertOpen() throws NamingException { + if (this.delegateLdapContext == null) { + throw new NamingException("LdapContext is closed."); + } + + super.assertOpen(); + } + + + //***** Object methods *****// + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof LdapContext)) { + return false; + } + + final LdapContext thisLdapContext = this.getInnermostDelegateLdapContext(); + LdapContext otherLdapContext = (LdapContext)obj; + if (otherLdapContext instanceof DelegatingLdapContext) { + otherLdapContext = ((DelegatingLdapContext)otherLdapContext).getInnermostDelegateLdapContext(); + } + + return thisLdapContext == otherLdapContext || (thisLdapContext != null && thisLdapContext.equals(otherLdapContext)); + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + final LdapContext context = this.getInnermostDelegateLdapContext(); + return (context != null ? context.hashCode() : 0); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + final LdapContext context = this.getInnermostDelegateLdapContext(); + return (context != null ? context.toString() : "LdapContext is closed"); + } + + + //***** LdapContext Interface Delegates *****// + + /** + * @see javax.naming.ldap.LdapContext#extendedOperation(javax.naming.ldap.ExtendedRequest) + */ + public ExtendedResponse extendedOperation(ExtendedRequest request) throws NamingException { + this.assertOpen(); + return this.getDelegateLdapContext().extendedOperation(request); + } + + /** + * @see javax.naming.ldap.LdapContext#getConnectControls() + */ + public Control[] getConnectControls() throws NamingException { + this.assertOpen(); + return this.getDelegateLdapContext().getConnectControls(); + } + + /** + * @see javax.naming.ldap.LdapContext#getRequestControls() + */ + public Control[] getRequestControls() throws NamingException { + this.assertOpen(); + return this.getDelegateLdapContext().getRequestControls(); + } + + /** + * @see javax.naming.ldap.LdapContext#getResponseControls() + */ + public Control[] getResponseControls() throws NamingException { + this.assertOpen(); + return this.getDelegateLdapContext().getResponseControls(); + } + + /** + * @see javax.naming.ldap.LdapContext#newInstance(javax.naming.ldap.Control[]) + */ + public LdapContext newInstance(Control[] requestControls) throws NamingException { + throw new UnsupportedOperationException("Cannot call newInstance on a pooled context"); + } + + /** + * @see javax.naming.ldap.LdapContext#reconnect(javax.naming.ldap.Control[]) + */ + public void reconnect(Control[] connCtls) throws NamingException { + throw new UnsupportedOperationException("Cannot call reconnect on a pooled context"); + } + + /** + * @see javax.naming.ldap.LdapContext#setRequestControls(javax.naming.ldap.Control[]) + */ + public void setRequestControls(Control[] requestControls) throws NamingException { + throw new UnsupportedOperationException("Cannot call setRequestControls on a pooled context"); + } + + /** + * @see DelegatingDirContext#close() + */ + public void close() throws NamingException { + if (this.delegateLdapContext == null) { + return; + } + + super.close(); + this.delegateLdapContext = null; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/DirContextType.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DirContextType.java new file mode 100644 index 00000000..93b5ae50 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/DirContextType.java @@ -0,0 +1,53 @@ +/* + * Copyright 2005-2007 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.pool; + +import javax.naming.directory.DirContext; + +import org.springframework.ldap.core.ContextSource; + + +/** + * An enum representing the two types of {@link DirContext}s that can be returned by a + * {@link ContextSource}. + * + * @author Eric Dalquist + */ +public final class DirContextType { + private String name; + + private DirContextType(String name) { + this.name = name; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return name; + } + + /** + * The type of {@link DirContext} returned by {@link ContextSource#getReadOnlyContext()} + */ + public static final DirContextType READ_ONLY = new DirContextType("READ_ONLY"); + + /** + * The type of {@link DirContext} returned by {@link ContextSource#getReadWriteContext()} + */ + public static final DirContextType READ_WRITE = new DirContextType("READ_WRITE"); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/DirContextPoolableObjectFactory.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/DirContextPoolableObjectFactory.java new file mode 100644 index 00000000..b71e82c9 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/DirContextPoolableObjectFactory.java @@ -0,0 +1,204 @@ +/* + * Copyright 2005-2007 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.pool.factory; + +import javax.naming.directory.DirContext; + +import org.apache.commons.lang.Validate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.BaseKeyedPoolableObjectFactory; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.pool.DirContextType; +import org.springframework.ldap.pool.validation.DirContextValidator; + +/** + * Factory that creates {@link DirContext} instances for pooling via a + * configured {@link ContextSource}. The {@link DirContext}s are keyed based + * on if they are read only or read/write. The expected key type is the + * {@link DirContextType} enum. + * + *
+ *
+ * Configuration: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
PropertyDescriptionRequiredDefault
contextSource The {@link ContextSource} to get {@link DirContext}s from + * for adding to the pool. Yesnull
dirContextValidator The {@link DirContextValidator} to use to validate + * {@link DirContext}s. This is only required if the pool has validation of any + * kind turned on. Nonull
+ * + * @author Eric Dalquist eric.dalquist@doit.wisc.edu + */ +class DirContextPoolableObjectFactory extends BaseKeyedPoolableObjectFactory { + /** + * Logger for this class and subclasses + */ + protected final Log logger = LogFactory.getLog(this.getClass()); + + private ContextSource contextSource; + + private DirContextValidator dirContextValidator; + + /** + * @return the contextSource + */ + public ContextSource getContextSource() { + return this.contextSource; + } + + /** + * @param contextSource + * the contextSource to set + */ + public void setContextSource(ContextSource contextSource) { + if (contextSource == null) { + throw new IllegalArgumentException("contextSource may not be null"); + } + + this.contextSource = contextSource; + } + + /** + * @return the dirContextValidator + */ + public DirContextValidator getDirContextValidator() { + return this.dirContextValidator; + } + + /** + * @param dirContextValidator + * the dirContextValidator to set + */ + public void setDirContextValidator(DirContextValidator dirContextValidator) { + if (dirContextValidator == null) { + throw new IllegalArgumentException( + "dirContextValidator may not be null"); + } + + this.dirContextValidator = dirContextValidator; + } + + /** + * @see org.apache.commons.pool.BaseKeyedPoolableObjectFactory#makeObject(java.lang.Object) + */ + public Object makeObject(Object key) throws Exception { + Validate.notNull(this.contextSource, "ContextSource may not be null"); + Validate.isTrue(key instanceof DirContextType, + "key must be a DirContextType"); + + final DirContextType contextType = (DirContextType) key; + if (this.logger.isDebugEnabled()) { + this.logger.debug("Creating a new " + contextType + " DirContext"); + } + + if (contextType == DirContextType.READ_WRITE) { + final DirContext readWriteContext = this.contextSource + .getReadWriteContext(); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Created new " + DirContextType.READ_WRITE + + " DirContext='" + readWriteContext + "'"); + } + + return readWriteContext; + } else if (contextType == DirContextType.READ_ONLY) { + + final DirContext readOnlyContext = this.contextSource + .getReadOnlyContext(); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Created new " + DirContextType.READ_ONLY + + " DirContext='" + readOnlyContext + "'"); + } + + return readOnlyContext; + } else { + throw new IllegalArgumentException("Unrecognized ContextType: " + + contextType); + } + } + + /** + * @see org.apache.commons.pool.BaseKeyedPoolableObjectFactory#validateObject(java.lang.Object, + * java.lang.Object) + */ + public boolean validateObject(Object key, Object obj) { + Validate.notNull(this.dirContextValidator, + "DirContextValidator may not be null"); + Validate.isTrue(key instanceof DirContextType, + "key must be a DirContextType"); + Validate.isTrue(obj instanceof DirContext, + "The Object to validate must be of type '" + DirContext.class + + "'"); + + try { + final DirContextType contextType = (DirContextType) key; + final DirContext dirContext = (DirContext) obj; + return this.dirContextValidator.validateDirContext(contextType, + dirContext); + } catch (Exception e) { + this.logger.warn("Failed to validate '" + obj + + "' due to an unexpected exception.", e); + return false; + } + } + + + /** + * @see org.apache.commons.pool.BaseKeyedPoolableObjectFactory#destroyObject(java.lang.Object, + * java.lang.Object) + */ + public void destroyObject(Object key, Object obj) throws Exception { + Validate.isTrue(obj instanceof DirContext, + "The Object to validate must be of type '" + DirContext.class + + "'"); + + try { + final DirContext dirContext = (DirContext) obj; + if (this.logger.isDebugEnabled()) { + this.logger.debug("Closing " + key + " DirContext='" + + dirContext + "'"); + } + dirContext.close(); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Closed " + key + " DirContext='" + + dirContext + "'"); + } + } catch (Exception e) { + this.logger.warn( + "An exception occured while closing '" + obj + "'", e); + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/PoolingContextSource.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/PoolingContextSource.java new file mode 100644 index 00000000..1e5b1f87 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/PoolingContextSource.java @@ -0,0 +1,406 @@ +/* + * Copyright 2005-2007 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.pool.factory; + +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.impl.GenericKeyedObjectPool; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.ldap.NamingException; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.pool.DelegatingDirContext; +import org.springframework.ldap.pool.DelegatingLdapContext; +import org.springframework.ldap.pool.DirContextType; +import org.springframework.ldap.pool.validation.DirContextValidator; + +/** + * A {@link ContextSource} implementation that wraps an object pool and another {@link ContextSource}. + * {@link DirContext}s are retrieved from the pool which maintains them. + * + * + *
+ *
+ * Configuration: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
PropertyDescriptionRequiredDefault
contextSource + * The {@link ContextSource} to get {@link DirContext}s from for adding to the pool. + * Yesnull
dirContextValidator + * The {@link DirContextValidator} to use for validating {@link DirContext}s. Required + * if any of the test/validate options are enabled. + * Nonull
minIdle{@link GenericKeyedObjectPool#setMinIdle(int)}No0
maxIdle{@link GenericKeyedObjectPool#setMaxIdle(int)}No8
maxActive{@link GenericKeyedObjectPool#setMaxActive(int)}No8
maxTotal{@link GenericKeyedObjectPool#setMaxTotal(int)}No-1
maxWait{@link GenericKeyedObjectPool#setMaxWait(long)}No-1L
whenExhaustedAction{@link GenericKeyedObjectPool#setWhenExhaustedAction(byte)}No{@link GenericKeyedObjectPool#WHEN_EXHAUSTED_BLOCK}
testOnBorrow{@link GenericKeyedObjectPool#setTestOnBorrow(boolean)}Nofalse
testOnReturn{@link GenericKeyedObjectPool#setTestOnReturn(boolean)}Nofalse
testWhileIdle{@link GenericKeyedObjectPool#setTestWhileIdle(boolean)}Nofalse
timeBetweenEvictionRunsMillis{@link GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis(long)}No-1L
minEvictableIdleTimeMillis{@link GenericKeyedObjectPool#setMinEvictableIdleTimeMillis(long)}No1000L * 60L * 30L
numTestsPerEvictionRun{@link GenericKeyedObjectPool#setNumTestsPerEvictionRun(int)}No3
+ * + * + * @author Eric Dalquist + */ +public class PoolingContextSource implements ContextSource, DisposableBean { + /** + * The logger for this class and sub-classes + */ + protected final Log logger = LogFactory.getLog(this.getClass()); + + private final GenericKeyedObjectPool keyedObjectPool; + private final DirContextPoolableObjectFactory dirContextPoolableObjectFactory; + + /** + * Creates a new pooling context source, setting up the DirContext object factory + * and generic keyed object pool. + */ + public PoolingContextSource() { + this.dirContextPoolableObjectFactory = new DirContextPoolableObjectFactory(); + this.keyedObjectPool = new GenericKeyedObjectPool(); + this.keyedObjectPool.setFactory(this.dirContextPoolableObjectFactory); + } + + + //***** Pool Property Configuration *****// + + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getMaxActive() + */ + public int getMaxActive() { + return this.keyedObjectPool.getMaxActive(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getMaxIdle() + */ + public int getMaxIdle() { + return this.keyedObjectPool.getMaxIdle(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getMaxTotal() + */ + public int getMaxTotal() { + return this.keyedObjectPool.getMaxTotal(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getMaxWait() + */ + public long getMaxWait() { + return this.keyedObjectPool.getMaxWait(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() + */ + public long getMinEvictableIdleTimeMillis() { + return this.keyedObjectPool.getMinEvictableIdleTimeMillis(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getMinIdle() + */ + public int getMinIdle() { + return this.keyedObjectPool.getMinIdle(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getNumActive() + */ + public int getNumActive() { + return this.keyedObjectPool.getNumActive(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getNumIdle() + */ + public int getNumIdle() { + return this.keyedObjectPool.getNumIdle(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getNumTestsPerEvictionRun() + */ + public int getNumTestsPerEvictionRun() { + return this.keyedObjectPool.getNumTestsPerEvictionRun(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getTestOnBorrow() + */ + public boolean getTestOnBorrow() { + return this.keyedObjectPool.getTestOnBorrow(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getTestOnReturn() + */ + public boolean getTestOnReturn() { + return this.keyedObjectPool.getTestOnReturn(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getTestWhileIdle() + */ + public boolean getTestWhileIdle() { + return this.keyedObjectPool.getTestWhileIdle(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() + */ + public long getTimeBetweenEvictionRunsMillis() { + return this.keyedObjectPool.getTimeBetweenEvictionRunsMillis(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#getWhenExhaustedAction() + */ + public byte getWhenExhaustedAction() { + return this.keyedObjectPool.getWhenExhaustedAction(); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setMaxActive(int) + */ + public void setMaxActive(int maxActive) { + this.keyedObjectPool.setMaxActive(maxActive); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setMaxIdle(int) + */ + public void setMaxIdle(int maxIdle) { + this.keyedObjectPool.setMaxIdle(maxIdle); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setMaxTotal(int) + */ + public void setMaxTotal(int maxTotal) { + this.keyedObjectPool.setMaxTotal(maxTotal); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setMaxWait(long) + */ + public void setMaxWait(long maxWait) { + this.keyedObjectPool.setMaxWait(maxWait); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setMinEvictableIdleTimeMillis(long) + */ + public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { + this.keyedObjectPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setMinIdle(int) + */ + public void setMinIdle(int poolSize) { + this.keyedObjectPool.setMinIdle(poolSize); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setNumTestsPerEvictionRun(int) + */ + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { + this.keyedObjectPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setTestOnBorrow(boolean) + */ + public void setTestOnBorrow(boolean testOnBorrow) { + this.keyedObjectPool.setTestOnBorrow(testOnBorrow); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setTestOnReturn(boolean) + */ + public void setTestOnReturn(boolean testOnReturn) { + this.keyedObjectPool.setTestOnReturn(testOnReturn); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setTestWhileIdle(boolean) + */ + public void setTestWhileIdle(boolean testWhileIdle) { + this.keyedObjectPool.setTestWhileIdle(testWhileIdle); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis(long) + */ + public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { + this.keyedObjectPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + } + /** + * @see org.apache.commons.pool.impl.GenericKeyedObjectPool#setWhenExhaustedAction(byte) + */ + public void setWhenExhaustedAction(byte whenExhaustedAction) { + this.keyedObjectPool.setWhenExhaustedAction(whenExhaustedAction); + } + + + //***** Object Factory Property Configuration *****// + + /** + * @return the contextSource + */ + public ContextSource getContextSource() { + return this.dirContextPoolableObjectFactory.getContextSource(); + } + /** + * @return the dirContextValidator + */ + public DirContextValidator getDirContextValidator() { + return this.dirContextPoolableObjectFactory.getDirContextValidator(); + } + /** + * @param contextSource the contextSource to set + * @Required + */ + public void setContextSource(ContextSource contextSource) { + this.dirContextPoolableObjectFactory.setContextSource(contextSource); + } + /** + * @param dirContextValidator the dirContextValidator to set + * @Required + */ + public void setDirContextValidator(DirContextValidator dirContextValidator) { + this.dirContextPoolableObjectFactory.setDirContextValidator(dirContextValidator); + } + + + //***** DisposableBean interface methods *****// + + /* (non-Javadoc) + * @see org.springframework.beans.factory.DisposableBean#destroy() + */ + public void destroy() throws Exception { + try { + this.keyedObjectPool.close(); + } + catch (Exception e) { + this.logger.warn("An exception occured while closing the underlying pool.", e); + } + } + + + //***** ContextSource interface methods *****// + + /* + * @see ContextSource#getReadOnlyContext() + */ + public DirContext getReadOnlyContext() throws NamingException { + return this.getContext(DirContextType.READ_ONLY); + } + + /* + * @see ContextSource#getReadWriteContext() + */ + public DirContext getReadWriteContext() throws NamingException { + return this.getContext(DirContextType.READ_WRITE); + } + + /** + * Gets a DirContext of the specified type from the keyed object pool. + * + * @param dirContextType The type of context to return. + * @return A wrapped DirContext of the specified type. + * @throws DataAccessResourceFailureException If retreiving the object from the pool throws an exception + */ + protected DirContext getContext(DirContextType dirContextType) { + final DirContext dirContext; + try { + dirContext = (DirContext)this.keyedObjectPool.borrowObject(dirContextType); + } + catch (Exception e) { + throw new DataAccessResourceFailureException("Failed to borrow DirContext from pool.", e); + } + + if (dirContext instanceof LdapContext) { + return new DelegatingLdapContext(this.keyedObjectPool, (LdapContext)dirContext, dirContextType); + } + + return new DelegatingDirContext(this.keyedObjectPool, dirContext, dirContextType); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/package.html new file mode 100644 index 00000000..bcf1cd83 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/factory/package.html @@ -0,0 +1,7 @@ + + + +Core classes for the pooling library. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/pool/package.html new file mode 100644 index 00000000..85bee723 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/package.html @@ -0,0 +1,7 @@ + + + +Base classes for the pooling library. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/DefaultDirContextValidator.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/DefaultDirContextValidator.java new file mode 100644 index 00000000..450e88e4 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/DefaultDirContextValidator.java @@ -0,0 +1,173 @@ +/* + * Copyright 2005-2007 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.pool.validation; + +import javax.naming.NamingEnumeration; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; + +import org.apache.commons.lang.Validate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.pool.DirContextType; + +/** + * Default {@link DirContext} validator that executes {@link DirContext#search(String, String, SearchControls)}. The + * name, filter and {@link SearchControls} are all configurable. There is no special handling for read only versus + * read write {@link DirContext}s. + * + *
+ *
+ * Configuration: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
PropertyDescriptionRequiredDefault
base + * The name parameter to the search method. + * No""
filter + * The filter parameter to the search method. + * No"objectclass=*"
searchControls + * The {@link SearchControls} parameter to the search method. + * No + * {@link SearchControls#setCountLimit(long)} = 1
+ * {@link SearchControls#setReturningAttributes(String[])} = new String[] { "objectclass" }
+ * {@link SearchControls#setTimeLimit(int)} = 500 + *
+ * + * @author Eric Dalquist + */ +public class DefaultDirContextValidator implements DirContextValidator { + /** + * Logger for this class and sub-classes + */ + protected final Log logger = LogFactory.getLog(this.getClass()); + + private String base; + private String filter; + private SearchControls searchControls; + + /** + * Create the default validator, creates {@link SearchControls} with a countLimit of 1, returningAttributes of + * objectclass and timeLimit of 500. The default base is an empty string and the default filter is objectclass=* + */ + public DefaultDirContextValidator() { + this.searchControls = new SearchControls(); + this.searchControls.setCountLimit(1); + this.searchControls.setReturningAttributes(new String[] { "objectclass" }); + this.searchControls.setTimeLimit(500); + + this.base = ""; + + this.filter = "objectclass=*"; + } + + /** + * @return the baseName + */ + public String getBase() { + return this.base; + } + /** + * @param base the baseName to set + */ + public void setBase(String base) { + this.base = base; + } + /** + * @return the filter + */ + public String getFilter() { + return this.filter; + } + /** + * @param filter the filter to set + */ + public void setFilter(String filter) { + if (filter == null) { + throw new IllegalArgumentException("filter may not be null"); + } + + this.filter = filter; + } + /** + * @return the searchControls + */ + public SearchControls getSearchControls() { + return this.searchControls; + } + /** + * @param searchControls the searchControls to set + */ + public void setSearchControls(SearchControls searchControls) { + if (searchControls == null) { + throw new IllegalArgumentException("searchControls may not be null"); + } + + this.searchControls = searchControls; + } + + + /** + * @see DirContextValidator#validateDirContext(DirContextType, javax.naming.directory.DirContext) + */ + public boolean validateDirContext(DirContextType contextType, DirContext dirContext) { + Validate.notNull(contextType, "contextType may not be null"); + Validate.notNull(dirContext, "dirContext may not be null"); + + try { + final NamingEnumeration searchResults = dirContext.search(this.base, this.filter, this.searchControls); + + if (searchResults.hasMore()) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("DirContext '" + dirContext + "' passed validation."); + } + + return true; + } + } + catch (Exception e) { + this.logger.warn("DirContext '" + dirContext + "' failed validation with an exception.", e); + } + + if (this.logger.isInfoEnabled()) { + this.logger.info("DirContext '" + dirContext + "' failed validation."); + } + return false; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/DirContextValidator.java b/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/DirContextValidator.java new file mode 100644 index 00000000..5eab0faf --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/DirContextValidator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2005-2007 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.pool.validation; + +import javax.naming.directory.DirContext; + +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.pool.DirContextType; + +/** + * A validator for {@link DirContext}s. + * + * @author Eric Dalquist + */ +public interface DirContextValidator { + /** + * Validates the {@link DirContext}. A valid {@link DirContext} should be able + * to answer queries and if applicable write to the directory. + * + * @param contextType The type of the {@link DirContext}, refers to if {@link ContextSource#getReadOnlyContext()} or {@link ContextSource#getReadWriteContext()} was called to create the {@link DirContext} + * @param dirContext The {@link DirContext} to validate. + * @return true if the {@link DirContext} operated correctly during validation. + */ + public boolean validateDirContext(DirContextType contextType, DirContext dirContext); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/package.html new file mode 100644 index 00000000..788f99a5 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/pool/validation/package.html @@ -0,0 +1,7 @@ + + + +Connection validation support for the pooling library. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/support/LdapUtils.java b/mvn-build/core/src/main/java/org/springframework/ldap/support/LdapUtils.java new file mode 100644 index 00000000..ba03e977 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/support/LdapUtils.java @@ -0,0 +1,209 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.directory.DirContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.NamingException; +import org.springframework.util.Assert; + +/** + * Generic utility methods for working with LDAP. Mainly for internal use within + * the framework, but also useful for custom code. + * + * @author Ulrik Sandberg + * @since 1.2 + */ +public final class LdapUtils { + + private static final Log logger = LogFactory.getLog(LdapUtils.class); + + /** + * Not to be instantiated. + */ + private LdapUtils() { + + } + + /** + * Close the given JNDI Context and ignore any thrown exception. This is + * useful for typical finally blocks in JNDI code. + * + * @param context the JNDI Context to close (may be null) + */ + public static void closeContext(DirContext context) { + if (context != null) { + try { + context.close(); + } + catch (NamingException ex) { + logger.debug("Could not close JNDI DirContext", ex); + } + catch (Throwable ex) { + // We don't trust the JNDI provider: It might throw + // RuntimeException or Error. + logger.debug("Unexpected exception on closing JNDI DirContext", ex); + } + } + } + + /** + * 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 + * @return the Spring LDAP runtime NamingException wrapping the given + * exception + */ + public static NamingException convertLdapException(javax.naming.NamingException ex) { + Assert.notNull(ex, "NamingException must not be null"); + + if (ex.getClass().equals(javax.naming.directory.AttributeInUseException.class)) { + return new org.springframework.ldap.AttributeInUseException( + (javax.naming.directory.AttributeInUseException) ex); + } + if (ex.getClass().equals(javax.naming.directory.AttributeModificationException.class)) { + return new org.springframework.ldap.AttributeModificationException( + (javax.naming.directory.AttributeModificationException) ex); + } + if (ex.getClass().equals(javax.naming.CannotProceedException.class)) { + return new org.springframework.ldap.CannotProceedException((javax.naming.CannotProceedException) ex); + } + if (ex.getClass().equals(javax.naming.CommunicationException.class)) { + return new org.springframework.ldap.CommunicationException((javax.naming.CommunicationException) ex); + } + if (ex.getClass().equals(javax.naming.ConfigurationException.class)) { + return new org.springframework.ldap.ConfigurationException((javax.naming.ConfigurationException) ex); + } + if (ex.getClass().equals(javax.naming.ContextNotEmptyException.class)) { + return new org.springframework.ldap.ContextNotEmptyException((javax.naming.ContextNotEmptyException) ex); + } + if (ex.getClass().equals(javax.naming.InsufficientResourcesException.class)) { + return new org.springframework.ldap.InsufficientResourcesException( + (javax.naming.InsufficientResourcesException) ex); + } + if (ex.getClass().equals(javax.naming.InterruptedNamingException.class)) { + return new org.springframework.ldap.InterruptedNamingException((javax.naming.InterruptedNamingException) ex); + } + if (ex.getClass().equals(javax.naming.directory.InvalidAttributeIdentifierException.class)) { + return new org.springframework.ldap.InvalidAttributeIdentifierException( + (javax.naming.directory.InvalidAttributeIdentifierException) ex); + } + if (ex.getClass().equals(javax.naming.directory.InvalidAttributesException.class)) { + return new org.springframework.ldap.InvalidAttributesException( + (javax.naming.directory.InvalidAttributesException) ex); + } + if (ex.getClass().equals(javax.naming.directory.InvalidAttributeValueException.class)) { + return new org.springframework.ldap.InvalidAttributeValueException( + (javax.naming.directory.InvalidAttributeValueException) ex); + } + if (ex.getClass().equals(javax.naming.InvalidNameException.class)) { + return new org.springframework.ldap.InvalidNameException((javax.naming.InvalidNameException) ex); + } + if (ex.getClass().equals(javax.naming.directory.InvalidSearchControlsException.class)) { + return new org.springframework.ldap.InvalidSearchControlsException( + (javax.naming.directory.InvalidSearchControlsException) ex); + } + if (ex.getClass().equals(javax.naming.directory.InvalidSearchFilterException.class)) { + 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 + if (ex instanceof javax.naming.ldap.LdapReferralException) { + return new org.springframework.ldap.LdapReferralException((javax.naming.ldap.LdapReferralException) ex); + } + // Skipping the abstract superclass javax.naming.ReferralException + + // LimitExceededException hierarchy + if (ex.getClass().equals(javax.naming.SizeLimitExceededException.class)) { + return new org.springframework.ldap.SizeLimitExceededException((javax.naming.SizeLimitExceededException) ex); + } + if (ex.getClass().equals(javax.naming.TimeLimitExceededException.class)) { + return new org.springframework.ldap.TimeLimitExceededException((javax.naming.TimeLimitExceededException) ex); + } + // this class is the superclass of the two above + if (ex.getClass().equals(javax.naming.LimitExceededException.class)) { + return new org.springframework.ldap.LimitExceededException((javax.naming.LimitExceededException) ex); + } + + // LinkException hierarchy + if (ex.getClass().equals(javax.naming.LinkLoopException.class)) { + return new org.springframework.ldap.LinkLoopException((javax.naming.LinkLoopException) ex); + } + if (ex.getClass().equals(javax.naming.MalformedLinkException.class)) { + return new org.springframework.ldap.MalformedLinkException((javax.naming.MalformedLinkException) ex); + } + // this class is the superclass of the two above + if (ex.getClass().equals(javax.naming.LinkException.class)) { + return new org.springframework.ldap.LinkException((javax.naming.LinkException) ex); + } + + if (ex.getClass().equals(javax.naming.NameAlreadyBoundException.class)) { + return new org.springframework.ldap.NameAlreadyBoundException((javax.naming.NameAlreadyBoundException) ex); + } + if (ex.getClass().equals(javax.naming.NameNotFoundException.class)) { + return new org.springframework.ldap.NameNotFoundException((javax.naming.NameNotFoundException) ex); + } + + // NamingSecurityException hierarchy + if (ex.getClass().equals(javax.naming.NoPermissionException.class)) { + return new org.springframework.ldap.NoPermissionException((javax.naming.NoPermissionException) ex); + } + if (ex.getClass().equals(javax.naming.AuthenticationException.class)) { + return new org.springframework.ldap.AuthenticationException((javax.naming.AuthenticationException) ex); + } + if (ex.getClass().equals(javax.naming.AuthenticationNotSupportedException.class)) { + return new org.springframework.ldap.AuthenticationNotSupportedException( + (javax.naming.AuthenticationNotSupportedException) ex); + } + // Skipping the abstract superclass javax.naming.NamingSecurityException + + if (ex.getClass().equals(javax.naming.NoInitialContextException.class)) { + return new org.springframework.ldap.NoInitialContextException((javax.naming.NoInitialContextException) ex); + } + if (ex.getClass().equals(javax.naming.directory.NoSuchAttributeException.class)) { + return new org.springframework.ldap.NoSuchAttributeException( + (javax.naming.directory.NoSuchAttributeException) ex); + } + if (ex.getClass().equals(javax.naming.NotContextException.class)) { + return new org.springframework.ldap.NotContextException((javax.naming.NotContextException) ex); + } + if (ex.getClass().equals(javax.naming.OperationNotSupportedException.class)) { + return new org.springframework.ldap.OperationNotSupportedException( + (javax.naming.OperationNotSupportedException) ex); + } + if (ex.getClass().equals(javax.naming.PartialResultException.class)) { + return new org.springframework.ldap.PartialResultException((javax.naming.PartialResultException) ex); + } + if (ex.getClass().equals(javax.naming.directory.SchemaViolationException.class)) { + return new org.springframework.ldap.SchemaViolationException( + (javax.naming.directory.SchemaViolationException) ex); + } + if (ex.getClass().equals(javax.naming.ServiceUnavailableException.class)) { + return new org.springframework.ldap.ServiceUnavailableException( + (javax.naming.ServiceUnavailableException) ex); + } + + // fallback + return new org.springframework.ldap.UncategorizedLdapException(ex); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/support/ListComparator.java b/mvn-build/core/src/main/java/org/springframework/ldap/support/ListComparator.java new file mode 100644 index 00000000..1e5c9807 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/support/ListComparator.java @@ -0,0 +1,65 @@ +/* + * Copyright 2005-2007 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; + +import java.util.Comparator; +import java.util.List; + +/** + * Comparator for comparing lists of Comparable objects. + * + * @author Mattias Arthursson + */ +public class ListComparator implements Comparator { + + /** + * Compare two lists of Comparable objects. + * + * @param o1 the first object to be compared. + * @param o2 the second object to be compared. + * @throws ClassCastException if any of the lists contains an object that + * is not Comparable. + */ + public int compare(Object o1, Object o2) { + List list1 = (List) o1; + List list2 = (List) o2; + + for (int i = 0; i < list1.size(); i++) { + if (list2.size() > i) { + Comparable component1 = (Comparable) list1.get(i); + Comparable component2 = (Comparable) list2.get(i); + int componentsCompared = component1.compareTo(component2); + if (componentsCompared != 0) { + return componentsCompared; + } + } + else { + // First instance has more components, so that instance is + // greater. + return 1; + } + } + + // All components so far are equal - if the other instance has + // more components it is greater otherwise they are equal. + if (list2.size() > list1.size()) { + return -1; + } + else { + return 0; + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/support/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/support/package.html new file mode 100644 index 00000000..5eabecc8 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/support/package.html @@ -0,0 +1,7 @@ + + + +Support classes for Spring-LDAP. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/BindOperationExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/BindOperationExecutor.java new file mode 100644 index 00000000..27805ae0 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/BindOperationExecutor.java @@ -0,0 +1,127 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; +import javax.naming.directory.Attributes; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +/** + * A {@link CompensatingTransactionOperationExecutor} to manage a bind + * operation. Performs a bind in {@link #performOperation()}, a corresponding + * unbind in {@link #rollback()}, and nothing in {@link #commit()}. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class BindOperationExecutor implements + CompensatingTransactionOperationExecutor { + private static Log log = LogFactory.getLog(BindOperationExecutor.class); + + private LdapOperations ldapOperations; + + private Name dn; + + private Object originalObject; + + private Attributes originalAttributes; + + /** + * Constructor. + * + * @param ldapOperations + * {@link LdapOperations} to use for performing the rollback + * operation. + * @param dn + * DN of the entry to be unbound. + * @param originalObject + * original value sent to the 'object' parameter of the bind + * operation. + * @param originalAttributes + * original value sent to the 'attributes' parameter of the bind + * operation. + */ + public BindOperationExecutor(LdapOperations ldapOperations, Name dn, + Object originalObject, Attributes originalAttributes) { + this.ldapOperations = ldapOperations; + this.dn = dn; + this.originalObject = originalObject; + this.originalAttributes = originalAttributes; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#rollback() + */ + public void rollback() { + try { + ldapOperations.unbind(dn); + } catch (Exception e) { + log.warn("Failed to rollback, dn:" + dn.toString(), e); + } + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#commit() + */ + public void commit() { + log.debug("Nothing to do in commit for bind operation"); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#performOperation() + */ + public void performOperation() { + log.debug("Performing bind operation"); + ldapOperations.bind(dn, originalObject, originalAttributes); + } + + /** + * Get the DN. Package private for testing purposes. + * + * @return the target DN. + */ + Name getDn() { + return dn; + } + + /** + * Get the LdapOperations. Package private for testing purposes. + * + * @return the LdapOperations. + */ + LdapOperations getLdapOperations() { + return ldapOperations; + } + + Attributes getOriginalAttributes() { + return originalAttributes; + } + + Object getOriginalObject() { + return originalObject; + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/BindOperationRecorder.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/BindOperationRecorder.java new file mode 100644 index 00000000..d6f8d179 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/BindOperationRecorder.java @@ -0,0 +1,79 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; + +/** + * A {@link CompensatingTransactionOperationRecorder} keeping track of bind + * operations. Creates {@link BindOperationExecutor} objects in + * {@link #recordOperation(Object[])}. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class BindOperationRecorder implements + CompensatingTransactionOperationRecorder { + + private LdapOperations ldapOperations; + + /** + * Constructor. + * + * @param ldapOperations + * {@link LdapOperations} to use for supplying to the + * corresponding rollback operation. + */ + public BindOperationRecorder(LdapOperations ldapOperations) { + this.ldapOperations = ldapOperations; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationRecorder#recordOperation(java.lang.Object[]) + */ + public CompensatingTransactionOperationExecutor recordOperation( + Object[] args) { + if (args == null || args.length != 3) { + throw new IllegalArgumentException( + "Invalid arguments for bind operation"); + } + Name dn = LdapTransactionUtils.getFirstArgumentAsName(args); + Object object = args[1]; + Attributes attributes = null; + if (args[2] != null && !(args[2] instanceof Attributes)) { + throw new IllegalArgumentException( + "Invalid third argument to bind operation"); + } else if (args[2] != null) { + attributes = (Attributes) args[2]; + } + + return new BindOperationExecutor(ldapOperations, dn, object, attributes); + } + + /** + * Get the LdapOperations. For testing purposes.s + * + * @return the LdapOperations. + */ + LdapOperations getLdapOperations() { + return ldapOperations; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/LdapCompensatingTransactionOperationFactory.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/LdapCompensatingTransactionOperationFactory.java new file mode 100644 index 00000000..968b7396 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/LdapCompensatingTransactionOperationFactory.java @@ -0,0 +1,194 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import javax.naming.directory.DirContext; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.NamingException; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DirContextProxy; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.transaction.compensating.CompensatingTransactionOperationFactory; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; + +/** + * {@link CompensatingTransactionOperationRecorder} implementation for LDAP + * operations. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class LdapCompensatingTransactionOperationFactory implements + CompensatingTransactionOperationFactory { + private static Log log = LogFactory + .getLog(LdapCompensatingTransactionOperationFactory.class); + + private TempEntryRenamingStrategy renamingStrategy; + + /** + * Constructor. + * + * @param renamingStrategy + * the {@link TempEntryRenamingStrategy} to supply to relevant + * operations. + */ + public LdapCompensatingTransactionOperationFactory( + TempEntryRenamingStrategy renamingStrategy) { + this.renamingStrategy = renamingStrategy; + } + + /* + * @see org.springframework.transaction.compensating.CompensatingTransactionOperationFactory#createRecordingOperation(java.lang.Object, + * java.lang.String) + */ + public CompensatingTransactionOperationRecorder createRecordingOperation( + Object resource, String operation) { + if (StringUtils + .equals(operation, LdapTransactionUtils.BIND_METHOD_NAME)) { + log.debug("Bind operation recorded"); + return new BindOperationRecorder( + createLdapOperationsInstance((DirContext) resource)); + } else if (StringUtils.equals(operation, + LdapTransactionUtils.REBIND_METHOD_NAME)) { + log.debug("Rebind operation recorded"); + return new RebindOperationRecorder( + createLdapOperationsInstance((DirContext) resource), + renamingStrategy); + } else if (StringUtils.equals(operation, + LdapTransactionUtils.RENAME_METHOD_NAME)) { + log.debug("Rename operation recorded"); + return new RenameOperationRecorder( + createLdapOperationsInstance((DirContext) resource)); + } else if (StringUtils.equals(operation, + LdapTransactionUtils.MODIFY_ATTRIBUTES_METHOD_NAME)) { + return new ModifyAttributesOperationRecorder( + createLdapOperationsInstance((DirContext) resource)); + } else if (StringUtils.equals(operation, + LdapTransactionUtils.UNBIND_METHOD_NAME)) { + return new UnbindOperationRecorder( + createLdapOperationsInstance((DirContext) resource), + renamingStrategy); + } + + log + .warn("No suitable CompensatingTransactionOperationRecorder found for method " + + operation + ". Operation will not be transacted."); + return new NullOperationRecorder(); + } + + LdapOperations createLdapOperationsInstance(DirContext ctx) { + return new LdapTemplate(new SingleContextSource(ctx)); + } + + /** + * A {@link ContextSource} implementation using returning + * {@link NonClosingDirContextInvocationHandler} proxies on the same + * DirContext instance for each call. + * + * @author Mattias Arthursson + */ + static class SingleContextSource implements ContextSource { + private DirContext ctx; + + /** + * Constructor. + * + * @param ctx + * the target DirContext. + */ + public SingleContextSource(DirContext ctx) { + this.ctx = ctx; + } + + /* + * @see org.springframework.ldap.ContextSource#getReadOnlyContext() + */ + public DirContext getReadOnlyContext() throws NamingException { + return getNonClosingDirContextProxy(ctx); + } + + /* + * @see org.springframework.ldap.ContextSource#getReadWriteContext() + */ + public DirContext getReadWriteContext() throws NamingException { + return getNonClosingDirContextProxy(ctx); + } + + private DirContext getNonClosingDirContextProxy(DirContext context) { + return (DirContext) Proxy.newProxyInstance(DirContextProxy.class + .getClassLoader(), new Class[] { + LdapTransactionUtils.getActualTargetClass(context), + DirContextProxy.class }, + new NonClosingDirContextInvocationHandler(context)); + + } + } + + /** + * A proxy for DirContext forwarding all operation to the target DirContext, + * but making sure that no close operations will be + * performed. + * + * @author Mattias Arthursson + */ + public static class NonClosingDirContextInvocationHandler implements + InvocationHandler { + + private DirContext target; + + public NonClosingDirContextInvocationHandler(DirContext target) { + this.target = target; + } + + /* + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, + * java.lang.reflect.Method, java.lang.Object[]) + */ + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + + String methodName = method.getName(); + if (methodName.equals("getTargetContext")) { + return target; + } else if (methodName.equals("equals")) { + // Only consider equal when proxies are identical. + return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); + } else if (methodName.equals("hashCode")) { + // Use hashCode of Connection proxy. + return new Integer(proxy.hashCode()); + } else if (methodName.equals("close")) { + // Never close the target context, as this class will only be + // used for operations concerning the compensating transactions. + return null; + } + + try { + return method.invoke(target, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/LdapTransactionUtils.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/LdapTransactionUtils.java new file mode 100644 index 00000000..4f56746c --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/LdapTransactionUtils.java @@ -0,0 +1,121 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.util.Assert; + +/** + * Utility methods for working with LDAP transactions. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public final class LdapTransactionUtils { + + public static final String REBIND_METHOD_NAME = "rebind"; + + public static final String BIND_METHOD_NAME = "bind"; + + public static final String RENAME_METHOD_NAME = "rename"; + + public static final String UNBIND_METHOD_NAME = "unbind"; + + public static final String MODIFY_ATTRIBUTES_METHOD_NAME = "modifyAttributes"; + + /** + * Not to be instantiated. + */ + private LdapTransactionUtils() { + + } + + /** + * Get the first parameter in the argument list as a Name. + * + * @param args + * arguments supplied to a ldap operation. + * @return a Name representation of the first argument, or the Name itself + * if it is a name. + */ + public static Name getFirstArgumentAsName(Object[] args) { + Assert.notEmpty(args); + + Object firstArg = args[0]; + return getArgumentAsName(firstArg); + } + + /** + * Get the argument as a Name. + * + * @param arg + * an argument supplied to an Ldap operation. + * @return a Name representation of the argument, or the Name itself if it + * is a Name. + */ + public static Name getArgumentAsName(Object arg) { + if (arg instanceof String) { + return new DistinguishedName((String) arg); + } else if (arg instanceof Name) { + return (Name) arg; + } else { + throw new IllegalArgumentException( + "First argument needs to be a Name or a String representation thereof"); + } + } + + /** + * Check whether the supplied method is a method for which transactions is + * supported (and which should be recorded for possible rollback later). + * + * @param methodName + * name of the method to check. + * @return true if this is a supported transaction operation, + * false otherwise. + */ + public static boolean isSupportedWriteTransactionOperation(String methodName) { + return (StringUtils.equals(methodName, BIND_METHOD_NAME) + || StringUtils.equals(methodName, REBIND_METHOD_NAME) + || StringUtils.equals(methodName, RENAME_METHOD_NAME) + || StringUtils + .equals(methodName, MODIFY_ATTRIBUTES_METHOD_NAME) || StringUtils + .equals(methodName, UNBIND_METHOD_NAME)); + + } + + /** + * Get the actual class of the supplied DirContext instance; LdapContext or + * DirContext. + * + * @param context + * the DirContext instance to check. + * @return LdapContext.class if context is an LdapContext, DirContext.class + * otherwise. + */ + public static Class getActualTargetClass(DirContext context) { + if (context instanceof LdapContext) { + return LdapContext.class; + } + + return DirContext.class; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationExecutor.java new file mode 100644 index 00000000..c413ef68 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationExecutor.java @@ -0,0 +1,116 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; +import javax.naming.directory.ModificationItem; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +/** + * A {@link CompensatingTransactionOperationExecutor} to manage a + * modifyAttributes operation. Performs a + * modifyAttributes in {@link #performOperation()}, a negating + * modifyAttributes in {@link #rollback()}, and nothing in {@link #commit()}. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class ModifyAttributesOperationExecutor implements + CompensatingTransactionOperationExecutor { + + private static Log log = LogFactory + .getLog(ModifyAttributesOperationExecutor.class); + + private LdapOperations ldapOperations; + + private Name dn; + + private ModificationItem[] compensatingModifications; + + private ModificationItem[] actualModifications; + + /** + * Constructor. + * + * @param ldapOperations + * The {@link LdapOperations} to use to perform the rollback + * operation. + * @param dn + * the DN of the target entry. + * @param actualModifications + * the actual modificationItems that were sent to the + * modifyAttributes operation. + * @param compensatingModifications + * the ModificationItems to undo the recorded operation. + */ + public ModifyAttributesOperationExecutor(LdapOperations ldapOperations, + Name dn, ModificationItem[] actualModifications, + ModificationItem[] compensatingModifications) { + this.ldapOperations = ldapOperations; + this.dn = dn; + this.actualModifications = actualModifications; + this.compensatingModifications = compensatingModifications; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#rollback() + */ + public void rollback() { + try { + log.debug("Rolling back modifyAttributes operation"); + ldapOperations.modifyAttributes(dn, compensatingModifications); + } catch (Exception e) { + log + .warn("Failed to rollback ModifyAttributes operation, dn: " + + dn); + } + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#commit() + */ + public void commit() { + log.debug("Nothing to do in commit for modifyAttributes"); + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#performOperation() + */ + public void performOperation() { + log.debug("Performing modifyAttributes operation"); + ldapOperations.modifyAttributes(dn, actualModifications); + } + + Name getDn() { + return dn; + } + + LdapOperations getLdapOperations() { + return ldapOperations; + } + + ModificationItem[] getActualModifications() { + return actualModifications; + } + + ModificationItem[] getCompensatingModifications() { + return compensatingModifications; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationRecorder.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationRecorder.java new file mode 100644 index 00000000..0a96ccef --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationRecorder.java @@ -0,0 +1,165 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import java.util.HashSet; +import java.util.Set; + +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; + +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; +import org.springframework.util.Assert; + +/** + * A {@link CompensatingTransactionOperationRecorder} keeping track of + * modifyAttributes operations, creating corresponding + * {@link ModifyAttributesOperationExecutor} instances for rollback. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class ModifyAttributesOperationRecorder implements + CompensatingTransactionOperationRecorder { + + private LdapOperations ldapOperations; + + public ModifyAttributesOperationRecorder(LdapOperations ldapOperations) { + this.ldapOperations = ldapOperations; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationRecorder#recordOperation(java.lang.Object[]) + */ + public CompensatingTransactionOperationExecutor recordOperation( + Object[] args) { + Assert.notNull(args); + Name dn = LdapTransactionUtils.getFirstArgumentAsName(args); + if (args.length != 2 || !(args[1] instanceof ModificationItem[])) { + throw new IllegalArgumentException( + "Unexpected arguments to ModifyAttributes operation"); + } + + ModificationItem[] incomingModifications = (ModificationItem[]) args[1]; + + Set set = new HashSet(); + for (int i = 0; i < incomingModifications.length; i++) { + set.add(incomingModifications[i].getAttribute().getID()); + } + + // Get the current values of all referred Attributes. + String[] attributeNameArray = (String[]) set.toArray(new String[set + .size()]); + Attributes currentAttributes = (Attributes) ldapOperations.lookup(dn, + attributeNameArray, getAttributesMapper()); + + // Get a compensating ModificationItem for each of the incoming + // modification. + ModificationItem[] rollbackItems = new ModificationItem[incomingModifications.length]; + for (int i = 0; i < incomingModifications.length; i++) { + rollbackItems[i] = getCompensatingModificationItem( + currentAttributes, incomingModifications[i]); + } + + return new ModifyAttributesOperationExecutor(ldapOperations, dn, + incomingModifications, rollbackItems); + } + + /** + * Get an {@link AttributesMapper} that just returns the supplied + * Attributes. + * + * @return the {@link AttributesMapper} to use for getting the current + * Attributes of the target DN. + */ + AttributesMapper getAttributesMapper() { + return new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + return attributes; + } + }; + } + + /** + * Get a ModificationItem to use for rollback of the supplied modification. + * + * @param originalAttributes + * All Attributes of the target DN that are affected of any of + * the ModificationItems. + * @param modificationItem + * the ModificationItem to create a rollback item for. + * @return A ModificationItem to use for rollback of the supplied + * ModificationItem. + */ + protected ModificationItem getCompensatingModificationItem( + Attributes originalAttributes, ModificationItem modificationItem) { + Attribute modificationAttribute = modificationItem.getAttribute(); + Attribute originalAttribute = originalAttributes + .get(modificationAttribute.getID()); + + if (modificationItem.getModificationOp() == DirContext.REMOVE_ATTRIBUTE) { + if (modificationAttribute.size() == 0) { + // If the modification attribute size it means that the + // Attribute should be removed entirely - we should store a + // ModificationItem to restore all present values for rollback. + return new ModificationItem(DirContext.ADD_ATTRIBUTE, + (Attribute) originalAttribute.clone()); + } else { + // The rollback modification will be to re-add the removed + // attribute values. + return new ModificationItem(DirContext.ADD_ATTRIBUTE, + (Attribute) modificationAttribute.clone()); + } + } else if (modificationItem.getModificationOp() == DirContext.REPLACE_ATTRIBUTE) { + if (originalAttribute != null) { + return new ModificationItem(DirContext.REPLACE_ATTRIBUTE, + (Attribute) originalAttribute.clone()); + } else { + // The attribute doesn't previously exist - the rollback + // operation will be to remove the attribute. + return new ModificationItem(DirContext.REMOVE_ATTRIBUTE, + new BasicAttribute(modificationAttribute.getID())); + } + } else { + // An ADD_ATTRIBUTE operation + if (originalAttribute == null) { + // The attribute doesn't previously exist - the rollback + // operation will be to remove the attribute. + return new ModificationItem(DirContext.REMOVE_ATTRIBUTE, + new BasicAttribute(modificationAttribute.getID())); + } else { + // The attribute does exist before - we should store the + // previous value and it should be used for replacing in + // rollback. + return new ModificationItem(DirContext.REPLACE_ATTRIBUTE, + (Attribute) originalAttribute.clone()); + } + } + } + + LdapOperations getLdapOperations() { + return ldapOperations; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/NullOperationExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/NullOperationExecutor.java new file mode 100644 index 00000000..9600c4a8 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/NullOperationExecutor.java @@ -0,0 +1,47 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +/** + * A {@link CompensatingTransactionOperationExecutor} that performs nothing. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class NullOperationExecutor implements + CompensatingTransactionOperationExecutor { + + private static Log log = LogFactory.getLog(NullOperationExecutor.class); + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#rollback() + */ + public void rollback() { + log.info("Rolling back null operation"); + } + + public void commit() { + log.info("Committing back null operation"); + } + + public void performOperation() { + log.info("Performing null operation"); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/NullOperationRecorder.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/NullOperationRecorder.java new file mode 100644 index 00000000..4c5cebd3 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/NullOperationRecorder.java @@ -0,0 +1,43 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationManager; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; + +/** + * A {@link CompensatingTransactionOperationRecorder} performing nothing, + * returning a {@link NullOperationExecutor} regardless of the input. Instances + * of this class will be created if the + * {@link CompensatingTransactionOperationManager} cannot determine any + * appropriate {@link CompensatingTransactionOperationRecorder} for the current + * operation. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class NullOperationRecorder implements + CompensatingTransactionOperationRecorder { + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationRecorder#recordOperation(java.lang.Object[]) + */ + public CompensatingTransactionOperationExecutor recordOperation( + Object[] args) { + return new NullOperationExecutor(); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RebindOperationExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RebindOperationExecutor.java new file mode 100644 index 00000000..18b4a3d6 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RebindOperationExecutor.java @@ -0,0 +1,135 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; +import javax.naming.directory.Attributes; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +/** + * A {@link CompensatingTransactionOperationExecutor} to manage a rebind + * operation. The methods in this class do not behave as expected, since it + * might be impossible to retrieve all the original attributes from the entry. + * Instead this class performs a rename in {@link #performOperation()}, + * a negating rename in {@link #rollback()}, and the {@link #commit()} + * operation unbinds the original entry from its temporary location and binds a + * new entry to the original location using the attributes supplied to the + * original rebind opertaion. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class RebindOperationExecutor implements + CompensatingTransactionOperationExecutor { + + private static Log log = LogFactory.getLog(RebindOperationExecutor.class); + + private LdapOperations ldapOperations; + + private Name originalDn; + + private Name temporaryDn; + + private Object originalObject; + + private Attributes originalAttributes; + + /** + * Constructor. + * + * @param ldapOperations + * the {@link LdapOperations} to use to perform the rollback. + * @param originalDn + * The original DN of the entry to bind. + * @param temporaryDn + * The temporary DN of the entry. + * @param originalObject + * Original 'object' parameter sent to the rebind operation. + * @param originalAttributes + * Original 'attributes' parameter sent to the rebind operation + */ + public RebindOperationExecutor(LdapOperations ldapOperations, + Name originalDn, Name temporaryDn, Object originalObject, + Attributes originalAttributes) { + this.ldapOperations = ldapOperations; + this.originalDn = originalDn; + this.temporaryDn = temporaryDn; + this.originalObject = originalObject; + this.originalAttributes = originalAttributes; + } + + /** + * Get the LdapOperations. Package private for testing purposes. + * + * @return the LdapOperations. + */ + LdapOperations getLdapOperations() { + return ldapOperations; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#rollback() + */ + public void rollback() { + log.debug("Rolling back rebind operation"); + try { + ldapOperations.unbind(originalDn); + ldapOperations.rename(temporaryDn, originalDn); + } catch (Exception e) { + log.warn("Failed to rollback operation, dn: " + originalDn + + "; temporary DN: " + temporaryDn, e); + } + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#commit() + */ + public void commit() { + log.debug("Committing rebind operation"); + ldapOperations.unbind(temporaryDn); + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#performOperation() + */ + public void performOperation() { + log.debug("Performing rebind operation - " + + "renaming original entry and " + + "binding new contents to entry."); + ldapOperations.rename(originalDn, temporaryDn); + ldapOperations.bind(originalDn, originalObject, originalAttributes); + } + + Attributes getOriginalAttributes() { + return originalAttributes; + } + + Name getOriginalDn() { + return originalDn; + } + + Object getOriginalObject() { + return originalObject; + } + + Name getTemporaryDn() { + return temporaryDn; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RebindOperationRecorder.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RebindOperationRecorder.java new file mode 100644 index 00000000..2d832b8c --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RebindOperationRecorder.java @@ -0,0 +1,93 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; + +/** + * A {@link CompensatingTransactionOperationRecorder} keeping track of a rebind + * operation. Creates {@link RebindOperationExecutor} objects in + * {@link #recordOperation(Object[])}. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class RebindOperationRecorder implements + CompensatingTransactionOperationRecorder { + + private LdapOperations ldapOperations; + + private TempEntryRenamingStrategy renamingStrategy; + + /** + * Constructor. + * + * @param ldapOperations + * {@link LdapOperations} to use for getting the rollback + * information and supply to the {@link RebindOperationExecutor}. + * @param renamingStrategy + * {@link TempEntryRenamingStrategy} to use for generating temp + * DNs. + */ + public RebindOperationRecorder(LdapOperations ldapOperations, + TempEntryRenamingStrategy renamingStrategy) { + this.ldapOperations = ldapOperations; + this.renamingStrategy = renamingStrategy; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationRecorder#recordOperation(java.lang.Object[]) + */ + public CompensatingTransactionOperationExecutor recordOperation( + Object[] args) { + if (args == null || args.length != 3) { + throw new IllegalArgumentException( + "Invalid arguments for bind operation"); + } + Name dn = LdapTransactionUtils.getFirstArgumentAsName(args); + Object object = args[1]; + Attributes attributes = null; + if (args[2] != null && !(args[2] instanceof Attributes)) { + throw new IllegalArgumentException( + "Invalid third argument to bind operation"); + } else if (args[2] != null) { + attributes = (Attributes) args[2]; + } + + Name temporaryName = renamingStrategy.getTemporaryName(dn); + + return new RebindOperationExecutor(ldapOperations, dn, temporaryName, + object, attributes); + } + + /** + * Get the LdapOperations. For testing purposes. + * + * @return the LdapOperations. + */ + LdapOperations getLdapOperations() { + return ldapOperations; + } + + public TempEntryRenamingStrategy getRenamingStrategy() { + return renamingStrategy; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RenameOperationExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RenameOperationExecutor.java new file mode 100644 index 00000000..a8d59ae3 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RenameOperationExecutor.java @@ -0,0 +1,101 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +/** + * A {@link CompensatingTransactionOperationExecutor} to manage a rename + * operation. Performs a rename operation in {@link #performOperation()}, a + * negating rename in {@link #rollback()}, and nothing in {@link #commit()}. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class RenameOperationExecutor implements + CompensatingTransactionOperationExecutor { + + private static Log log = LogFactory.getLog(RenameOperationExecutor.class); + + private LdapOperations ldapOperations; + + private Name newDn; + + private Name originalDn; + + /** + * Constructor. + * + * @param ldapOperations + * The {@link LdapOperations} to use for performing the rollback + * operation. + * @param originalDn + * DN that the entry was moved from in the recorded operation. + * @param newDn + * DN that the entry has been moved to in the recorded operation. + */ + public RenameOperationExecutor(LdapOperations ldapOperations, + Name originalDn, Name newDn) { + this.ldapOperations = ldapOperations; + this.originalDn = originalDn; + this.newDn = newDn; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#rollback() + */ + public void rollback() { + log.debug("Rolling back rename operation"); + try { + ldapOperations.rename(newDn, originalDn); + } catch (Exception e) { + log.warn("Unable to rollback rename operation. " + "originalDn: " + + newDn + "; newDn: " + originalDn); + } + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#commit() + */ + public void commit() { + log.debug("Nothing to do in commit for rename operation"); + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#performOperation() + */ + public void performOperation() { + log.debug("Performing rename operation"); + ldapOperations.rename(originalDn, newDn); + } + + Name getNewDn() { + return newDn; + } + + LdapOperations getLdapOperations() { + return ldapOperations; + } + + Name getOriginalDn() { + return originalDn; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RenameOperationRecorder.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RenameOperationRecorder.java new file mode 100644 index 00000000..e9bb8952 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/RenameOperationRecorder.java @@ -0,0 +1,72 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; +import org.springframework.util.Assert; + +/** + * A {@link CompensatingTransactionOperationRecorder} for keeping track of + * rename operations. Creates {@link RenameOperationExecutor} objects for + * rolling back. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class RenameOperationRecorder implements + CompensatingTransactionOperationRecorder { + + private static Log log = LogFactory.getLog(RenameOperationRecorder.class); + + private LdapOperations ldapOperations; + + /** + * Constructor. + * + * @param ldapOperations + * The {@link LdapOperations} to supply to the created + * {@link RebindOperationExecutor} objects. + */ + public RenameOperationRecorder(LdapOperations ldapOperations) { + this.ldapOperations = ldapOperations; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationRecorder#recordOperation(java.lang.Object[]) + */ + public CompensatingTransactionOperationExecutor recordOperation( + Object[] args) { + log.debug("Storing rollback information for rename operation"); + Assert.notEmpty(args); + if (args.length != 2) { + // This really shouldn't happen. + throw new IllegalArgumentException("Illegal argument length"); + } + Name oldDn = LdapTransactionUtils.getArgumentAsName(args[0]); + Name newDn = LdapTransactionUtils.getArgumentAsName(args[1]); + return new RenameOperationExecutor(ldapOperations, oldDn, newDn); + } + + LdapOperations getLdapOperations() { + return ldapOperations; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/TempEntryRenamingStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/TempEntryRenamingStrategy.java new file mode 100644 index 00000000..1a47f7ab --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/TempEntryRenamingStrategy.java @@ -0,0 +1,38 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; + +/** + * Interface for different strategies to rename temporary entries for unbind and + * rebind operations. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public interface TempEntryRenamingStrategy { + + /** + * Get a temporary name for the current entry to be renamed to. + * + * @param originalName + * The original name of the entry. + * @return The name to which the entry should be temporarily renamed + * according to this strategy. + */ + public Name getTemporaryName(Name originalName); +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/UnbindOperationExecutor.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/UnbindOperationExecutor.java new file mode 100644 index 00000000..2e75ec70 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/UnbindOperationExecutor.java @@ -0,0 +1,106 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +/** + * A {@link CompensatingTransactionOperationExecutor} to manage an unbind + * operation. The methods in this class do not behave as expected, since it + * might be impossible to retrieve all the original attributes from the entry. + * Instead this class performs a rename in {@link #performOperation()}, + * a negating rename in {@link #rollback()}, and {@link #commit()} unbinds the + * entry from its temporary location. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class UnbindOperationExecutor implements + CompensatingTransactionOperationExecutor { + + private static Log log = LogFactory.getLog(UnbindOperationExecutor.class); + + private LdapOperations ldapOperations; + + private Name originalDn; + + private Name temporaryDn; + + /** + * Constructor. + * + * @param ldapOperations + * The {@link LdapOperations} to use for performing the rollback + * operation. + * @param originalDn + * The original DN of the entry to be removed. + * @param temporaryDn + * Temporary DN of the entry to be removed; this is where the + * entry is temporarily stored during the transaction. + */ + public UnbindOperationExecutor(LdapOperations ldapOperations, + Name originalDn, Name temporaryDn) { + this.ldapOperations = ldapOperations; + this.originalDn = originalDn; + this.temporaryDn = temporaryDn; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#rollback() + */ + public void rollback() { + try { + ldapOperations.rename(temporaryDn, originalDn); + } catch (Exception e) { + log.warn("Filed to rollback unbind operation, temporaryDn: " + + temporaryDn + "; originalDn: " + originalDn); + } + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#commit() + */ + public void commit() { + log.debug("Committing unbind operation - unbinding temporary entry"); + ldapOperations.unbind(temporaryDn); + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationExecutor#performOperation() + */ + public void performOperation() { + log.debug("Performing operation for unbind -" + + " renaming to temporary entry."); + ldapOperations.rename(originalDn, temporaryDn); + } + + LdapOperations getLdapOperations() { + return ldapOperations; + } + + Name getOriginalDn() { + return originalDn; + } + + Name getTemporaryDn() { + return temporaryDn; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/UnbindOperationRecorder.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/UnbindOperationRecorder.java new file mode 100644 index 00000000..d34e40ed --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/UnbindOperationRecorder.java @@ -0,0 +1,74 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; + +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; + +/** + * {@link CompensatingTransactionOperationRecorder} to keep track of unbind + * operations. This class creates {@link UnbindOperationExecutor} objects for + * rollback. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class UnbindOperationRecorder implements + CompensatingTransactionOperationRecorder { + + private LdapOperations ldapOperations; + + private TempEntryRenamingStrategy renamingStrategy; + + /** + * Constructor. + * + * @param ldapOperations + * {@link LdapOperations} to use for getting the data prior to + * unbinding the entry and to supply to the + * {@link UnbindOperationExecutor} for rollback. + * @param renamingStrategy + * the {@link TempEntryRenamingStrategy} to use when generating + * DNs for temporary entries. + */ + public UnbindOperationRecorder(LdapOperations ldapOperations, + TempEntryRenamingStrategy renamingStrategy) { + this.ldapOperations = ldapOperations; + this.renamingStrategy = renamingStrategy; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationRecorder#recordOperation(java.lang.Object[]) + */ + public CompensatingTransactionOperationExecutor recordOperation( + Object[] args) { + Name dn = LdapTransactionUtils.getFirstArgumentAsName(args); + Name temporaryDn = renamingStrategy.getTemporaryName(dn); + + return new UnbindOperationExecutor(ldapOperations, dn, temporaryDn); + } + + LdapOperations getLdapOperations() { + return ldapOperations; + } + + public TempEntryRenamingStrategy getRenamingStrategy() { + return renamingStrategy; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceAndDataSourceTransactionManager.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceAndDataSourceTransactionManager.java new file mode 100644 index 00000000..bdd31683 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceAndDataSourceTransactionManager.java @@ -0,0 +1,202 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.TransactionSuspensionNotSupportedException; +import org.springframework.transaction.support.DefaultTransactionStatus; + +/** + * A Transaction Manager to manage LDAP and JDBC operations within the same + * transaction. Note that even though the same logical transaction is used, this + * is not a JTA XA transaction; no two-phase commit will be performed, + * and thus commit and rollback may yield unexpected results. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class ContextSourceAndDataSourceTransactionManager extends + DataSourceTransactionManager { + + private static final long serialVersionUID = 6832868697460384648L; + + private ContextSourceTransactionManagerDelegate ldapManagerDelegate = new ContextSourceTransactionManagerDelegate(); + + public ContextSourceAndDataSourceTransactionManager() { + super(); + // Override the default behaviour. + setNestedTransactionAllowed(false); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction(java.lang.Object) + */ + protected boolean isExistingTransaction(Object transaction) { + ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) transaction; + + return super.isExistingTransaction(actualTransactionObject + .getDataSourceTransactionObject()); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction() + */ + protected Object doGetTransaction() throws TransactionException { + Object dataSourceTransactionObject = super.doGetTransaction(); + Object contextSourceTransactionObject = ldapManagerDelegate + .doGetTransaction(); + + return new ContextSourceAndDataSourceTransactionObject( + contextSourceTransactionObject, dataSourceTransactionObject); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin(java.lang.Object, + * org.springframework.transaction.TransactionDefinition) + */ + protected void doBegin(Object transaction, TransactionDefinition definition) + throws TransactionException { + ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) transaction; + + super.doBegin(actualTransactionObject.getDataSourceTransactionObject(), + definition); + ldapManagerDelegate.doBegin(actualTransactionObject + .getLdapTransactionObject(), definition); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCleanupAfterCompletion(java.lang.Object) + */ + protected void doCleanupAfterCompletion(Object transaction) { + ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) transaction; + + super.doCleanupAfterCompletion(actualTransactionObject + .getDataSourceTransactionObject()); + ldapManagerDelegate.doCleanupAfterCompletion(actualTransactionObject + .getLdapTransactionObject()); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus) + */ + protected void doCommit(DefaultTransactionStatus status) + throws TransactionException { + + ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) status + .getTransaction(); + + try { + super.doCommit(new DefaultTransactionStatus(actualTransactionObject + .getDataSourceTransactionObject(), status + .isNewTransaction(), status.isNewSynchronization(), status + .isReadOnly(), status.isDebug(), status + .getSuspendedResources())); + } catch (TransactionException ex) { + if (isRollbackOnCommitFailure()) { + logger.debug("Failed to commit db resource, rethrowing", ex); + // If we are to rollback on commit failure, just rethrow the + // exception - this will cause a rollback to be performed on + // both resources. + throw ex; + } else { + logger + .warn("Failed to commit and resource is rollbackOnCommit not set -" + + " proceeding to commit ldap resource."); + } + } + ldapManagerDelegate.doCommit(new DefaultTransactionStatus( + actualTransactionObject.getLdapTransactionObject(), status + .isNewTransaction(), status.isNewSynchronization(), + status.isReadOnly(), status.isDebug(), status + .getSuspendedResources())); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus) + */ + protected void doRollback(DefaultTransactionStatus status) + throws TransactionException { + ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) status + .getTransaction(); + + super.doRollback(new DefaultTransactionStatus(actualTransactionObject + .getDataSourceTransactionObject(), status.isNewTransaction(), + status.isNewSynchronization(), status.isReadOnly(), status + .isDebug(), status.getSuspendedResources())); + ldapManagerDelegate.doRollback(new DefaultTransactionStatus( + actualTransactionObject.getLdapTransactionObject(), status + .isNewTransaction(), status.isNewSynchronization(), + status.isReadOnly(), status.isDebug(), status + .getSuspendedResources())); + } + + public ContextSource getContextSource() { + return ldapManagerDelegate.getContextSource(); + } + + public void setContextSource(ContextSource contextSource) { + ldapManagerDelegate.setContextSource(contextSource); + } + + protected void setRenamingStrategy( + TempEntryRenamingStrategy renamingStrategy) { + ldapManagerDelegate.setRenamingStrategy(renamingStrategy); + } + + private class ContextSourceAndDataSourceTransactionObject { + private Object ldapTransactionObject; + + private Object dataSourceTransactionObject; + + public ContextSourceAndDataSourceTransactionObject( + Object ldapTransactionObject, Object dataSourceTransactionObject) { + this.ldapTransactionObject = ldapTransactionObject; + this.dataSourceTransactionObject = dataSourceTransactionObject; + } + + public Object getDataSourceTransactionObject() { + return dataSourceTransactionObject; + } + + public Object getLdapTransactionObject() { + return ldapTransactionObject; + } + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doSuspend(java.lang.Object) + */ + protected Object doSuspend(Object transaction) throws TransactionException { + throw new TransactionSuspensionNotSupportedException( + "Transaction manager [" + getClass().getName() + + "] does not support transaction suspension"); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doResume(java.lang.Object, + * java.lang.Object) + */ + protected void doResume(Object transaction, Object suspendedResources) + throws TransactionException { + throw new TransactionSuspensionNotSupportedException( + "Transaction manager [" + getClass().getName() + + "] does not support transaction suspension"); + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManager.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManager.java new file mode 100644 index 00000000..a35e0144 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManager.java @@ -0,0 +1,190 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; +import org.springframework.ldap.transaction.compensating.UnbindOperationExecutor; +import org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; +import org.springframework.transaction.compensating.support.DefaultCompensatingTransactionOperationManager; +import org.springframework.transaction.support.AbstractPlatformTransactionManager; +import org.springframework.transaction.support.DefaultTransactionStatus; + +/** + * TransactionManager for managing LDAP transactions. Since transactions are not + * supported in the LDAP protocol, this class and its collaborators aim to + * provide compensating transactions instead. Should a transaction + * need to be rolled back, this TransactionManager will try to restore the + * original state using information recorded prior to each operation. The + * operation where the original state is restored is called a compensating + * operation. + *

+ * NOTE: The transactions provided by this TransactionManager are all + * client side and are by no means 'real' transactions, in the sense + * that we know them in the ordinary database world, e.g.: + *

    + *
  • Should the transaction failure be caused by a network failure, there is + * no way whatsoever that this TransactionManager can restore the database + * state. In this case, all possibilities for rollback will be utterly lost.
  • + *
  • Transaction isolation is not provided, i.e. entries participating in a + * transaction for one client may very well participate in another transaction + * for another client at the same time. Should one of these transactions be + * rolled back, the outcome of this is undetermined, and may in the worst case + * result in total failure.
  • + *
+ *

+ *

+ * While the points above should be noted and considered, the compensating + * transaction approach will be perfectly sufficient for all but the most + * unfortunate of circumstances. Considering that there currently is a total + * absence of server-side transaction support in the LDAP world, being able to + * mark operations as transactional in the same way as for relational database + * operations is surely a step forward. + *

+ *

+ * An LDAP transaction is tied to a {@link ContextSource}, to be supplied to + * the {@link #setContextSource(ContextSource)} method. While the actual + * ContextSource used by the target LdapTemplate instance needs to be of the + * type {@link TransactionAwareContextSourceProxy}, the ContextSource supplied + * to this class should be the actual target ContextSource. + *

+ *

+ * Using this TransactionManager along with + * {@link TransactionAwareContextSourceProxy}, all modifying operations (bind, + * unbind, rebind, rename, modifyAttributes) in a transaction will be + * intercepted. Each modification has its corresponding + * {@link CompensatingTransactionOperationRecorder}, which collects the + * information necessary to perform a rollback and produces a + * {@link CompensatingTransactionOperationExecutor} which is then used to + * execute the actual operation and is later called for performing the commit or + * rollback. + *

+ *

+ * For several of the operations, performing a rollback is pretty + * straightforward. For example, in order to roll back a rename operation, it + * will only be required to rename the entry back to its original position. For + * other operations, however, it's a bit more complicated. An unbind operation + * is not possible to roll back by simply binding the entry back with the + * attributes retrieved from the original entry. It might not be possible to get + * all the information from the original entry. Consequently, the + * {@link UnbindOperationExecutor} will move the original entry to a temporary + * location in its performOperation() method. The commit() method will know that + * everything went well, so it will be OK to unbind the entry. The rollback + * operation will be to rename the entry back to its original location. The same + * behaviour is used for rebind() operations. The operation of calculating a + * temporary location for an entry is delegated to a + * {@link TempEntryRenamingStrategy} (default + * {@link DefaultTempEntryRenamingStrategy}), specified in + * {@link #setRenamingStrategy(TempEntryRenamingStrategy)}. + *

+ *

+ * The actual work of this Transaction Manager is delegated to a + * {@link ContextSourceTransactionManagerDelegate}. This is because the exact + * same logic needs to be used if we want to wrap a JDBC and LDAP transaction in + * the same logical transaction. + *

+ * + * @author Mattias Arthursson + * + * @see ContextSourceAndDataSourceTransactionManager + * @see ContextSourceTransactionManagerDelegate + * @see DefaultCompensatingTransactionOperationManager + * @see TempEntryRenamingStrategy + * @see TransactionAwareContextSourceProxy + * @since 1.2 + */ +public class ContextSourceTransactionManager extends + AbstractPlatformTransactionManager { + + private static final long serialVersionUID = 7138208218687237856L; + + private ContextSourceTransactionManagerDelegate delegate = new ContextSourceTransactionManagerDelegate(); + + /* + * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doBegin(java.lang.Object, + * org.springframework.transaction.TransactionDefinition) + */ + protected void doBegin(Object transaction, TransactionDefinition definition) + throws TransactionException { + delegate.doBegin(transaction, definition); + } + + /* + * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doCleanupAfterCompletion(java.lang.Object) + */ + protected void doCleanupAfterCompletion(Object transaction) { + delegate.doCleanupAfterCompletion(transaction); + } + + /* + * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus) + */ + protected void doCommit(DefaultTransactionStatus status) + throws TransactionException { + delegate.doCommit(status); + } + + /* + * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doGetTransaction() + */ + protected Object doGetTransaction() throws TransactionException { + return delegate.doGetTransaction(); + } + + /* + * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus) + */ + protected void doRollback(DefaultTransactionStatus status) + throws TransactionException { + delegate.doRollback(status); + } + + /** + * Get the ContextSource. + * + * @return the contextSource. + * @see ContextSourceTransactionManagerDelegate#getContextSource() + */ + public ContextSource getContextSource() { + return delegate.getContextSource(); + } + + /** + * Set the ContextSource. + * + * @param contextSource + * the ContextSource. + * @see ContextSourceTransactionManagerDelegate#setContextSource(ContextSource) + */ + public void setContextSource(ContextSource contextSource) { + delegate.setContextSource(contextSource); + } + + /** + * Set the {@link TempEntryRenamingStrategy}. + * + * @param renamingStrategy + * the Renaming Strategy. + * @see ContextSourceTransactionManagerDelegate#setRenamingStrategy(TempEntryRenamingStrategy) + */ + public void setRenamingStrategy(TempEntryRenamingStrategy renamingStrategy) { + delegate.setRenamingStrategy(renamingStrategy); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerDelegate.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerDelegate.java new file mode 100644 index 00000000..77f699d9 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerDelegate.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.transaction.compensating.LdapCompensatingTransactionOperationFactory; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; +import org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy; +import org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate; +import org.springframework.transaction.compensating.support.CompensatingTransactionHolderSupport; +import org.springframework.transaction.compensating.support.DefaultCompensatingTransactionOperationManager; + +/** + * This delegate performs all the work for the + * {@link ContextSourceTransactionManager}. The work is delegated in order to + * be able to perform the exact same work for the LDAP part in + * {@link ContextSourceAndDataSourceTransactionManager}. + * + * @author Mattias Arthursson + * @see ContextSourceTransactionManager + * @see ContextSourceAndDataSourceTransactionManager + * @since 1.2 + */ +public class ContextSourceTransactionManagerDelegate extends + AbstractCompensatingTransactionManagerDelegate { + + private static final Log log = LogFactory + .getLog(ContextSourceTransactionManagerDelegate.class); + + private ContextSource contextSource; + + private TempEntryRenamingStrategy renamingStrategy = new DefaultTempEntryRenamingStrategy(); + + /** + * Set the ContextSource to work on. Even though the actual ContextSource + * sent to the LdapTemplate instance should be a + * {@link TransactionAwareContextSourceProxy}, the one sent to this method + * should be the target of that proxy. If it is not, the target will be + * extracted and used instead. + * + * @param contextSource + * the ContextSource to work on. + */ + public void setContextSource(ContextSource contextSource) { + if (contextSource instanceof TransactionAwareContextSourceProxy) { + TransactionAwareContextSourceProxy proxy = (TransactionAwareContextSourceProxy) contextSource; + this.contextSource = proxy.getTarget(); + } else { + this.contextSource = contextSource; + } + } + + public ContextSource getContextSource() { + return contextSource; + } + + /* + * @see org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate#getTransactionSynchronizationKey() + */ + protected Object getTransactionSynchronizationKey() { + return getContextSource(); + } + + /* + * @see org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate#getNewHolder() + */ + protected CompensatingTransactionHolderSupport getNewHolder() { + DirContext newCtx = getContextSource().getReadOnlyContext(); + DirContextHolder contextHolder = new DirContextHolder( + new DefaultCompensatingTransactionOperationManager( + new LdapCompensatingTransactionOperationFactory( + renamingStrategy)), newCtx); + return contextHolder; + } + + /* + * @see org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate#closeTargetResource(org.springframework.transaction.compensating.support.CompensatingTransactionHolderSupport) + */ + protected void closeTargetResource( + CompensatingTransactionHolderSupport transactionHolderSupport) { + DirContextHolder contextHolder = (DirContextHolder) transactionHolderSupport; + DirContext ctx = contextHolder.getCtx(); + + try { + log.debug("Closing target context"); + ctx.close(); + } catch (NamingException e) { + log.warn("Failed to close target context", e); + } + } + + /** + * Set the {@link TempEntryRenamingStrategy} to be used when renaming + * temporary entries in unbind and rebind operations. Default value is a + * {@link DefaultTempEntryRenamingStrategy}. + * + * @param renamingStrategy + * the {@link TempEntryRenamingStrategy} to use. + */ + public void setRenamingStrategy(TempEntryRenamingStrategy renamingStrategy) { + this.renamingStrategy = renamingStrategy; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/DirContextHolder.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/DirContextHolder.java new file mode 100644 index 00000000..7d0c1ce6 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/DirContextHolder.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import javax.naming.directory.DirContext; + +import org.springframework.transaction.compensating.CompensatingTransactionOperationManager; +import org.springframework.transaction.compensating.support.CompensatingTransactionHolderSupport; + +/** + * Keeps track of the transaction DirContext. The same DirContext instance will + * be reused throughout a transaction. Also keeps a + * {@link CompensatingTransactionOperationManager}, responsible for performing + * operations and keeping track of all changes and storing information necessary + * for commit or rollback. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class DirContextHolder extends CompensatingTransactionHolderSupport { + private DirContext ctx; + + /** + * Constructor. + * + * @param manager + * The {@link CompensatingTransactionOperationManager}. + * @param ctx + * The DirContext associated with the current transaction. + */ + public DirContextHolder(CompensatingTransactionOperationManager manager, + DirContext ctx) { + super(manager); + this.ctx = ctx; + } + + /** + * Set the DirContext associated with the current transaction. + * + * @param ctx + * The DirContext associated with the current transaction. + */ + public void setCtx(DirContext ctx) { + this.ctx = ctx; + } + + /** + * Return the DirContext associated with the current transaction. + */ + public DirContext getCtx() { + return ctx; + } + + /* + * @see org.springframework.transaction.compensating.support.CompensatingTransactionHolderSupport#getTransactedResource() + */ + protected Object getTransactedResource() { + return ctx; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareContextSourceProxy.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareContextSourceProxy.java new file mode 100644 index 00000000..cacf52d8 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareContextSourceProxy.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import java.lang.reflect.Proxy; + +import javax.naming.directory.DirContext; + +import org.springframework.ldap.NamingException; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DirContextProxy; +import org.springframework.ldap.transaction.compensating.LdapTransactionUtils; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * A proxy for ContextSource to make sure that the returned DirContext objects + * are aware of the surrounding transactions. This makes sure that the + * DirContext is not closed during the transaction and that all modifying + * operations are recorded, keeping track of the corresponding rollback + * operations. All returned DirContext instances will be of the type + * {@link TransactionAwareDirContextInvocationHandler}. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class TransactionAwareContextSourceProxy implements ContextSource { + private ContextSource target; + + /** + * Constructor. + * + * @param target + * the target ContextSource. + */ + public TransactionAwareContextSourceProxy(ContextSource target) { + this.target = target; + } + + /** + * Get the target ContextSource. + * + * @return the target ContextSource. + */ + public ContextSource getTarget() { + return target; + } + + /* + * @see org.springframework.ldap.core.ContextSource#getReadOnlyContext() + */ + public DirContext getReadOnlyContext() throws NamingException { + return getReadWriteContext(); + } + + private DirContext getTransactionAwareDirContextProxy(DirContext context, + ContextSource target) { + return (DirContext) Proxy + .newProxyInstance(DirContextProxy.class.getClassLoader(), + new Class[] { + LdapTransactionUtils + .getActualTargetClass(context), + DirContextProxy.class }, + new TransactionAwareDirContextInvocationHandler( + context, target)); + + } + + /* + * @see org.springframework.ldap.core.ContextSource#getReadWriteContext() + */ + public DirContext getReadWriteContext() throws NamingException { + DirContextHolder contextHolder = (DirContextHolder) TransactionSynchronizationManager + .getResource(target); + DirContext ctx = null; + + if (contextHolder != null) { + ctx = contextHolder.getCtx(); + } + + if (ctx == null) { + ctx = target.getReadWriteContext(); + if (contextHolder != null) { + contextHolder.setCtx(ctx); + } + } + return getTransactionAwareDirContextProxy(ctx, target); + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareDirContextInvocationHandler.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareDirContextInvocationHandler.java new file mode 100644 index 00000000..a72f5cb4 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareDirContextInvocationHandler.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.naming.directory.DirContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.NamingException; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.transaction.compensating.LdapTransactionUtils; +import org.springframework.transaction.compensating.support.CompensatingTransactionUtils; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * Proxy implementation for DirContext, making sure that the instance is not + * closed during a transaction, and that all modifying operations are recorded, + * storing compensating rollback operations for them. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class TransactionAwareDirContextInvocationHandler implements + InvocationHandler { + + private static Log log = LogFactory + .getLog(TransactionAwareDirContextInvocationHandler.class); + + private DirContext target; + + private ContextSource contextSource; + + /** + * Constructor. + * + * @param target + * The target DirContext. + * @param contextSource + * The transactional ContextSource, needed to get hold of the + * current transaction's {@link DirContextHolder}. + */ + public TransactionAwareDirContextInvocationHandler(DirContext target, + ContextSource contextSource) { + this.target = target; + this.contextSource = contextSource; + } + + /* + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, + * java.lang.reflect.Method, java.lang.Object[]) + */ + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + + String methodName = method.getName(); + if (methodName.equals("getTargetContext")) { + return target; + } else if (methodName.equals("equals")) { + // Only consider equal when proxies are identical. + return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); + } else if (methodName.equals("hashCode")) { + // Use hashCode of Connection proxy. + return new Integer(hashCode()); + } else if (methodName.equals("close")) { + doCloseConnection(target, contextSource); + return null; + } else if (LdapTransactionUtils + .isSupportedWriteTransactionOperation(methodName)) { + // Store transaction data and allow operation to proceed. + CompensatingTransactionUtils.performOperation(contextSource, + target, method, args); + return null; + } else { + try { + return method.invoke(target, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + } + + /** + * Close the supplied context, but only if it is not associated with the + * current transaction. + * + * @param context + * the DirContext to close. + * @param contextSource + * the ContextSource bound to the transaction. + * @throws NamingException + */ + void doCloseConnection(DirContext context, ContextSource contextSource) + throws javax.naming.NamingException { + DirContextHolder transactionContextHolder = (DirContextHolder) TransactionSynchronizationManager + .getResource(contextSource); + if (transactionContextHolder == null + || transactionContextHolder.getCtx() != context) { + log.debug("Closing context"); + // This is not the transactional context or the transaction is + // no longer active - we should close it. + context.close(); + } else { + log.debug("Leaving transactional context open"); + } + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/package.html new file mode 100644 index 00000000..2cac0e87 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/manager/package.html @@ -0,0 +1,7 @@ + + + +The core implementation classes for client-side LDAP transactions. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/package.html new file mode 100644 index 00000000..a5d44d78 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/package.html @@ -0,0 +1,7 @@ + + + +LDAP specific implementations of the Compensating Transaction interfaces. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/DefaultTempEntryRenamingStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/DefaultTempEntryRenamingStrategy.java new file mode 100644 index 00000000..3b7bfb20 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/DefaultTempEntryRenamingStrategy.java @@ -0,0 +1,94 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import java.util.List; + +import javax.naming.Name; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapRdn; +import org.springframework.ldap.core.LdapRdnComponent; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; + +/** + * Default implementation of {@link TempEntryRenamingStrategy}. This + * implementation simply adds "_temp" to the leftmost (least significant part) + * of the name. For example: + * + *
+ * cn=john doe, ou=company1, c=SE
+ * 
+ * + * becomes: + * + *
+ * cn=john doe_temp, ou=company1, c=SE
+ * 
+ *

+ * Note that using this strategy means that the entry remains in virtually the + * same location as where it originally resided. This means that searches later + * in the same transaction might return references to the temporary entry even + * though it should have been removed or rebound. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class DefaultTempEntryRenamingStrategy implements + TempEntryRenamingStrategy { + + /** + * The default temp entry suffix, "_temp". + */ + public static final String DEFAULT_TEMP_SUFFIX = "_temp"; + + private String tempSuffix = DEFAULT_TEMP_SUFFIX; + + /* + * @see org.springframework.ldap.support.transaction.TempEntryRenamingStrategy#getTemporaryName(javax.naming.Name) + */ + public Name getTemporaryName(Name originalName) { + DistinguishedName temporaryName = new DistinguishedName(originalName); + List names = temporaryName.getNames(); + LdapRdn rdn = (LdapRdn) names.get(names.size() - 1); + LdapRdnComponent rdnComponent = rdn.getComponent(); + String value = rdnComponent.getValue(); + rdnComponent.setValue(value + DEFAULT_TEMP_SUFFIX); + + return temporaryName; + } + + /** + * Get the suffix that will be used for renaming temporary entries. + * + * @return the suffix. + */ + public String getTempSuffix() { + return tempSuffix; + } + + /** + * Set the suffix to use for renaming temporary entries. Default value is + * {@link #DEFAULT_TEMP_SUFFIX}. + * + * @param tempSuffix + * the suffix. + */ + public void setTempSuffix(String tempSuffix) { + this.tempSuffix = tempSuffix; + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/DifferentSubtreeTempEntryRenamingStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/DifferentSubtreeTempEntryRenamingStrategy.java new file mode 100644 index 00000000..2513d234 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/DifferentSubtreeTempEntryRenamingStrategy.java @@ -0,0 +1,89 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import java.util.List; + +import javax.naming.Name; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapRdn; +import org.springframework.ldap.core.LdapRdnComponent; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; + +/** + * A {@link TempEntryRenamingStrategy} that moves the entry to a different + * subtree than the original entry. The specified subtree needs to be present in + * the LDAP tree; it will not be created and operations using this strategy will + * fail if the destination is not in place. However, this strategy is preferable + * to {@link DefaultTempEntryRenamingStrategy}, as it makes searches have the + * expected result even though the temporary entry still exists during the + * transaction. + *

+ * Example: If the specified subtreeNode is + * ou=tempEntries and the originalName is + * cn=john doe, ou=company1, c=SE, the result of + * {@link #getTemporaryName(Name)} will be + * cn=john doe1, ou=tempEntries. The "1" suffix is a + * sequence number needed to prevent potential collisions in the temporary + * storage. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class DifferentSubtreeTempEntryRenamingStrategy implements + TempEntryRenamingStrategy { + + private Name subtreeNode; + + private static int nextSequenceNo = 1; + + public DifferentSubtreeTempEntryRenamingStrategy(Name subtreeNode) { + this.subtreeNode = subtreeNode; + } + + public Name getSubtreeNode() { + return subtreeNode; + } + + public void setSubtreeNode(Name subtreeNode) { + this.subtreeNode = subtreeNode; + } + + int getNextSequenceNo() { + return nextSequenceNo; + } + + /* + * @see org.springframework.ldap.support.transaction.TempEntryRenamingStrategy#getTemporaryName(javax.naming.Name) + */ + public Name getTemporaryName(Name originalName) { + DistinguishedName tempName = new DistinguishedName(originalName); + List names = tempName.getNames(); + LdapRdn rdn = (LdapRdn) names.get(names.size() - 1); + LdapRdnComponent component = rdn.getComponent(); + + LdapRdn newRdn; + synchronized (this) { + newRdn = new LdapRdn(component.getKey(), component.getValue() + + nextSequenceNo++); + } + + DistinguishedName newName = new DistinguishedName(subtreeNode); + newName.add(newRdn); + return newName; + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/package.html b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/package.html new file mode 100644 index 00000000..b62d994f --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/transaction/compensating/support/package.html @@ -0,0 +1,7 @@ + + + +Useful helper implementations for client side Compensating LDAP Transactions. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationExecutor.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationExecutor.java new file mode 100644 index 00000000..afa196ad --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationExecutor.java @@ -0,0 +1,62 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +/** + * Responsible for executing a single recorded operation as well as committing + * or rolling it back, depending on the transaction outcome. Instances of this + * interface are constructed by {@link CompensatingTransactionOperationRecorder} + * objects, supplying them with the information necessary for the respective + * operations. + *

+ * The actual operations performed by the respective methods of this class might + * not be what would originally be expected. E.g. one would expect that the + * {@link #performOperation()} method of a + * CompensatingTransactionOperationExecutor implementation would actually delete + * the entry, leaving it for the {@link #rollback()} method to recreate it using + * data from the original entry. However, this will not always be possible. In + * an LDAP system, for instance, it might not be possible to retrieve all the + * stored data from the original entry. In that case, the + * {@link #performOperation()} method will instead move the entry to a temporary + * location and leave it for the {@link #commit()} method to actually remove the + * entry. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public interface CompensatingTransactionOperationExecutor { + /** + * Rollback the operation, restoring state of the target as it was before + * the operation was performed using the information supplied on creation of + * this instance. + */ + public void rollback(); + + /** + * Commit the operation. In many cases, this will not require any work at + * all to be performed. However, in some cases there will be interesting + * stuff to do. See class description for elaboration on this. + */ + public void commit(); + + /** + * Perform the operation. This will most often require performing the + * recorded operation, but in some cases the actual operation performed by + * this method might be something else. See class description for + * elaboration on this. + */ + public void performOperation(); +} diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationFactory.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationFactory.java new file mode 100644 index 00000000..d494e14f --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import org.springframework.transaction.compensating.support.DefaultCompensatingTransactionOperationManager; + +/** + * Factory interface for creating + * {@link CompensatingTransactionOperationRecorder} objects based on operation + * method names. + * + * @author Mattias Arthursson + * @see DefaultCompensatingTransactionOperationManager + * @since 1.2 + */ +public interface CompensatingTransactionOperationFactory { + /** + * Create an appropriate {@link CompensatingTransactionOperationRecorder} + * instance corresponding to the supplied method name. + * + * @param resource + * The target transaction resource. + * @param method + * the method name to create a + * {@link CompensatingTransactionOperationRecorder} for. + * + * @return a new {@link CompensatingTransactionOperationRecorder} instance. + */ + public CompensatingTransactionOperationRecorder createRecordingOperation( + Object resource, String method); +} diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationManager.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationManager.java new file mode 100644 index 00000000..5c117d8f --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationManager.java @@ -0,0 +1,55 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +/** + * A CompensatingTransactionOperationManager implementation records and performs + * operations that are to be performed within a compensating transaction. It + * keeps track of compensating actions necessary for rolling back each + * individual operation. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public interface CompensatingTransactionOperationManager { + /** + * Indicates that the supplied operation (method name) is to be performed. + * This method is responsible for recording the current state (prior to the + * operation), performing the operation, and storing the necessary + * information to roll back or commit the performed operation. + * + * @param resource + * the target resource to perform the operation on. + * @param operation + * The method to be invoked. + * @param args + * Arguments supplied to the method. + */ + public void performOperation(Object resource, String operation, + Object[] args); + + /** + * Rollback all recorded operations by performing each of the recorded + * rollback operations. + */ + public void rollback(); + + /** + * Commit all recorded operations. In many cases this means doing nothing, + * but in some cases some temporary data will need to be removed. + */ + public void commit(); +} diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationRecorder.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationRecorder.java new file mode 100644 index 00000000..9a9804bb --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/CompensatingTransactionOperationRecorder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +/** + * An implementation of this interface is responsible for recording data and + * supplying a {@link CompensatingTransactionOperationExecutor} to be invoked + * for execution and compensating transaction management of the operation. + * Recording of an operation should not fail (throwing an Exception), but + * instead log the result. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public interface CompensatingTransactionOperationRecorder { + /** + * Record information about the operation performed and return a + * corresponding {@link CompensatingTransactionOperationExecutor} to be used + * if the operation would need to be rolled back. + * + * @param args + * The arguments that have been sent to the operation. + * @return A {@link CompensatingTransactionOperationExecutor} to be used if + * the recorded operation should need to be rolled back. + */ + public CompensatingTransactionOperationExecutor recordOperation( + Object[] args); +} diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/package.html b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/package.html new file mode 100644 index 00000000..6d166abe --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/package.html @@ -0,0 +1,8 @@ + + + +Interface definitions for a general Compensating Transaction framework +based on PlatformTransactionManager. + + + diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/AbstractCompensatingTransactionManagerDelegate.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/AbstractCompensatingTransactionManagerDelegate.java new file mode 100644 index 00000000..6288d2fe --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/AbstractCompensatingTransactionManagerDelegate.java @@ -0,0 +1,130 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.support.DefaultTransactionStatus; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * Abstract superclass for Compensating TransactionManager delegates. The actual + * transaction work is extracted to a delegate to enable composite Transaction + * Managers. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public abstract class AbstractCompensatingTransactionManagerDelegate { + + private static Log log = LogFactory + .getLog(AbstractCompensatingTransactionManagerDelegate.class); + + /** + * Close the target resource - the implementation specific resource held in + * the specified {@link CompensatingTransactionHolderSupport}. + * + * @param transactionHolderSupport + * the {@link CompensatingTransactionHolderSupport} that holds + * the transaction specific target resource. + */ + protected abstract void closeTargetResource( + CompensatingTransactionHolderSupport transactionHolderSupport); + + /** + * Get a new implementation specific + * {@link CompensatingTransactionHolderSupport} instance. + * + * @return a new {@link CompensatingTransactionHolderSupport} instance. + */ + protected abstract CompensatingTransactionHolderSupport getNewHolder(); + + /** + * Get the key (normally, a DataSource or similar) that should be used for + * transaction synchronization. + * + * @return the transaction synchronization key + */ + protected abstract Object getTransactionSynchronizationKey(); + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction() + */ + public Object doGetTransaction() throws TransactionException { + CompensatingTransactionHolderSupport holder = (CompensatingTransactionHolderSupport) TransactionSynchronizationManager + .getResource(getTransactionSynchronizationKey()); + CompensatingTransactionObject txObject = new CompensatingTransactionObject( + holder); + return txObject; + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin(java.lang.Object, + * org.springframework.transaction.TransactionDefinition) + */ + public void doBegin(Object transaction, TransactionDefinition definition) + throws TransactionException { + CompensatingTransactionObject txObject = (CompensatingTransactionObject) transaction; + + if (txObject.getHolder() == null) { + CompensatingTransactionHolderSupport contextHolder = getNewHolder(); + txObject.setHolder(contextHolder); + + TransactionSynchronizationManager.bindResource( + getTransactionSynchronizationKey(), contextHolder); + } + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus) + */ + public void doCommit(DefaultTransactionStatus status) + throws TransactionException { + CompensatingTransactionObject txObject = (CompensatingTransactionObject) status + .getTransaction(); + txObject.getHolder().getTransactionOperationManager().commit(); + + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus) + */ + public void doRollback(DefaultTransactionStatus status) + throws TransactionException { + CompensatingTransactionObject txObject = (CompensatingTransactionObject) status + .getTransaction(); + txObject.getHolder().getTransactionOperationManager().rollback(); + } + + /* + * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCleanupAfterCompletion(java.lang.Object) + */ + public void doCleanupAfterCompletion(Object transaction) { + log.debug("Cleaning stored transaction synchronization"); + TransactionSynchronizationManager + .unbindResource(getTransactionSynchronizationKey()); + + CompensatingTransactionObject txObject = (CompensatingTransactionObject) transaction; + CompensatingTransactionHolderSupport transactionHolderSupport = (CompensatingTransactionHolderSupport) txObject + .getHolder(); + + closeTargetResource(transactionHolderSupport); + + txObject.getHolder().clear(); + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionHolderSupport.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionHolderSupport.java new file mode 100644 index 00000000..de90a8e0 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionHolderSupport.java @@ -0,0 +1,80 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import org.springframework.transaction.compensating.CompensatingTransactionOperationManager; +import org.springframework.transaction.support.ResourceHolderSupport; + +/** + * Base class for compensating transaction resource holders. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public abstract class CompensatingTransactionHolderSupport extends + ResourceHolderSupport { + + private CompensatingTransactionOperationManager transactionOperationManager; + + /** + * Constructor. + * + * @param manager + * The {@link CompensatingTransactionOperationManager} to use for + * creating Compensating operations. + */ + public CompensatingTransactionHolderSupport( + CompensatingTransactionOperationManager manager) { + this.transactionOperationManager = manager; + } + + /** + * Get the actual transacted resource. + * + * @return the transaction's target resource + */ + protected abstract Object getTransactedResource(); + + /* + * @see org.springframework.transaction.support.ResourceHolderSupport#clear() + */ + public void clear() { + super.clear(); + transactionOperationManager = null; + } + + /** + * Get the CompensatingTransactionOperationManager to handle the data for + * the current transaction. + * + * @return the CompensatingTransactionOperationManager. + */ + public CompensatingTransactionOperationManager getTransactionOperationManager() { + return transactionOperationManager; + } + + /** + * Set the CompensatingTransactionOperationManager. For testing purposes + * only. + * + * @param transactionOperationManager + * the CompensatingTransactionOperationManager to use. + */ + public void setTransactionOperationManager( + CompensatingTransactionOperationManager transactionOperationManager) { + this.transactionOperationManager = transactionOperationManager; + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionObject.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionObject.java new file mode 100644 index 00000000..16dc4be7 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionObject.java @@ -0,0 +1,62 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +/** + * Transaction object used by + * {@link AbstractCompensatingTransactionManagerDelegate}. Keeps a reference to + * the {@link CompensatingTransactionHolderSupport} associated with the current + * transaction. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class CompensatingTransactionObject { + private CompensatingTransactionHolderSupport holder; + + /** + * Constructor. + * + * @param holder + * the {@link CompensatingTransactionHolderSupport} associated + * with the current transaction. + */ + public CompensatingTransactionObject( + CompensatingTransactionHolderSupport holder) { + this.holder = holder; + } + + /** + * Get the DirContextHolder. + * + * @return the DirContextHolder. + */ + public CompensatingTransactionHolderSupport getHolder() { + return holder; + } + + /** + * Set the {@link CompensatingTransactionHolderSupport} associated with the + * current transaction. + * + * @param holder + * the {@link CompensatingTransactionHolderSupport} associated + * with the current transaction. + */ + public void setHolder(CompensatingTransactionHolderSupport holder) { + this.holder = holder; + } +} \ No newline at end of file diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionUtils.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionUtils.java new file mode 100644 index 00000000..594d4e75 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/CompensatingTransactionUtils.java @@ -0,0 +1,76 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.springframework.transaction.compensating.CompensatingTransactionOperationManager; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * Common methods for use with compensating transactions. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class CompensatingTransactionUtils { + + /** + * Not to be instantiated. + */ + private CompensatingTransactionUtils() { + } + + /** + * Perform the specified operation, storing the state prior to the operation + * in order to enable commit/rollback later. If no transaction is currently + * active, proceed with the original call on the target. + * + * @param synchronizationKey + * the transaction synchronization key we are operating on + * (typically something similar to a DataSource). + * @param target + * the actual target resource that should be used for invoking + * the operation on should no transaction be active. + * @param method + * name of the method to be invoked. + * @param args + * arguments with which the operation is invoked. + */ + public static void performOperation(Object synchronizationKey, + Object target, Method method, Object[] args) throws Throwable { + CompensatingTransactionHolderSupport transactionResourceHolder = (CompensatingTransactionHolderSupport) TransactionSynchronizationManager + .getResource(synchronizationKey); + if (transactionResourceHolder != null) { + + CompensatingTransactionOperationManager transactionOperationManager = transactionResourceHolder + .getTransactionOperationManager(); + transactionOperationManager.performOperation( + transactionResourceHolder.getTransactedResource(), method + .getName(), args); + } else { + // Perform the target operation + try { + method.invoke(target, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/DefaultCompensatingTransactionOperationManager.java b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/DefaultCompensatingTransactionOperationManager.java new file mode 100644 index 00000000..b947d970 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/DefaultCompensatingTransactionOperationManager.java @@ -0,0 +1,128 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import java.util.Iterator; +import java.util.Stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.transaction.TransactionSystemException; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationFactory; +import org.springframework.transaction.compensating.CompensatingTransactionOperationManager; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; + +/** + * Default implementation of {@link CompensatingTransactionOperationManager}. + * Manages a stack of {@link CompensatingTransactionOperationExecutor} objects + * and performs rollback of these in the reverse order. + * + * @author Mattias Arthursson + * @since 1.2 + */ +public class DefaultCompensatingTransactionOperationManager implements + CompensatingTransactionOperationManager { + + private static Log log = LogFactory + .getLog(DefaultCompensatingTransactionOperationManager.class); + + private Stack operationExecutors = new Stack(); + + private CompensatingTransactionOperationFactory operationFactory; + + /** + * Set the {@link CompensatingTransactionOperationFactory} to use. + * + * @param operationFactory + * the {@link CompensatingTransactionOperationFactory}. + */ + public DefaultCompensatingTransactionOperationManager( + CompensatingTransactionOperationFactory operationFactory) { + this.operationFactory = operationFactory; + } + + /* + * @see org.springframework.transaction.compensating.CompensatingTransactionOperationManager#performOperation(java.lang.Object, + * java.lang.String, java.lang.Object[]) + */ + public void performOperation(Object resource, String operation, + Object[] args) { + CompensatingTransactionOperationRecorder recorder = operationFactory + .createRecordingOperation(resource, operation); + CompensatingTransactionOperationExecutor executor = recorder + .recordOperation(args); + + executor.performOperation(); + + // Don't push the executor until the actual operation passed. + operationExecutors.push(executor); + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationManager#rollback() + */ + public void rollback() { + log.debug("Performing rollback"); + while (!operationExecutors.isEmpty()) { + CompensatingTransactionOperationExecutor rollbackOperation = (CompensatingTransactionOperationExecutor) operationExecutors + .pop(); + try { + rollbackOperation.rollback(); + } catch (Exception e) { + throw new TransactionSystemException( + "Error occurred during rollback", e); + } + } + } + + /** + * Get the rollback operations. Used for testing purposes. + * + * @return the rollback operations. + */ + protected Stack getOperationExecutors() { + return operationExecutors; + } + + /** + * Set the rollback operations. Package protected - for testing purposes + * only. + * + * @param operationExecutors + * the rollback operations. + */ + void setOperationExecutors(Stack operationExecutors) { + this.operationExecutors = operationExecutors; + } + + /* + * @see org.springframework.ldap.support.transaction.CompensatingTransactionOperationManager#commit() + */ + public void commit() { + log.debug("Performing rollback"); + for (Iterator iter = operationExecutors.iterator(); iter.hasNext();) { + CompensatingTransactionOperationExecutor operationExecutor = (CompensatingTransactionOperationExecutor) iter + .next(); + try { + operationExecutor.commit(); + } catch (Exception e) { + throw new TransactionSystemException( + "Error occurred during commit", e); + } + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/package.html b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/package.html new file mode 100644 index 00000000..383254c7 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/transaction/compensating/support/package.html @@ -0,0 +1,9 @@ + + + +Support package for general Compensating Transaction Framework. +Contains default implementations of core interfaces as well as useful +helper classes. + + + diff --git a/mvn-build/core/src/main/java/overview.html b/mvn-build/core/src/main/java/overview.html new file mode 100644 index 00000000..fe97647c --- /dev/null +++ b/mvn-build/core/src/main/java/overview.html @@ -0,0 +1,3 @@ + +This document is the API specification for the Spring LDAP Framework. + \ No newline at end of file diff --git a/mvn-build/core/src/main/javacc/DnParserImpl.jj b/mvn-build/core/src/main/javacc/DnParserImpl.jj new file mode 100644 index 00000000..ff13ddf9 --- /dev/null +++ b/mvn-build/core/src/main/javacc/DnParserImpl.jj @@ -0,0 +1,154 @@ +/* + * Copyright 2005-2007 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. + */ + +options { + LOOKAHEAD = 1; + CHOICE_AMBIGUITY_CHECK = 3; + OTHER_AMBIGUITY_CHECK = 3; + STATIC = false; + DEBUG_PARSER = false; + DEBUG_LOOKAHEAD = false; + DEBUG_TOKEN_MANAGER = false; + ERROR_REPORTING = true; + JAVA_UNICODE_ESCAPE = false; + UNICODE_INPUT = true; + IGNORE_CASE = false; + USER_TOKEN_MANAGER = false; + USER_CHAR_STREAM = false; + BUILD_PARSER = true; + BUILD_TOKEN_MANAGER = true; + SANITY_CHECK = true; + FORCE_LA_CHECK = true; +} + +PARSER_BEGIN(DnParserImpl) + +package org.springframework.ldap.core; + +public class DnParserImpl implements DnParser{ + +} + +PARSER_END(DnParserImpl) + +TOKEN: { <#ALPHA: ["a"-"z", "A"-"Z"] > } +TOKEN: { <#DIGIT: ["0"-"9"]> } +TOKEN: { <#STRINGCHAR: ~[",","=","\r","+","<",">","#",";","\\","\""]> } +TOKEN: { <#STRINGENDCHAR: ~[",","=","\r","+","<",">","#",";","\\","\""," "]> } +TOKEN: { <#SPECIAL: [",","=","\r","+","<",">","#",";"]> } +TOKEN: { <#HEXCHAR: ["0"-"9","a"-"f","A"-"F"]> } +TOKEN: { <#HEXPAIR: > } +TOKEN: { <#BACKSLASHCHAR: "\\"> } +TOKEN: { <#PAIR: ( | | | )> } +TOKEN: { <#ESCAPEDSPACE: >} +TOKEN: { <#ESCAPEDSTART: ( | )> } +TOKEN: { <#STRINGEND: ( | | )> } + + TOKEN: +{ + + | + + | + ( | | "-")*> + | + )+ ("." ()+ )* > + | + +} + + TOKEN: +{ + ( | | )+ + | + ()+ + | + ()? ( | )* + )> +} + + TOKEN: +{ + )* "=" ()*> +} + +/** + * input -> dn + * dn -> rdn ( ( "," | ";" ) rdn )* + * rdn -> attributeTypeAndValue ( "+" attributeTypeAndValue )* + * attributeTypeAndValue -> + * ( )* AttributeType SpacedEquals AttributeValue ( )* + * SpacedEquals -> + * AttributeType -> | + * AttributeValue -> + */ +void input() : +{ +} +{ + dn() +} + +DistinguishedName dn(): +{ +DistinguishedName dn = new DistinguishedName(); +LdapRdn rdn; +} +{ + ( rdn = rdn() {dn.add(0, rdn);} (("," | ";") rdn = rdn() {dn.add(0, rdn);})* ) + {return dn;} +} + +LdapRdn rdn(): +{ +LdapRdnComponent rdnComponent; +LdapRdn rdn = new LdapRdn(); +} +{ + (rdnComponent = attributeTypeAndValue() {rdn.addComponent(rdnComponent);} ("+" rdnComponent = attributeTypeAndValue() {rdn.addComponent(rdnComponent);})*) + {return rdn;} +} + +LdapRdnComponent attributeTypeAndValue(): +{ +String attributeType; +String value; +} +{ + ( ()* attributeType = AttributeType() SpacedEquals() value = AttributeValue() ()*) + {return new LdapRdnComponent(attributeType, value, true);} +} + +void SpacedEquals(): +{} +{ + {token_source.SwitchTo(SPACED_EQUALS_S);} +} + +String AttributeType(): +{Token t;} +{ + ( t = | t = ) + {return t.image.toString();} +} + +String AttributeValue(): +{Token t;} +{ + {token_source.SwitchTo(ATTRVALUE_S);} t = {token_source.SwitchTo(DEFAULT);} + {return t.image.toString();} +} \ No newline at end of file diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/NamingExceptionTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/NamingExceptionTest.java new file mode 100644 index 00000000..8418c7ae --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/NamingExceptionTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2005-2007 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; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import javax.naming.directory.InitialDirContext; + +import junit.framework.TestCase; + +/** + * Unit tests for the NamingException class. + * + * @author Ulrik Sandberg + */ +public class NamingExceptionTest extends TestCase { + private ByteArrayOutputStream byteArrayOutputStream; + + public void testNamingExceptionWithNonSerializableResolvedObj() + throws Exception { + javax.naming.NameAlreadyBoundException wrappedException = new javax.naming.NameAlreadyBoundException( + "some error"); + wrappedException.setResolvedObj(new InitialDirContext()); + NamingException exception = new NameAlreadyBoundException( + wrappedException); + writeToStream(exception); + NamingException deSerializedException = readFromStream(); + assertNotNull( + "Original exception resolvedObj after serialization should not be null", + exception.getResolvedObj()); + assertNull("De-serialized exception resolvedObj should be null", + deSerializedException.getResolvedObj()); + } + + private NamingException readFromStream() throws IOException, + ClassNotFoundException { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( + byteArrayOutputStream.toByteArray()); + ObjectInputStream in = new ObjectInputStream(byteArrayInputStream); + NamingException deSerializedException; + try { + deSerializedException = (NamingException) in.readObject(); + } finally { + in.close(); + } + return deSerializedException; + } + + private void writeToStream(NamingException exception) throws IOException { + byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream); + try { + out.writeObject(exception); + out.flush(); + } finally { + out.close(); + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/authentication/AcegiAuthenticationSourceTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/authentication/AcegiAuthenticationSourceTest.java new file mode 100644 index 00000000..dd9d89d4 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/authentication/AcegiAuthenticationSourceTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2005-2007 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.authentication; + +import junit.framework.TestCase; + +import org.acegisecurity.Authentication; +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; +import org.acegisecurity.userdetails.User; +import org.acegisecurity.userdetails.ldap.LdapUserDetails; +import org.easymock.MockControl; +import org.springframework.ldap.authentication.AcegiAuthenticationSource; + +public class AcegiAuthenticationSourceTest extends TestCase { + + private MockControl authenticationControl; + + private Authentication authenticationMock; + + private MockControl ldapUserDetailsControl; + + private LdapUserDetails ldapUserDetailsMock; + + private AcegiAuthenticationSource tested; + + protected void setUp() throws Exception { + super.setUp(); + + authenticationControl = MockControl.createControl(Authentication.class); + authenticationMock = (Authentication) authenticationControl.getMock(); + + ldapUserDetailsControl = MockControl + .createControl(LdapUserDetails.class); + ldapUserDetailsMock = (LdapUserDetails) ldapUserDetailsControl + .getMock(); + + tested = new AcegiAuthenticationSource(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + authenticationControl = null; + authenticationMock = null; + + ldapUserDetailsControl = null; + ldapUserDetailsMock = null; + + tested = null; + } + + protected void replay() { + authenticationControl.replay(); + ldapUserDetailsControl.replay(); + } + + protected void verify() { + authenticationControl.verify(); + ldapUserDetailsControl.verify(); + } + + public void testGetPrincipalAndCredentials() { + authenticationControl.expectAndReturn( + authenticationMock.getPrincipal(), ldapUserDetailsMock); + authenticationControl.expectAndReturn(authenticationMock + .getCredentials(), "secret"); + + ldapUserDetailsControl.expectAndDefaultReturn(ldapUserDetailsMock + .getDn(), "cn=Manager"); + + SecurityContextHolder.getContext() + .setAuthentication(authenticationMock); + + replay(); + + String principal = tested.getPrincipal(); + String credentials = tested.getCredentials(); + + verify(); + + assertEquals("secret", credentials); + assertEquals("cn=Manager", principal); + } + + public void testGetPrincipalAndCredentials_nullAuthentication() { + SecurityContextHolder.getContext().setAuthentication(null); + + replay(); + + assertEquals("", tested.getPrincipal()); + assertEquals("", tested.getCredentials()); + verify(); + } + + public void testGetPrincipal_InvalidPrincipal() { + authenticationControl.expectAndReturn( + authenticationMock.getPrincipal(), new User("dummy", "dummy", + true, true, true, true, new GrantedAuthority[0])); + + SecurityContextHolder.getContext() + .setAuthentication(authenticationMock); + + replay(); + + try { + tested.getPrincipal(); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + verify(); + } + + public void testGetPrincipalWithAnonymousAuthenticationToken() { + + SecurityContextHolder.getContext().setAuthentication( + new AnonymousAuthenticationToken("dummy", "dummy", + new GrantedAuthority[] { new DummyAuthoroty() })); + + assertEquals("", tested.getPrincipal()); + } + + private static class DummyAuthoroty implements GrantedAuthority { + public String getAuthority() { + return null; + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/authentication/DefaultValuesAuthenticationSourceDecoratorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/authentication/DefaultValuesAuthenticationSourceDecoratorTest.java new file mode 100644 index 00000000..39596f7e --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/authentication/DefaultValuesAuthenticationSourceDecoratorTest.java @@ -0,0 +1,134 @@ +/* + * Copyright 2005-2007 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.authentication; + +import org.easymock.MockControl; +import org.springframework.ldap.authentication.DefaultValuesAuthenticationSourceDecorator; +import org.springframework.ldap.core.AuthenticationSource; + +import junit.framework.TestCase; + +public class DefaultValuesAuthenticationSourceDecoratorTest extends TestCase { + + private static final String DEFAULT_PASSWORD = "defaultPassword"; + + private static final String DEFAULT_USER = "cn=defaultUser"; + + private DefaultValuesAuthenticationSourceDecorator tested; + + private MockControl authenticationSourceControl; + + private AuthenticationSource authenticationSourceMock; + + protected void setUp() throws Exception { + super.setUp(); + + authenticationSourceControl = MockControl + .createControl(AuthenticationSource.class); + authenticationSourceMock = (AuthenticationSource) authenticationSourceControl + .getMock(); + tested = new DefaultValuesAuthenticationSourceDecorator(); + tested.setDefaultUser(DEFAULT_USER); + tested.setDefaultPassword(DEFAULT_PASSWORD); + tested.setTarget(authenticationSourceMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + tested = null; + authenticationSourceControl = null; + authenticationSourceMock = null; + } + + public void testGetPrincipal_TargetHasPrincipal() { + authenticationSourceControl.expectAndDefaultReturn( + authenticationSourceMock.getPrincipal(), "cn=someUser"); + authenticationSourceControl.replay(); + + String principal = tested.getPrincipal(); + + authenticationSourceControl.verify(); + assertEquals("cn=someUser", principal); + } + + public void testGetPrincipal_TargetHasNoPrincipal() { + authenticationSourceControl.expectAndDefaultReturn( + authenticationSourceMock.getPrincipal(), ""); + authenticationSourceControl.replay(); + + String principal = tested.getPrincipal(); + + authenticationSourceControl.verify(); + assertEquals(DEFAULT_USER, principal); + } + + public void testGetCredentials_TargetHasPrincipal() { + authenticationSourceControl.expectAndDefaultReturn( + authenticationSourceMock.getPrincipal(), "cn=someUser"); + authenticationSourceControl.expectAndDefaultReturn( + authenticationSourceMock.getCredentials(), "somepassword"); + authenticationSourceControl.replay(); + + String credentials = tested.getCredentials(); + + authenticationSourceControl.verify(); + assertEquals("somepassword", credentials); + } + + public void testGetCredentials_TargetHasNoPrincipal() { + authenticationSourceControl.expectAndDefaultReturn( + authenticationSourceMock.getPrincipal(), ""); + authenticationSourceControl.expectAndDefaultReturn( + authenticationSourceMock.getCredentials(), "somepassword"); + authenticationSourceControl.replay(); + + String credentials = tested.getCredentials(); + + authenticationSourceControl.verify(); + assertEquals(DEFAULT_PASSWORD, credentials); + } + + public void testAfterPropertiesSet_noTarget() throws Exception { + tested.setTarget(null); + try { + tested.afterPropertiesSet(); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testAfterPropertiesSet_noDefaultUser() throws Exception { + tested.setDefaultUser(null); + try { + tested.afterPropertiesSet(); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testAfterPropertiesSet_noDefaultPassword() throws Exception { + tested.setDefaultPassword(null); + try { + tested.afterPropertiesSet(); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultTest.java new file mode 100644 index 00000000..e42aa115 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2005-2007 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.control; + +import java.util.LinkedList; +import java.util.List; + +import org.springframework.ldap.control.PagedResult; +import org.springframework.ldap.control.PagedResultsCookie; + +import com.gargoylesoftware.base.testing.EqualsTester; + +import junit.framework.TestCase; + +public class PagedResultTest extends TestCase { + public void testEquals() throws Exception { + List expectedList = new LinkedList(); + expectedList.add("dummy"); + List otherList = new LinkedList(); + otherList.add("different"); + + PagedResult originalObject = new PagedResult(expectedList, + new PagedResultsCookie(null)); + PagedResult identicalObject = new PagedResult(expectedList, + new PagedResultsCookie(null)); + PagedResult differentObject = new PagedResult(otherList, + new PagedResultsCookie(null)); + PagedResult subclassObject = new PagedResult(expectedList, + new PagedResultsCookie(null)) { + + }; + + new EqualsTester(originalObject, identicalObject, differentObject, + subclassObject); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultsCookieTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultsCookieTest.java new file mode 100644 index 00000000..07566e35 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultsCookieTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2005-2007 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.control; + +import org.springframework.ldap.control.PagedResultsCookie; + +import com.gargoylesoftware.base.testing.EqualsTester; + +import junit.framework.TestCase; + +public class PagedResultsCookieTest extends TestCase { + public void testEquals() { + byte[] expectedCookie = new byte[] { 1, 2 }; + byte[] differentCookie = new byte[] { 2, 3 }; + + PagedResultsCookie originalObject = new PagedResultsCookie( + expectedCookie); + PagedResultsCookie identicalObject = new PagedResultsCookie( + expectedCookie); + PagedResultsCookie differentObject = new PagedResultsCookie( + differentCookie); + PagedResultsCookie subclassObject = new PagedResultsCookie( + expectedCookie) { + + }; + + new EqualsTester(originalObject, identicalObject, differentObject, + subclassObject); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultsRequestControlTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultsRequestControlTest.java new file mode 100644 index 00000000..0b72e5a1 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultsRequestControlTest.java @@ -0,0 +1,207 @@ +/* + * Copyright 2005-2007 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.control; + +import java.io.IOException; + +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.control.PagedResultsCookie; +import org.springframework.ldap.control.PagedResultsRequestControl; + +import com.sun.jndi.ldap.Ber; +import com.sun.jndi.ldap.BerDecoder; +import com.sun.jndi.ldap.BerEncoder; +import com.sun.jndi.ldap.ctl.DirSyncResponseControl; +import com.sun.jndi.ldap.ctl.PagedResultsControl; +import com.sun.jndi.ldap.ctl.PagedResultsResponseControl; + +public class PagedResultsRequestControlTest extends TestCase { + + private MockControl ldapContextControl; + + private LdapContext ldapContextMock; + + protected void setUp() throws Exception { + super.setUp(); + + // Create ldapContext mock + ldapContextControl = MockControl.createControl(LdapContext.class); + ldapContextMock = (LdapContext) ldapContextControl.getMock(); + + } + + protected void tearDown() throws Exception { + super.tearDown(); + + ldapContextControl = null; + ldapContextMock = null; + + } + + protected void replay() { + ldapContextControl.replay(); + } + + protected void verify() { + ldapContextControl.verify(); + } + + public void testCreateRequestControl() throws Exception { + PagedResultsRequestControl tested = new PagedResultsRequestControl(20); + + PagedResultsControl control = (PagedResultsControl) tested + .createRequestControl(); + assertNotNull(control); + } + + public void testCreateRequestControl_CookieSet() throws Exception { + PagedResultsCookie cookie = new PagedResultsCookie(new byte[0]); + PagedResultsRequestControl tested = new PagedResultsRequestControl(20, + cookie); + + PagedResultsControl control = (PagedResultsControl) tested + .createRequestControl(); + assertNotNull(control); + } + + public void testPostProcess() throws Exception { + int resultSize = 50; + byte pageSize = 8; + + byte[] value = new byte[1]; + value[0] = pageSize; + byte[] cookie = encodeValue(resultSize, value); + PagedResultsResponseControl control = new PagedResultsResponseControl( + "dummy", true, cookie); + + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getResponseControls(), new Control[] { control }); + + PagedResultsRequestControl tested = new PagedResultsRequestControl(20); + + replay(); + + tested.postProcess(ldapContextMock); + + verify(); + + PagedResultsCookie returnedCookie = tested.getCookie(); + assertEquals(8, returnedCookie.getCookie()[0]); + assertEquals(20, tested.getPageSize()); + assertEquals(50, tested.getResultSize()); + } + + public void testPostProcess_InvalidResponseControl() throws Exception { + int resultSize = 50; + byte pageSize = 8; + + byte[] value = new byte[1]; + value[0] = pageSize; + byte[] cookie = encodeDirSyncValue(resultSize, value); + + // Using another response control to verify that it is ignored + DirSyncResponseControl control = new DirSyncResponseControl( + "dummy", true, cookie); + + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getResponseControls(), new Control[] { control }); + + PagedResultsRequestControl tested = new PagedResultsRequestControl(20); + + replay(); + + tested.postProcess(ldapContextMock); + + verify(); + + assertNull(tested.getCookie()); + assertEquals(20, tested.getPageSize()); + assertEquals(0, tested.getResultSize()); + } + + public void testPostProcess_NoResponseControls() throws Exception { + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getResponseControls(), null); + + PagedResultsRequestControl tested = new PagedResultsRequestControl(20); + + replay(); + + tested.postProcess(ldapContextMock); + + verify(); + + assertNull(tested.getCookie()); + assertEquals(20, tested.getPageSize()); + assertEquals(0, tested.getResultSize()); + } + + public void testBerDecoding() throws Exception { + byte[] value = new byte[1]; + value[0] = 8; + int pageSize = 20; + byte[] cookie = encodeValue(pageSize, value); + + BerDecoder ber = new BerDecoder(cookie, 0, cookie.length); + + ber.parseSeq(null); + int actualPageSize = ber.parseInt(); + byte[] actualValue = ber.parseOctetString(Ber.ASN_OCTET_STR, null); + + assertEquals("pageSize,", 20, actualPageSize); + assertEquals("value length", value.length, actualValue.length); + for (int i = 0; i < value.length; i++) { + assertEquals("value (index " + i + "),", value[i], actualValue[i]); + } + } + + private byte[] encodeValue(int pageSize, byte[] cookie) + throws IOException { + + // build the ASN.1 encoding + BerEncoder ber = new BerEncoder(10 + cookie.length); + + ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + ber.encodeInt(pageSize); + ber.encodeOctetString(cookie, Ber.ASN_OCTET_STR); + ber.endSeq(); + + return ber.getTrimmedBuf(); + } + + /** + * Encode a value suitable for the DirSyncResponseControl used in a test. + */ + private byte[] encodeDirSyncValue(int pageSize, byte[] cookie) + throws IOException { + + // build the ASN.1 encoding + BerEncoder ber = new BerEncoder(10 + cookie.length); + + ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + ber.encodeInt(1); // flag + ber.encodeInt(pageSize); // maxReturnLength + ber.encodeOctetString(cookie, Ber.ASN_OCTET_STR); + ber.endSeq(); + + return ber.getTrimmedBuf(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/RequestControlDirContextProcessorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/RequestControlDirContextProcessorTest.java new file mode 100644 index 00000000..edaa3b48 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/RequestControlDirContextProcessorTest.java @@ -0,0 +1,159 @@ +/* + * Copyright 2005-2007 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.control; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.control.AbstractRequestControlDirContextProcessor; + +public class RequestControlDirContextProcessorTest extends TestCase { + + private AbstractRequestControlDirContextProcessor tested; + + private MockControl requestControlControl; + + private Control requestControlMock; + + private MockControl requestControl2Control; + + private Control requestControl2Mock; + + private MockControl ldapContextControl; + + private LdapContext ldapContextMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + protected void setUp() throws Exception { + super.setUp(); + // Create requestControl mock + requestControlControl = MockControl.createControl(Control.class); + requestControlMock = (Control) requestControlControl.getMock(); + + // Create requestControl2 mock + requestControl2Control = MockControl.createControl(Control.class); + requestControl2Mock = (Control) requestControl2Control.getMock(); + + // Create ldapContext mock + ldapContextControl = MockControl.createControl(LdapContext.class); + ldapContextMock = (LdapContext) ldapContextControl.getMock(); + + // Create dirContext mock + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + tested = new AbstractRequestControlDirContextProcessor() { + + public Control createRequestControl() { + return requestControlMock; + } + + public void postProcess(DirContext ctx) throws NamingException { + } + + }; + } + + protected void tearDown() throws Exception { + super.tearDown(); + + requestControlControl = null; + requestControlMock = null; + + requestControl2Control = null; + requestControl2Mock = null; + + ldapContextControl = null; + ldapContextMock = null; + + dirContextControl = null; + dirContextMock = null; + + } + + protected void replay() { + requestControlControl.replay(); + requestControl2Control.replay(); + ldapContextControl.replay(); + dirContextControl.replay(); + } + + protected void verify() { + requestControlControl.verify(); + requestControl2Control.verify(); + ldapContextControl.verify(); + dirContextControl.verify(); + } + + public void testPreProcess() throws NamingException { + ldapContextControl.setDefaultMatcher(MockControl.ARRAY_MATCHER); + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getRequestControls(), new Control[] { requestControl2Mock }); + ldapContextMock.setRequestControls(new Control[] { requestControl2Mock, + requestControlMock }); + + replay(); + + tested.preProcess(ldapContextMock); + + verify(); + } + + public void testPreProcess_NoExistingControls() throws NamingException { + ldapContextControl.setDefaultMatcher(MockControl.ARRAY_MATCHER); + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getRequestControls(), new Control[0]); + ldapContextMock + .setRequestControls(new Control[] { requestControlMock }); + + replay(); + + tested.preProcess(ldapContextMock); + + verify(); + } + + public void testPreProcess_NullControls() throws NamingException { + ldapContextControl.setDefaultMatcher(MockControl.ARRAY_MATCHER); + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getRequestControls(), null); + ldapContextMock + .setRequestControls(new Control[] { requestControlMock }); + + replay(); + + tested.preProcess(ldapContextMock); + + verify(); + } + + public void testPreProcess_NotLdapContext() throws Exception { + try { + tested.preProcess(dirContextMock); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/SortControlDirContextProcessorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/SortControlDirContextProcessorTest.java new file mode 100644 index 00000000..fe9e5673 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/SortControlDirContextProcessorTest.java @@ -0,0 +1,193 @@ +/* + * Copyright 2005-2007 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.control; + +import java.io.IOException; + +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; +import javax.naming.ldap.PagedResultsControl; + +import junit.framework.TestCase; + +import org.easymock.MockControl; + +import com.sun.jndi.ldap.Ber; +import com.sun.jndi.ldap.BerDecoder; +import com.sun.jndi.ldap.BerEncoder; +import com.sun.jndi.ldap.ctl.DirSyncResponseControl; +import com.sun.jndi.ldap.ctl.SortControl; +import com.sun.jndi.ldap.ctl.SortResponseControl; + +/** + * Unit tests for the SortControlDirContextProcessor class. + * {@link javax.naming.ldap.SortControl} + * {@link javax.naming.ldap.SortResponseControl} + * {@link PagedResultsControl} + * + * @author Ulrik Sandberg + */ +public class SortControlDirContextProcessorTest extends TestCase { + + private MockControl ldapContextControl; + + private LdapContext ldapContextMock; + + protected void setUp() throws Exception { + super.setUp(); + + // Create ldapContext mock + ldapContextControl = MockControl.createControl(LdapContext.class); + ldapContextMock = (LdapContext) ldapContextControl.getMock(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + ldapContextControl = null; + ldapContextMock = null; + } + + protected void replay() { + ldapContextControl.replay(); + } + + protected void verify() { + ldapContextControl.verify(); + } + + public void testCreateRequestControl() throws Exception { + SortControlDirContextProcessor tested = new SortControlDirContextProcessor( + "key"); + + SortControl result = (SortControl) tested.createRequestControl(); + assertNotNull(result); + assertEquals("1.2.840.113556.1.4.473", result.getID()); + assertEquals(9, result.getEncodedValue().length); + } + + public void testPostProcess() throws Exception { + byte sortResult = 0; // success + + byte[] value = encodeValue(sortResult); + SortResponseControl control = new SortResponseControl( + "dummy", true, value); + + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getResponseControls(), new Control[] { control }); + + SortControlDirContextProcessor tested = new SortControlDirContextProcessor("key"); + + replay(); + + tested.postProcess(ldapContextMock); + + verify(); + + assertEquals(true, tested.isSorted()); + assertEquals(0, tested.getResultCode()); + } + + public void testPostProcess_NonSuccess() throws Exception { + byte sortResult = 1; + + byte[] value = encodeValue(sortResult); + SortResponseControl control = new SortResponseControl( + "dummy", true, value); + + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getResponseControls(), new Control[] { control }); + + SortControlDirContextProcessor tested = new SortControlDirContextProcessor("key"); + + replay(); + + tested.postProcess(ldapContextMock); + + verify(); + + assertEquals(false, tested.isSorted()); + assertEquals(1, tested.getResultCode()); + } + + public void testPostProcess_InvalidResponseControl() throws Exception { + int resultSize = 50; + byte pageSize = 8; + + byte[] value = new byte[1]; + value[0] = pageSize; + byte[] cookie = encodeDirSyncValue(resultSize, value); + + // Using another response control to verify that it is ignored + DirSyncResponseControl control = new DirSyncResponseControl("dummy", + true, cookie); + + ldapContextControl.expectAndDefaultReturn(ldapContextMock + .getResponseControls(), new Control[] { control }); + + SortControlDirContextProcessor tested = new SortControlDirContextProcessor("key"); + + replay(); + + tested.postProcess(ldapContextMock); + + verify(); + + assertEquals(false, tested.isSorted()); + } + + public void testBerDecoding() throws Exception { + int sortResult = 53; // unwilling to perform + byte[] encoded = encodeValue(sortResult); + + BerDecoder ber = new BerDecoder(encoded, 0, encoded.length); + + ber.parseSeq(null); + int actualSortResult = ber.parseEnumeration(); + + assertEquals("sortResult,", 53, actualSortResult); + } + + private byte[] encodeValue(int sortResult) throws IOException { + + // build the ASN.1 encoding + BerEncoder ber = new BerEncoder(10); + + ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + ber.encodeInt(sortResult, Ber.ASN_ENUMERATED); + ber.endSeq(); + + return ber.getTrimmedBuf(); + } + + /** + * Encode a value suitable for the DirSyncResponseControl used in a test. + */ + private byte[] encodeDirSyncValue(int pageSize, byte[] cookie) + throws IOException { + + // build the ASN.1 encoding + BerEncoder ber = new BerEncoder(10 + cookie.length); + + ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + ber.encodeInt(1); // flag + ber.encodeInt(pageSize); // maxReturnLength + ber.encodeOctetString(cookie, Ber.ASN_OCTET_STR); + ber.endSeq(); + + return ber.getTrimmedBuf(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/CollectingNameClassPairCallbackHandlerTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/CollectingNameClassPairCallbackHandlerTest.java new file mode 100644 index 00000000..4e8bf080 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/CollectingNameClassPairCallbackHandlerTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.List; + +import javax.naming.NameClassPair; + +import org.springframework.ldap.core.CollectingNameClassPairCallbackHandler; + +import junit.framework.TestCase; + +public class CollectingNameClassPairCallbackHandlerTest extends TestCase { + + private CollectingNameClassPairCallbackHandler tested; + + private Object expectedResult; + + private NameClassPair expectedNameClassPair; + + protected void setUp() throws Exception { + super.setUp(); + + expectedResult = new Object(); + expectedNameClassPair = new NameClassPair(null, null); + tested = new CollectingNameClassPairCallbackHandler() { + public Object getObjectFromNameClassPair(NameClassPair nameClassPair) { + assertSame(expectedNameClassPair, nameClassPair); + return expectedResult; + } + }; + } + + protected void tearDown() throws Exception { + super.tearDown(); + + expectedNameClassPair = null; + expectedResult = null; + tested = null; + } + + public void testHandleNameClassPair() { + tested.handleNameClassPair(expectedNameClassPair); + List result = tested.getList(); + assertEquals(1, result.size()); + assertSame(expectedResult, result.get(0)); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/ContextMapperCallbackHandlerTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/ContextMapperCallbackHandlerTest.java new file mode 100644 index 00000000..48158bd2 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/ContextMapperCallbackHandlerTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Binding; + +import org.easymock.MockControl; + +import junit.framework.TestCase; + +public class ContextMapperCallbackHandlerTest extends TestCase { + + private MockControl mapperControl; + + private ContextMapper mapperMock; + + private ContextMapperCallbackHandler tested; + + protected void setUp() throws Exception { + super.setUp(); + + mapperControl = MockControl.createControl(ContextMapper.class); + mapperMock = (ContextMapper) mapperControl.getMock(); + tested = new ContextMapperCallbackHandler(mapperMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + mapperControl = null; + mapperMock = null; + tested = null; + } + + public void testConstructorWithEmptyArgument() { + try { + new ContextMapperCallbackHandler(null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testGetObjectFromNameClassPair() { + Object expectedObject = "object"; + Object expectedResult = "result"; + Binding expectedBinding = new Binding("some name", expectedObject); + + mapperControl.expectAndReturn( + mapperMock.mapFromContext(expectedObject), expectedResult); + + mapperControl.replay(); + + Object actualResult = tested + .getObjectFromNameClassPair(expectedBinding); + + mapperControl.verify(); + + assertEquals(expectedResult, actualResult); + } + + public void testGetObjectFromNameClassPairObjectRetrievalException() { + Binding expectedBinding = new Binding("some name", null); + + mapperControl.replay(); + + try { + tested.getObjectFromNameClassPair(expectedBinding); + fail("ObjectRetrievalException expected"); + } catch (ObjectRetrievalException expected) { + assertTrue(true); + } + mapperControl.verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/DirContextAdapterBugTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/DirContextAdapterBugTest.java new file mode 100644 index 00000000..96289f2b --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/DirContextAdapterBugTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Name; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; + +import junit.framework.TestCase; + + +/** + * Unit tests that serve as regression tests for bugs that have been fixed. + * + * @author Luke Taylor + */ +public class DirContextAdapterBugTest extends TestCase { + + public void testResetAttributeValuesNotReportedAsModifications() { + BasicAttributes attrs = new BasicAttributes("myattr", "a"); + attrs.get("myattr").add("b"); + attrs.get("myattr").add("c"); + UpdateAdapter ctx = new UpdateAdapter(attrs, new DistinguishedName()); + + ctx.setAttributeValues("myattr", new String[] { "a", "b" }); + ctx.setAttributeValues("myattr", new String[] { "a", "b", "c" }); + + assertEquals(0, ctx.getModificationItems().length); + } + + public void testResetAttributeValuesSameLengthNotReportedAsModifications() { + BasicAttributes attrs = new BasicAttributes("myattr", "a"); + attrs.get("myattr").add("b"); + attrs.get("myattr").add("c"); + UpdateAdapter ctx = new UpdateAdapter(attrs, new DistinguishedName()); + + ctx.setAttributeValues("myattr", new String[] { "a", "b", "d" }); + ctx.setAttributeValues("myattr", new String[] { "a", "b", "c" }); + + assertEquals(0, ctx.getModificationItems().length); + } + + /** + * This test starts with an array with a null value in it (because that's + * how BasicAttributes will do it), changes to [a], and then + * changes to null. The current code interprets this as a + * change and will replace the original array with an empty array. + * + * TODO Is this correct behaviour? + */ + public void testResetNullAttributeValuesReportedAsModifications() { + BasicAttributes attrs = new BasicAttributes("myattr", null); + UpdateAdapter ctx = new UpdateAdapter(attrs, new DistinguishedName()); + + ctx.setAttributeValues("myattr", new String[] { "a" }); + ctx.setAttributeValues("myattr", null); + + assertEquals(1, ctx.getModificationItems().length); + } + + public void testResetNullAttributeValueNotReportedAsModification() throws Exception { + BasicAttributes attrs = new BasicAttributes("myattr", "b"); + UpdateAdapter ctx = new UpdateAdapter(attrs, new DistinguishedName()); + + ctx.setAttributeValue("myattr", "a"); + ctx.setAttributeValue("myattr", "b"); + + assertEquals(0, ctx.getModificationItems().length); + } + + private static class UpdateAdapter extends DirContextAdapter { + public UpdateAdapter(Attributes attrs, Name dn) { + super(attrs, dn); + setUpdateMode(true); + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/DirContextAdapterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/DirContextAdapterTest.java new file mode 100644 index 00000000..526381c3 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/DirContextAdapterTest.java @@ -0,0 +1,1005 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; + +import junit.framework.TestCase; + +/** + * Tests the DirContextAdapter class. + * + * @author Andreas Ronge + * @author Mattias Arthursson + */ +public class DirContextAdapterTest extends TestCase { + private static final DistinguishedName BASE_NAME = new DistinguishedName( + "dc=jayway, dc=se"); + + private static final DistinguishedName DUMMY_NAME = new DistinguishedName( + "c=SE, dc=jayway, dc=se"); + + private DirContextAdapter tested; + + protected void setUp() throws Exception { + super.setUp(); + tested = new DirContextAdapter(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testSetUpdateMode() throws Exception { + assertFalse(tested.isUpdateMode()); + tested.setUpdateMode(true); + assertTrue(tested.isUpdateMode()); + tested.setUpdateMode(false); + assertFalse(tested.isUpdateMode()); + } + + public void testGetModificationItems() throws Exception { + ModificationItem[] items = tested.getModificationItems(); + assertEquals(0, items.length); + tested.setUpdateMode(true); + assertEquals(0, items.length); + } + + public void testAlwaysReplace() throws Exception { + ModificationItem[] items = tested.getModificationItems(); + assertEquals(0, items.length); + tested.setUpdateMode(true); + assertEquals(0, items.length); + } + + public void testGetStringAttributeNotExists() throws Exception { + String s = tested.getStringAttribute("does not exist"); + assertNull(s); + } + + public void testGetStringAttributeExists() throws Exception { + final Attributes attrs = new BasicAttributes(); + attrs.put(new BasicAttribute("abc", "def")); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(attrs, null); + } + } + tested = new TestableDirContextAdapter(); + String s = tested.getStringAttribute("abc"); + assertEquals("def", s); + } + + public void testGetStringAttributesExists() 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(); + String s[] = tested.getStringAttributes("abc"); + assertEquals("123", s[0]); + assertEquals("234", s[1]); + assertEquals(2, s.length); + } + + public void testGetStringAttributesNotExists() throws Exception { + String s[] = tested.getStringAttributes("abc"); + assertNull(s); + } + + public void testAddAttributeValue() throws NamingException { + // Perform test + tested.addAttributeValue("abc", "123"); + + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + } + + public void testAddAttributeValueAttributeWithOtherValueExists() + throws NamingException { + tested.setAttribute(new BasicAttribute("abc", "321")); + + // Perform test + tested.addAttributeValue("abc", "123"); + + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("321", (String) attr.get(0)); + assertEquals("123", (String) attr.get(1)); + } + + public void testAddAttributeValueAttributeWithSameValueExists() + throws NamingException { + tested.setAttribute(new BasicAttribute("abc", "123")); + + // Perform test + tested.addAttributeValue("abc", "123"); + + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals(1, attr.size()); + assertEquals("123", (String) attr.get(0)); + } + + public void testAddAttributeValueInUpdateMode() throws NamingException { + tested.setUpdateMode(true); + tested.addAttributeValue("abc", "123"); + + // Perform test + Attributes attrs = tested.getAttributes(); + assertNull(attrs.get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute attribute = modificationItems[0].getAttribute(); + assertEquals("abc", attribute.getID()); + assertEquals("123", attribute.get()); + } + + public void testAddAttributeValueInUpdateModeAttributeWithOtherValueExistsInOrigAttrs() + throws NamingException { + + tested.setAttribute(new BasicAttribute("abc", "321")); + tested.setUpdateMode(true); + + // Perform test + tested.addAttributeValue("abc", "123"); + + Attributes attrs = tested.getAttributes(); + assertNotNull(attrs.get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute attribute = modificationItems[0].getAttribute(); + assertEquals(1, attribute.size()); + assertEquals("abc", attribute.getID()); + assertEquals("123", attribute.get()); + } + + public void testAddAttributeValueInUpdateModeAttributeWithSameValueExistsInOrigAttrs() + throws NamingException { + + tested.setAttribute(new BasicAttribute("abc", "123")); + tested.setUpdateMode(true); + + // Perform test + tested.addAttributeValue("abc", "123"); + + Attributes attrs = tested.getAttributes(); + assertNotNull(attrs.get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(0, modificationItems.length); + } + + public void testAddAttributeValueInUpdateModeAttributeWithOtherValueExistsInUpdAttrs() + throws NamingException { + tested.setUpdateMode(true); + tested.setAttributeValue("abc", "321"); + + // Perform test + tested.addAttributeValue("abc", "123"); + + Attributes attrs = tested.getAttributes(); + assertNull(attrs.get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute attribute = modificationItems[0].getAttribute(); + assertEquals("abc", attribute.getID()); + assertEquals("321", attribute.get(0)); + assertEquals("123", attribute.get(1)); + } + + public void testAddAttributeValueInUpdateModeAttributeWithSameValueExistsInUpdAttrs() + throws NamingException { + tested.setUpdateMode(true); + tested.setAttributeValue("abc", "123"); + + // Perform test + tested.addAttributeValue("abc", "123"); + + Attributes attrs = tested.getAttributes(); + assertNull(attrs.get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute attribute = modificationItems[0].getAttribute(); + assertEquals(1, attribute.size()); + assertEquals("abc", attribute.getID()); + assertEquals("123", attribute.get()); + } + + public void testRemoveAttributeValueAttributeDoesntExist() { + // Perform test + tested.removeAttributeValue("abc", "123"); + + Attributes attributes = tested.getAttributes(); + assertNull(attributes.get("abc")); + } + + public void testRemoveAttributeValueAttributeWithOtherValueExists() + throws NamingException { + tested.setAttribute(new BasicAttribute("abc", "321")); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + Attributes attributes = tested.getAttributes(); + Attribute attr = attributes.get("abc"); + assertNotNull(attr); + assertEquals(1, attr.size()); + assertEquals("321", attr.get()); + } + + public void testRemoveAttributeValueAttributeWithSameValueExists() { + tested.setAttribute(new BasicAttribute("abc", "123")); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + Attributes attributes = tested.getAttributes(); + Attribute attr = attributes.get("abc"); + assertNull(attr); + } + + public void testRemoveAttributeValueAttributeWithOtherAndSameValueExists() + throws NamingException { + BasicAttribute basicAttribute = new BasicAttribute("abc"); + basicAttribute.add("123"); + basicAttribute.add("321"); + tested.setAttribute(basicAttribute); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + Attributes attributes = tested.getAttributes(); + Attribute attr = attributes.get("abc"); + assertNotNull(attr); + assertEquals(1, attr.size()); + assertEquals("321", attr.get()); + } + + public void testRemoveAttributeValueInUpdateMode() { + tested.setUpdateMode(true); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + assertNull(tested.getAttributes().get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(0, modificationItems.length); + } + + public void testRemoveAttributeValueInUpdateModeSameValueExistsInUpdatedAttrs() { + tested.setUpdateMode(true); + tested.setAttributeValue("abc", "123"); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + assertNull(tested.getAttributes().get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(0, modificationItems.length); + } + + public void testRemoveAttributeValueInUpdateModeOtherValueExistsInUpdatedAttrs() + throws NamingException { + tested.setUpdateMode(true); + tested.setAttributeValue("abc", "321"); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + assertNull(tested.getAttributes().get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute modificationAttribute = modificationItems[0].getAttribute(); + assertEquals("abc", modificationAttribute.getID()); + assertEquals(1, modificationAttribute.size()); + assertEquals("321", modificationAttribute.get()); + } + + public void testRemoveAttributeValueInUpdateModeOtherAndSameValueExistsInUpdatedAttrs() + throws NamingException { + tested.setUpdateMode(true); + tested.setAttributeValues("abc", new String[] { "321", "123" }); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + assertNull(tested.getAttributes().get("abc")); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute modificationAttribute = modificationItems[0].getAttribute(); + assertEquals("abc", modificationAttribute.getID()); + assertEquals(1, modificationAttribute.size()); + } + + public void testRemoveAttributeValueInUpdateModeSameValueExistsInOrigAttrs() { + tested.setAttribute(new BasicAttribute("abc", "123")); + tested.setUpdateMode(true); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute modificationAttribute = modificationItems[0].getAttribute(); + assertEquals("abc", modificationAttribute.getID()); + assertEquals(0, modificationAttribute.size()); + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + } + + public void testRemoveAttributeValueInUpdateModeSameAndOtherValueExistsInOrigAttrs() + throws NamingException { + BasicAttribute basicAttribute = new BasicAttribute("abc"); + basicAttribute.add("123"); + basicAttribute.add("321"); + tested.setAttribute(basicAttribute); + tested.setUpdateMode(true); + + // Perform test + tested.removeAttributeValue("abc", "123"); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + Attribute modificationAttribute = modificationItems[0].getAttribute(); + assertEquals("abc", modificationAttribute.getID()); + assertEquals(1, modificationAttribute.size()); + assertEquals("123", modificationAttribute.get()); + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + } + + public void testSetStringAttribute() throws Exception { + assertFalse(tested.isUpdateMode()); + tested.setAttributeValue("abc", "123"); + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + } + + public void testSetStringAttributeNull() throws Exception { + assertFalse(tested.isUpdateMode()); + tested.setAttributeValue("abc", null); + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertNull(attr); + } + + public void testAddAttribute() throws Exception { + tested.setUpdateMode(true); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValue("abc", "123"); + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertNull(attr); + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.ADD_ATTRIBUTE, mods[0].getModificationOp()); + attr = mods[0].getAttribute(); + assertEquals("123", (String) attr.get()); + + String[] modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(1, modNames.length); + assertEquals("abc", modNames[0]); + + tested.update(); + mods = tested.getModificationItems(); + assertEquals(0, mods.length); + modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + attrs = tested.getAttributes(); + attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + } + + public void testGetDn() throws Exception { + DirContextAdapter tested = new DirContextAdapter(DUMMY_NAME); + Name result = tested.getDn(); + assertEquals(DUMMY_NAME, result); + } + + public void testGetDn_BasePath() { + DirContextAdapter tested = new DirContextAdapter(null, DUMMY_NAME, + BASE_NAME); + Name result = tested.getDn(); + assertEquals(DUMMY_NAME, result); + } + + public void testGetNameInNamespace() { + DirContextAdapter tested = new DirContextAdapter(DUMMY_NAME); + String result = tested.getNameInNamespace(); + assertEquals(DUMMY_NAME.toString(), result); + } + + public void testGetNameInNamespace_BasePath() { + DirContextAdapter tested = new DirContextAdapter(null, + new DistinguishedName("c=SE"), BASE_NAME); + String result = tested.getNameInNamespace(); + assertEquals(DUMMY_NAME.toString(), result); + } + + public void testAddMultiAttributes() throws Exception { + tested.setUpdateMode(true); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", new String[] { "123", "456" }); + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertNull(attr); + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.ADD_ATTRIBUTE, mods[0].getModificationOp()); + attr = mods[0].getAttribute(); + assertEquals(2, attr.size()); + assertEquals("123", (String) attr.get(0)); + assertEquals("456", (String) attr.get(1)); + + String[] modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(1, modNames.length); + assertEquals("abc", modNames[0]); + + tested.update(); + mods = tested.getModificationItems(); + assertEquals(0, mods.length); + modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + attrs = tested.getAttributes(); + attr = attrs.get("abc"); + assertEquals("123", (String) attr.get(0)); + assertEquals("456", (String) attr.get(1)); + } + + public void testRemoveAttribute() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + fixtureAttrs.put(new BasicAttribute("abc", "123")); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + + tested.setUpdateMode(true); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValue("abc", null); + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.REMOVE_ATTRIBUTE, mods[0].getModificationOp()); + attr = mods[0].getAttribute(); + assertEquals("abc", (String) attr.getID()); + String[] modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(1, modNames.length); + assertEquals("abc", modNames[0]); + + tested.update(); + mods = tested.getModificationItems(); + assertEquals(0, mods.length); + modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + attrs = tested.getAttributes(); + attr = attrs.get("abc"); + assertNull(attr); + } + + public void testRemoveMultiAttribute() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute abc = new BasicAttribute("abc"); + abc.add("123"); + abc.add("456"); + fixtureAttrs.put(abc); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + + tested.setUpdateMode(true); + tested.setAttributeValues("abc", new String[] {}); + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.REMOVE_ATTRIBUTE, mods[0].getModificationOp()); + Attribute attr = mods[0].getAttribute(); + assertEquals("abc", (String) attr.getID()); + assertEquals(0, attr.size()); + } + + public void testChangeAttribute() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + fixtureAttrs.put(new BasicAttribute("abc", "123")); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + tested.setAttributeValue("abc", "234"); // change + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.REPLACE_ATTRIBUTE, mods[0].getModificationOp()); + Attribute attr = mods[0].getAttribute(); + assertEquals("abc", (String) attr.getID()); + assertEquals("234", (String) attr.get()); + } + + public void testNoChangeAttribute() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + fixtureAttrs.put(new BasicAttribute("abc", "123")); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValue("abc", "123"); // change + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(0, mods.length); + } + + public void testNoChangeMultiAttribute() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", new String[] { "123", "qwe" }); + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(0, mods.length); + String[] modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + } + + public void testNoChangeMultiAttributeOrderDoesNotMatter() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + tested.setAttributeValues("abc", new String[] { "qwe", "123" }); + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(0, mods.length); + String[] modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + } + + public void testChangeMultiAttributeOrderDoesMatter() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", new String[] { "qwe", "123" }, true); + + // change + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.REPLACE_ATTRIBUTE, mods[0].getModificationOp()); + Attribute attr = mods[0].getAttribute(); + assertEquals("qwe", attr.get(0)); + assertEquals("123", attr.get(1)); + } + + public void testChangeMultiAttribute_AddValue() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested + .setAttributeValues("abc", + new String[] { "123", "qwe", "klytt" }); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + assertEquals(DirContext.ADD_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + assertEquals("klytt", modificationItems[0].getAttribute().get()); + } + + public void testChangeMultiAttribute_RemoveValue() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", new String[] { "123" }); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + assertEquals("qwe", modificationItems[0].getAttribute().get()); + } + + public void testChangeMultiAttribute_RemoveTwoValues() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + multi.add("rty"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", new String[] { "123" }); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + assertEquals("qwe", modificationItems[0].getAttribute().get(0)); + assertEquals("rty", modificationItems[0].getAttribute().get(1)); + } + + public void testChangeMultiAttribute_RemoveAllValues() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", null); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + } + + public void testChangeMultiAttribute_SameValue() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", new String[] { "123", "qwe" }); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(0, modificationItems.length); + } + + public void testChangeMultiAttribute_AddAndRemoveValue() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + multi.add("rty"); + multi.add("uio"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("abc", new String[] { "123", "qwe", "klytt", + "kalle" }); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(2, modificationItems.length); + + Attribute modifiedAttribute = modificationItems[0].getAttribute(); + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + assertEquals("abc", modifiedAttribute.getID()); + assertEquals(2, modifiedAttribute.size()); + assertEquals("rty", modifiedAttribute.get(0)); + assertEquals("uio", modifiedAttribute.get(1)); + + assertEquals(DirContext.ADD_ATTRIBUTE, modificationItems[1] + .getModificationOp()); + modifiedAttribute = modificationItems[1].getAttribute(); + assertEquals("abc", modifiedAttribute.getID()); + assertEquals(2, modifiedAttribute.size()); + assertEquals("klytt", modifiedAttribute.get(0)); + assertEquals("kalle", modifiedAttribute.get(1)); + } + + public void testAddAttribute_Multivalue() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + Attribute multi = new BasicAttribute("abc"); + multi.add("123"); + multi.add("qwe"); + fixtureAttrs.put(multi); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValues("def", new String[] { "kalle", "klytt" }); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(1, modificationItems.length); + assertEquals("def", modificationItems[0].getAttribute().getID()); + } + + public void testChangeAttributeTwice() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + fixtureAttrs.put(new BasicAttribute("abc", "123")); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValue("abc", "234"); // change + tested.setAttributeValue("abc", "987"); + // change a second time + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.REPLACE_ATTRIBUTE, mods[0].getModificationOp()); + Attribute attr = mods[0].getAttribute(); + assertEquals("abc", (String) attr.getID()); + assertEquals("987", (String) attr.get()); + + tested.update(); + mods = tested.getModificationItems(); + assertEquals(0, mods.length); + String[] modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + Attributes attrs = tested.getAttributes(); + attr = attrs.get("abc"); + assertEquals("987", (String) attr.get()); + assertEquals("987", tested.getStringAttribute("abc")); + } + + public void testAddReplaceAndChangeAttribute() throws Exception { + final Attributes fixtureAttrs = new BasicAttributes(); + fixtureAttrs.put(new BasicAttribute("abc", "123")); + fixtureAttrs.put(new BasicAttribute("qwe", "42")); + class TestableDirContextAdapter extends DirContextAdapter { + public TestableDirContextAdapter() { + super(fixtureAttrs, null); + setUpdateMode(true); + } + } + tested = new TestableDirContextAdapter(); + assertTrue(tested.isUpdateMode()); + tested.setAttributeValue("abc", "234"); // change + tested.setAttributeValue("qwe", null); // remove + tested.setAttributeValue("zzz", "new"); // new + Attributes attrs = tested.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + assertEquals(2, attrs.size()); + + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(3, mods.length); + String[] modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(3, modNames.length); + + ModificationItem mod = getModificationItem(mods, + DirContext.REPLACE_ATTRIBUTE); + assertNotNull(mod); + attr = mod.getAttribute(); + assertEquals("abc", (String) attr.getID()); + assertEquals("234", (String) attr.get()); + + mod = getModificationItem(mods, DirContext.REMOVE_ATTRIBUTE); + assertNotNull(mod); + attr = mod.getAttribute(); + assertEquals("qwe", (String) attr.getID()); + + mod = getModificationItem(mods, DirContext.ADD_ATTRIBUTE); + assertNotNull(mod); + attr = mod.getAttribute(); + assertEquals("zzz", (String) attr.getID()); + assertEquals("new", (String) attr.get()); + + tested.update(); + mods = tested.getModificationItems(); + assertEquals(0, mods.length); + modNames = tested.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + + attrs = tested.getAttributes(); + assertEquals(2, attrs.size()); + attr = attrs.get("abc"); + assertEquals("234", (String) attr.get()); + assertEquals("new", tested.getStringAttribute("zzz")); + } + + /** + * Test for LDAP-15: DirContextAdapter.setAttribute(). Verifies that setting + * an Attribute should modify updatedAttrs if in update mode. + * + * @throws NamingException + */ + public void testSetAttribute_UpdateMode() throws NamingException { + // Set original attribute value + Attribute attribute = new BasicAttribute("cn", "john doe"); + tested.setAttribute(attribute); + + // Set to update mode + tested.setUpdateMode(true); + + // Perform test - update the attribute + Attribute updatedAttribute = new BasicAttribute("cn", "nisse hult"); + tested.setAttribute(updatedAttribute); + + // Verify result + ModificationItem[] mods = tested.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.REPLACE_ATTRIBUTE, mods[0].getModificationOp()); + + Attribute modificationAttribute = mods[0].getAttribute(); + assertEquals("cn", modificationAttribute.getID()); + assertEquals("nisse hult", modificationAttribute.get()); + } + + public void testGetStringAttributes_NullValue() { + String result = tested.getStringAttribute("someAbsentAttribute"); + assertNull(result); + } + + public void testGetStringAttributes_AttributeExists_NullValue() { + tested.setAttribute(new BasicAttribute("someAttribute", null)); + String result = tested.getStringAttribute("someAttribute"); + assertNull(result); + } + + private ModificationItem getModificationItem(ModificationItem[] mods, + int operation) { + for (int i = 0; i < mods.length; i++) { + if (mods[i].getModificationOp() == operation) + return mods[i]; + } + return null; + } + + public void testModifyMultiValueAttributeModificationOrder() + throws NamingException { + BasicAttribute attribute = new BasicAttribute("abc"); + attribute.add("Some Person"); + attribute.add("Some Other Person"); + + tested.setAttribute(attribute); + tested.setUpdateMode(true); + + tested.setAttributeValues("abc", new String[] { "some person", + "Some Other Person" }); + + // Perform test + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(2, modificationItems.length); + ModificationItem modificationItem = modificationItems[0]; + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItem + .getModificationOp()); + assertEquals("Some Person", modificationItem.getAttribute().get()); + modificationItem = modificationItems[1]; + assertEquals(DirContext.ADD_ATTRIBUTE, modificationItem + .getModificationOp()); + assertEquals("some person", modificationItem.getAttribute().get()); + } + + /** + * Test for LDAP-13. + */ + public void testModifyAttributeByteArray() { + tested.setAttribute(new BasicAttribute("abc", new byte[] { 1, 2, 3 })); + + tested.setUpdateMode(true); + + // Perform test + tested.setAttributeValue("abc", new byte[] { 1, 2, 3 }); + + ModificationItem[] modificationItems = tested.getModificationItems(); + assertEquals(0, modificationItems.length); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/DistinguishedNameEditorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/DistinguishedNameEditorTest.java new file mode 100644 index 00000000..c912e566 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/DistinguishedNameEditorTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2005-2007 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.core; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link DistinguishedNameEditor}. + * + * @author Mattias Arthursson + */ +public class DistinguishedNameEditorTest extends TestCase { + + private DistinguishedNameEditor tested; + + protected void setUp() throws Exception { + tested = new DistinguishedNameEditor(); + } + + protected void tearDown() throws Exception { + tested = null; + } + + public void testSetAsText() throws Exception { + String expectedDn = "dc=jayway, dc=se"; + + tested.setAsText(expectedDn); + DistinguishedName result = (DistinguishedName) tested.getValue(); + assertEquals(new DistinguishedName(expectedDn), result); + + try { + result.getNames().add(new LdapRdn("cn", "john doe")); + fail("UnsupportedOperationException expected"); + } + catch (UnsupportedOperationException expected) { + assertTrue(true); + } + } + + public void testSetAsTextNullValue() throws Exception { + tested.setAsText(null); + Object result = tested.getValue(); + assertNull(result); + } + + public void testGetAsText() throws Exception { + String expectedDn = "dc=jayway, dc=se"; + tested.setValue(new DistinguishedName(expectedDn)); + String text = tested.getAsText(); + assertEquals(expectedDn, text); + } + + public void testGetAsTextNullValue() throws Exception { + tested.setValue(null); + String text = tested.getAsText(); + assertNull(text); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/DistinguishedNameTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/DistinguishedNameTest.java new file mode 100644 index 00000000..44c33413 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/DistinguishedNameTest.java @@ -0,0 +1,501 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.Enumeration; +import java.util.List; + +import javax.naming.CompositeName; +import javax.naming.InvalidNameException; +import javax.naming.Name; + +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.core.DistinguishedName; + +import junit.framework.TestCase; + +import com.gargoylesoftware.base.testing.EqualsTester; + +/** + * Unit tests for the {@link DistinguishedName} class. + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class DistinguishedNameTest extends TestCase { + + public void testDistinguishedName_CompositeWithSlash() throws Exception { + Name testPath = new CompositeName("cn=foo\\/bar"); + DistinguishedName path = new DistinguishedName(testPath.toString()); + assertEquals("cn=foo/bar", path.toString()); + } + + public void testEmptyPathImmutable() throws Exception { + DistinguishedName emptyPath = DistinguishedName.EMPTY_PATH; + try { + emptyPath.add("cn=John Doe"); + fail("UnsupportedOperationException expected"); + } + catch (UnsupportedOperationException expected) { + assertTrue(true); + } + } + + public void testDistinguishedName() { + + String testPath = "cn=foo\\,bar,OU=FOO\\,bar , OU=foo\\;bar;OU=foo\\;bar" + + " ; ou=foo\\,,ou=foo\\,;ou=foo\\;;ou=foo\\,;ou=bar\\,"; + System.out.println(testPath); + + DistinguishedName path = new DistinguishedName(testPath); + + assertEquals("cn", path.getLdapRdn(8).getComponent().getKey()); + assertEquals("foo,bar", path.getLdapRdn(8).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(7).getComponent().getKey()); + assertEquals("FOO,bar", path.getLdapRdn(7).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(6).getComponent().getKey()); + assertEquals("foo;bar", path.getLdapRdn(6).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(5).getComponent().getKey()); + assertEquals("foo;bar", path.getLdapRdn(5).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(4).getComponent().getKey()); + assertEquals("foo,", path.getLdapRdn(4).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(3).getComponent().getKey()); + assertEquals("foo,", path.getLdapRdn(3).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(2).getComponent().getKey()); + assertEquals("foo;", path.getLdapRdn(2).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(1).getComponent().getKey()); + assertEquals("foo,", path.getLdapRdn(1).getComponent().getValue()); + assertEquals("ou", path.getLdapRdn(0).getComponent().getKey()); + assertEquals("bar,", path.getLdapRdn(0).getComponent().getValue()); + } + + public void testRemove() throws InvalidNameException { + + String testPath = "cn=john.doe, OU=Users,OU=Some Company,OU=G,OU=I,OU=M"; + DistinguishedName path = new DistinguishedName(testPath); + + path.remove(1); + path.remove(3); + + assertEquals("cn=john.doe, ou=Some Company, ou=G, ou=M", path.toString()); + } + + /** + * Tests parsing and toString. + */ + public void testContains() { + + DistinguishedName migpath = new DistinguishedName("OU=G,OU=I,OU=M"); + DistinguishedName path1 = new DistinguishedName("cn=john.doe, OU=Users,OU=SE,OU=G,OU=I,OU=M"); + DistinguishedName path2 = new DistinguishedName("cn=john.doe, OU=Users,OU=SE,ou=G,OU=i,OU=M, ou=foo"); + DistinguishedName path3 = new DistinguishedName("ou=G,OU=i,OU=M, ou=foo"); + DistinguishedName path4 = new DistinguishedName("ou=G,OU=i,ou=m"); + + DistinguishedName pathE1 = new DistinguishedName("cn=john.doe, OU=Users,OU=SE,ou=G,OU=L,OU=M, ou=foo"); + DistinguishedName pathE2 = new DistinguishedName("cn=john.doe, OU=Users,OU=SE"); + + assertTrue("Contains MIG", path1.contains(migpath)); + assertTrue("Contains MIG", path2.contains(migpath)); + assertTrue("Contains MIG", path3.contains(migpath)); + assertTrue("Contains MIG", path4.contains(migpath)); + + assertFalse("Does not contain MIG", pathE1.contains(migpath)); + assertFalse("Does not contain MIG", pathE2.contains(migpath)); + } + + public void testAppend() { + DistinguishedName path1 = new DistinguishedName("ou=foo, OU=bar"); + DistinguishedName path2 = new DistinguishedName("OU=baz"); + + path1.append(path2); + + assertEquals("Append failed", "ou=baz, ou=foo, ou=bar", path1.toString()); + } + + public void testPrepend() { + DistinguishedName path1 = new DistinguishedName("ou=foo, OU=bar"); + DistinguishedName path2 = new DistinguishedName("cn=fie, OU=baz"); + + path1.prepend(path2); + + assertEquals("Append failed", "ou=foo, ou=bar, cn=fie, ou=baz", path1.toString()); + } + + public void testEquals() throws Exception { + + // original object + final Object originalObject = new DistinguishedName("cn=john.doe, OU=Users,OU=Some company,C=SE"); + + // another object that has the same values as the original + final Object identicalObject = new DistinguishedName("cn=john.doe, OU=Users,OU=Some company,C=SE"); + + // another object with different values + final Object differentObject = new DistinguishedName("cn=john.doe, OU=Users,OU=Some other company,C=SE"); + + // a subclass with the same values as the original + final Object subclassObject = new DistinguishedName("cn=john.doe, OU=Users,OU=Some company,C=SE") { + private static final long serialVersionUID = 1L; + }; + + new EqualsTester(originalObject, identicalObject, differentObject, subclassObject); + } + + public void testClone() { + + DistinguishedName path1 = new DistinguishedName("cn=john.doe, OU=Users,OU=Some company,C=SE"); + + DistinguishedName path2 = (DistinguishedName) path1.clone(); + + assertEquals("Should be equal", path1, path2); + + path2.removeFirst(); + assertFalse("Should not be equal", path1.equals(path2)); + + } + + public void testEndsWith_true() { + DistinguishedName path1 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName ending1 = new DistinguishedName("uid=mtah.test"); + + DistinguishedName path2 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName ending2 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU"); + + assertTrue(path1.endsWith(ending1)); + assertTrue(path2.endsWith(ending2)); + } + + public void testEndsWith_false() { + DistinguishedName path1 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName ending1 = new DistinguishedName("ou=people"); + + DistinguishedName path2 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName ending2 = new DistinguishedName("ou=EU, o=example.com"); + + assertFalse(path1.endsWith(ending1)); + assertFalse(path2.endsWith(ending2)); + } + + public void testGetAll() throws Exception { + DistinguishedName path = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + + Enumeration elements = path.getAll(); + + String element = (String) elements.nextElement(); + assertEquals("o=example.com", element); + + element = (String) elements.nextElement(); + assertEquals("ou=EU", element); + + element = (String) elements.nextElement(); + assertEquals("ou=people", element); + + element = (String) elements.nextElement(); + assertEquals("uid=mtah.test", element); + } + + public void testGet() throws Exception { + DistinguishedName path = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + + String string = path.get(1); + + assertEquals("ou=EU", string); + } + + public void testSize() { + DistinguishedName path1 = new DistinguishedName(); + assertEquals(0, path1.size()); + + DistinguishedName path2 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + assertEquals(4, path2.size()); + } + + public void testGetPrefix() { + DistinguishedName path = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + + Name prefix = path.getPrefix(0); + assertEquals(0, prefix.size()); + + prefix = path.getPrefix(1); + assertEquals(1, prefix.size()); + assertEquals("o=example.com", prefix.get(0)); + + prefix = path.getPrefix(2); + assertEquals(2, prefix.size()); + assertEquals("o=example.com", prefix.get(0)); + assertEquals("ou=EU", prefix.get(1)); + } + + public void testGetSuffix() { + DistinguishedName path = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + + Name suffix = path.getSuffix(0); + assertEquals(4, suffix.size()); + + suffix = path.getSuffix(2); + assertEquals(2, suffix.size()); + assertEquals("ou=people", suffix.get(0)); + + suffix = path.getSuffix(4); + assertEquals(0, suffix.size()); + + try { + path.getSuffix(5); + fail("ArrayIndexOutOfBoundsException expected"); + } + catch (ArrayIndexOutOfBoundsException expected) { + assertTrue(true); + } + } + + public void testStartsWith_true() { + DistinguishedName path1 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName start1 = new DistinguishedName("o=example.com"); + + DistinguishedName path2 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName start2 = new DistinguishedName("ou=people, ou=EU, o=example.com"); + + assertTrue(path1.startsWith(start1)); + assertTrue(path2.startsWith(start2)); + } + + public void testStartsWith_false() { + DistinguishedName path1 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName start1 = new DistinguishedName("ou=people"); + + DistinguishedName path2 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + DistinguishedName start2 = new DistinguishedName("uid=mtah.test, ou=EU, ou=people"); + + assertFalse(path1.startsWith(start1)); + assertFalse(path2.startsWith(start2)); + } + + public void testStartsWith_Longer() { + DistinguishedName path1 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + + DistinguishedName path2 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com, o=a.com"); + + assertFalse(path1.startsWith(path2)); + } + + public void testStartsWith_EmptyPath() { + DistinguishedName path1 = new DistinguishedName("uid=mtah.test, ou=people, ou=EU, o=example.com"); + + DistinguishedName path2 = new DistinguishedName(); + + assertFalse(path1.startsWith(path2)); + } + + public void testIsEmpty_True() { + DistinguishedName path = new DistinguishedName(); + assertTrue(path.isEmpty()); + } + + public void testIsEmpty_False() { + DistinguishedName path = new DistinguishedName("o=example.com"); + assertFalse(path.isEmpty()); + } + + public void testAddAll() throws Exception { + DistinguishedName path1 = new DistinguishedName("ou=foo, OU=bar"); + DistinguishedName path2 = new DistinguishedName("OU=baz"); + + path1.addAll(path2); + + assertEquals("AddAll failed", "ou=baz, ou=foo, ou=bar", path1.toString()); + } + + public void testAddAll_Index() throws InvalidNameException { + DistinguishedName path1 = new DistinguishedName("ou=foo, OU=bar"); + DistinguishedName path2 = new DistinguishedName("OU=baz"); + + path1.addAll(1, path2); + + assertEquals("AddAll failed", "ou=foo, ou=baz, ou=bar", path1.toString()); + } + + public void testAdd() throws InvalidNameException { + DistinguishedName path1 = new DistinguishedName("ou=foo, ou=bar"); + path1.add("ou=baz"); + + assertEquals("Add failed", "ou=baz, ou=foo, ou=bar", path1.toString()); + } + + public void testAdd_Index() throws InvalidNameException { + DistinguishedName path1 = new DistinguishedName("ou=foo, ou=bar"); + path1.add(1, "ou=baz"); + + assertEquals("Add failed", "ou=foo, ou=baz, ou=bar", path1.toString()); + } + + public void testToUrl() { + DistinguishedName path = new DistinguishedName("dc=jayway, dc=se"); + String url = path.toUrl(); + + assertEquals("dc=jayway,dc=se", url); + } + + public void testMultiValueRdn() throws Exception { + DistinguishedName path = new DistinguishedName("firstName=Rod+lastName=Johnson,ou=UK,dc=interface21,dc=com"); + assertEquals(4, path.size()); + assertEquals("firstname=Rod+lastname=Johnson", path.get(3)); + } + + public void testCompareTo_Equals() throws Exception { + DistinguishedName name1 = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + DistinguishedName name2 = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + + int result = name1.compareTo(name2); + assertEquals(0, result); + } + + public void testCompareTo_Less() throws Exception { + DistinguishedName name1 = new DistinguishedName("cn=john doe, ou=Some company, c=DK"); + DistinguishedName name2 = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + + int result = name1.compareTo(name2); + assertTrue(result < 0); + } + + public void testCompareTo_Less_MoreSignificant() throws Exception { + DistinguishedName name1 = new DistinguishedName("an=john doe, ou=Some company, c=DK"); + DistinguishedName name2 = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + + int result = name1.compareTo(name2); + assertTrue(result < 0); + } + + public void testCompareTo_Greater() throws Exception { + DistinguishedName name1 = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + DistinguishedName name2 = new DistinguishedName("cn=john doe, ou=Some company, c=DK"); + + int result = name1.compareTo(name2); + assertTrue(result > 0); + } + + public void testCompareTo_Longer() throws Exception { + DistinguishedName name1 = new DistinguishedName("leaf=someleaf, cn=john doe, ou=Some company, c=SE"); + DistinguishedName name2 = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + + int result = name1.compareTo(name2); + assertTrue(result > 0); + } + + public void testCompareTo_Shorter() throws Exception { + DistinguishedName name1 = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + DistinguishedName name2 = new DistinguishedName("leaf=someleaf, cn=john doe, ou=Some company, c=SE"); + + int result = name1.compareTo(name2); + assertTrue(result < 0); + } + + public void testGetLdapRdnForKey() throws Exception { + DistinguishedName dn = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + LdapRdn ldapRdn = dn.getLdapRdn("ou"); + assertEquals(new LdapRdn("ou=Some company"), ldapRdn); + } + + public void testGetLdapRdnForKeyNoMatchingKeyThrowsException() throws Exception { + DistinguishedName dn = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + try { + dn.getLdapRdn("nosuchkey"); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testGetValue() throws Exception { + DistinguishedName dn = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + String value = dn.getValue("ou"); + assertEquals("Some company", value); + } + + public void testGetValueNoMatchingKeyThrowsException() throws Exception { + DistinguishedName dn = new DistinguishedName("cn=john doe, ou=Some company, c=SE"); + try { + dn.getValue("nosuchkey"); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void test_longDN() throws InvalidNameException { + DistinguishedName name = new DistinguishedName(""); + assertNotNull(name); + } + + /** + * Test case to verify correct parsing for issue on forums. + */ + public void testParseAtSign() { + DistinguishedName name = new DistinguishedName("cn=testname@example.com"); + assertNotNull(name); + } + + /** + * Test case to verify correct parsing for issue on forums. + */ + public void testParseAtSign2() { + DistinguishedName name = new DistinguishedName("cn=te\\+stname@example.com"); + assertNotNull(name); + } + + /** + * Test case to verify correct parsing for issue on forums. + */ + public void testParseInvalidPlus() { + try { + new DistinguishedName("cn=te+stname@example.com"); + fail("BadLdapGrammarException expected"); + } + catch (BadLdapGrammarException expected) { + assertTrue(true); + } + } + + /** + * Test case to verify correct parsing for issue on forums. + */ + public void testParseValidQuotation() { + DistinguishedName name = new DistinguishedName("cn=jo\"hn doe"); + assertNotNull(name); + } + + public void testAppendChained() { + DistinguishedName tested = new DistinguishedName("dc=mycompany,dc=com"); + tested.append("ou", "company1").append("cn", "john doe"); + + assertEquals("cn=john doe, ou=company1, dc=mycompany, dc=com", tested.toString()); + } + + public void testUnmodifiableDistinguishedName() throws Exception { + DistinguishedName name = new DistinguishedName("cn=john doe"); + + DistinguishedName result = name.immutableDistinguishedName(); + List names = result.getNames(); + try { + names.add(new LdapRdnComponent("somekey", "somevalue")); + fail("UnsupportedOperationException expected"); + } + catch (UnsupportedOperationException expected) { + assertTrue(true); + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapEncoderTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapEncoderTest.java new file mode 100644 index 00000000..09a4b942 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapEncoderTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2005-2007 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.core; + +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.core.LdapEncoder; + +import junit.framework.TestCase; + +/** + * Unit test for the LdapEncode class. + * + * @author Adam Skogman + */ +public class LdapEncoderTest extends TestCase { + + /** + * Constructor for LdapEncoderTest. + * + * @param name + */ + public LdapEncoderTest(String name) { + super(name); + } + + public void testFilterEncode() { + String correct = "\\2aa\\2ab\\28c\\29d\\2a\\5c"; + assertEquals(correct, LdapEncoder.filterEncode("*a*b(c)d*\\")); + + } + + public void testNameEncode() { + + String res = LdapEncoder.nameEncode("# foo ,+\"\\<>; "); + + assertEquals("\\# foo \\,\\+\\\"\\\\\\<\\>\\;\\ ", res); + } + + public void testNameDecode() { + + String res = (String) LdapEncoder + .nameDecode("\\# foo \\,\\+\\\"\\\\\\<\\>\\;\\ "); + + assertEquals("# foo ,+\"\\<>; ", res); + } + + public void testNameDecode_slashlast() { + + try { + LdapEncoder.nameDecode("\\"); + fail("Should throw BadLdapGrammarException"); + } catch (BadLdapGrammarException e) { + assertTrue(true); + } + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapRdnComponentTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapRdnComponentTest.java new file mode 100644 index 00000000..9643b109 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapRdnComponentTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2005-2007 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.core; + +import org.springframework.ldap.core.LdapRdnComponent; + +import junit.framework.TestCase; + +/** + * Tests for LdapRdnComponent. + * + * @author Mattias Arthursson + */ +public class LdapRdnComponentTest extends TestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCompareTo_Less() { + LdapRdnComponent component1 = new LdapRdnComponent("cn", "john doe"); + LdapRdnComponent component2 = new LdapRdnComponent("sn", "doe"); + int result = component1.compareTo(component2); + assertTrue(result < 0); + } + + public void testCompareTo_Greater() { + LdapRdnComponent component1 = new LdapRdnComponent("sn", "doe"); + LdapRdnComponent component2 = new LdapRdnComponent("cn", "john doe"); + int result = component1.compareTo(component2); + assertTrue(result > 0); + } + + public void testCompareTo_Equal() { + LdapRdnComponent component1 = new LdapRdnComponent("cn", "john doe"); + LdapRdnComponent component2 = new LdapRdnComponent("cn", "john doe"); + int result = component1.compareTo(component2); + assertEquals(0, result); + } +} \ No newline at end of file diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapRdnTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapRdnTest.java new file mode 100644 index 00000000..b228dc40 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapRdnTest.java @@ -0,0 +1,240 @@ +/* + * Copyright 2005-2007 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.core; + +import junit.framework.TestCase; + +import org.springframework.ldap.BadLdapGrammarException; + +import com.gargoylesoftware.base.testing.EqualsTester; + +/** + * Unit test for the LdapRdn class. + * + * @author Adam Skogman + */ +public class LdapRdnTest extends TestCase { + + public void testLdapRdn_parse_simple() { + + LdapRdn rdn = new LdapRdn("foo=bar"); + + assertEquals("foo", rdn.getComponent().getKey()); + assertEquals("bar", rdn.getComponent().getValue()); + assertEquals("foo=bar", rdn.getComponent().getLdapEncoded()); + assertEquals("foo", rdn.getKey()); + assertEquals("bar", rdn.getValue()); + } + + public void testLdapRdn_parse_spaces() { + + LdapRdn rdn = new LdapRdn(" foo = bar "); + + assertEquals("foo", rdn.getComponent().getKey()); + assertEquals("bar", rdn.getComponent().getValue()); + assertEquals("foo=bar", rdn.getComponent().getLdapEncoded()); + } + + public void testLdapRdn_parse_escape() { + + LdapRdn rdn = new LdapRdn("foo=bar\\=fum"); + + assertEquals("foo", rdn.getComponent().getKey()); + assertEquals("bar=fum", rdn.getComponent().getValue()); + assertEquals("foo=bar\\=fum", rdn.getComponent().getLdapEncoded()); + } + + public void testLdapRdn_parse_hexEscape() { + + LdapRdn rdn = new LdapRdn("foo=bar\\0dfum"); + + assertEquals("foo", rdn.getComponent().getKey()); + assertEquals("bar\rfum", rdn.getComponent().getValue()); + assertEquals("foo=bar\\0Dfum", rdn.getComponent().getLdapEncoded()); + } + + public void testLdapRdn_parse_trailingBackslash() { + + try { + new LdapRdn("foo=bar\\"); + fail("Should throw BadLdapGrammarException"); + } catch (BadLdapGrammarException e) { + assertTrue(true); + } + } + + public void testLdapRdn_parse_spaces_escape() { + + LdapRdn rdn = new LdapRdn(" foo = \\ bar\\20 \\ "); + + assertEquals("foo", rdn.getComponent().getKey()); + assertEquals(" bar ", rdn.getComponent().getValue()); + assertEquals("foo=\\ bar \\ ", rdn.getComponent().getLdapEncoded()); + } + + public void testLdapRdn_parse_tooMuchTrim() { + try { + new LdapRdn("foo=bar\\"); + fail("Should throw BadLdapGrammarException"); + } catch (BadLdapGrammarException e) { + assertTrue(true); + } + } + + public void testLdapRdn_parse_slash() { + LdapRdn rdn = new LdapRdn("ou=Clerical / Secretarial Staff"); + + assertEquals("ou", rdn.getComponent().getKey()); + assertEquals("Clerical / Secretarial Staff", rdn.getComponent() + .getValue()); + assertEquals("ou=Clerical / Secretarial Staff", rdn.getComponent() + .getLdapEncoded()); + } + + public void testLdapRdn_parse_quoteInKey() { + try { + new LdapRdn("\"umanroleid=2583"); + fail("Should throw BadLdapGrammarException"); + } catch (BadLdapGrammarException e) { + assertTrue(true); + } + } + + public void testLdapRdn_KeyValue_simple() { + LdapRdn rdn = new LdapRdn("foo", "bar"); + + assertEquals("foo", rdn.getComponent().getKey()); + assertEquals("bar", rdn.getComponent().getValue()); + assertEquals("foo=bar", rdn.getComponent().getLdapEncoded()); + } + + public void testLdapRdn_KeyValue_valueNeedsEscape() { + LdapRdn rdn = new LdapRdn("foo", "bar\\"); + + assertEquals("foo", rdn.getComponent().getKey()); + assertEquals("bar\\", rdn.getComponent().getValue()); + assertEquals("foo=bar\\\\", rdn.getComponent().getLdapEncoded()); + } + + public void testEncodeUrl() { + LdapRdn rdn = new LdapRdn("o = example.com "); + assertEquals("o=example.com", rdn.encodeUrl()); + } + + public void testEncodeUrl_SpacesInValue() { + LdapRdn rdn = new LdapRdn("o = my organization "); + assertEquals("o=my%20organization", rdn.encodeUrl()); + } + + public void testLdapRdn_Parse_MultipleComponents() { + LdapRdn rdn = new LdapRdn("cn=John Doe+sn=Doe"); + assertEquals("cn=John Doe", rdn.getComponent(0).encodeLdap()); + assertEquals("sn=Doe", rdn.getComponent(1).encodeLdap()); + assertEquals("cn=John Doe+sn=Doe", rdn.getLdapEncoded()); + assertEquals("cn", rdn.getKey()); + assertEquals("John Doe", rdn.getValue()); + assertEquals("John Doe", rdn.getValue("cn")); + assertEquals("Doe", rdn.getValue("sn")); + } + + public void testGetValueNoKeyWithCorrectValue() { + LdapRdn tested = new LdapRdn("cn=john doe"); + try { + tested.getValue("sn"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testGetValueNoComponents() { + LdapRdn tested = new LdapRdn(); + try { + tested.getValue("sn"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testEquals() throws Exception { + // original object + final Object originalObject = new LdapRdn("cn", "john.doe"); + + // another object that has the same values as the original + final Object identicalObject = new LdapRdn("cn", "john.doe"); + + // another object with different values + final Object differentObject = new LdapRdn("cn", "john.svensson"); + + // a subclass with the same values as the original + final Object subclassObject = new LdapRdn("cn", "john.doe") { + private static final long serialVersionUID = 1L; + }; + + new EqualsTester(originalObject, identicalObject, differentObject, + subclassObject); + } + + public void testCompareTo_Equals() throws Exception { + LdapRdn rdn1 = new LdapRdn("cn=john doe"); + LdapRdn rdn2 = new LdapRdn("cn=john doe"); + + int result = rdn1.compareTo(rdn2); + assertEquals(0, result); + } + + public void testCompareTo_EqualsComplex() throws Exception { + LdapRdn rdn1 = new LdapRdn("cn=john doe+sn=doe"); + LdapRdn rdn2 = new LdapRdn("cn=john doe+sn=doe"); + + int result = rdn1.compareTo(rdn2); + assertEquals(0, result); + } + + public void testCompareTo_Less() { + LdapRdn rdn1 = new LdapRdn("cn=john doe+sn=doe"); + LdapRdn rdn2 = new LdapRdn("cn=john doe+tn=doe"); + + int result = rdn1.compareTo(rdn2); + assertTrue(result < 0); + } + + public void testCompareTo_Greater() { + LdapRdn rdn1 = new LdapRdn("cn=john doe+sn=doe"); + LdapRdn rdn2 = new LdapRdn("cn=john doe+an=doe"); + + int result = rdn1.compareTo(rdn2); + assertTrue(result > 0); + } + + public void testCompareTo_Shorter() { + LdapRdn rdn1 = new LdapRdn("cn=john doe+sn=doe"); + LdapRdn rdn2 = new LdapRdn("cn=john doe+sn=doe+description=tjo"); + + int result = rdn1.compareTo(rdn2); + assertTrue(result < 0); + } + + public void testCompareTo_Longer() { + LdapRdn rdn1 = new LdapRdn("cn=john doe+sn=doe+description=tjo"); + LdapRdn rdn2 = new LdapRdn("cn=john doe+sn=doe"); + + int result = rdn1.compareTo(rdn2); + assertTrue(result > 0); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateListTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateListTest.java new file mode 100644 index 00000000..bdd6959f --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateListTest.java @@ -0,0 +1,419 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.List; + +import javax.naming.Binding; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.LimitExceededException; +import org.springframework.ldap.PartialResultException; + +/** + * Unit tests for the list operations in {@link LdapTemplate}. + * + * @author Ulrik Sandberg + */ +public class LdapTemplateListTest extends TestCase { + + private static final String NAME = "o=example.com"; + + private static final String CLASS = "com.example.SomeClass"; + + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + private MockControl namingEnumerationControl; + + private NamingEnumeration namingEnumerationMock; + + private MockControl nameControl; + + private Name nameMock; + + private MockControl handlerControl; + + private NameClassPairCallbackHandler handlerMock; + + private MockControl contextMapperControl; + + private ContextMapper contextMapperMock; + + private LdapTemplate tested; + + protected void setUp() throws Exception { + super.setUp(); + + // Setup ContextSource mock + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + // Setup LdapContext mock + dirContextControl = MockControl.createControl(LdapContext.class); + dirContextMock = (LdapContext) dirContextControl.getMock(); + + // Setup NamingEnumeration mock + namingEnumerationControl = MockControl + .createControl(NamingEnumeration.class); + namingEnumerationMock = (NamingEnumeration) namingEnumerationControl + .getMock(); + + // Setup Name mock + nameControl = MockControl.createControl(Name.class); + nameMock = (Name) nameControl.getMock(); + + // Setup Handler mock + handlerControl = MockControl + .createControl(NameClassPairCallbackHandler.class); + handlerMock = (NameClassPairCallbackHandler) handlerControl.getMock(); + + contextMapperControl = MockControl.createControl(ContextMapper.class); + contextMapperMock = (ContextMapper) contextMapperControl.getMock(); + + tested = new LdapTemplate(contextSourceMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextSourceControl = null; + contextSourceMock = null; + + dirContextControl = null; + dirContextMock = null; + + namingEnumerationControl = null; + namingEnumerationMock = null; + + nameControl = null; + nameMock = null; + + handlerControl = null; + handlerMock = null; + + contextMapperControl = null; + contextMapperMock = null; + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + namingEnumerationControl.replay(); + nameControl.replay(); + handlerControl.replay(); + contextMapperControl.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + namingEnumerationControl.verify(); + nameControl.verify(); + handlerControl.verify(); + contextMapperControl.verify(); + } + + private void expectGetReadOnlyContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadOnlyContext(), dirContextMock); + } + + private void setupStringListAndNamingEnumeration(NameClassPair listResult) + throws NamingException { + dirContextControl.expectAndReturn(dirContextMock.list(NAME), + namingEnumerationMock); + + setupNamingEnumeration(listResult); + } + + private void setupListAndNamingEnumeration(NameClassPair listResult) + throws NamingException { + dirContextControl.expectAndReturn(dirContextMock.list(nameMock), + namingEnumerationMock); + + setupNamingEnumeration(listResult); + } + + private void setupStringListBindingsAndNamingEnumeration( + NameClassPair listResult) throws NamingException { + dirContextControl.expectAndReturn(dirContextMock.listBindings(NAME), + namingEnumerationMock); + + setupNamingEnumeration(listResult); + } + + private void setupListBindingsAndNamingEnumeration(NameClassPair listResult) + throws NamingException { + dirContextControl.expectAndReturn( + dirContextMock.listBindings(nameMock), namingEnumerationMock); + + setupNamingEnumeration(listResult); + } + + private void setupNamingEnumeration(NameClassPair listResult) + throws NamingException { + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + namingEnumerationControl.expectAndReturn(namingEnumerationMock.next(), + listResult); + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + } + + public void testList_Name() throws NamingException { + expectGetReadOnlyContext(); + + NameClassPair listResult = new NameClassPair(NAME, CLASS); + + setupListAndNamingEnumeration(listResult); + + dirContextMock.close(); + + replay(); + + List list = tested.list(nameMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(NAME, list.get(0)); + } + + public void testList_String() throws NamingException { + expectGetReadOnlyContext(); + + NameClassPair listResult = new NameClassPair(NAME, CLASS); + + setupStringListAndNamingEnumeration(listResult); + + dirContextMock.close(); + + replay(); + + List list = tested.list(NAME); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(NAME, list.get(0)); + } + + public void testList_Name_CallbackHandler() throws NamingException { + expectGetReadOnlyContext(); + + NameClassPair listResult = new NameClassPair(NAME, CLASS); + + setupListAndNamingEnumeration(listResult); + + handlerMock.handleNameClassPair(listResult); + + dirContextMock.close(); + + replay(); + + tested.list(nameMock, handlerMock); + + verify(); + } + + public void testList_String_CallbackHandler() throws NamingException { + expectGetReadOnlyContext(); + + NameClassPair listResult = new NameClassPair(NAME, CLASS); + + setupStringListAndNamingEnumeration(listResult); + + handlerMock.handleNameClassPair(listResult); + + dirContextMock.close(); + + replay(); + + tested.list("o=example.com", handlerMock); + + verify(); + } + + public void testList_PartialResultException() throws NamingException { + expectGetReadOnlyContext(); + + javax.naming.PartialResultException pre = new javax.naming.PartialResultException(); + dirContextControl.expectAndThrow(dirContextMock.list(NAME), pre); + + dirContextMock.close(); + + replay(); + + try { + tested.list(NAME); + fail("PartialResultException expected"); + } catch (PartialResultException expected) { + assertTrue(true); + } + + verify(); + } + + public void testList_PartialResultException_Ignore() throws NamingException { + expectGetReadOnlyContext(); + + javax.naming.PartialResultException pre = new javax.naming.PartialResultException(); + dirContextControl.expectAndThrow(dirContextMock.list(NAME), pre); + + dirContextMock.close(); + + tested.setIgnorePartialResultException(true); + + replay(); + + List list = tested.list(NAME); + + verify(); + + assertNotNull(list); + assertEquals(0, list.size()); + } + + public void testList_NamingException() throws NamingException { + expectGetReadOnlyContext(); + + javax.naming.LimitExceededException ne = new javax.naming.LimitExceededException(); + dirContextControl.expectAndThrow(dirContextMock.list(NAME), ne); + + dirContextMock.close(); + + replay(); + + try { + tested.list(NAME); + fail("LimitExceededException expected"); + } catch (LimitExceededException expected) { + assertTrue(true); + } + + verify(); + } + + // Tests for listBindings + + public void testListBindings_String() throws NamingException { + expectGetReadOnlyContext(); + + Binding listResult = new Binding(NAME, CLASS, null); + + setupStringListBindingsAndNamingEnumeration(listResult); + + dirContextMock.close(); + + replay(); + + List list = tested.listBindings(NAME); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(NAME, list.get(0)); + } + + public void testListBindings_Name() throws NamingException { + expectGetReadOnlyContext(); + + Binding listResult = new Binding(NAME, CLASS, null); + + setupListBindingsAndNamingEnumeration(listResult); + + dirContextMock.close(); + + replay(); + + List list = tested.listBindings(nameMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(NAME, list.get(0)); + } + + public void testListBindings_ContextMapper() throws NamingException { + expectGetReadOnlyContext(); + + Object expectedObject = new Object(); + Binding listResult = new Binding("", expectedObject); + + setupStringListBindingsAndNamingEnumeration(listResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.listBindings(NAME, contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testListBindings_Name_ContextMapper() throws NamingException { + expectGetReadOnlyContext(); + + Object expectedObject = new Object(); + Binding listResult = new Binding("", expectedObject); + + setupListBindingsAndNamingEnumeration(listResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.listBindings(nameMock, contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateLookupTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateLookupTest.java new file mode 100644 index 00000000..0715eeb8 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateLookupTest.java @@ -0,0 +1,426 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Name; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.NameNotFoundException; + +public class LdapTemplateLookupTest extends TestCase { + + private static final String DEFAULT_BASE_STRING = "o=example.com"; + + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + private MockControl attributesMapperControl; + + private AttributesMapper attributesMapperMock; + + private MockControl nameControl; + + private Name nameMock; + + private MockControl contextMapperControl; + + private ContextMapper contextMapperMock; + + private LdapTemplate tested; + + protected void setUp() throws Exception { + super.setUp(); + + // Setup ContextSource mock + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + // Setup LdapContext mock + dirContextControl = MockControl.createControl(LdapContext.class); + dirContextMock = (LdapContext) dirContextControl.getMock(); + + // Setup Name mock + nameControl = MockControl.createControl(Name.class); + nameMock = (Name) nameControl.getMock(); + + contextMapperControl = MockControl.createControl(ContextMapper.class); + contextMapperMock = (ContextMapper) contextMapperControl.getMock(); + + attributesMapperControl = MockControl + .createControl(AttributesMapper.class); + attributesMapperMock = (AttributesMapper) attributesMapperControl + .getMock(); + + tested = new LdapTemplate(contextSourceMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextSourceControl = null; + contextSourceMock = null; + + dirContextControl = null; + dirContextMock = null; + + nameControl = null; + nameMock = null; + + contextMapperControl = null; + contextMapperMock = null; + + attributesMapperControl = null; + attributesMapperMock = null; + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + nameControl.replay(); + contextMapperControl.replay(); + attributesMapperControl.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + nameControl.verify(); + contextMapperControl.verify(); + attributesMapperControl.verify(); + } + + private void expectGetReadOnlyContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadOnlyContext(), dirContextMock); + } + + // Tests for lookup(name) + + public void testLookup() throws Exception { + expectGetReadOnlyContext(); + + Object expected = new Object(); + dirContextControl.expectAndReturn(dirContextMock.lookup(nameMock), + expected); + + dirContextMock.close(); + + replay(); + + Object actual = tested.lookup(nameMock); + + verify(); + + assertSame(expected, actual); + } + + public void testLookup_String() throws Exception { + expectGetReadOnlyContext(); + + Object expected = new Object(); + dirContextControl.expectAndReturn(dirContextMock + .lookup(DEFAULT_BASE_STRING), expected); + + dirContextMock.close(); + + replay(); + + Object actual = tested.lookup(DEFAULT_BASE_STRING); + + verify(); + + assertSame(expected, actual); + } + + public void testLookup_NamingException() throws Exception { + expectGetReadOnlyContext(); + + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + dirContextControl.expectAndThrow(dirContextMock.lookup(nameMock), ne); + + dirContextMock.close(); + + replay(); + + try { + tested.lookup(nameMock); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + // Tests for lookup(name, AttributesMapper) + + public void testLookup_AttributesMapper() throws Exception { + expectGetReadOnlyContext(); + + BasicAttributes expectedAttributes = new BasicAttributes(); + dirContextControl.expectAndReturn(dirContextMock + .getAttributes(nameMock), expectedAttributes); + dirContextMock.close(); + + Object expected = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expected); + + replay(); + + Object actual = tested.lookup(nameMock, attributesMapperMock); + + verify(); + + assertSame(expected, actual); + } + + public void testLookup_String_AttributesMapper() throws Exception { + expectGetReadOnlyContext(); + + BasicAttributes expectedAttributes = new BasicAttributes(); + dirContextControl.expectAndReturn(dirContextMock + .getAttributes(DEFAULT_BASE_STRING), expectedAttributes); + dirContextMock.close(); + + Object expected = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expected); + + replay(); + + Object actual = tested + .lookup(DEFAULT_BASE_STRING, attributesMapperMock); + + verify(); + + assertSame(expected, actual); + } + + public void testLookup_AttributesMapper_NamingException() throws Exception { + expectGetReadOnlyContext(); + + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + dirContextControl.expectAndThrow( + dirContextMock.getAttributes(nameMock), ne); + dirContextMock.close(); + + replay(); + + try { + tested.lookup(nameMock, attributesMapperMock); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + // Tests for lookup(name, ContextMapper) + + public void testLookup_ContextMapper() throws Exception { + expectGetReadOnlyContext(); + + Object transformed = new Object(); + Object expected = new Object(); + dirContextControl.expectAndReturn(dirContextMock.lookup(nameMock), + expected); + + dirContextMock.close(); + + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expected), transformed); + + replay(); + + Object actual = tested.lookup(nameMock, contextMapperMock); + + verify(); + + assertSame(transformed, actual); + } + + public void testLookup_String_ContextMapper() throws Exception { + expectGetReadOnlyContext(); + + Object transformed = new Object(); + Object expected = new Object(); + dirContextControl.expectAndReturn(dirContextMock + .lookup(DEFAULT_BASE_STRING), expected); + + dirContextMock.close(); + + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expected), transformed); + + replay(); + + Object actual = tested.lookup(DEFAULT_BASE_STRING, contextMapperMock); + + verify(); + + assertSame(transformed, actual); + } + + public void testLookup_ContextMapper_NamingException() throws Exception { + expectGetReadOnlyContext(); + + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + dirContextControl.expectAndThrow(dirContextMock.lookup(nameMock), ne); + + dirContextMock.close(); + + replay(); + + try { + tested.lookup(nameMock, contextMapperMock); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + // Tests for lookup(name, attributes, AttributesMapper) + + public void testLookup_ReturnAttributes_AttributesMapper() throws Exception { + expectGetReadOnlyContext(); + + String[] attributeNames = new String[] { "cn" }; + + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("cn", "Some Name"); + + dirContextControl.expectAndReturn(dirContextMock.getAttributes( + nameMock, attributeNames), expectedAttributes); + dirContextMock.close(); + + Object expected = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expected); + + replay(); + + Object actual = tested.lookup(nameMock, attributeNames, + attributesMapperMock); + + verify(); + + assertSame(expected, actual); + } + + public void testLookup_String_ReturnAttributes_AttributesMapper() + throws Exception { + expectGetReadOnlyContext(); + + String[] attributeNames = new String[] { "cn" }; + + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("cn", "Some Name"); + + dirContextControl.expectAndReturn(dirContextMock.getAttributes( + DEFAULT_BASE_STRING, attributeNames), expectedAttributes); + dirContextMock.close(); + + Object expected = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expected); + + replay(); + + Object actual = tested.lookup(DEFAULT_BASE_STRING, attributeNames, + attributesMapperMock); + + verify(); + + assertSame(expected, actual); + } + + // Tests for lookup(name, attributes, ContextMapper) + + public void testLookup_ReturnAttributes_ContextMapper() throws Exception { + expectGetReadOnlyContext(); + + String[] attributeNames = new String[] { "cn" }; + + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("cn", "Some Name"); + + DistinguishedName name = new DistinguishedName(DEFAULT_BASE_STRING); + DirContextAdapter adapter = new DirContextAdapter(expectedAttributes, + name); + + dirContextControl.expectAndReturn(dirContextMock.getAttributes(name, + attributeNames), expectedAttributes); + dirContextMock.close(); + + Object transformed = new Object(); + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(adapter), transformed); + + replay(); + + Object actual = tested.lookup(name, attributeNames, contextMapperMock); + + verify(); + + assertSame(transformed, actual); + } + + public void testLookup_String_ReturnAttributes_ContextMapper() + throws Exception { + expectGetReadOnlyContext(); + + String[] attributeNames = new String[] { "cn" }; + + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("cn", "Some Name"); + + dirContextControl.expectAndReturn(dirContextMock.getAttributes( + DEFAULT_BASE_STRING, attributeNames), expectedAttributes); + dirContextMock.close(); + + DistinguishedName name = new DistinguishedName(DEFAULT_BASE_STRING); + DirContextAdapter adapter = new DirContextAdapter(expectedAttributes, + name); + + Object transformed = new Object(); + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(adapter), transformed); + + replay(); + + Object actual = tested.lookup(DEFAULT_BASE_STRING, attributeNames, + contextMapperMock); + + verify(); + + assertSame(transformed, actual); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateRenameTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateRenameTest.java new file mode 100644 index 00000000..938d7c0d --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateRenameTest.java @@ -0,0 +1,171 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.Name; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.NameAlreadyBoundException; +import org.springframework.ldap.UncategorizedLdapException; + +/** + * Unit tests for the rename operations in the LdapTemplate class. + * + * @author Ulrik Sandberg + */ +public class LdapTemplateRenameTest extends TestCase { + + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + private MockControl oldNameControl; + + private Name oldNameMock; + + private MockControl newNameControl; + + private Name newNameMock; + + private LdapTemplate tested; + + protected void setUp() throws Exception { + super.setUp(); + + // Setup ContextSource mock + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + // Setup LdapContext mock + dirContextControl = MockControl.createControl(LdapContext.class); + dirContextMock = (LdapContext) dirContextControl.getMock(); + + // Setup Name mock for old name + oldNameControl = MockControl.createControl(Name.class); + oldNameMock = (Name) oldNameControl.getMock(); + + // Setup Name mock for new name + newNameControl = MockControl.createControl(Name.class); + newNameMock = (Name) newNameControl.getMock(); + + tested = new LdapTemplate(contextSourceMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextSourceControl = null; + contextSourceMock = null; + + dirContextControl = null; + dirContextMock = null; + + oldNameControl = null; + newNameMock = null; + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + oldNameControl.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + oldNameControl.verify(); + } + + private void expectGetReadWriteContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadWriteContext(), dirContextMock); + } + + public void testRename() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.rename(oldNameMock, newNameMock); + dirContextMock.close(); + + replay(); + + tested.rename(oldNameMock, newNameMock); + + verify(); + } + + public void testRename_NameAlreadyBoundException() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.rename(oldNameMock, newNameMock); + javax.naming.NameAlreadyBoundException ne = new javax.naming.NameAlreadyBoundException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + replay(); + + try { + tested.rename(oldNameMock, newNameMock); + fail("NameAlreadyBoundException expected"); + } catch (NameAlreadyBoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testRename_NamingException() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.rename(oldNameMock, newNameMock); + javax.naming.NamingException ne = new javax.naming.NamingException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + replay(); + + try { + tested.rename(oldNameMock, newNameMock); + fail("UncategorizedLdapException expected"); + } catch (UncategorizedLdapException expected) { + assertTrue(true); + } + + verify(); + } + + public void testRename_String() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.rename("o=example.com", "o=somethingelse.com"); + dirContextMock.close(); + + replay(); + + tested.rename("o=example.com", "o=somethingelse.com"); + + verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateTest.java new file mode 100644 index 00000000..2b69f7a3 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/LdapTemplateTest.java @@ -0,0 +1,1785 @@ +/* + * Copyright 2005-2007 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.core; + +import java.util.List; + +import javax.naming.Binding; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.AbstractMatcher; +import org.easymock.MockControl; +import org.springframework.ldap.LimitExceededException; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.ldap.PartialResultException; + +public class LdapTemplateTest extends TestCase { + + private static final String DEFAULT_BASE_STRING = "o=example.com"; + + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + private MockControl attributesMapperControl; + + private AttributesMapper attributesMapperMock; + + private MockControl namingEnumerationControl; + + private NamingEnumeration namingEnumerationMock; + + private MockControl nameControl; + + private Name nameMock; + + private MockControl handlerControl; + + private NameClassPairCallbackHandler handlerMock; + + private MockControl contextMapperControl; + + private ContextMapper contextMapperMock; + + private MockControl contextExecutorControl; + + private ContextExecutor contextExecutorMock; + + private MockControl searchExecutorControl; + + private SearchExecutor searchExecutorMock; + + private LdapTemplate tested; + + private MockControl dirContextProcessorControl; + + private DirContextProcessor dirContextProcessorMock; + + private MockControl dirContextOperationsConrol; + + private DirContextOperations dirContextOperationsMock; + + protected void setUp() throws Exception { + super.setUp(); + + // Setup ContextSource mock + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + // Setup LdapContext mock + dirContextControl = MockControl.createControl(LdapContext.class); + dirContextMock = (LdapContext) dirContextControl.getMock(); + + // Setup NamingEnumeration mock + namingEnumerationControl = MockControl + .createControl(NamingEnumeration.class); + namingEnumerationMock = (NamingEnumeration) namingEnumerationControl + .getMock(); + + // Setup Name mock + nameControl = MockControl.createControl(Name.class); + nameMock = (Name) nameControl.getMock(); + + // Setup Handler mock + handlerControl = MockControl + .createControl(NameClassPairCallbackHandler.class); + handlerMock = (NameClassPairCallbackHandler) handlerControl.getMock(); + + contextMapperControl = MockControl.createControl(ContextMapper.class); + contextMapperMock = (ContextMapper) contextMapperControl.getMock(); + + attributesMapperControl = MockControl + .createControl(AttributesMapper.class); + attributesMapperMock = (AttributesMapper) attributesMapperControl + .getMock(); + + contextExecutorControl = MockControl + .createControl(ContextExecutor.class); + contextExecutorMock = (ContextExecutor) contextExecutorControl + .getMock(); + + searchExecutorControl = MockControl.createControl(SearchExecutor.class); + searchExecutorMock = (SearchExecutor) searchExecutorControl.getMock(); + + dirContextProcessorControl = MockControl + .createControl(DirContextProcessor.class); + dirContextProcessorMock = (DirContextProcessor) dirContextProcessorControl + .getMock(); + + dirContextOperationsConrol = MockControl + .createControl(DirContextOperations.class); + dirContextOperationsMock = (DirContextOperations) dirContextOperationsConrol + .getMock(); + + tested = new LdapTemplate(contextSourceMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextSourceControl = null; + contextSourceMock = null; + + dirContextControl = null; + dirContextMock = null; + + namingEnumerationControl = null; + namingEnumerationMock = null; + + nameControl = null; + nameMock = null; + + handlerControl = null; + handlerMock = null; + + contextMapperControl = null; + contextMapperMock = null; + + attributesMapperControl = null; + attributesMapperMock = null; + + contextExecutorControl = null; + contextExecutorMock = null; + + searchExecutorControl = null; + searchExecutorMock = null; + + dirContextProcessorControl = null; + dirContextProcessorMock = null; + + dirContextOperationsConrol = null; + dirContextOperationsMock = null; + + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + namingEnumerationControl.replay(); + nameControl.replay(); + handlerControl.replay(); + contextMapperControl.replay(); + attributesMapperControl.replay(); + contextExecutorControl.replay(); + searchExecutorControl.replay(); + dirContextProcessorControl.replay(); + dirContextOperationsConrol.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + namingEnumerationControl.verify(); + nameControl.verify(); + handlerControl.verify(); + contextMapperControl.verify(); + attributesMapperControl.verify(); + contextExecutorControl.verify(); + searchExecutorControl.verify(); + dirContextProcessorControl.verify(); + dirContextOperationsConrol.verify(); + } + + private void expectGetReadWriteContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadWriteContext(), dirContextMock); + } + + private void expectGetReadOnlyContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadOnlyContext(), dirContextMock); + } + + public void testSearch_CallbackHandler() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(true); + + SearchResult searchResult = new SearchResult("", new Object(), + new BasicAttributes()); + + setupSearchAndNamingEnumeration(controls, searchResult); + + handlerMock.handleNameClassPair(searchResult); + + dirContextMock.close(); + + replay(); + + tested.search(nameMock, "(ou=somevalue)", 1, true, handlerMock); + + verify(); + } + + public void testSearch_StringBase_CallbackHandler() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(true); + + SearchResult searchResult = new SearchResult("", new Object(), + new BasicAttributes()); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + handlerMock.handleNameClassPair(searchResult); + + dirContextMock.close(); + + replay(); + + tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", 1, true, + handlerMock); + + verify(); + } + + private void setupStringSearchAndNamingEnumeration(SearchControls controls, + SearchResult searchResult) throws Exception { + dirContextControl.setDefaultMatcher(new SearchControlsMatcher()); + dirContextControl.expectAndReturn(dirContextMock.search( + DEFAULT_BASE_STRING, "(ou=somevalue)", controls), + namingEnumerationMock); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + namingEnumerationControl.expectAndReturn(namingEnumerationMock.next(), + searchResult); + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + } + + public void testSearch_CallbackHandler_Defaults() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + SearchResult searchResult = new SearchResult("", new Object(), + new BasicAttributes()); + + setupSearchAndNamingEnumeration(controls, searchResult); + + handlerMock.handleNameClassPair(searchResult); + + dirContextMock.close(); + + replay(); + + tested.search(nameMock, "(ou=somevalue)", handlerMock); + + verify(); + } + + public void testSearch_String_CallbackHandler_Defaults() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + SearchResult searchResult = new SearchResult("", new Object(), + new BasicAttributes()); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + handlerMock.handleNameClassPair(searchResult); + + dirContextMock.close(); + + replay(); + + tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", handlerMock); + + verify(); + } + + private void setupSearchAndNamingEnumeration(SearchControls controls, + SearchResult searchResult) throws Exception { + dirContextControl.setDefaultMatcher(new SearchControlsMatcher()); + dirContextControl.expectAndReturn(dirContextMock.search(nameMock, + "(ou=somevalue)", controls), namingEnumerationMock); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + namingEnumerationControl.expectAndReturn(namingEnumerationMock.next(), + searchResult); + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + } + + public void testSearch_NameNotFoundException() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + dirContextControl.setDefaultMatcher(new SearchControlsMatcher()); + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + dirContextControl.expectAndThrow(dirContextMock.search(nameMock, + "(ou=somevalue)", controls), ne); + + dirContextMock.close(); + + replay(); + + tested.search(nameMock, "(ou=somevalue)", handlerMock); + + verify(); + } + + public void testSearch_NamingException() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + dirContextControl.setDefaultMatcher(new SearchControlsMatcher()); + javax.naming.LimitExceededException ne = new javax.naming.LimitExceededException(); + dirContextControl.expectAndThrow(dirContextMock.search(nameMock, + "(ou=somevalue)", controls), ne); + + dirContextMock.close(); + + replay(); + + try { + tested.search(nameMock, "(ou=somevalue)", handlerMock); + fail("LimitExceededException expected"); + } catch (LimitExceededException expected) { + // expected + } + + verify(); + } + + public void testSearch_CallbackHandler_DirContextProcessor() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + SearchResult searchResult = new SearchResult("", new Object(), + new BasicAttributes()); + + dirContextProcessorMock.preProcess(dirContextMock); + + setupSearchAndNamingEnumeration(controls, searchResult); + + handlerMock.handleNameClassPair(searchResult); + + dirContextProcessorMock.postProcess(dirContextMock); + + dirContextMock.close(); + + replay(); + + tested.search(nameMock, "(ou=somevalue)", controls, handlerMock, + dirContextProcessorMock); + + verify(); + } + + public void testSearch_String_CallbackHandler_DirContextProcessor() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + SearchResult searchResult = new SearchResult("", new Object(), + new BasicAttributes()); + + dirContextProcessorMock.preProcess(dirContextMock); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + handlerMock.handleNameClassPair(searchResult); + + dirContextProcessorMock.postProcess(dirContextMock); + + dirContextMock.close(); + + replay(); + + tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", controls, + handlerMock, dirContextProcessorMock); + + verify(); + } + + public void testSearch_String_AttributesMapper_DirContextProcessor() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + dirContextProcessorMock.preProcess(dirContextMock); + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextProcessorMock.postProcess(dirContextMock); + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", + controls, attributesMapperMock, dirContextProcessorMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_Name_AttributesMapper_DirContextProcessor() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + dirContextProcessorMock.preProcess(dirContextMock); + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextProcessorMock.postProcess(dirContextMock); + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", controls, + attributesMapperMock, dirContextProcessorMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_SearchControls_ContextMapper_DirContextProcessor() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + dirContextProcessorMock.preProcess(dirContextMock); + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextProcessorMock.postProcess(dirContextMock); + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", + controls, contextMapperMock, dirContextProcessorMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_Name_SearchControls_ContextMapper_DirContextProcessor() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + dirContextProcessorMock.preProcess(dirContextMock); + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextProcessorMock.postProcess(dirContextMock); + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", controls, + contextMapperMock, dirContextProcessorMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_AttributesMapper_ReturningAttrs() throws Exception { + expectGetReadOnlyContext(); + + String[] attrs = new String[0]; + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + controls.setReturningAttributes(attrs); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", 1, attrs, + attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_AttributesMapper_ReturningAttrs() + throws Exception { + expectGetReadOnlyContext(); + + String[] attrs = new String[0]; + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + controls.setReturningAttributes(attrs); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", 1, + attrs, attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_AttributesMapper() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", 1, + attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_AttributesMapper() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", 1, + attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_AttributesMapper_Default() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", + attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_AttributesMapper_Default() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", + attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_ContextMapper() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", 1, + contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_ContextMapper_ReturningAttrs() throws Exception { + expectGetReadOnlyContext(); + + String[] attrs = new String[0]; + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(true); + controls.setReturningAttributes(attrs); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", 1, attrs, + contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_ContextMapper_ReturningAttrs() + throws Exception { + expectGetReadOnlyContext(); + + String[] attrs = new String[0]; + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(true); + controls.setReturningAttributes(attrs); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", 1, + attrs, contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_ContextMapper() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", 1, + contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_ContextMapper_Default() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested + .search(nameMock, "(ou=somevalue)", contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_ContextMapper_Default() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", + contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_SearchControls_ContextMapper() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", + controls, contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_SearchControls_ContextMapper_ReturningObjFlagNotSet() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + SearchControls expectedControls = new SearchControls(); + expectedControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + expectedControls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupStringSearchAndNamingEnumeration(expectedControls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", + controls, contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_Name_SearchControls_ContextMapper() throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(true); + + Object expectedObject = new Object(); + SearchResult searchResult = new SearchResult("", expectedObject, + new BasicAttributes()); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = expectedObject; + contextMapperControl.expectAndReturn(contextMapperMock + .mapFromContext(expectedObject), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", controls, + contextMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_String_SearchControls_AttributesMapper() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupStringSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(DEFAULT_BASE_STRING, "(ou=somevalue)", + controls, attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testSearch_Name_SearchControls_AttributesMapper() + throws Exception { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(1); + controls.setReturningObjFlag(false); + + BasicAttributes expectedAttributes = new BasicAttributes(); + SearchResult searchResult = new SearchResult("", null, + expectedAttributes); + + setupSearchAndNamingEnumeration(controls, searchResult); + + Object expectedResult = new Object(); + attributesMapperControl.expectAndReturn(attributesMapperMock + .mapFromAttributes(expectedAttributes), expectedResult); + + dirContextMock.close(); + + replay(); + + List list = tested.search(nameMock, "(ou=somevalue)", controls, + attributesMapperMock); + + verify(); + + assertNotNull(list); + assertEquals(1, list.size()); + assertSame(expectedResult, list.get(0)); + } + + public void testModifyAttributes() throws Exception { + expectGetReadWriteContext(); + + ModificationItem[] mods = new ModificationItem[0]; + dirContextMock.modifyAttributes(nameMock, mods); + + dirContextMock.close(); + + replay(); + + tested.modifyAttributes(nameMock, mods); + + verify(); + } + + public void testModifyAttributes_String() throws Exception { + expectGetReadWriteContext(); + + ModificationItem[] mods = new ModificationItem[0]; + dirContextMock.modifyAttributes(DEFAULT_BASE_STRING, mods); + + dirContextMock.close(); + + replay(); + + tested.modifyAttributes(DEFAULT_BASE_STRING, mods); + + verify(); + } + + public void testModifyAttributes_NamingException() throws Exception { + expectGetReadWriteContext(); + + ModificationItem[] mods = new ModificationItem[0]; + dirContextMock.modifyAttributes(nameMock, mods); + javax.naming.LimitExceededException ne = new javax.naming.LimitExceededException(); + dirContextControl.setThrowable(ne); + + dirContextMock.close(); + + replay(); + + try { + tested.modifyAttributes(nameMock, mods); + fail("LimitExceededException expected"); + } catch (LimitExceededException expected) { + assertTrue(true); + } + + verify(); + } + + public void testBind() throws Exception { + expectGetReadWriteContext(); + + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + dirContextMock.bind(nameMock, expectedObject, expectedAttributes); + dirContextMock.close(); + + replay(); + + tested.bind(nameMock, expectedObject, expectedAttributes); + + verify(); + + } + + public void testBind_String() throws Exception { + expectGetReadWriteContext(); + + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + dirContextMock.bind(DEFAULT_BASE_STRING, expectedObject, + expectedAttributes); + dirContextMock.close(); + + replay(); + + tested.bind(DEFAULT_BASE_STRING, expectedObject, expectedAttributes); + + verify(); + + } + + public void testBind_NamingException() throws Exception { + expectGetReadWriteContext(); + + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + dirContextMock.bind(nameMock, expectedObject, expectedAttributes); + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + replay(); + + try { + tested.bind(nameMock, expectedObject, expectedAttributes); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + verify(); + + } + + public void testUnbind() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.unbind(nameMock); + dirContextMock.close(); + replay(); + + tested.unbind(nameMock); + + verify(); + } + + public void testUnbind_String() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.unbind(DEFAULT_BASE_STRING); + dirContextMock.close(); + replay(); + + tested.unbind(DEFAULT_BASE_STRING); + + verify(); + } + + public void testUnbindRecursive() throws Exception { + expectGetReadWriteContext(); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + Binding binding = new Binding("cn=Some name", null); + namingEnumerationControl.expectAndReturn(namingEnumerationMock.next(), + binding); + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + + DistinguishedName listDn = new DistinguishedName(DEFAULT_BASE_STRING); + dirContextMock.listBindings(listDn); + dirContextControl.setReturnValue(namingEnumerationMock); + DistinguishedName subListDn = new DistinguishedName( + "cn=Some name, o=example.com"); + dirContextMock.listBindings(subListDn); + dirContextControl.setReturnValue(namingEnumerationMock); + + dirContextMock.unbind(subListDn); + dirContextMock.unbind(listDn); + dirContextMock.close(); + + // Caused by creating a DistinguishedName from a Name + nameControl.expectAndReturn(nameMock.size(), 1, 2); + nameControl.expectAndReturn(nameMock.get(0), "o=example.com"); + + replay(); + + tested.unbind(nameMock, true); + + verify(); + } + + public void testUnbindRecursive_String() throws Exception { + expectGetReadWriteContext(); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + Binding binding = new Binding("cn=Some name", null); + namingEnumerationControl.expectAndReturn(namingEnumerationMock.next(), + binding); + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + + DistinguishedName listDn = new DistinguishedName(DEFAULT_BASE_STRING); + dirContextMock.listBindings(listDn); + dirContextControl.setReturnValue(namingEnumerationMock); + DistinguishedName subListDn = new DistinguishedName( + "cn=Some name, o=example.com"); + dirContextMock.listBindings(subListDn); + dirContextControl.setReturnValue(namingEnumerationMock); + + dirContextMock.unbind(subListDn); + dirContextMock.unbind(listDn); + dirContextMock.close(); + replay(); + + tested.unbind(DEFAULT_BASE_STRING, true); + + verify(); + } + + public void testRebind() throws Exception { + expectGetReadWriteContext(); + + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + dirContextMock.rebind(nameMock, expectedObject, expectedAttributes); + + dirContextMock.close(); + + replay(); + + tested.rebind(nameMock, expectedObject, expectedAttributes); + + verify(); + } + + public void testRebind_String() throws Exception { + expectGetReadWriteContext(); + + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + dirContextMock.rebind(DEFAULT_BASE_STRING, expectedObject, + expectedAttributes); + + dirContextMock.close(); + + replay(); + + tested.rebind(DEFAULT_BASE_STRING, expectedObject, expectedAttributes); + + verify(); + } + + public void testUnbind_NamingException() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.unbind(nameMock); + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + replay(); + + try { + tested.unbind(nameMock); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testExecuteReadOnly() throws Exception { + expectGetReadOnlyContext(); + + Object object = new Object(); + contextExecutorControl.expectAndReturn(contextExecutorMock + .executeWithContext(dirContextMock), object); + + dirContextMock.close(); + + replay(); + + Object result = tested.executeReadOnly(contextExecutorMock); + + verify(); + + assertSame(object, result); + } + + public void testExecuteReadOnly_NamingException() throws Exception { + expectGetReadOnlyContext(); + + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + contextExecutorControl.expectAndThrow(contextExecutorMock + .executeWithContext(dirContextMock), ne); + + dirContextMock.close(); + + replay(); + + try { + tested.executeReadOnly(contextExecutorMock); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testExecuteReadWrite() throws Exception { + expectGetReadWriteContext(); + + Object object = new Object(); + contextExecutorControl.expectAndReturn(contextExecutorMock + .executeWithContext(dirContextMock), object); + + dirContextMock.close(); + + replay(); + + Object result = tested.executeReadWrite(contextExecutorMock); + + verify(); + + assertSame(object, result); + } + + public void testExecuteReadWrite_NamingException() throws Exception { + expectGetReadWriteContext(); + + javax.naming.NameNotFoundException ne = new javax.naming.NameNotFoundException(); + contextExecutorControl.expectAndThrow(contextExecutorMock + .executeWithContext(dirContextMock), ne); + + dirContextMock.close(); + + replay(); + + try { + tested.executeReadWrite(contextExecutorMock); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testDoSearch_DirContextProcessor() throws Exception { + expectGetReadOnlyContext(); + + SearchResult searchResult = new SearchResult(null, null, null); + + dirContextProcessorMock.preProcess(dirContextMock); + + searchExecutorControl.expectAndReturn(searchExecutorMock + .executeSearch(dirContextMock), namingEnumerationMock); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + namingEnumerationControl.expectAndReturn(namingEnumerationMock.next(), + searchResult); + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + + handlerMock.handleNameClassPair(searchResult); + + dirContextProcessorMock.postProcess(dirContextMock); + + dirContextMock.close(); + + replay(); + + tested.search(searchExecutorMock, handlerMock, dirContextProcessorMock); + + verify(); + } + + public void testDoSearch_DirContextProcessor_NamingException() + throws Exception { + expectGetReadOnlyContext(); + + dirContextProcessorMock.preProcess(dirContextMock); + + javax.naming.LimitExceededException ne = new javax.naming.LimitExceededException(); + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), ne); + + dirContextProcessorMock.postProcess(dirContextMock); + dirContextMock.close(); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock, + dirContextProcessorMock); + fail("LimitExceededException expected"); + } catch (LimitExceededException expected) { + assertTrue(true); + } + + verify(); + } + + public void testDoSearch() throws Exception { + expectGetReadOnlyContext(); + + SearchResult searchResult = new SearchResult(null, null, null); + + searchExecutorControl.expectAndReturn(searchExecutorMock + .executeSearch(dirContextMock), namingEnumerationMock); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + namingEnumerationControl.expectAndReturn(namingEnumerationMock.next(), + searchResult); + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + namingEnumerationMock.close(); + + handlerMock.handleNameClassPair(searchResult); + + dirContextMock.close(); + + replay(); + + tested.search(searchExecutorMock, handlerMock); + + verify(); + } + + public void testDoSearch_NamingException() throws Exception { + expectGetReadOnlyContext(); + + javax.naming.LimitExceededException ne = new javax.naming.LimitExceededException(); + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), ne); + + dirContextMock.close(); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock); + fail("LimitExceededException expected"); + } catch (LimitExceededException expected) { + assertTrue(true); + } + + verify(); + } + + public void testDoSearch_NamingException_NamingEnumeration() + throws Exception { + expectGetReadOnlyContext(); + + searchExecutorControl.expectAndReturn(searchExecutorMock + .executeSearch(dirContextMock), namingEnumerationMock); + + javax.naming.LimitExceededException ne = new javax.naming.LimitExceededException(); + namingEnumerationControl.expectAndThrow( + namingEnumerationMock.hasMore(), ne); + namingEnumerationMock.close(); + + dirContextMock.close(); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock); + fail("LimitExceededException expected"); + } catch (LimitExceededException expected) { + assertTrue(true); + } + + verify(); + } + + public void testDoSearch_NameNotFoundException() throws Exception { + expectGetReadOnlyContext(); + + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), + new javax.naming.NameNotFoundException()); + dirContextMock.close(); + + replay(); + + tested.search(searchExecutorMock, handlerMock); + + verify(); + } + + public void testSearch_PartialResult_IgnoreNotSet() throws Exception { + expectGetReadOnlyContext(); + + dirContextProcessorMock.preProcess(dirContextMock); + + javax.naming.PartialResultException ex = new javax.naming.PartialResultException(); + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), ex); + dirContextProcessorMock.postProcess(dirContextMock); + dirContextMock.close(); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock, + dirContextProcessorMock); + fail("PartialResultException expected"); + } catch (PartialResultException expected) { + assertTrue(true); + } + + verify(); + } + + public void testSearch_PartialResult_IgnoreSet() throws Exception { + tested.setIgnorePartialResultException(true); + + expectGetReadOnlyContext(); + + dirContextProcessorMock.preProcess(dirContextMock); + + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), + new javax.naming.PartialResultException()); + + dirContextProcessorMock.postProcess(dirContextMock); + dirContextMock.close(); + + replay(); + + tested.search(searchExecutorMock, handlerMock, dirContextProcessorMock); + + verify(); + } + + public void testLookupContextWithName() { + final DirContextAdapter expectedResult = new DirContextAdapter(); + + LdapTemplate tested = new LdapTemplate() { + public Object lookup(Name dn) { + assertSame(DistinguishedName.EMPTY_PATH, dn); + return expectedResult; + } + }; + + DirContextOperations result = tested + .lookupContext(DistinguishedName.EMPTY_PATH); + assertSame(expectedResult, result); + + } + + public void testLookupContextWithString() { + final DirContextAdapter expectedResult = new DirContextAdapter(); + final String expectedName = "cn=John Doe"; + + LdapTemplate tested = new LdapTemplate() { + public Object lookup(String dn) { + assertSame(expectedName, dn); + return expectedResult; + } + }; + + DirContextOperations result = tested.lookupContext(expectedName); + assertSame(expectedResult, result); + } + + public void testModifyAttributesWithDirContextOperations() throws Exception { + final ModificationItem[] expectedModifications = new ModificationItem[0]; + + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getDn(), DistinguishedName.EMPTY_PATH); + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .isUpdateMode(), true); + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getModificationItems(), expectedModifications); + + LdapTemplate tested = new LdapTemplate() { + public void modifyAttributes(Name dn, ModificationItem[] mods) { + assertSame(DistinguishedName.EMPTY_PATH, dn); + assertSame(expectedModifications, mods); + } + }; + + replay(); + + tested.modifyAttributes(dirContextOperationsMock); + + verify(); + } + + public void testModifyAttributesWithDirContextOperationsNotInitializedDn() + throws Exception { + + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getDn(), DistinguishedName.EMPTY_PATH); + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .isUpdateMode(), false); + + LdapTemplate tested = new LdapTemplate() { + public void modifyAttributes(Name dn, ModificationItem[] mods) { + fail("The call to the base modifyAttributes should not have occured."); + } + }; + + replay(); + + try { + tested.modifyAttributes(dirContextOperationsMock); + fail("IllegalStateException expected"); + } catch (IllegalStateException expected) { + assertTrue(true); + } + verify(); + } + + public void testModifyAttributesWithDirContextOperationsNotInitializedInUpdateMode() + throws Exception { + + dirContextOperationsConrol.expectAndReturn(dirContextOperationsMock + .getDn(), null); + + LdapTemplate tested = new LdapTemplate() { + public void modifyAttributes(Name dn, ModificationItem[] mods) { + fail("The call to the base modifyAttributes should not have occured."); + } + }; + + replay(); + + try { + tested.modifyAttributes(dirContextOperationsMock); + fail("IllegalStateException expected"); + } catch (IllegalStateException expected) { + assertTrue(true); + } + verify(); + } + + /** + * Needed to verify search control values. + * + * @author Mattias Arthursson + */ + private static class SearchControlsMatcher extends AbstractMatcher { + protected boolean argumentMatches(Object arg0, Object arg1) { + if (arg0 instanceof SearchControls + && arg1 instanceof SearchControls) { + SearchControls s0 = (SearchControls) arg0; + SearchControls s1 = (SearchControls) arg1; + + return s0.getSearchScope() == s1.getSearchScope() + && s0.getReturningObjFlag() == s1.getReturningObjFlag() + && s0.getDerefLinkFlag() == s1.getDerefLinkFlag() + && s0.getCountLimit() == s1.getCountLimit() + && s0.getTimeLimit() == s1.getTimeLimit() + && s0.getReturningAttributes() == s1 + .getReturningAttributes(); + } else { + return super.argumentMatches(arg0, arg1); + } + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/RequestControlMatcher.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/RequestControlMatcher.java new file mode 100644 index 00000000..fdbdf6f5 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/RequestControlMatcher.java @@ -0,0 +1,72 @@ +/* + * Copyright 2005-2007 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.core; + +import javax.naming.ldap.Control; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.easymock.AbstractMatcher; + +/** + * Custom argument matcher that matches javax.naming.ldap.Control objects. + * + * @author Adam Skogman + */ +public class RequestControlMatcher extends AbstractMatcher { + + /** + * @see org.easymock.AbstractMatcher#argumentMatches(java.lang.Object, + * java.lang.Object) + */ + protected boolean argumentMatches(Object expected, Object actual) { + + // null checks + if (expected == null && actual == null) { + return true; + } + if (expected == null || actual == null) { + return false; + } + + // Both params should be arrays + Object[] expArray = (Object[]) expected; + Object[] actArray = (Object[]) actual; + + if (expArray.length != actArray.length) { + return false; + } + + // Compary each object + for (int i = 0; i < expArray.length; i++) { + if (!controlMatches((Control) expArray[i], (Control) actArray[i])) { + return false; + } + } + return true; + + } + + private boolean controlMatches(Control expected, Control actual) { + + // Compare SortControl + return StringUtils.equals(expected.getID(), actual.getID()) + && expected.isCritical() == actual.isCritical() + && ArrayUtils.isEquals(expected.getEncodedValue(), actual + .getEncodedValue()); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/support/AggregateDirContextProcessorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/AggregateDirContextProcessorTest.java new file mode 100644 index 00000000..79b4f26f --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/AggregateDirContextProcessorTest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2005-2007 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.core.support; + +import javax.naming.NamingException; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DirContextProcessor; +import org.springframework.ldap.core.support.AggregateDirContextProcessor; + +public class AggregateDirContextProcessorTest extends TestCase { + + private MockControl processor1Control; + + private DirContextProcessor processor1Mock; + + private MockControl processor2Control; + + private DirContextProcessor processor2Mock; + + private AggregateDirContextProcessor tested; + + protected void setUp() throws Exception { + super.setUp(); + + // Create processor1 mock + processor1Control = MockControl + .createControl(DirContextProcessor.class); + processor1Mock = (DirContextProcessor) processor1Control.getMock(); + + // Create processor2 mock + processor2Control = MockControl + .createControl(DirContextProcessor.class); + processor2Mock = (DirContextProcessor) processor2Control.getMock(); + + tested = new AggregateDirContextProcessor(); + tested.addDirContextProcessor(processor1Mock); + tested.addDirContextProcessor(processor2Mock); + + } + + protected void tearDown() throws Exception { + super.tearDown(); + + processor1Control = null; + processor1Mock = null; + + processor2Control = null; + processor2Mock = null; + } + + protected void replay() { + processor1Control.replay(); + processor2Control.replay(); + + } + + protected void verify() { + processor1Control.verify(); + processor2Control.verify(); + } + + public void testPreProcess() throws NamingException { + processor1Mock.preProcess(null); + processor2Mock.preProcess(null); + + replay(); + + tested.preProcess(null); + + verify(); + } + + public void testPostProcess() throws NamingException { + processor1Mock.postProcess(null); + processor2Mock.postProcess(null); + + replay(); + + tested.postProcess(null); + + verify(); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostProcessorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostProcessorTest.java new file mode 100644 index 00000000..6a64d39b --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostProcessorTest.java @@ -0,0 +1,159 @@ +/* + * Copyright 2005-2007 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.core.support; + +import org.easymock.MockControl; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.ldap.core.DistinguishedName; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link BaseLdapPathBeanPostProcessor}. + * + * @author Mattias Arthursson + */ +public class BaseLdapPathBeanPostProcessorTest extends TestCase { + + private BaseLdapPathBeanPostProcessor tested; + + private MockControl ldapPathAwareControl; + + private BaseLdapPathAware ldapPathAwareMock; + + private MockControl applicationContextControl; + + private ApplicationContext applicationContextMock; + + protected void setUp() throws Exception { + tested = new BaseLdapPathBeanPostProcessor(); + + ldapPathAwareControl = MockControl.createControl(BaseLdapPathAware.class); + ldapPathAwareMock = (BaseLdapPathAware) ldapPathAwareControl.getMock(); + + applicationContextControl = MockControl.createControl(ApplicationContext.class); + applicationContextMock = (ApplicationContext) applicationContextControl.getMock(); + + tested.setApplicationContext(applicationContextMock); + } + + protected void tearDown() throws Exception { + tested = null; + ldapPathAwareControl = null; + ldapPathAwareMock = null; + + applicationContextControl = null; + applicationContextMock = null; + } + + public void testPostProcessBeforeInitializationWithLdapPathAwareBasePathSet() throws Exception { + String expectedPath = "dc=example, dc=com"; + tested.setBasePath(new DistinguishedName(expectedPath)); + + ldapPathAwareMock.setBaseLdapPath(new DistinguishedName(expectedPath)); + + ldapPathAwareControl.replay(); + + Object result = tested.postProcessBeforeInitialization(ldapPathAwareMock, "someName"); + + ldapPathAwareControl.verify(); + + assertSame(ldapPathAwareMock, result); + } + + public void testPostProcessBeforeInitializationWithLdapPathAwareNoBasePathSet() throws Exception { + final LdapContextSource expectedContextSource = new LdapContextSource(); + String expectedPath = "dc=example, dc=com"; + expectedContextSource.setBase(expectedPath); + + tested = new BaseLdapPathBeanPostProcessor() { + BaseLdapPathSource getBaseLdapPathSourceFromApplicationContext() { + return expectedContextSource; + } + }; + + ldapPathAwareMock.setBaseLdapPath(new DistinguishedName(expectedPath)); + + ldapPathAwareControl.replay(); + + Object result = tested.postProcessBeforeInitialization(ldapPathAwareMock, "someName"); + + ldapPathAwareControl.verify(); + + assertSame(ldapPathAwareMock, result); + } + + public void testGetAbstractContextSourceFromApplicationContext() throws Exception { + applicationContextControl.expectAndReturn(applicationContextMock + .getBeanNamesForType(BaseLdapPathSource.class), new String[] { "contextSource" }); + LdapContextSource expectedContextSource = new LdapContextSource(); + applicationContextControl.expectAndReturn(applicationContextMock.getBean("contextSource"), + expectedContextSource); + + applicationContextControl.replay(); + + BaseLdapPathSource result = tested.getBaseLdapPathSourceFromApplicationContext(); + + applicationContextControl.verify(); + assertSame(expectedContextSource, result); + } + + public void testGetAbstractContextSourceFromApplicationContextNoContextSource() throws Exception { + applicationContextControl.expectAndReturn(applicationContextMock + .getBeanNamesForType(BaseLdapPathSource.class), new String[0]); + + applicationContextControl.replay(); + + try { + tested.getBaseLdapPathSourceFromApplicationContext(); + fail("NoSuchBeanDefinitionException expected"); + } + catch (NoSuchBeanDefinitionException expected) { + assertTrue(true); + } + applicationContextControl.verify(); + } + + public void testGetAbstractContextSourceFromApplicationContextTwoContextSources() throws Exception { + applicationContextControl.expectAndReturn(applicationContextMock + .getBeanNamesForType(BaseLdapPathSource.class), new String[2]); + + applicationContextControl.replay(); + + try { + tested.getBaseLdapPathSourceFromApplicationContext(); + fail("NoSuchBeanDefinitionException expected"); + } + catch (NoSuchBeanDefinitionException expected) { + assertTrue(true); + } + applicationContextControl.verify(); + } + + public void testGetAbstractContextSourceFromApplicationContextTwoContextSourcesAndSpecifiedName() throws Exception { + LdapContextSource expectedContextSource = new LdapContextSource(); + + tested.setBaseLdapPathSourceName("myContextSource"); + applicationContextControl.expectAndReturn(applicationContextMock.getBean("myContextSource"), + expectedContextSource); + + applicationContextControl.replay(); + + tested.getBaseLdapPathSourceFromApplicationContext(); + applicationContextControl.verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/support/CountNameClassPairResultCallbackHandlerTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/CountNameClassPairResultCallbackHandlerTest.java new file mode 100644 index 00000000..5c056e3a --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/CountNameClassPairResultCallbackHandlerTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005-2007 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.core.support; + +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.core.support.CountNameClassPairCallbackHandler; + + +import junit.framework.TestCase; + +public class CountNameClassPairResultCallbackHandlerTest extends TestCase { + + + private CountNameClassPairCallbackHandler tested; + + protected void setUp() throws Exception { + super.setUp(); + + tested = new CountNameClassPairCallbackHandler(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + tested = null; + } + + public void testHandleSearchResult() throws Exception { + SearchResult dummy = new SearchResult(null, null, null); + tested.handleNameClassPair(dummy); + tested.handleNameClassPair(dummy); + tested.handleNameClassPair(dummy); + + assertEquals(3, tested.getNoOfRows()); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/support/DefaultDirObjectFactoryTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/DefaultDirObjectFactoryTest.java new file mode 100644 index 00000000..b501f103 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/DefaultDirObjectFactoryTest.java @@ -0,0 +1,156 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.directory.BasicAttributes; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; + +public class DefaultDirObjectFactoryTest extends TestCase { + + private MockControl contextControl; + + private Context contextMock; + + private static final Name DN = new DistinguishedName( + "ou=some unit, dc=jayway, dc=se"); + + private DefaultDirObjectFactory tested; + + private MockControl contextControl2; + + private Context contextMock2; + + protected void setUp() throws Exception { + super.setUp(); + + contextControl = MockControl.createControl(Context.class); + contextMock = (Context) contextControl.getMock(); + + contextControl2 = MockControl.createControl(Context.class); + contextMock2 = (Context) contextControl2.getMock(); + + tested = new DefaultDirObjectFactory(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextControl = null; + contextMock = null; + + contextControl2 = null; + contextMock2 = null; + + tested = null; + } + + protected void replay() { + contextControl.replay(); + contextControl2.replay(); + } + + protected void verify() { + contextControl.verify(); + contextControl2.verify(); + } + + public void testGetObjectInstance() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); + + contextMock.close(); + + replay(); + + DirContextAdapter adapter = (DirContextAdapter) tested + .getObjectInstance(contextMock, DN, null, new Hashtable(), + expectedAttributes); + + verify(); + + assertEquals(DN, adapter.getDn()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } + + public void testGetObjectInstance_nullObject() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); + + replay(); + + DirContextAdapter adapter = (DirContextAdapter) tested + .getObjectInstance(null, DN, null, new Hashtable(), + expectedAttributes); + + verify(); + + assertEquals(DN, adapter.getDn()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } + + public void testGetObjectInstance_ObjectNotContext() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); + + replay(); + + DirContextAdapter adapter = (DirContextAdapter) tested + .getObjectInstance(new Object(), DN, null, new Hashtable(), + expectedAttributes); + + verify(); + + assertEquals(DN, adapter.getDn()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } + + /** + * Make sure that the base suffix is stripped off from the DN. + * + * @throws Exception + */ + public void testGetObjectInstance_BaseSet() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); + + contextControl2.expectAndReturn(contextMock2.getNameInNamespace(), + "dc=jayway, dc=se"); + contextMock.close(); + + replay(); + + DirContextAdapter adapter = (DirContextAdapter) tested + .getObjectInstance(contextMock, new DistinguishedName( + "ou=some unit"), contextMock2, new Hashtable(), + expectedAttributes); + + verify(); + + assertEquals("ou=some unit", adapter.getDn().toString()); + assertEquals("ou=some unit, dc=jayway, dc=se", adapter + .getNameInNamespace()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/core/support/LdapContextSourceTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/LdapContextSourceTest.java new file mode 100644 index 00000000..abce5c51 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/core/support/LdapContextSourceTest.java @@ -0,0 +1,288 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.HashMap; +import java.util.Hashtable; + +import javax.naming.Context; + +import junit.framework.TestCase; + +import org.springframework.ldap.core.AuthenticationSource; +import org.springframework.ldap.core.DistinguishedName; + +/** + * Unit tests for the LdapContextSource class. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapContextSourceTest extends TestCase { + + private LdapContextSource tested; + + protected void setUp() throws Exception { + tested = new LdapContextSource(); + } + + protected void tearDown() throws Exception { + tested = null; + } + + public void testAfterPropertiesSet_NoUrl() throws Exception { + try { + tested.afterPropertiesSet(); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testAfterPropertiesSet_BaseAndTooEarlyJdk() throws Exception { + tested = new LdapContextSource() { + String getJdkVersion() { + return "1.4.1_03"; + } + }; + + tested.setUrl("http://ldap.example.com:389"); + tested.setBase("dc=jayway,dc=se"); + try { + tested.afterPropertiesSet(); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testGetAnonymousEnv() throws Exception { + tested.setBase("dc=example,dc=se"); + tested.setUrl("ldap://ldap.example.com:389"); + tested.setPooled(true); + tested.setUserDn("cn=Some User"); + tested.setPassword("secret"); + tested.afterPropertiesSet(); + Hashtable env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap.example.com:389/dc=example,dc=se", env.get(Context.PROVIDER_URL)); + assertEquals("true", env.get(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + assertNull(env.get(Context.SECURITY_PRINCIPAL)); + assertNull(env.get(Context.SECURITY_CREDENTIALS)); + + // check that base was added to environment + assertEquals(new DistinguishedName("dc=example,dc=se"), env.get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY)); + + // Verify that changing values does not change the environment values. + tested.setBase("dc=other,dc=se"); + tested.setUrl("ldap://ldap2.example.com:389"); + tested.setPooled(false); + + env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap.example.com:389/dc=example,dc=se", env.get(Context.PROVIDER_URL)); + assertEquals("true", env.get(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + assertNull(env.get(Context.SECURITY_PRINCIPAL)); + assertNull(env.get(Context.SECURITY_CREDENTIALS)); + + assertEquals(new DistinguishedName("dc=example,dc=se"), env.get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY)); + } + + public void testGetAnonymousEnvWithNoBaseSet() throws Exception { + tested.setUrl("ldap://ldap.example.com:389"); + tested.afterPropertiesSet(); + Hashtable env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap.example.com:389", env.get(Context.PROVIDER_URL)); + + // check that base was not added to environment + assertNull(env.get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY)); + } + + public void testGetAnonymousEnvWithBaseEnvironment() throws Exception { + tested.setUrl("ldap://ldap.example.com:389"); + HashMap map = new HashMap(); + map.put(LdapContextSource.SUN_LDAP_POOLING_FLAG, "true"); + tested.setBaseEnvironmentProperties(map); + tested.afterPropertiesSet(); + Hashtable env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap.example.com:389", env.get(Context.PROVIDER_URL)); + assertEquals("true", env.get(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + } + + public void testGetAnonymousEnvWithPoolingInBaseEnvironmentAndPoolingOff() throws Exception { + tested.setUrl("ldap://ldap.example.com:389"); + HashMap map = new HashMap(); + map.put(LdapContextSource.SUN_LDAP_POOLING_FLAG, "true"); + tested.setBaseEnvironmentProperties(map); + tested.setPooled(false); + tested.afterPropertiesSet(); + Hashtable env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap.example.com:389", env.get(Context.PROVIDER_URL)); + assertNull(env.get(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + } + + public void testGetAnonymousEnvWithEmptyBaseSet() throws Exception { + tested.setUrl("ldap://ldap.example.com:389"); + tested.setBase(null); + tested.afterPropertiesSet(); + Hashtable env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap.example.com:389", env.get(Context.PROVIDER_URL)); + + // check that base was not added to environment + assertNull(env.get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY)); + } + + public void testOldJdkWithNoBaseSetShouldWork() throws Exception { + tested = new LdapContextSource() { + String getJdkVersion() { + return "1.3"; + } + }; + tested.setUrl("ldap://ldap.example.com:389"); + tested.afterPropertiesSet(); + + // check that base was not added to environment + Hashtable env = tested.getAnonymousEnv(); + assertNull(env.get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY)); + } + + public void testOldJdkWithBaseSetShouldNotWork() throws Exception { + tested = new LdapContextSource() { + String getJdkVersion() { + return "1.3"; + } + }; + tested.setUrl("ldap://ldap.example.com:389"); + tested.setBase("dc=example,dc=com"); + try { + tested.afterPropertiesSet(); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testOldJdkWithBaseSetToEmptyPathShouldWork() throws Exception { + tested = new LdapContextSource() { + String getJdkVersion() { + return "1.3"; + } + }; + tested.setUrl("ldap://ldap.example.com:389"); + tested.setBase(null); + tested.afterPropertiesSet(); + + // check that base was not added to environment + Hashtable env = tested.getAnonymousEnv(); + assertNull(env.get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY)); + } + + public void testGetAuthenticatedEnv() throws Exception { + tested.setBase("dc=example,dc=se"); + tested.setUrl("ldap://ldap.example.com:389"); + tested.setPooled(true); + tested.setUserDn("cn=Some User"); + tested.setPassword("secret"); + tested.afterPropertiesSet(); + + Hashtable env = tested.getAuthenticatedEnv(); + assertEquals("ldap://ldap.example.com:389/dc=example,dc=se", env.get(Context.PROVIDER_URL)); + assertEquals("true", env.get(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + assertEquals("cn=Some User", env.get(Context.SECURITY_PRINCIPAL)); + assertEquals("secret", env.get(Context.SECURITY_CREDENTIALS)); + + // check that base was added to environment + assertEquals(new DistinguishedName("dc=example,dc=se"), env.get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY)); + } + + public void testGetAuthenticatedEnv_DummyAuthenticationProvider() throws Exception { + tested.setBase("dc=example,dc=se"); + tested.setUrl("ldap://ldap.example.com:389"); + tested.setPooled(true); + DummyAuthenticationProvider authenticationProvider = new DummyAuthenticationProvider(); + tested.setAuthenticationSource(authenticationProvider); + authenticationProvider.setPrincipal("cn=Some User"); + authenticationProvider.setCredentials("secret"); + tested.afterPropertiesSet(); + + Hashtable env = tested.getAuthenticatedEnv(); + assertEquals("ldap://ldap.example.com:389/dc=example,dc=se", env.get(Context.PROVIDER_URL)); + assertEquals("true", env.get(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + assertEquals("cn=Some User", env.get(Context.SECURITY_PRINCIPAL)); + assertEquals("secret", env.get(Context.SECURITY_CREDENTIALS)); + } + + public void testGetAuthenticatedEnv_DummyAuthenticationProvider_Changed() throws Exception { + tested.setBase("dc=example,dc=se"); + tested.setUrl("ldap://ldap.example.com:389"); + tested.setPooled(true); + DummyAuthenticationProvider authenticationProvider = new DummyAuthenticationProvider(); + tested.setAuthenticationSource(authenticationProvider); + authenticationProvider.setPrincipal("cn=Some User"); + authenticationProvider.setCredentials("secret"); + tested.afterPropertiesSet(); + + authenticationProvider.setPrincipal("cn=Some Other User"); + authenticationProvider.setCredentials("other secret"); + + Hashtable env = tested.getAuthenticatedEnv(); + assertEquals("cn=Some Other User", env.get(Context.SECURITY_PRINCIPAL)); + assertEquals("other secret", env.get(Context.SECURITY_CREDENTIALS)); + } + + public void testGetAnonymousEnvWhenCacheIsOff() throws Exception { + tested.setBase("dc=example,dc=se"); + tested.setUrl("ldap://ldap.example.com:389"); + tested.setPooled(true); + tested.setUserDn("cn=Some User"); + tested.setPassword("secret"); + tested.setCacheEnvironmentProperties(false); + tested.afterPropertiesSet(); + Hashtable env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap.example.com:389/dc=example,dc=se", env.get(Context.PROVIDER_URL)); + assertEquals("true", env.get(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + assertNull(env.get(Context.SECURITY_PRINCIPAL)); + assertNull(env.get(Context.SECURITY_CREDENTIALS)); + + tested.setUrl("ldap://ldap2.example.com:389"); + env = tested.getAnonymousEnv(); + assertEquals("ldap://ldap2.example.com:389/dc=example,dc=se", env.get(Context.PROVIDER_URL)); + } + + private class DummyAuthenticationProvider implements AuthenticationSource { + private String principal; + + private String credentials; + + public void setCredentials(String credentials) { + this.credentials = credentials; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public String getPrincipal() { + return principal; + } + + public String getCredentials() { + return credentials; + } + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/AbstractFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/AbstractFilterTest.java new file mode 100644 index 00000000..553fe077 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/AbstractFilterTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.AbstractFilter; + +import junit.framework.TestCase; + +/** + * @author Adam Skogman + */ +public class AbstractFilterTest extends TestCase { + + /** + * Constructor for AbstractFilterTest. + * + * @param name + */ + public AbstractFilterTest(String name) { + super(name); + } + + /* + * Test for String encode() + */ + public void testEncode() { + + AbstractFilter af = new AbstractFilter() { + public StringBuffer encode(StringBuffer buff) { + return buff.append("foo"); + } + }; + + assertEquals("foo", af.encode()); + + } + + /* + * Test for toString() + */ + public void testToString() { + + AbstractFilter af = new AbstractFilter() { + public StringBuffer encode(StringBuffer buff) { + return buff.append("foo"); + } + }; + + assertEquals("foo", af.toString()); + + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/AndFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/AndFilterTest.java new file mode 100644 index 00000000..55479054 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/AndFilterTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.AndFilter; +import org.springframework.ldap.filter.EqualsFilter; + +import com.gargoylesoftware.base.testing.EqualsTester; + +import junit.framework.TestCase; + +/** + * @author Adam Skogman + */ +public class AndFilterTest extends TestCase { + + /** + * Constructor for AndFilterTest. + * + * @param name + */ + public AndFilterTest(String name) { + super(name); + } + + public void testZero() { + AndFilter aq = new AndFilter(); + + assertEquals("", aq.encode()); + } + + public void testOne() { + AndFilter aq = new AndFilter().and(new EqualsFilter("a", "b")); + + assertEquals("(a=b)", aq.encode()); + } + + public void testTwo() { + AndFilter aq = new AndFilter().and(new EqualsFilter("a", "b")).and( + new EqualsFilter("c", "d")); + + assertEquals("(&(a=b)(c=d))", aq.encode()); + } + + public void testThree() { + AndFilter aq = new AndFilter().and(new EqualsFilter("a", "b")).and( + new EqualsFilter("c", "d")).and(new EqualsFilter("e", "f")); + + assertEquals("(&(a=b)(c=d)(e=f))", aq.encode()); + } + + public void testEquals() { + AndFilter originalObject = new AndFilter().and(new EqualsFilter("a", + "b")); + AndFilter identicalObject = new AndFilter().and(new EqualsFilter("a", + "b")); + AndFilter differentObject = new AndFilter().and(new EqualsFilter("b", + "b")); + AndFilter subclassObject = new AndFilter() { + }.and(new EqualsFilter("a", "b")); + + new EqualsTester(originalObject, identicalObject, differentObject, + subclassObject); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/EqualsFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/EqualsFilterTest.java new file mode 100644 index 00000000..e6adbca1 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/EqualsFilterTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.EqualsFilter; + +import com.gargoylesoftware.base.testing.EqualsTester; + +import junit.framework.TestCase; + +/** + * @author Adam Skogman + */ +public class EqualsFilterTest extends TestCase { + + /** + * Constructor for EqualsQueryTest. + * + * @param name + */ + public EqualsFilterTest(String name) { + super(name); + } + + public void testEncode() { + + EqualsFilter eqq = new EqualsFilter("foo", "*bar(fie)"); + + StringBuffer buff = new StringBuffer(); + eqq.encode(buff); + + assertEquals("(foo=\\2abar\\28fie\\29)", buff.toString()); + + } + + public void testEncodeInt() { + + EqualsFilter eqq = new EqualsFilter("foo", 456); + + StringBuffer buff = new StringBuffer(); + eqq.encode(buff); + + assertEquals("(foo=456)", buff.toString()); + + } + + public void testEquals() { + EqualsFilter originalObject = new EqualsFilter("a", "b"); + EqualsFilter identicalObject = new EqualsFilter("a", "b"); + EqualsFilter differentObject = new EqualsFilter("b", "b"); + EqualsFilter subclassObject = new EqualsFilter("a", "b") { + }; + + new EqualsTester(originalObject, identicalObject, differentObject, + subclassObject); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/GreaterThanOrEqualsFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/GreaterThanOrEqualsFilterTest.java new file mode 100644 index 00000000..90b639c5 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/GreaterThanOrEqualsFilterTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.GreaterThanOrEqualsFilter; + +import junit.framework.TestCase; + +/** + * @author Mattias Arthursson + */ +public class GreaterThanOrEqualsFilterTest extends TestCase { + + /** + * Constructor for EqualsQueryTest. + * + * @param name + */ + public GreaterThanOrEqualsFilterTest(String name) { + super(name); + } + + public void testEncode() { + + GreaterThanOrEqualsFilter eqq = new GreaterThanOrEqualsFilter("foo", + "*bar(fie)"); + + StringBuffer buff = new StringBuffer(); + eqq.encode(buff); + + assertEquals("(foo>=\\2abar\\28fie\\29)", buff.toString()); + + } + + public void testEncodeInt() { + + GreaterThanOrEqualsFilter eqq = new GreaterThanOrEqualsFilter("foo", + 456); + + StringBuffer buff = new StringBuffer(); + eqq.encode(buff); + + assertEquals("(foo>=456)", buff.toString()); + + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/LessThanOrEqualsFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/LessThanOrEqualsFilterTest.java new file mode 100644 index 00000000..a1339b24 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/LessThanOrEqualsFilterTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.LessThanOrEqualsFilter; + +import junit.framework.TestCase; + +/** + * @author Mattias Arthursson + */ +public class LessThanOrEqualsFilterTest extends TestCase { + + /** + * Constructor for EqualsQueryTest. + * + * @param name + */ + public LessThanOrEqualsFilterTest(String name) { + super(name); + } + + public void testEncode() { + + LessThanOrEqualsFilter eqq = new LessThanOrEqualsFilter("foo", + "*bar(fie)"); + + StringBuffer buff = new StringBuffer(); + eqq.encode(buff); + + assertEquals("(foo<=\\2abar\\28fie\\29)", buff.toString()); + + } + + public void testEncodeInt() { + + LessThanOrEqualsFilter eqq = new LessThanOrEqualsFilter("foo", 456); + + StringBuffer buff = new StringBuffer(); + eqq.encode(buff); + + assertEquals("(foo<=456)", buff.toString()); + + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/LikeFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/LikeFilterTest.java new file mode 100644 index 00000000..d12996d0 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/LikeFilterTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.LikeFilter; + +import junit.framework.TestCase; + +/** + * @author Anders Henja + */ +public class LikeFilterTest extends TestCase { + /** + * Constructor for LikeFilterTest. + * + * @param name + */ + public LikeFilterTest(String name) { + super(name); + } + + final public void testEncodeValue_blank() { + assertEquals("", new LikeFilter("", null).getEncodedValue()); + assertEquals(" ", new LikeFilter("", " ").getEncodedValue()); + } + + final public void testEncodeValue_normal() { + assertEquals("foo", new LikeFilter("", "foo").getEncodedValue()); + assertEquals("foo*bar", new LikeFilter("", "foo*bar").getEncodedValue()); + assertEquals("*foo*bar*", new LikeFilter("", "*foo*bar*") + .getEncodedValue()); + assertEquals("**foo**bar**", new LikeFilter("", "**foo**bar**") + .getEncodedValue()); + } + + final public void testEncodeValue_escape() { + assertEquals("*\\28*\\29*", new LikeFilter("", "*(*)*") + .getEncodedValue()); + assertEquals("*\\5c2a*", new LikeFilter("", "*\\2a*").getEncodedValue()); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/NotFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/NotFilterTest.java new file mode 100644 index 00000000..41888514 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/NotFilterTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.EqualsFilter; +import org.springframework.ldap.filter.NotFilter; + +import com.gargoylesoftware.base.testing.EqualsTester; + +import junit.framework.TestCase; + +/** + * @author Mattias Arthursson + */ +public class NotFilterTest extends TestCase { + + public void testConstructor() { + EqualsFilter filter = new EqualsFilter("a", "b"); + NotFilter notFilter = new NotFilter(filter); + + assertEquals("(!(a=b))", notFilter.encode()); + } + + public void testEquals() { + EqualsFilter filter = new EqualsFilter("a", "b"); + NotFilter originalObject = new NotFilter(filter); + NotFilter identicalObject = new NotFilter(filter); + NotFilter differentObject = new NotFilter(new EqualsFilter("a", "a")); + NotFilter subclassObject = new NotFilter(filter) { + }; + + new EqualsTester(originalObject, identicalObject, differentObject, + subclassObject); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/OrFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/OrFilterTest.java new file mode 100644 index 00000000..e5d6337c --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/OrFilterTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.EqualsFilter; +import org.springframework.ldap.filter.OrFilter; + +import junit.framework.TestCase; + +/** + * @author Adam Skogman + */ +public class OrFilterTest extends TestCase { + + /** + * Constructor for OrFilterTest. + * + * @param name + */ + public OrFilterTest(String name) { + super(name); + } + + public void testZero() { + OrFilter of = new OrFilter(); + + assertEquals("", of.encode()); + } + + public void testOne() { + OrFilter of = new OrFilter().or(new EqualsFilter("a", "b")); + + assertEquals("(a=b)", of.encode()); + } + + public void testTwo() { + OrFilter of = new OrFilter().or(new EqualsFilter("a", "b")).or( + new EqualsFilter("c", "d")); + + assertEquals("(|(a=b)(c=d))", of.encode()); + } + + public void testThree() { + OrFilter of = new OrFilter().or(new EqualsFilter("a", "b")).or( + new EqualsFilter("c", "d")).or(new EqualsFilter("e", "f")); + + assertEquals("(|(a=b)(c=d)(e=f))", of.encode()); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/filter/WhitespaceWildcardsFilterTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/filter/WhitespaceWildcardsFilterTest.java new file mode 100644 index 00000000..b0094daf --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/filter/WhitespaceWildcardsFilterTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2005-2007 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.filter; + +import org.springframework.ldap.filter.WhitespaceWildcardsFilter; + +import junit.framework.TestCase; + +/** + * @author Adam Skogman + */ +public class WhitespaceWildcardsFilterTest extends TestCase { + + /** + * Constructor for WhitespaceWildcardsFilterTest. + * + * @param name + */ + public WhitespaceWildcardsFilterTest(String name) { + super(name); + } + + final public void testEncodeValue_blank() { + + // blank + assertEquals("*", new WhitespaceWildcardsFilter("", null) + .getEncodedValue()); + assertEquals("*", new WhitespaceWildcardsFilter("", " ") + .getEncodedValue()); + assertEquals("*", new WhitespaceWildcardsFilter("", " ") + .getEncodedValue()); + assertEquals("*", new WhitespaceWildcardsFilter("", "\t") + .getEncodedValue()); + + } + + final public void testEncodeValue_normal() { + + assertEquals("*foo*", new WhitespaceWildcardsFilter("", "foo") + .getEncodedValue()); + assertEquals("*foo*bar*", new WhitespaceWildcardsFilter("", "foo bar") + .getEncodedValue()); + assertEquals("*foo*bar*", + new WhitespaceWildcardsFilter("", " foo bar ") + .getEncodedValue()); + assertEquals("*foo*bar*", new WhitespaceWildcardsFilter("", + " \t foo \n bar \r ").getEncodedValue()); + } + + final public void testEncodeValue_escape() { + + assertEquals("*\\28\\2a\\29*", new WhitespaceWildcardsFilter("", "(*)") + .getEncodedValue()); + assertEquals("*\\2a*", new WhitespaceWildcardsFilter("", "*") + .getEncodedValue()); + assertEquals("*\\5c*", new WhitespaceWildcardsFilter("", " \\ ") + .getEncodedValue()); + + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/pool/AbstractPoolTestCase.java b/mvn-build/core/src/test/java/org/springframework/ldap/pool/AbstractPoolTestCase.java new file mode 100644 index 00000000..b5994969 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/pool/AbstractPoolTestCase.java @@ -0,0 +1,123 @@ +/* + * Copyright 2005-2007 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.pool; + +import javax.naming.Context; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.pool.KeyedObjectPool; +import org.easymock.MockControl; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.pool.validation.DirContextValidator; + +import junit.framework.TestCase; + +/** + * Contains mocks common to many tests for the connection pool. + * + * @author Ulrik Sandberg + */ +public abstract class AbstractPoolTestCase extends TestCase { + + protected MockControl contextControl; + + protected Context contextMock; + + protected MockControl dirContextControl; + + protected DirContext dirContextMock; + + protected MockControl ldapContextControl; + + protected LdapContext ldapContextMock; + + protected MockControl keyedObjectPoolControl; + + protected KeyedObjectPool keyedObjectPoolMock; + + protected MockControl contextSourceControl; + + protected ContextSource contextSourceMock; + + protected MockControl dirContextValidatorControl; + + protected DirContextValidator dirContextValidatorMock; + + protected void setUp() throws Exception { + super.setUp(); + + contextControl = MockControl.createControl(Context.class); + contextMock = (Context) contextControl.getMock(); + + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + ldapContextControl = MockControl.createControl(LdapContext.class); + ldapContextMock = (LdapContext) ldapContextControl.getMock(); + + keyedObjectPoolControl = MockControl + .createControl(KeyedObjectPool.class); + keyedObjectPoolMock = (KeyedObjectPool) keyedObjectPoolControl + .getMock(); + + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + dirContextValidatorControl = MockControl.createControl(DirContextValidator.class); + dirContextValidatorMock = (DirContextValidator) dirContextValidatorControl.getMock(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextControl = null; + contextMock = null; + + dirContextControl = null; + dirContextMock = null; + + ldapContextControl = null; + ldapContextMock = null; + + keyedObjectPoolControl = null; + keyedObjectPoolMock = null; + + contextSourceControl = null; + contextSourceMock = null; + + dirContextValidatorControl = null; + dirContextValidatorMock = null; + } + + protected void replay() { + contextControl.replay(); + dirContextControl.replay(); + ldapContextControl.replay(); + keyedObjectPoolControl.replay(); + contextSourceControl.replay(); + dirContextValidatorControl.replay(); + } + + protected void verify() { + contextControl.verify(); + dirContextControl.verify(); + ldapContextControl.verify(); + keyedObjectPoolControl.verify(); + contextSourceControl.verify(); + dirContextValidatorControl.verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingContextTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingContextTest.java new file mode 100644 index 00000000..b8ce60cb --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingContextTest.java @@ -0,0 +1,446 @@ +/* + * Copyright 2005-2007 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.pool; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingException; + +import org.apache.commons.pool.KeyedObjectPool; +import org.easymock.MockControl; + +/** + * @author Eric Dalquist eric.dalquist@doit.wisc.edu + */ +public class DelegatingContextTest extends AbstractPoolTestCase { + + public void testConstructorAssertions() { + + replay(); + + try { + new DelegatingContext(null, contextMock, DirContextType.READ_ONLY); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DelegatingContext(keyedObjectPoolMock, null, + DirContextType.READ_ONLY); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DelegatingContext(keyedObjectPoolMock, contextMock, null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + verify(); + } + + public void testHelperMethods() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, contextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + // Wrap the Context once + final DelegatingContext delegatingContext = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + + final Context delegateContext = delegatingContext.getDelegateContext(); + assertEquals(contextMock, delegateContext); + + final Context innerDelegateContext = delegatingContext + .getInnermostDelegateContext(); + assertEquals(contextMock, innerDelegateContext); + + delegatingContext.assertOpen(); + + // Wrap the wrapper + MockControl secondKeyedObjectPoolControl = MockControl + .createControl(KeyedObjectPool.class); + KeyedObjectPool secondKeyedObjectPoolMock = (KeyedObjectPool) secondKeyedObjectPoolControl + .getMock(); + secondKeyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + delegatingContext); + secondKeyedObjectPoolControl.setVoidCallable(1); + secondKeyedObjectPoolControl.replay(); + + final DelegatingContext delegatingContext2 = new DelegatingContext( + secondKeyedObjectPoolMock, delegatingContext, + DirContextType.READ_ONLY); + + final Context delegateContext2 = delegatingContext2 + .getDelegateContext(); + assertEquals(delegatingContext, delegateContext2); + + final Context innerDelegateContext2 = delegatingContext2 + .getInnermostDelegateContext(); + assertEquals(contextMock, innerDelegateContext2); + + delegatingContext2.assertOpen(); + + // Close the outer wrapper + delegatingContext2.close(); + + final Context delegateContext2closed = delegatingContext2 + .getDelegateContext(); + assertNull(delegateContext2closed); + + final Context innerDelegateContext2closed = delegatingContext2 + .getInnermostDelegateContext(); + assertNull(innerDelegateContext2closed); + + try { + delegatingContext2.assertOpen(); + fail("delegatingContext2.assertOpen() should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + // Close the outer wrapper + delegatingContext.close(); + + final Context delegateContextclosed = delegatingContext + .getDelegateContext(); + assertNull(delegateContextclosed); + + final Context innerDelegateContextclosed = delegatingContext + .getInnermostDelegateContext(); + assertNull(innerDelegateContextclosed); + + try { + delegatingContext.assertOpen(); + fail("delegatingContext.assertOpen() should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + verify(); + secondKeyedObjectPoolControl.verify(); + } + + public void testObjectMethods() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, contextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + // Wrap the Context once + final DelegatingContext delegatingContext = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + assertEquals("EasyMock for interface javax.naming.Context", + delegatingContext.toString()); + delegatingContext.hashCode(); // Run it to make sure it doesn't fail + + assertTrue(delegatingContext.equals(delegatingContext)); + assertFalse(delegatingContext.equals(new Object())); + + final DelegatingContext delegatingContext2 = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + assertTrue(delegatingContext.equals(delegatingContext2)); + assertTrue(delegatingContext2.equals(delegatingContext)); + assertTrue(delegatingContext.equals(contextMock)); + + // Close the contextMock and try again + delegatingContext.close(); + + assertEquals("Context is closed", delegatingContext.toString()); + assertEquals(0, delegatingContext.hashCode()); // Run it to make sure + // it doesn't fail + + assertTrue(delegatingContext.equals(delegatingContext)); + assertFalse(delegatingContext.equals(new Object())); + + assertFalse(delegatingContext.equals(delegatingContext2)); + assertFalse(delegatingContext2.equals(delegatingContext)); + assertFalse(delegatingContext.equals(contextMock)); + + verify(); + } + + public void testUnsupportedMethods() throws Exception { + + replay(); + + final DelegatingContext delegatingContext = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + + try { + delegatingContext.addToEnvironment(null, null); + fail("DelegatingContext.addToEnvironment Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingContext.createSubcontext((Name) null); + fail("DelegatingContext.createSubcontext Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingContext.createSubcontext((String) null); + fail("DelegatingContext.createSubcontext Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingContext.destroySubcontext((Name) null); + fail("DelegatingContext.destroySubcontext Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingContext.destroySubcontext((String) null); + fail("DelegatingContext.destroySubcontext Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingContext.removeFromEnvironment(null); + fail("DelegatingContext.removeFromEnvironment Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + verify(); + } + + public void testAllMethodsOpened() throws Exception { + contextControl = MockControl.createNiceControl(Context.class); + contextMock = (Context) contextControl.getMock(); + + replay(); + + final DelegatingContext delegatingContext = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + + delegatingContext.bind((Name) null, null); + delegatingContext.bind((String) null, null); + delegatingContext.composeName((Name) null, (Name) null); + delegatingContext.composeName((String) null, (String) null); + delegatingContext.getEnvironment(); + delegatingContext.getNameInNamespace(); + delegatingContext.getNameParser((Name) null); + delegatingContext.getNameParser((String) null); + delegatingContext.list((Name) null); + delegatingContext.list((String) null); + delegatingContext.listBindings((Name) null); + delegatingContext.listBindings((String) null); + delegatingContext.lookup((Name) null); + delegatingContext.lookup((String) null); + delegatingContext.lookupLink((Name) null); + delegatingContext.lookupLink((String) null); + delegatingContext.rebind((Name) null, null); + delegatingContext.rebind((String) null, null); + delegatingContext.rename((Name) null, (Name) null); + delegatingContext.rename((String) null, (String) null); + delegatingContext.unbind((Name) null); + delegatingContext.unbind((String) null); + + verify(); + } + + public void testAllMethodsClosed() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, contextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + final DelegatingContext delegatingContext = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + + delegatingContext.close(); + + try { + delegatingContext.bind((Name) null, null); + fail("DelegatingContext.bind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.bind((String) null, null); + fail("DelegatingContext.bind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.composeName((Name) null, (Name) null); + fail("DelegatingContext.composeName should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.composeName((String) null, (String) null); + fail("DelegatingContext.composeName should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.getEnvironment(); + fail("DelegatingContext.getEnvironment should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.getNameInNamespace(); + fail("DelegatingContext.getNameInNamespace should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.getNameParser((Name) null); + fail("DelegatingContext.getNameParser should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.getNameParser((String) null); + fail("DelegatingContext.getNameParser should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.list((Name) null); + fail("DelegatingContext.list should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.list((String) null); + fail("DelegatingContext.list should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.listBindings((Name) null); + fail("DelegatingContext.listBindings should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.listBindings((String) null); + fail("DelegatingContext.listBindings should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.lookup((Name) null); + fail("DelegatingContext.lookup should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.lookup((String) null); + fail("DelegatingContext.lookup should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.lookupLink((Name) null); + fail("DelegatingContext.lookupLink should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.lookupLink((String) null); + fail("DelegatingContext.lookupLink should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.rebind((Name) null, null); + fail("DelegatingContext.rebind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.rebind((String) null, null); + fail("DelegatingContext.rebind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.rename((Name) null, (Name) null); + fail("DelegatingContext.rename should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.rename((String) null, (String) null); + fail("DelegatingContext.rename should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.unbind((Name) null); + fail("DelegatingContext.unbind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingContext.unbind((String) null); + fail("DelegatingContext.unbind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + verify(); + } + + public void testDoubleClose() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, contextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + final DelegatingContext delegatingContext = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + + delegatingContext.close(); + + // noop close + delegatingContext.close(); + + verify(); + } + + public void testPoolExceptionOnClose() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, contextMock); + keyedObjectPoolControl.setThrowable(new Exception( + "Fake Pool returnObject Exception")); + + replay(); + + final DelegatingContext delegatingContext = new DelegatingContext( + keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY); + + try { + delegatingContext.close(); + fail("DelegatingContext.close should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingDirContextTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingDirContextTest.java new file mode 100644 index 00000000..be39901c --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingDirContextTest.java @@ -0,0 +1,419 @@ +/* + * Copyright 2005-2007 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.pool; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; + +import org.apache.commons.pool.KeyedObjectPool; +import org.easymock.MockControl; + +/** + * @author Eric Dalquist eric.dalquist@doit.wisc.edu + */ +public class DelegatingDirContextTest extends AbstractPoolTestCase { + public void testConstructorAssertions() { + replay(); + + try { + new DelegatingDirContext(keyedObjectPoolMock, null, + DirContextType.READ_ONLY); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DelegatingDirContext(keyedObjectPoolMock, dirContextMock, null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + verify(); + } + + public void testHelperMethods() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + dirContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + // Wrap the DirContext once + final DelegatingDirContext delegatingDirContext = new DelegatingDirContext( + keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY); + + final Context delegateContext = delegatingDirContext + .getDelegateContext(); + assertEquals(dirContextMock, delegateContext); + + final DirContext delegateDirContext = delegatingDirContext + .getDelegateDirContext(); + assertEquals(dirContextMock, delegateDirContext); + + final DirContext innerDelegateDirContext = delegatingDirContext + .getInnermostDelegateDirContext(); + assertEquals(dirContextMock, innerDelegateDirContext); + + delegatingDirContext.assertOpen(); + + // Wrap the wrapper + MockControl secondKeyedObjectPoolControl = MockControl + .createControl(KeyedObjectPool.class); + KeyedObjectPool secondKeyedObjectPoolMock = (KeyedObjectPool) secondKeyedObjectPoolControl + .getMock(); + secondKeyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + delegatingDirContext); + secondKeyedObjectPoolControl.setVoidCallable(1); + secondKeyedObjectPoolControl.replay(); + + final DelegatingDirContext delegatingDirContext2 = new DelegatingDirContext( + secondKeyedObjectPoolMock, delegatingDirContext, + DirContextType.READ_ONLY); + + final DirContext delegateDirContext2 = delegatingDirContext2 + .getDelegateDirContext(); + assertEquals(delegatingDirContext, delegateDirContext2); + + final DirContext innerDelegateDirContext2 = delegatingDirContext2 + .getInnermostDelegateDirContext(); + assertEquals(dirContextMock, innerDelegateDirContext2); + + delegatingDirContext2.assertOpen(); + + // Close the outer wrapper + delegatingDirContext2.close(); + + final DirContext delegateContext2closed = delegatingDirContext2 + .getDelegateDirContext(); + assertNull(delegateContext2closed); + + final DirContext innerDelegateContext2closed = delegatingDirContext2 + .getInnermostDelegateDirContext(); + assertNull(innerDelegateContext2closed); + + try { + delegatingDirContext2.assertOpen(); + fail("delegatingDirContext2.assertOpen() should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + // Close the outer wrapper + delegatingDirContext.close(); + + final DirContext delegateDirContextClosed = delegatingDirContext + .getDelegateDirContext(); + assertNull(delegateDirContextClosed); + + final DirContext innerDelegateDirContextClosed = delegatingDirContext + .getInnermostDelegateDirContext(); + assertNull(innerDelegateDirContextClosed); + + try { + delegatingDirContext.assertOpen(); + fail("delegatingDirContext.assertOpen() should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + secondKeyedObjectPoolControl.verify(); + verify(); + } + + public void testObjectMethods() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + dirContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + // Wrap the DirContext once + final DelegatingDirContext delegatingDirContext = new DelegatingDirContext( + keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY); + assertEquals( + "EasyMock for interface javax.naming.directory.DirContext", + delegatingDirContext.toString()); + delegatingDirContext.hashCode(); // Run it to make sure it doesn't + // fail + + assertTrue(delegatingDirContext.equals(delegatingDirContext)); + assertFalse(delegatingDirContext.equals(new Object())); + + final DelegatingDirContext delegatingDirContext2 = new DelegatingDirContext( + keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY); + assertTrue(delegatingDirContext.equals(delegatingDirContext2)); + assertTrue(delegatingDirContext2.equals(delegatingDirContext)); + assertTrue(delegatingDirContext.equals(dirContextMock)); + + // Close the context and try again + delegatingDirContext.close(); + + assertEquals("DirContext is closed", delegatingDirContext.toString()); + assertEquals(0, delegatingDirContext.hashCode()); // Run it to make + // sure it doesn't + // fail + + assertTrue(delegatingDirContext.equals(delegatingDirContext)); + assertFalse(delegatingDirContext.equals(new Object())); + + assertFalse(delegatingDirContext.equals(delegatingDirContext2)); + assertFalse(delegatingDirContext2.equals(delegatingDirContext)); + assertFalse(delegatingDirContext.equals(dirContextMock)); + + verify(); + } + + // nice + public void testUnsupportedMethods() throws Exception { + replay(); + + final DelegatingDirContext delegatingDirContext = new DelegatingDirContext( + keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY); + + try { + delegatingDirContext.createSubcontext((Name) null, null); + fail("DelegatingDirContext.createSubcontext Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingDirContext.createSubcontext((String) null, null); + fail("DelegatingDirContext.createSubcontext Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingDirContext.getSchema((Name) null); + fail("DelegatingDirContext.getSchema Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingDirContext.getSchema((String) null); + fail("DelegatingDirContext.getSchema Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingDirContext.getSchemaClassDefinition((Name) null); + fail("DelegatingDirContext.getSchemaClassDefinition Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingDirContext.getSchemaClassDefinition((String) null); + fail("DelegatingDirContext.getSchemaClassDefinition Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + + verify(); + } + + public void testAllMethodsOpened() throws Exception { + // override with a nice control + dirContextControl = MockControl.createNiceControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + replay(); + + final DelegatingDirContext delegatingDirContext = new DelegatingDirContext( + keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY); + + delegatingDirContext.bind((Name) null, null, null); + delegatingDirContext.bind((String) null, null, null); + delegatingDirContext.getAttributes((Name) null, null); + delegatingDirContext.getAttributes((Name) null); + delegatingDirContext.getAttributes((String) null, null); + delegatingDirContext.getAttributes((String) null); + delegatingDirContext.modifyAttributes((Name) null, 0, null); + delegatingDirContext.modifyAttributes((Name) null, null); + delegatingDirContext.modifyAttributes((String) null, 0, null); + delegatingDirContext.modifyAttributes((String) null, null); + delegatingDirContext.rebind((Name) null, null, null); + delegatingDirContext.rebind((String) null, null, null); + delegatingDirContext.search((Name) null, (Attributes) null, null); + delegatingDirContext.search((Name) null, null); + delegatingDirContext.search((Name) null, null, null, null); + delegatingDirContext.search((Name) null, (String) null, null); + delegatingDirContext.search((String) null, (Attributes) null, null); + delegatingDirContext.search((String) null, null); + delegatingDirContext.search((String) null, null, null, null); + delegatingDirContext.search((String) null, (String) null, null); + + verify(); + } + + public void testAllMethodsClosed() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + dirContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + final DelegatingDirContext delegatingDirContext = new DelegatingDirContext( + keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY); + + delegatingDirContext.close(); + + try { + delegatingDirContext.bind((Name) null, null, null); + fail("DelegatingDirContext.bind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.bind((String) null, null, null); + fail("DelegatingDirContext.bind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.getAttributes((Name) null, null); + fail("DelegatingDirContext.getAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.getAttributes((Name) null); + fail("DelegatingDirContext.getAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.getAttributes((String) null, null); + fail("DelegatingDirContext.getAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.getAttributes((String) null); + fail("DelegatingDirContext.getAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.modifyAttributes((Name) null, 0, null); + fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.modifyAttributes((Name) null, null); + fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.modifyAttributes((String) null, 0, null); + fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.modifyAttributes((String) null, null); + fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.rebind((Name) null, null, null); + fail("DelegatingDirContext.rebind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.rebind((String) null, null, null); + fail("DelegatingDirContext.rebind should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((Name) null, (Attributes) null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((Name) null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((Name) null, null, null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((Name) null, (String) null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((String) null, (Attributes) null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((String) null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((String) null, null, null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingDirContext.search((String) null, (String) null, null); + fail("DelegatingDirContext.search should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + verify(); + } + + public void testDoubleClose() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + dirContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + final DelegatingDirContext delegatingDirContext = new DelegatingDirContext( + keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY); + + delegatingDirContext.close(); + + // noop close + delegatingDirContext.close(); + + verify(); + } +} \ No newline at end of file diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingLdapContextTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingLdapContextTest.java new file mode 100644 index 00000000..46023958 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/pool/DelegatingLdapContextTest.java @@ -0,0 +1,287 @@ +/* + * Copyright 2005-2007 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.pool; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.pool.KeyedObjectPool; +import org.easymock.MockControl; + +/** + * @author Eric Dalquist eric.dalquist@doit.wisc.edu + */ +public class DelegatingLdapContextTest extends AbstractPoolTestCase { + public void testConstructorAssertions() { + replay(); + + try { + new DelegatingLdapContext(keyedObjectPoolMock, null, + DirContextType.READ_ONLY); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DelegatingLdapContext(keyedObjectPoolMock, ldapContextMock, + null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + verify(); + } + + public void testHelperMethods() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + ldapContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + // Wrap the LdapContext once + final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext( + keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY); + + final DirContext delegateDirContext = delegatingLdapContext + .getDelegateDirContext(); + assertEquals(ldapContextMock, delegateDirContext); + + final LdapContext delegateLdapContext = delegatingLdapContext + .getDelegateLdapContext(); + assertEquals(ldapContextMock, delegateLdapContext); + + final LdapContext innerDelegateLdapContext = delegatingLdapContext + .getInnermostDelegateLdapContext(); + assertEquals(ldapContextMock, innerDelegateLdapContext); + + delegatingLdapContext.assertOpen(); + + // Wrap the wrapper + MockControl secondKeyedObjectPoolControl = MockControl + .createControl(KeyedObjectPool.class); + KeyedObjectPool secondKeyedObjectPoolMock = (KeyedObjectPool) secondKeyedObjectPoolControl + .getMock(); + + secondKeyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + delegatingLdapContext); + secondKeyedObjectPoolControl.setVoidCallable(1); + secondKeyedObjectPoolControl.replay(); + + final DelegatingLdapContext delegatingLdapContext2 = new DelegatingLdapContext( + secondKeyedObjectPoolMock, delegatingLdapContext, + DirContextType.READ_ONLY); + + final LdapContext delegateLdapContext2 = delegatingLdapContext2 + .getDelegateLdapContext(); + assertEquals(delegatingLdapContext, delegateLdapContext2); + + final LdapContext innerDelegateLdapContext2 = delegatingLdapContext2 + .getInnermostDelegateLdapContext(); + assertEquals(ldapContextMock, innerDelegateLdapContext2); + + delegatingLdapContext2.assertOpen(); + + // Close the outer wrapper + delegatingLdapContext2.close(); + + final LdapContext delegateContext2closed = delegatingLdapContext2 + .getDelegateLdapContext(); + assertNull(delegateContext2closed); + + final LdapContext innerDelegateContext2closed = delegatingLdapContext2 + .getInnermostDelegateLdapContext(); + assertNull(innerDelegateContext2closed); + + try { + delegatingLdapContext2.assertOpen(); + fail("delegatingLdapContext2.assertOpen() should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + // Close the outer wrapper + delegatingLdapContext.close(); + + final LdapContext delegateLdapContextClosed = delegatingLdapContext + .getDelegateLdapContext(); + assertNull(delegateLdapContextClosed); + + final LdapContext innerDelegateLdapContextClosed = delegatingLdapContext + .getInnermostDelegateLdapContext(); + assertNull(innerDelegateLdapContextClosed); + + try { + delegatingLdapContext.assertOpen(); + fail("delegatingLdapContext.assertOpen() should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + + secondKeyedObjectPoolControl.verify(); + verify(); + } + + public void testObjectMethods() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + ldapContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + // Wrap the LdapContext once + final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext( + keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY); + assertEquals("EasyMock for interface javax.naming.ldap.LdapContext", + delegatingLdapContext.toString()); + delegatingLdapContext.hashCode(); // Run it to make sure it doesn't fail + + assertTrue(delegatingLdapContext.equals(delegatingLdapContext)); + assertFalse(delegatingLdapContext.equals(new Object())); + + final DelegatingLdapContext delegatingLdapContext2 = new DelegatingLdapContext( + keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY); + assertTrue(delegatingLdapContext.equals(delegatingLdapContext2)); + assertTrue(delegatingLdapContext2.equals(delegatingLdapContext)); + assertTrue(delegatingLdapContext.equals(ldapContextMock)); + + // Close the context and try again + delegatingLdapContext.close(); + + assertEquals("LdapContext is closed", delegatingLdapContext.toString()); + assertEquals(0, delegatingLdapContext.hashCode()); // Run it to make + // sure it doesn't + // fail + + assertTrue(delegatingLdapContext.equals(delegatingLdapContext)); + assertFalse(delegatingLdapContext.equals(new Object())); + + assertFalse(delegatingLdapContext.equals(delegatingLdapContext2)); + assertFalse(delegatingLdapContext2.equals(delegatingLdapContext)); + assertFalse(delegatingLdapContext.equals(ldapContextMock)); + + verify(); + } + + public void testUnsupportedMethods() throws Exception { + replay(); + + final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext( + keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY); + + try { + delegatingLdapContext.newInstance(null); + fail("DelegatingLdapContext.newInstance Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingLdapContext.reconnect(null); + fail("DelegatingLdapContext.reconnect Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + try { + delegatingLdapContext.setRequestControls(null); + fail("DelegatingLdapContext.setRequestControls Should have thrown an UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // Expected + } + + verify(); + } + + // nice + public void testAllMethodsOpened() throws Exception { + MockControl ldapContextControl = MockControl.createNiceControl(LdapContext.class); + LdapContext ldapContextMock = (LdapContext) ldapContextControl.getMock(); + + ldapContextControl.replay(); + replay(); + + final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext( + keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY); + + delegatingLdapContext.extendedOperation(null); + delegatingLdapContext.getConnectControls(); + delegatingLdapContext.getRequestControls(); + delegatingLdapContext.getResponseControls(); + + ldapContextControl.verify(); + verify(); + } + + public void testAllMethodsClosed() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + ldapContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext( + keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY); + + delegatingLdapContext.close(); + + try { + delegatingLdapContext.extendedOperation(null); + fail("DelegatingLdapContext.extendedOperation should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingLdapContext.getConnectControls(); + fail("DelegatingLdapContext.getConnectControls should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingLdapContext.getRequestControls(); + fail("DelegatingLdapContext.getRequestControls should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + try { + delegatingLdapContext.getResponseControls(); + fail("DelegatingLdapContext.getResponseControls should have thrown a NamingException"); + } catch (NamingException ne) { + // Expected + } + verify(); + } + + public void testDoubleClose() throws Exception { + keyedObjectPoolMock.returnObject(DirContextType.READ_ONLY, + ldapContextMock); + keyedObjectPoolControl.setVoidCallable(1); + + replay(); + + final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext( + keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY); + + delegatingLdapContext.close(); + + // noop close + delegatingLdapContext.close(); + + verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/pool/factory/DirContextPoolableObjectFactoryTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/pool/factory/DirContextPoolableObjectFactoryTest.java new file mode 100644 index 00000000..fe267467 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/pool/factory/DirContextPoolableObjectFactoryTest.java @@ -0,0 +1,243 @@ +/* + * Copyright 2005-2007 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.pool.factory; + +import javax.naming.directory.DirContext; + +import org.easymock.MockControl; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.pool.AbstractPoolTestCase; +import org.springframework.ldap.pool.DirContextType; +import org.springframework.ldap.pool.validation.DirContextValidator; + +/** + * @author Eric Dalquist + */ +public class DirContextPoolableObjectFactoryTest extends AbstractPoolTestCase { + + public void testProperties() throws Exception { + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + + replay(); + + try { + objectFactory.setContextSource(null); + fail("DirContextPoolableObjectFactory.setContextSource should have thrown an IllegalArgumentException"); + } + catch (IllegalArgumentException iae) { + // Expected + } + + objectFactory.setContextSource(contextSourceMock); + final ContextSource contextSource2 = objectFactory.getContextSource(); + assertEquals(contextSourceMock, contextSource2); + + + try { + objectFactory.setDirContextValidator(null); + fail("DirContextPoolableObjectFactory.setDirContextValidator should have thrown an IllegalArgumentException"); + } + catch (IllegalArgumentException iae) { + // Expected + } + + objectFactory.setDirContextValidator(dirContextValidatorMock); + final DirContextValidator dirContextValidator2 = objectFactory.getDirContextValidator(); + assertEquals(dirContextValidatorMock, dirContextValidator2); + verify(); + } + + public void testMakeObjectAssertions() throws Exception { + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + + replay(); + + try { + objectFactory.makeObject(DirContextType.READ_ONLY); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + objectFactory.setContextSource(contextSourceMock); + + try { + objectFactory.makeObject(null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + verify(); + } + + public void testMakeObjectReadOnly() throws Exception { + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + + MockControl readOnlyContextControl = MockControl.createControl(DirContext.class); + DirContext readOnlyContextMock = (DirContext) readOnlyContextControl.getMock(); + + readOnlyContextControl.replay(); + + contextSourceControl.expectAndReturn(contextSourceMock.getReadOnlyContext(), readOnlyContextMock, 1); + objectFactory.setContextSource(contextSourceMock); + + replay(); + + final Object createdDirContext = objectFactory.makeObject(DirContextType.READ_ONLY); + + readOnlyContextControl.verify(); + verify(); + assertEquals(readOnlyContextMock, createdDirContext); + } + + public void testMakeObjectReadWrite() throws Exception { + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + + MockControl readWriteContextControl = MockControl.createControl(DirContext.class); + DirContext readWriteContextMock = (DirContext) readWriteContextControl.getMock(); + + readWriteContextControl.replay(); + + contextSourceControl.expectAndReturn(contextSourceMock.getReadWriteContext(), readWriteContextMock, 1); + objectFactory.setContextSource(contextSourceMock); + + replay(); + + final Object createdDirContext = objectFactory.makeObject(DirContextType.READ_WRITE); + + readWriteContextControl.verify(); + verify(); + assertEquals(readWriteContextMock, createdDirContext); + } + + public void testValidateObjectAssertions() throws Exception { + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + + replay(); + + try { + objectFactory.validateObject(DirContextType.READ_ONLY, dirContextMock); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + objectFactory.setDirContextValidator(dirContextValidatorMock); + + try { + objectFactory.validateObject(null, dirContextMock); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + objectFactory.validateObject(new Object(), dirContextMock); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + objectFactory.validateObject(DirContextType.READ_ONLY, null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + objectFactory.validateObject(DirContextType.READ_ONLY, new Object()); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + verify(); + } + + public void testValidateObject() throws Exception { + dirContextValidatorControl.expectAndReturn(dirContextValidatorMock + .validateDirContext(DirContextType.READ_ONLY, dirContextMock), + true); + + replay(); + + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + objectFactory.setDirContextValidator(dirContextValidatorMock); + + final boolean valid = objectFactory.validateObject(DirContextType.READ_ONLY, dirContextMock); + assertTrue(valid); + + //Check exception in validator + MockControl secondDirContextValidatorControl = MockControl.createControl(DirContextValidator.class); + DirContextValidator secondDirContextValidatorMock = (DirContextValidator) secondDirContextValidatorControl.getMock(); + + secondDirContextValidatorControl.expectAndThrow(secondDirContextValidatorMock.validateDirContext(DirContextType.READ_ONLY, dirContextMock), new RuntimeException("Failed to validate")); + secondDirContextValidatorControl.replay(); + objectFactory.setDirContextValidator(secondDirContextValidatorMock); + + final boolean valid2 = objectFactory.validateObject(DirContextType.READ_ONLY, dirContextMock); + assertFalse(valid2); + + secondDirContextValidatorControl.verify(); + verify(); + } + + public void testDestroyObjectAssertions() throws Exception { + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + + replay(); + + try { + objectFactory.destroyObject(DirContextType.READ_ONLY, null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + objectFactory.validateObject(DirContextType.READ_ONLY, new Object()); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + verify(); + } + + public void testDestroyObject() throws Exception { + dirContextMock.close(); + dirContextControl.setVoidCallable(1); + + replay(); + + final DirContextPoolableObjectFactory objectFactory = new DirContextPoolableObjectFactory(); + + objectFactory.destroyObject(DirContextType.READ_ONLY, dirContextMock); + + MockControl throwingDirContextControl = MockControl.createControl(DirContext.class); + DirContext throwingDirContextMock = (DirContext) throwingDirContextControl.getMock(); + + throwingDirContextMock.close(); + throwingDirContextControl.setThrowable(new RuntimeException("Failed to close")); + throwingDirContextControl.replay(); + + objectFactory.destroyObject(DirContextType.READ_ONLY, throwingDirContextMock); + throwingDirContextControl.verify(); + verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/pool/factory/PoolingContextSourceTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/pool/factory/PoolingContextSourceTest.java new file mode 100644 index 00000000..00b8dc2d --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/pool/factory/PoolingContextSourceTest.java @@ -0,0 +1,273 @@ +/* + * Copyright 2005-2007 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.pool.factory; + +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.pool.impl.GenericKeyedObjectPool; +import org.easymock.MockControl; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.pool.AbstractPoolTestCase; +import org.springframework.ldap.pool.validation.DirContextValidator; + +/** + * @author Eric Dalquist + */ +public class PoolingContextSourceTest extends AbstractPoolTestCase { + + public void testProperties() throws Exception { + final PoolingContextSource poolingContextSource = new PoolingContextSource(); + + replay(); + + try { + poolingContextSource.setContextSource(null); + fail("PoolingContextSource.setBaseName should have thrown an IllegalArgumentException"); + } + catch (IllegalArgumentException iae) { + // Expected + } + poolingContextSource.setContextSource(contextSourceMock); + final ContextSource contextSource2 = poolingContextSource.getContextSource(); + assertEquals(contextSourceMock, contextSource2); + + try { + poolingContextSource.setDirContextValidator(null); + fail("PoolingContextSource.setDirContextValidator should have thrown an IllegalArgumentException"); + } + catch (IllegalArgumentException iae) { + // Expected + } + poolingContextSource.setDirContextValidator(dirContextValidatorMock); + final DirContextValidator dirContextValidator2 = poolingContextSource.getDirContextValidator(); + assertEquals(dirContextValidatorMock, dirContextValidator2); + + poolingContextSource.setMaxActive(1000); + final int maxActive = poolingContextSource.getMaxActive(); + assertEquals(1000, maxActive); + + poolingContextSource.setMaxIdle(500); + final int maxIdle = poolingContextSource.getMaxIdle(); + assertEquals(500, maxIdle); + + poolingContextSource.setMaxTotal(5000); + final int maxTotal = poolingContextSource.getMaxTotal(); + assertEquals(5000, maxTotal); + + poolingContextSource.setMaxWait(2000L); + final long maxWait = poolingContextSource.getMaxWait(); + assertEquals(2000L, maxWait); + + poolingContextSource.setMinEvictableIdleTimeMillis(60000L); + final long minEvictableIdleTimeMillis = poolingContextSource.getMinEvictableIdleTimeMillis(); + assertEquals(60000L, minEvictableIdleTimeMillis); + + poolingContextSource.setMinIdle(100); + final int minIdle = poolingContextSource.getMinIdle(); + assertEquals(100, minIdle); + + poolingContextSource.setNumTestsPerEvictionRun(5); + final int numTestsPerEvictionRun = poolingContextSource.getNumTestsPerEvictionRun(); + assertEquals(5, numTestsPerEvictionRun); + + poolingContextSource.setTestOnBorrow(true); + final boolean testOnBorrow = poolingContextSource.getTestOnBorrow(); + assertEquals(true, testOnBorrow); + + poolingContextSource.setTestOnReturn(true); + final boolean testOnReturn = poolingContextSource.getTestOnReturn(); + assertEquals(true, testOnReturn); + + poolingContextSource.setTestWhileIdle(true); + final boolean testWhileIdle = poolingContextSource.getTestWhileIdle(); + assertEquals(true, testWhileIdle); + + poolingContextSource.setTimeBetweenEvictionRunsMillis(120000L); + final long timeBetweenEvictionRunsMillis = poolingContextSource.getTimeBetweenEvictionRunsMillis(); + assertEquals(120000L, timeBetweenEvictionRunsMillis); + + poolingContextSource.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK); + final byte whenExhaustedAction = poolingContextSource.getWhenExhaustedAction(); + assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK, whenExhaustedAction); + + final int numActive = poolingContextSource.getNumActive(); + assertEquals(0, numActive); + + final int numIdle = poolingContextSource.getNumIdle(); + assertEquals(0, numIdle); + } + + public void testGetReadOnlyContextPool() throws Exception { + MockControl secondDirContextControl = MockControl.createControl(DirContext.class); + DirContext secondDirContextMock = (DirContext) secondDirContextControl.getMock(); + + contextSourceControl.expectAndReturn(contextSourceMock.getReadOnlyContext(), dirContextMock); + contextSourceControl.expectAndReturn(contextSourceMock.getReadOnlyContext(), secondDirContextMock); + + replay(); + + final PoolingContextSource poolingContextSource = new PoolingContextSource(); + poolingContextSource.setContextSource(contextSourceMock); + + //Get a context + final DirContext readOnlyContext1 = poolingContextSource.getReadOnlyContext(); + assertEquals(readOnlyContext1, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Close the context + readOnlyContext1.close(); + assertEquals(0, poolingContextSource.getNumActive()); + assertEquals(1, poolingContextSource.getNumIdle()); + + //Get the context again + final DirContext readOnlyContext2 = poolingContextSource.getReadOnlyContext(); + assertEquals(readOnlyContext2, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Get a new context + final DirContext readOnlyContext3 = poolingContextSource.getReadOnlyContext(); + assertEquals(readOnlyContext3, secondDirContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(2, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Close context + readOnlyContext2.close(); + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(1, poolingContextSource.getNumIdle()); + + //Close context + readOnlyContext3.close(); + assertEquals(0, poolingContextSource.getNumActive()); + assertEquals(2, poolingContextSource.getNumIdle()); + } + + public void testGetReadWriteContextPool() throws Exception { + MockControl secondDirContextControl = MockControl.createControl(DirContext.class); + DirContext secondDirContextMock = (DirContext) secondDirContextControl.getMock(); + + contextSourceControl.expectAndReturn(contextSourceMock.getReadWriteContext(), dirContextMock); + contextSourceControl.expectAndReturn(contextSourceMock.getReadWriteContext(), secondDirContextMock); + + replay(); + + final PoolingContextSource poolingContextSource = new PoolingContextSource(); + poolingContextSource.setContextSource(contextSourceMock); + + //Get a context + final DirContext readOnlyContext1 = poolingContextSource.getReadWriteContext(); + assertEquals(readOnlyContext1, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Close the context + readOnlyContext1.close(); + assertEquals(0, poolingContextSource.getNumActive()); + assertEquals(1, poolingContextSource.getNumIdle()); + + //Get the context again + final DirContext readOnlyContext2 = poolingContextSource.getReadWriteContext(); + assertEquals(readOnlyContext2, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Get a new context + final DirContext readOnlyContext3 = poolingContextSource.getReadWriteContext(); + assertEquals(readOnlyContext3, secondDirContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(2, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Close context + readOnlyContext2.close(); + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(1, poolingContextSource.getNumIdle()); + + //Close context + readOnlyContext3.close(); + assertEquals(0, poolingContextSource.getNumActive()); + assertEquals(2, poolingContextSource.getNumIdle()); + } + + public void testGetContextException() throws Exception { + contextSourceControl.expectAndThrow(contextSourceMock.getReadWriteContext(), new RuntimeException("Problem getting context")); + + replay(); + + final PoolingContextSource poolingContextSource = new PoolingContextSource(); + poolingContextSource.setContextSource(contextSourceMock); + + try { + poolingContextSource.getReadWriteContext(); + fail("PoolingContextSource.getReadWriteContext should have thrown DataAccessResourceFailureException"); + } + catch (DataAccessResourceFailureException darfe) { + // Expected + } + } + + public void testGetReadOnlyLdapContext() throws Exception { + MockControl secondLdapContextControl = MockControl.createControl(LdapContext.class); + LdapContext secondLdapContextMock = (LdapContext) secondLdapContextControl.getMock(); + + secondLdapContextControl.replay(); + + contextSourceControl.expectAndReturn(contextSourceMock.getReadOnlyContext(), ldapContextMock); + contextSourceControl.expectAndReturn(contextSourceMock.getReadOnlyContext(), secondLdapContextMock); + + replay(); + + final PoolingContextSource poolingContextSource = new PoolingContextSource(); + poolingContextSource.setContextSource(contextSourceMock); + + //Get a context + final DirContext readOnlyContext1 = poolingContextSource.getReadOnlyContext(); + assertEquals(readOnlyContext1, ldapContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Close the context + readOnlyContext1.close(); + assertEquals(0, poolingContextSource.getNumActive()); + assertEquals(1, poolingContextSource.getNumIdle()); + + //Get the context again + final DirContext readOnlyContext2 = poolingContextSource.getReadOnlyContext(); + assertEquals(readOnlyContext2, ldapContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Get a new context + final DirContext readOnlyContext3 = poolingContextSource.getReadOnlyContext(); + assertEquals(readOnlyContext3, secondLdapContextMock); //Order reversed because the 'wrapper' has the needed equals logic + assertEquals(2, poolingContextSource.getNumActive()); + assertEquals(0, poolingContextSource.getNumIdle()); + + //Close context + readOnlyContext2.close(); + assertEquals(1, poolingContextSource.getNumActive()); + assertEquals(1, poolingContextSource.getNumIdle()); + + //Close context + readOnlyContext3.close(); + assertEquals(0, poolingContextSource.getNumActive()); + assertEquals(2, poolingContextSource.getNumIdle()); + + secondLdapContextControl.verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/pool/validation/DefaultDirContextValidatorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/pool/validation/DefaultDirContextValidatorTest.java new file mode 100644 index 00000000..2234622a --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/pool/validation/DefaultDirContextValidatorTest.java @@ -0,0 +1,187 @@ +/* + * Copyright 2005-2007 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.pool.validation; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.pool.DirContextType; +import org.springframework.ldap.pool.validation.DefaultDirContextValidator; + +/** + * @author Eric Dalquist eric.dalquist@doit.wisc.edu + */ +public class DefaultDirContextValidatorTest extends TestCase { + + private MockControl namingEnumerationControl; + + private NamingEnumeration namingEnumerationMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + protected void setUp() throws Exception { + super.setUp(); + namingEnumerationControl = MockControl + .createControl(NamingEnumeration.class); + namingEnumerationMock = (NamingEnumeration) namingEnumerationControl + .getMock(); + + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + namingEnumerationControl = null; + namingEnumerationMock = null; + + dirContextControl = null; + dirContextMock = null; + } + + protected void replay() { + namingEnumerationControl.replay(); + dirContextControl.replay(); + } + + protected void verify() { + namingEnumerationControl.verify(); + dirContextControl.verify(); + } + + public void testProperties() throws Exception { + final DefaultDirContextValidator dirContextValidator = new DefaultDirContextValidator(); + + dirContextValidator.setBase("baseName"); + final String baseName = dirContextValidator.getBase(); + assertEquals("baseName", baseName); + + try { + dirContextValidator.setFilter(null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + dirContextValidator.setFilter("filter"); + final String filter = dirContextValidator.getFilter(); + assertEquals("filter", filter); + + try { + dirContextValidator.setSearchControls(null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + final SearchControls sc = new SearchControls(); + dirContextValidator.setSearchControls(sc); + final SearchControls sc2 = dirContextValidator.getSearchControls(); + assertEquals(sc, sc2); + } + + public void testValidateDirContextAssertions() throws Exception { + final DefaultDirContextValidator dirContextValidator = new DefaultDirContextValidator(); + + try { + dirContextValidator.validateDirContext(DirContextType.READ_ONLY, + null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + replay(); + + try { + dirContextValidator.validateDirContext(null, dirContextMock); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testValidateDirContextHasResult() throws Exception { + final DefaultDirContextValidator dirContextValidator = new DefaultDirContextValidator(); + + final String baseName = dirContextValidator.getBase(); + final String filter = dirContextValidator.getFilter(); + final SearchControls searchControls = dirContextValidator + .getSearchControls(); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), true); + dirContextControl.expectAndReturn(dirContextMock.search(baseName, + filter, searchControls), namingEnumerationMock); + + replay(); + + final boolean valid = dirContextValidator.validateDirContext( + DirContextType.READ_ONLY, dirContextMock); + + verify(); + assertTrue(valid); + } + + public void testValidateDirContextNoResult() throws Exception { + final DefaultDirContextValidator dirContextValidator = new DefaultDirContextValidator(); + + final String baseName = dirContextValidator.getBase(); + final String filter = dirContextValidator.getFilter(); + final SearchControls searchControls = dirContextValidator + .getSearchControls(); + + namingEnumerationControl.expectAndReturn(namingEnumerationMock + .hasMore(), false); + dirContextControl.expectAndReturn(dirContextMock.search(baseName, + filter, searchControls), namingEnumerationMock); + + replay(); + + final boolean valid = dirContextValidator.validateDirContext( + DirContextType.READ_ONLY, dirContextMock); + + verify(); + assertFalse(valid); + } + + public void testValidateDirContextException() throws Exception { + final DefaultDirContextValidator dirContextValidator = new DefaultDirContextValidator(); + + final String baseName = dirContextValidator.getBase(); + final String filter = dirContextValidator.getFilter(); + final SearchControls searchControls = dirContextValidator + .getSearchControls(); + + dirContextControl.expectAndThrow(dirContextMock.search(baseName, + filter, searchControls), + new NamingException("Failed to search")); + + replay(); + + final boolean valid = dirContextValidator.validateDirContext( + DirContextType.READ_ONLY, dirContextMock); + + verify(); + assertFalse(valid); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/BindOperationExecutorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/BindOperationExecutorTest.java new file mode 100644 index 00000000..116a8458 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/BindOperationExecutorTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.directory.BasicAttributes; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.BindOperationExecutor; + +public class BindOperationExecutorTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + } + + public void testPerformOperation() { + DistinguishedName expectedDn = new DistinguishedName("cn=john doe"); + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + BindOperationExecutor tested = new BindOperationExecutor( + ldapOperationsMock, expectedDn, expectedObject, + expectedAttributes); + + ldapOperationsMock.bind(expectedDn, expectedObject, expectedAttributes); + + replay(); + // perform teste + tested.performOperation(); + verify(); + } + + public void testCommit() { + DistinguishedName expectedDn = new DistinguishedName("cn=john doe"); + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + BindOperationExecutor tested = new BindOperationExecutor( + ldapOperationsMock, expectedDn, expectedObject, + expectedAttributes); + + // Nothing to do here. + + replay(); + // perform teste + tested.commit(); + verify(); + } + + public void testRollback() { + DistinguishedName expectedDn = new DistinguishedName("cn=john doe"); + BindOperationExecutor tested = new BindOperationExecutor( + ldapOperationsMock, expectedDn, null, null); + + ldapOperationsMock.unbind(expectedDn); + + replay(); + // perform teste + tested.rollback(); + verify(); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/BindOperationRecorderTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/BindOperationRecorderTest.java new file mode 100644 index 00000000..414cf291 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/BindOperationRecorderTest.java @@ -0,0 +1,98 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.directory.BasicAttributes; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.BindOperationExecutor; +import org.springframework.ldap.transaction.compensating.BindOperationRecorder; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +public class BindOperationRecorderTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + } + + public void testRecordOperation_DistinguishedName() { + BindOperationRecorder tested = new BindOperationRecorder( + ldapOperationsMock); + DistinguishedName expectedDn = new DistinguishedName("cn=John Doe"); + + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + // Perform test. + CompensatingTransactionOperationExecutor operation = tested + .recordOperation(new Object[] { expectedDn, expectedObject, + expectedAttributes }); + + assertTrue(operation instanceof BindOperationExecutor); + BindOperationExecutor rollbackOperation = (BindOperationExecutor) operation; + assertSame(expectedDn, rollbackOperation.getDn()); + assertSame(ldapOperationsMock, rollbackOperation.getLdapOperations()); + assertSame(expectedObject, rollbackOperation.getOriginalObject()); + assertSame(expectedAttributes, rollbackOperation + .getOriginalAttributes()); + } + + public void testPerformOperation_String() { + BindOperationRecorder tested = new BindOperationRecorder( + ldapOperationsMock); + String expectedDn = "cn=John Doe"; + + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + // Perform test. + CompensatingTransactionOperationExecutor operation = tested + .recordOperation(new Object[] { expectedDn, expectedObject, + expectedAttributes }); + + assertTrue(operation instanceof BindOperationExecutor); + BindOperationExecutor rollbackOperation = (BindOperationExecutor) operation; + assertEquals(expectedDn, rollbackOperation.getDn().toString()); + assertSame(ldapOperationsMock, rollbackOperation.getLdapOperations()); + } + + public void testPerformOperation_Invalid() { + BindOperationRecorder tested = new BindOperationRecorder( + ldapOperationsMock); + Object expectedDn = new Object(); + + try { + // Perform test. + tested.recordOperation(new Object[] { expectedDn }); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/LdapCompensatingTransactionOperationFactoryTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/LdapCompensatingTransactionOperationFactoryTest.java new file mode 100644 index 00000000..2a768b50 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/LdapCompensatingTransactionOperationFactoryTest.java @@ -0,0 +1,134 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.directory.DirContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; + +public class LdapCompensatingTransactionOperationFactoryTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + private MockControl renamingStrategyControl; + + private TempEntryRenamingStrategy renamingStrategyMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + private LdapCompensatingTransactionOperationFactory tested; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + renamingStrategyControl = MockControl + .createControl(TempEntryRenamingStrategy.class); + renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl + .getMock(); + + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + tested = new LdapCompensatingTransactionOperationFactory( + renamingStrategyMock) { + + LdapOperations createLdapOperationsInstance(DirContext ctx) { + assertEquals(dirContextMock, ctx); + return ldapOperationsMock; + } + }; + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + + renamingStrategyControl = null; + renamingStrategyMock = null; + + dirContextControl = null; + dirContextMock = null; + + tested = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + renamingStrategyControl.replay(); + dirContextControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + renamingStrategyControl.verify(); + dirContextControl.verify(); + } + + public void testGetRecordingOperation_Bind() throws Exception { + + CompensatingTransactionOperationRecorder result = tested + .createRecordingOperation(dirContextMock, "bind"); + assertTrue(result instanceof BindOperationRecorder); + BindOperationRecorder bindOperationRecorder = (BindOperationRecorder) result; + assertSame(ldapOperationsMock, bindOperationRecorder + .getLdapOperations()); + } + + public void testGetRecordingOperation_Rebind() throws Exception { + CompensatingTransactionOperationRecorder result = tested + .createRecordingOperation(dirContextMock, "rebind"); + assertTrue(result instanceof RebindOperationRecorder); + RebindOperationRecorder rebindOperationRecorder = (RebindOperationRecorder) result; + assertSame(ldapOperationsMock, rebindOperationRecorder + .getLdapOperations()); + assertSame(renamingStrategyMock, rebindOperationRecorder + .getRenamingStrategy()); + } + + public void testGetRecordingOperation_Rename() throws Exception { + CompensatingTransactionOperationRecorder result = tested + .createRecordingOperation(dirContextMock, "rename"); + assertTrue(result instanceof RenameOperationRecorder); + RenameOperationRecorder recordingOperation = (RenameOperationRecorder) result; + assertSame(ldapOperationsMock, recordingOperation.getLdapOperations()); + } + + public void testGetRecordingOperation_ModifyAttributes() throws Exception { + CompensatingTransactionOperationRecorder result = tested + .createRecordingOperation(dirContextMock, "modifyAttributes"); + assertTrue(result instanceof ModifyAttributesOperationRecorder); + ModifyAttributesOperationRecorder recordingOperation = (ModifyAttributesOperationRecorder) result; + assertSame(ldapOperationsMock, recordingOperation.getLdapOperations()); + } + + public void testGetRecordingOperation_Unbind() throws Exception { + CompensatingTransactionOperationRecorder result = tested + .createRecordingOperation(dirContextMock, "unbind"); + assertTrue(result instanceof UnbindOperationRecorder); + UnbindOperationRecorder recordingOperation = (UnbindOperationRecorder) result; + assertSame(ldapOperationsMock, recordingOperation.getLdapOperations()); + assertSame(renamingStrategyMock, recordingOperation + .getRenamingStrategy()); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/LdapTransactionUtilsTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/LdapTransactionUtilsTest.java new file mode 100644 index 00000000..5eed68e4 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/LdapTransactionUtilsTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.support.LdapUtils; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class LdapTransactionUtilsTest extends TestCase { + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + protected void setUp() throws Exception { + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + } + + protected void tearDown() throws Exception { + dirContextControl = null; + dirContextMock = null; + } + + protected void replay() { + dirContextControl.replay(); + } + + protected void verify() { + dirContextControl.verify(); + } + + public void testCloseContext() throws NamingException { + dirContextMock.close(); + + replay(); + LdapUtils.closeContext(dirContextMock); + verify(); + } + + public void testCloseContext_NullContext() throws NamingException { + replay(); + LdapUtils.closeContext(null); + verify(); + } + + public void testIsSupportedWriteTransactionOperation() { + assertTrue(LdapTransactionUtils + .isSupportedWriteTransactionOperation("bind")); + assertTrue(LdapTransactionUtils + .isSupportedWriteTransactionOperation("rebind")); + assertTrue(LdapTransactionUtils + .isSupportedWriteTransactionOperation("unbind")); + assertTrue(LdapTransactionUtils + .isSupportedWriteTransactionOperation("modifyAttributes")); + assertTrue(LdapTransactionUtils + .isSupportedWriteTransactionOperation("rename")); + assertFalse(LdapTransactionUtils + .isSupportedWriteTransactionOperation("lookup")); + assertFalse(LdapTransactionUtils + .isSupportedWriteTransactionOperation("search")); + } + + public void dummyMethod() { + + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationExecutorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationExecutorTest.java new file mode 100644 index 00000000..5a5e4928 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationExecutorTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.Name; +import javax.naming.directory.ModificationItem; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.ModifyAttributesOperationExecutor; + +import junit.framework.TestCase; + +public class ModifyAttributesOperationExecutorTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + } + + public void testPerformOperation() { + ModificationItem[] expectedCompensatingItems = new ModificationItem[0]; + ModificationItem[] expectedActualItems = new ModificationItem[0]; + + Name expectedDn = new DistinguishedName("cn=john doe"); + + ModifyAttributesOperationExecutor tested = new ModifyAttributesOperationExecutor( + ldapOperationsMock, expectedDn, expectedActualItems, + expectedCompensatingItems); + + ldapOperationsMock.modifyAttributes(expectedDn, expectedActualItems); + + replay(); + // Perform test + tested.performOperation(); + + verify(); + } + + public void testCommit() { + ModificationItem[] expectedCompensatingItems = new ModificationItem[0]; + ModificationItem[] expectedActualItems = new ModificationItem[0]; + + Name expectedDn = new DistinguishedName("cn=john doe"); + + ModifyAttributesOperationExecutor tested = new ModifyAttributesOperationExecutor( + ldapOperationsMock, expectedDn, expectedActualItems, + expectedCompensatingItems); + + // No operation here + + replay(); + // Perform test + tested.commit(); + + verify(); + } + + public void testRollback() { + ModificationItem[] expectedCompensatingItems = new ModificationItem[0]; + ModificationItem[] expectedActualItems = new ModificationItem[0]; + + Name expectedDn = new DistinguishedName("cn=john doe"); + + ModifyAttributesOperationExecutor tested = new ModifyAttributesOperationExecutor( + ldapOperationsMock, expectedDn, expectedActualItems, + expectedCompensatingItems); + + ldapOperationsMock.modifyAttributes(expectedDn, + expectedCompensatingItems); + + replay(); + // Perform test + tested.rollback(); + + verify(); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationRecorderTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationRecorderTest.java new file mode 100644 index 00000000..6da35c9f --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/ModifyAttributesOperationRecorderTest.java @@ -0,0 +1,271 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.ModifyAttributesOperationExecutor; +import org.springframework.ldap.transaction.compensating.ModifyAttributesOperationRecorder; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +public class ModifyAttributesOperationRecorderTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + private MockControl attributesMapperControl; + + private AttributesMapper attributesMapperMock; + + private ModifyAttributesOperationRecorder tested; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + attributesMapperControl = MockControl + .createControl(AttributesMapper.class); + attributesMapperMock = (AttributesMapper) attributesMapperControl + .getMock(); + + tested = new ModifyAttributesOperationRecorder(ldapOperationsMock); + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + + attributesMapperControl = null; + attributesMapperMock = null; + + tested = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + attributesMapperControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + attributesMapperControl.verify(); + } + + public void testRecordOperation() { + final ModificationItem incomingItem = new ModificationItem( + DirContext.ADD_ATTRIBUTE, new BasicAttribute("attribute1")); + ModificationItem[] incomingMods = new ModificationItem[] { incomingItem }; + final ModificationItem compensatingItem = new ModificationItem( + DirContext.ADD_ATTRIBUTE, new BasicAttribute("attribute2")); + + final Attributes expectedAttributes = new BasicAttributes(); + + tested = new ModifyAttributesOperationRecorder(ldapOperationsMock) { + AttributesMapper getAttributesMapper() { + return attributesMapperMock; + } + + protected ModificationItem getCompensatingModificationItem( + Attributes originalAttributes, + ModificationItem modificationItem) { + assertSame(expectedAttributes, originalAttributes); + assertSame(incomingItem, modificationItem); + return compensatingItem; + } + }; + + DistinguishedName expectedName = new DistinguishedName("cn=john doe"); + ldapOperationsControl.setDefaultMatcher(MockControl.ARRAY_MATCHER); + ldapOperationsControl.expectAndReturn(ldapOperationsMock.lookup( + expectedName, new String[] { "attribute1" }, + attributesMapperMock), expectedAttributes); + + replay(); + // Perform test + CompensatingTransactionOperationExecutor operation = tested + .recordOperation(new Object[] { expectedName, incomingMods }); + verify(); + + // Verify outcome + assertTrue(operation instanceof ModifyAttributesOperationExecutor); + ModifyAttributesOperationExecutor rollbackOperation = (ModifyAttributesOperationExecutor) operation; + assertSame(expectedName, rollbackOperation.getDn()); + assertSame(ldapOperationsMock, rollbackOperation.getLdapOperations()); + assertSame(incomingMods, rollbackOperation.getActualModifications()); + assertEquals(1, rollbackOperation.getCompensatingModifications().length); + assertSame(compensatingItem, rollbackOperation + .getCompensatingModifications()[0]); + } + + public void testGetCompensatingModificationItem_RemoveFullExistingAttribute() + throws NamingException { + BasicAttribute attribute = new BasicAttribute("someattr"); + attribute.add("value1"); + attribute.add("value2"); + Attributes attributes = new BasicAttributes(); + attributes.put(attribute); + + ModificationItem originalItem = new ModificationItem( + DirContext.REMOVE_ATTRIBUTE, new BasicAttribute("someattr")); + + // Perform test + ModificationItem result = tested.getCompensatingModificationItem( + attributes, originalItem); + + // Verify result + assertEquals(DirContext.ADD_ATTRIBUTE, result.getModificationOp()); + Attribute resultAttribute = result.getAttribute(); + assertEquals("someattr", resultAttribute.getID()); + Object object = resultAttribute.get(0); + assertEquals("value1", object); + assertEquals("value2", resultAttribute.get(1)); + } + + public void testGetCompensatingModificationItem_RemoveTwoAttributeValues() + throws NamingException { + BasicAttribute attribute = new BasicAttribute("someattr"); + attribute.add("value1"); + attribute.add("value2"); + attribute.add("value3"); + Attributes attributes = new BasicAttributes(); + attributes.put(attribute); + + BasicAttribute modificationAttribute = new BasicAttribute("someattr"); + modificationAttribute.add("value1"); + modificationAttribute.add("value2"); + ModificationItem originalItem = new ModificationItem( + DirContext.REMOVE_ATTRIBUTE, modificationAttribute); + + // Perform test + ModificationItem result = tested.getCompensatingModificationItem( + attributes, originalItem); + + // Verify result + assertEquals(DirContext.ADD_ATTRIBUTE, result.getModificationOp()); + Attribute resultAttribute = result.getAttribute(); + assertEquals("someattr", resultAttribute.getID()); + Object object = resultAttribute.get(0); + assertEquals("value1", object); + assertEquals("value2", resultAttribute.get(1)); + } + + public void testGetCompensatingModificationItem_ReplaceExistingAttribute() + throws NamingException { + BasicAttribute attribute = new BasicAttribute("someattr"); + attribute.add("value1"); + attribute.add("value2"); + Attributes attributes = new BasicAttributes(); + attributes.put(attribute); + + BasicAttribute modificationAttribute = new BasicAttribute("someattr"); + modificationAttribute.add("newvalue1"); + modificationAttribute.add("newvalue2"); + ModificationItem originalItem = new ModificationItem( + DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("someattr")); + + // Perform test + ModificationItem result = tested.getCompensatingModificationItem( + attributes, originalItem); + + // Verify result + assertEquals(DirContext.REPLACE_ATTRIBUTE, result.getModificationOp()); + Attribute resultAttribute = result.getAttribute(); + assertEquals("someattr", resultAttribute.getID()); + Object object = resultAttribute.get(0); + assertEquals("value1", object); + assertEquals("value2", resultAttribute.get(1)); + } + + public void testGetCompensatingModificationItem_ReplaceNonExistingAttribute() + throws NamingException { + Attributes attributes = new BasicAttributes(); + + BasicAttribute modificationAttribute = new BasicAttribute("someattr"); + modificationAttribute.add("newvalue1"); + modificationAttribute.add("newvalue2"); + ModificationItem originalItem = new ModificationItem( + DirContext.REPLACE_ATTRIBUTE, modificationAttribute); + + // Perform test + ModificationItem result = tested.getCompensatingModificationItem( + attributes, originalItem); + + // Verify result + assertEquals(DirContext.REMOVE_ATTRIBUTE, result.getModificationOp()); + Attribute resultAttribute = result.getAttribute(); + assertEquals("someattr", resultAttribute.getID()); + assertEquals(0, resultAttribute.size()); + } + + public void testGetCompensatingModificationItem_AddNonExistingAttribute() + throws NamingException { + Attributes attributes = new BasicAttributes(); + + BasicAttribute modificationAttribute = new BasicAttribute("someattr"); + modificationAttribute.add("newvalue1"); + modificationAttribute.add("newvalue2"); + ModificationItem originalItem = new ModificationItem( + DirContext.ADD_ATTRIBUTE, modificationAttribute); + + // Perform test + ModificationItem result = tested.getCompensatingModificationItem( + attributes, originalItem); + + // Verify result + assertEquals(DirContext.REMOVE_ATTRIBUTE, result.getModificationOp()); + Attribute resultAttribute = result.getAttribute(); + assertEquals("someattr", resultAttribute.getID()); + assertEquals(0, resultAttribute.size()); + } + + public void testGetCompensatingModificationItem_AddExistingAttribute() + throws NamingException { + BasicAttribute attribute = new BasicAttribute("someattr"); + attribute.add("value1"); + attribute.add("value2"); + Attributes attributes = new BasicAttributes(); + attributes.put(attribute); + + BasicAttribute modificationAttribute = new BasicAttribute("someattr"); + modificationAttribute.add("newvalue1"); + modificationAttribute.add("newvalue2"); + ModificationItem originalItem = new ModificationItem( + DirContext.ADD_ATTRIBUTE, new BasicAttribute("someattr")); + + // Perform test + ModificationItem result = tested.getCompensatingModificationItem( + attributes, originalItem); + + // Verify result + assertEquals(DirContext.REPLACE_ATTRIBUTE, result.getModificationOp()); + Attribute resultAttribute = result.getAttribute(); + assertEquals("someattr", resultAttribute.getID()); + assertEquals("value1", result.getAttribute().get(0)); + assertEquals("value2", result.getAttribute().get(1)); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RebindOperationExecutorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RebindOperationExecutorTest.java new file mode 100644 index 00000000..6adb47c3 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RebindOperationExecutorTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.directory.BasicAttributes; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.RebindOperationExecutor; + +public class RebindOperationExecutorTest extends TestCase { + + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + } + + public void testPerformOperation() { + DistinguishedName expectedOriginalDn = new DistinguishedName( + "cn=john doe"); + DistinguishedName expectedTempDn = new DistinguishedName( + "cn=john doe_temp"); + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + RebindOperationExecutor tested = new RebindOperationExecutor( + ldapOperationsMock, expectedOriginalDn, expectedTempDn, + expectedObject, expectedAttributes); + + ldapOperationsMock.rename(expectedOriginalDn, expectedTempDn); + ldapOperationsMock.bind(expectedOriginalDn, expectedObject, + expectedAttributes); + + replay(); + // perform test + tested.performOperation(); + verify(); + } + + public void testCommit() { + DistinguishedName expectedOriginalDn = new DistinguishedName( + "cn=john doe"); + DistinguishedName expectedTempDn = new DistinguishedName( + "cn=john doe_temp"); + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + RebindOperationExecutor tested = new RebindOperationExecutor( + ldapOperationsMock, expectedOriginalDn, expectedTempDn, + expectedObject, expectedAttributes); + + ldapOperationsMock.unbind(expectedTempDn); + + replay(); + // perform test + tested.commit(); + verify(); + } + + public void testRollback() { + DistinguishedName expectedOriginalDn = new DistinguishedName( + "cn=john doe"); + DistinguishedName expectedTempDn = new DistinguishedName( + "cn=john doe_temp"); + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + RebindOperationExecutor tested = new RebindOperationExecutor( + ldapOperationsMock, expectedOriginalDn, expectedTempDn, + expectedObject, expectedAttributes); + + ldapOperationsMock.unbind(expectedOriginalDn); + ldapOperationsMock.rename(expectedTempDn, expectedOriginalDn); + + replay(); + // perform test + tested.rollback(); + verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RebindOperationRecorderTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RebindOperationRecorderTest.java new file mode 100644 index 00000000..7be40068 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RebindOperationRecorderTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import javax.naming.directory.BasicAttributes; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.RebindOperationExecutor; +import org.springframework.ldap.transaction.compensating.RebindOperationRecorder; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +public class RebindOperationRecorderTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + private MockControl renamingStrategyControl; + + private TempEntryRenamingStrategy renamingStrategyMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + renamingStrategyControl = MockControl + .createControl(TempEntryRenamingStrategy.class); + renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl + .getMock(); + + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + + renamingStrategyControl = null; + renamingStrategyMock = null; + + } + + protected void replay() { + ldapOperationsControl.replay(); + renamingStrategyControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + renamingStrategyControl.verify(); + } + + public void testRecordOperation() { + final DistinguishedName expectedDn = new DistinguishedName( + "cn=john doe"); + final DistinguishedName expectedTempDn = new DistinguishedName( + "cn=john doe"); + RebindOperationRecorder tested = new RebindOperationRecorder( + ldapOperationsMock, renamingStrategyMock); + + renamingStrategyControl.expectAndReturn(renamingStrategyMock + .getTemporaryName(expectedDn), expectedTempDn); + + replay(); + Object expectedObject = new Object(); + BasicAttributes expectedAttributes = new BasicAttributes(); + + // perform test + CompensatingTransactionOperationExecutor result = tested + .recordOperation(new Object[] { expectedDn, expectedObject, + expectedAttributes }); + verify(); + + assertTrue(result instanceof RebindOperationExecutor); + RebindOperationExecutor rollbackOperation = (RebindOperationExecutor) result; + assertSame(ldapOperationsMock, rollbackOperation.getLdapOperations()); + assertSame(expectedDn, rollbackOperation.getOriginalDn()); + assertSame(expectedTempDn, rollbackOperation.getTemporaryDn()); + assertSame(expectedObject, rollbackOperation.getOriginalObject()); + assertSame(expectedAttributes, rollbackOperation + .getOriginalAttributes()); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RenameOperationExecutorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RenameOperationExecutorTest.java new file mode 100644 index 00000000..79a97c0d --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RenameOperationExecutorTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.RenameOperationExecutor; + +import junit.framework.TestCase; + +public class RenameOperationExecutorTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + } + + public void testPerformOperation() { + DistinguishedName expectedNewName = new DistinguishedName("ou=newOu"); + DistinguishedName expectedOldName = new DistinguishedName("ou=someou"); + RenameOperationExecutor tested = new RenameOperationExecutor( + ldapOperationsMock, expectedOldName, expectedNewName); + + ldapOperationsMock.rename(expectedOldName, expectedNewName); + + replay(); + // Perform test. + tested.performOperation(); + verify(); + } + + public void testCommit() { + DistinguishedName expectedNewName = new DistinguishedName("ou=newOu"); + DistinguishedName expectedOldName = new DistinguishedName("ou=someou"); + RenameOperationExecutor tested = new RenameOperationExecutor( + ldapOperationsMock, expectedOldName, expectedNewName); + + // Nothing to do for this operation. + + replay(); + // Perform test. + tested.commit(); + verify(); + } + + public void testRollback() { + DistinguishedName expectedNewName = new DistinguishedName("ou=newOu"); + DistinguishedName expectedOldName = new DistinguishedName("ou=someou"); + RenameOperationExecutor tested = new RenameOperationExecutor( + ldapOperationsMock, expectedOldName, expectedNewName); + + ldapOperationsMock.rename(expectedNewName, expectedOldName); + + replay(); + // Perform test. + tested.rollback(); + verify(); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RenameOperationRecorderTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RenameOperationRecorderTest.java new file mode 100644 index 00000000..2c3a3310 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/RenameOperationRecorderTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.RenameOperationExecutor; +import org.springframework.ldap.transaction.compensating.RenameOperationRecorder; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +public class RenameOperationRecorderTest extends TestCase { + + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + } + + public void testRecordOperation() { + RenameOperationRecorder tested = new RenameOperationRecorder( + ldapOperationsMock); + + replay(); + // Perform test + CompensatingTransactionOperationExecutor operation = tested + .recordOperation(new Object[] { "ou=someou", "ou=newou" }); + verify(); + + assertTrue(operation instanceof RenameOperationExecutor); + RenameOperationExecutor rollbackOperation = (RenameOperationExecutor) operation; + assertSame(ldapOperationsMock, rollbackOperation.getLdapOperations()); + assertEquals("ou=newou", rollbackOperation.getNewDn().toString()); + assertEquals("ou=someou", rollbackOperation.getOriginalDn().toString()); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/UnbindOperationExecutorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/UnbindOperationExecutorTest.java new file mode 100644 index 00000000..e84da183 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/UnbindOperationExecutorTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.UnbindOperationExecutor; + +public class UnbindOperationExecutorTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + + } + + protected void replay() { + ldapOperationsControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + } + + public void testPerformOperation() { + DistinguishedName expectedOldName = new DistinguishedName("cn=oldDn"); + DistinguishedName expectedTempName = new DistinguishedName("cn=newDn"); + UnbindOperationExecutor tested = new UnbindOperationExecutor( + ldapOperationsMock, expectedOldName, expectedTempName); + + ldapOperationsMock.rename(expectedOldName, expectedTempName); + + replay(); + // Perform test + tested.performOperation(); + verify(); + } + + public void testCommit() { + DistinguishedName expectedOldName = new DistinguishedName("cn=oldDn"); + DistinguishedName expectedTempName = new DistinguishedName("cn=newDn"); + UnbindOperationExecutor tested = new UnbindOperationExecutor( + ldapOperationsMock, expectedOldName, expectedTempName); + + ldapOperationsMock.unbind(expectedTempName); + + replay(); + // Perform test + tested.commit(); + verify(); + } + + public void testRollback() { + DistinguishedName expectedOldName = new DistinguishedName("cn=oldDn"); + DistinguishedName expectedTempName = new DistinguishedName("cn=newDn"); + UnbindOperationExecutor tested = new UnbindOperationExecutor( + ldapOperationsMock, expectedOldName, expectedTempName); + + ldapOperationsMock.rename(expectedTempName, expectedOldName); + + replay(); + // Perform test + tested.rollback(); + verify(); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/UnbindOperationRecorderTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/UnbindOperationRecorderTest.java new file mode 100644 index 00000000..8dca6f54 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/UnbindOperationRecorderTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2005-2007 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.transaction.compensating; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; +import org.springframework.ldap.transaction.compensating.UnbindOperationExecutor; +import org.springframework.ldap.transaction.compensating.UnbindOperationRecorder; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; + +public class UnbindOperationRecorderTest extends TestCase { + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + private MockControl renamingStrategyControl; + + private TempEntryRenamingStrategy renamingStrategyMock; + + protected void setUp() throws Exception { + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + renamingStrategyControl = MockControl + .createControl(TempEntryRenamingStrategy.class); + renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl + .getMock(); + + } + + protected void tearDown() throws Exception { + ldapOperationsControl = null; + ldapOperationsMock = null; + + renamingStrategyControl = null; + renamingStrategyMock = null; + + } + + protected void replay() { + ldapOperationsControl.replay(); + renamingStrategyControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + renamingStrategyControl.verify(); + } + + public void testRecordOperation() { + final DistinguishedName expectedTempName = new DistinguishedName( + "cn=john doe_temp"); + final DistinguishedName expectedDn = new DistinguishedName( + "cn=john doe"); + UnbindOperationRecorder tested = new UnbindOperationRecorder( + ldapOperationsMock, renamingStrategyMock); + + renamingStrategyControl.expectAndReturn(renamingStrategyMock + .getTemporaryName(expectedDn), expectedTempName); + + replay(); + // Perform test + CompensatingTransactionOperationExecutor operation = tested + .recordOperation(new Object[] { expectedDn }); + verify(); + + // Verify result + assertTrue(operation instanceof UnbindOperationExecutor); + UnbindOperationExecutor rollbackOperation = (UnbindOperationExecutor) operation; + assertSame(ldapOperationsMock, rollbackOperation.getLdapOperations()); + assertSame(expectedDn, rollbackOperation.getOriginalDn()); + assertSame(expectedTempName, rollbackOperation.getTemporaryDn()); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerTest.java new file mode 100644 index 00000000..3a044c1d --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerTest.java @@ -0,0 +1,201 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.manager; + +import javax.naming.directory.DirContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy; +import org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager; +import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.compensating.CompensatingTransactionOperationManager; +import org.springframework.transaction.compensating.support.CompensatingTransactionHolderSupport; +import org.springframework.transaction.compensating.support.CompensatingTransactionObject; +import org.springframework.transaction.support.DefaultTransactionStatus; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class ContextSourceTransactionManagerTest extends TestCase { + + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private MockControl contextControl; + + private DirContext contextMock; + + private ContextSourceTransactionManager tested; + + private MockControl transactionDefinitionControl; + + private MockControl transactionDataManagerControl; + + private CompensatingTransactionOperationManager transactionDataManagerMock; + + private TransactionDefinition transactionDefinitionMock; + + private MockControl renamingStrategyControl; + + private TempEntryRenamingStrategy renamingStrategyMock; + + protected void setUp() throws Exception { + super.setUp(); + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + contextControl = MockControl.createControl(DirContext.class); + contextMock = (DirContext) contextControl.getMock(); + + transactionDefinitionControl = MockControl + .createControl(TransactionDefinition.class); + transactionDefinitionMock = (TransactionDefinition) transactionDefinitionControl + .getMock(); + + transactionDataManagerControl = MockControl + .createControl(CompensatingTransactionOperationManager.class); + transactionDataManagerMock = (CompensatingTransactionOperationManager) transactionDataManagerControl + .getMock(); + + renamingStrategyControl = MockControl + .createControl(TempEntryRenamingStrategy.class); + renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl + .getMock(); + + tested = new ContextSourceTransactionManager(); + tested.setContextSource(contextSourceMock); + tested.setRenamingStrategy(renamingStrategyMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextControl = null; + contextMock = null; + + contextSourceControl = null; + contextSourceMock = null; + + transactionDefinitionControl = null; + transactionDefinitionMock = null; + + transactionDataManagerControl = null; + transactionDataManagerMock = null; + + renamingStrategyControl = null; + renamingStrategyMock = null; + + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + } + + public void testDoGetTransaction() { + Object result = tested.doGetTransaction(); + + assertNotNull(result); + assertTrue(result instanceof CompensatingTransactionObject); + CompensatingTransactionObject transactionObject = (CompensatingTransactionObject) result; + assertNull(transactionObject.getHolder()); + } + + public void testDoGetTransactionTransactionActive() { + CompensatingTransactionHolderSupport expectedContextHolder = new DirContextHolder( + null, null); + TransactionSynchronizationManager.bindResource(contextSourceMock, + expectedContextHolder); + Object result = tested.doGetTransaction(); + assertSame(expectedContextHolder, + ((CompensatingTransactionObject) result).getHolder()); + } + + public void testDoBegin() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadOnlyContext(), contextMock); + + contextSourceControl.replay(); + + CompensatingTransactionObject expectedTransactionObject = new CompensatingTransactionObject( + null); + tested.doBegin(expectedTransactionObject, transactionDefinitionMock); + + contextSourceControl.verify(); + + DirContextHolder foundContextHolder = (DirContextHolder) TransactionSynchronizationManager + .getResource(contextSourceMock); + assertSame(contextMock, foundContextHolder.getCtx()); + } + + public void testDoCommit() { + } + + public void testDoRollback() { + + DirContextHolder expectedContextHolder = new DirContextHolder(null, + contextMock); + expectedContextHolder + .setTransactionOperationManager(transactionDataManagerMock); + TransactionSynchronizationManager.bindResource(contextSourceMock, + expectedContextHolder); + + transactionDataManagerMock.rollback(); + transactionDataManagerControl.replay(); + CompensatingTransactionObject transactionObject = new CompensatingTransactionObject( + null); + transactionObject.setHolder(expectedContextHolder); + tested.doRollback(new DefaultTransactionStatus(transactionObject, + false, false, false, false, null)); + transactionDataManagerControl.verify(); + } + + public void testDoCleanupAfterCompletion() throws Exception { + DirContextHolder expectedContextHolder = new DirContextHolder(null, + contextMock); + TransactionSynchronizationManager.bindResource(contextSourceMock, + expectedContextHolder); + + contextMock.close(); + contextControl.replay(); + + tested.doCleanupAfterCompletion(new CompensatingTransactionObject( + expectedContextHolder)); + + contextControl.verify(); + assertNull(TransactionSynchronizationManager + .getResource(contextSourceMock)); + assertNull(expectedContextHolder.getTransactionOperationManager()); + } + + public void testSetContextSource_Proxy() { + TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy( + contextSourceMock); + + // Perform test + tested.setContextSource(proxy); + ContextSource result = tested.getContextSource(); + + // Verify result + assertSame(contextSourceMock, result); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareContextSourceProxyTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareContextSourceProxyTest.java new file mode 100644 index 00000000..a2b3d544 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareContextSourceProxyTest.java @@ -0,0 +1,127 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DirContextProxy; +import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy; + +/** + * Tests for {@link TransactionAwareContextSourceProxy}. + * + * @author Mattias Arthursson + */ +public class TransactionAwareContextSourceProxyTest extends TestCase { + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private TransactionAwareContextSourceProxy tested; + + private MockControl ldapContextControl; + + private LdapContext ldapContextMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + protected void setUp() throws Exception { + + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + ldapContextControl = MockControl.createControl(LdapContext.class); + ldapContextMock = (LdapContext) ldapContextControl.getMock(); + + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + tested = new TransactionAwareContextSourceProxy(contextSourceMock); + } + + protected void tearDown() throws Exception { + contextSourceControl = null; + contextSourceMock = null; + + ldapContextControl = null; + ldapContextMock = null; + + dirContextControl = null; + dirContextMock = null; + + tested = null; + } + + public void testGetReadWriteContext_LdapContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadWriteContext(), ldapContextMock); + + contextSourceControl.replay(); + + DirContext result = tested.getReadWriteContext(); + + contextSourceControl.verify(); + + assertNotNull("Result should not be null", result); + assertTrue("Should be an LdapContext instance", + result instanceof LdapContext); + assertTrue("Should be a DirContextProxy instance", + result instanceof DirContextProxy); + } + + public void testGetReadWriteContext_DirContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadWriteContext(), dirContextMock); + + contextSourceControl.replay(); + + DirContext result = tested.getReadWriteContext(); + + contextSourceControl.verify(); + + assertNotNull("Result should not be null", result); + assertTrue("Should be a DirContext instance", + result instanceof DirContext); + assertFalse("Should not be an LdapContext instance", + result instanceof LdapContext); + assertTrue("Should be a DirContextProxy instance", + result instanceof DirContextProxy); + } + + public void testGetReadOnlyContext_LdapContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadWriteContext(), ldapContextMock); + + contextSourceControl.replay(); + + DirContext result = tested.getReadOnlyContext(); + + contextSourceControl.verify(); + + assertNotNull("Result should not be null", result); + assertTrue("Should be an LdapContext instance", + result instanceof LdapContext); + assertTrue("Should be a DirContextProxy instance", + result instanceof DirContextProxy); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareDirContextInvocationHandlerTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareDirContextInvocationHandlerTest.java new file mode 100644 index 00000000..3ebbcbad --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/manager/TransactionAwareDirContextInvocationHandlerTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.transaction.compensating.manager.TransactionAwareDirContextInvocationHandler; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class TransactionAwareDirContextInvocationHandlerTest extends TestCase { + + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + private TransactionAwareDirContextInvocationHandler tested; + + private DirContextHolder holder; + + protected void setUp() throws Exception { + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + holder = new DirContextHolder(null, dirContextMock); + tested = new TransactionAwareDirContextInvocationHandler(null, null); + } + + protected void tearDown() throws Exception { + dirContextControl = null; + dirContextMock = null; + + contextSourceControl = null; + contextSourceMock = null; + + holder = null; + + tested = null; + } + + protected void replay() { + dirContextControl.replay(); + contextSourceControl.replay(); + } + + protected void verify() { + dirContextControl.verify(); + contextSourceControl.verify(); + } + + public void testDoCloseConnection_NoTransaction() throws NamingException { + dirContextMock.close(); + + replay(); + tested.doCloseConnection(dirContextMock, contextSourceMock); + verify(); + } + + public void testDoCloseConnection_ActiveTransaction() + throws NamingException { + TransactionSynchronizationManager.bindResource(contextSourceMock, + holder); + + // Context should not be closed. + + replay(); + tested.doCloseConnection(dirContextMock, contextSourceMock); + verify(); + } + + public void testDoCloseConnection_NotTransactionalContext() + throws NamingException { + TransactionSynchronizationManager.bindResource(contextSourceMock, + holder); + + MockControl dirContextControl2 = MockControl + .createControl(DirContext.class); + DirContext dirContextMock2 = (DirContext) dirContextControl2.getMock(); + + dirContextMock2.close(); + + dirContextControl2.replay(); + replay(); + tested.doCloseConnection(dirContextMock2, contextSourceMock); + verify(); + dirContextControl2.verify(); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/support/DefaultTempEntryRenamingStrategyTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/support/DefaultTempEntryRenamingStrategyTest.java new file mode 100644 index 00000000..e2812cc4 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/support/DefaultTempEntryRenamingStrategyTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import javax.naming.Name; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy; + +import junit.framework.TestCase; + +public class DefaultTempEntryRenamingStrategyTest extends TestCase { + + public void testGetTemporaryName() { + DistinguishedName expectedOriginalName = new DistinguishedName( + "cn=john doe, ou=somecompany, c=SE"); + DefaultTempEntryRenamingStrategy tested = new DefaultTempEntryRenamingStrategy(); + + Name result = tested.getTemporaryName(expectedOriginalName); + assertEquals("cn=john doe_temp, ou=somecompany, c=SE", result + .toString()); + assertNotSame(expectedOriginalName, result); + } + + public void testGetTemporaryDN_MultivalueDN() { + DistinguishedName expectedOriginalName = new DistinguishedName( + "cn=john doe+sn=doe, ou=somecompany, c=SE"); + DefaultTempEntryRenamingStrategy tested = new DefaultTempEntryRenamingStrategy(); + + Name result = tested.getTemporaryName(expectedOriginalName); + assertEquals("cn=john doe_temp+sn=doe, ou=somecompany, c=SE", result + .toString()); + } + + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/support/DifferentSubtreeTempEntryRenamingStrategyTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/support/DifferentSubtreeTempEntryRenamingStrategyTest.java new file mode 100644 index 00000000..86a27f99 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/transaction/compensating/support/DifferentSubtreeTempEntryRenamingStrategyTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import javax.naming.Name; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.transaction.compensating.support.DifferentSubtreeTempEntryRenamingStrategy; + +import junit.framework.TestCase; + +public class DifferentSubtreeTempEntryRenamingStrategyTest extends TestCase { + + public void testGetTemporaryName() { + DistinguishedName originalName = new DistinguishedName( + "cn=john doe, ou=somecompany, c=SE"); + DifferentSubtreeTempEntryRenamingStrategy tested = new DifferentSubtreeTempEntryRenamingStrategy( + new DistinguishedName("ou=tempEntries")); + + int nextSequenceNo = tested.getNextSequenceNo(); + + // Perform test + Name result = tested.getTemporaryName(originalName); + + // Verify result + assertEquals("cn=john doe" + nextSequenceNo + ", ou=tempEntries", + result.toString()); + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/util/ListComparatorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/util/ListComparatorTest.java new file mode 100644 index 00000000..fde9bf9a --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/ldap/util/ListComparatorTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2005-2007 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.util; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.ldap.support.ListComparator; + +import junit.framework.TestCase; + +/** + * Tests for ListComparator. + * + * @author Mattias Arthursson + */ +public class ListComparatorTest extends TestCase { + + private ListComparator tested; + + protected void setUp() throws Exception { + super.setUp(); + tested = new ListComparator(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCompare_Equals() { + List list1 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0) }); + List list2 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0) }); + + int result = tested.compare(list1, list2); + assertEquals(0, result); + } + + public void testCompare_Less() { + List list1 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0) }); + List list2 = Arrays.asList(new Object[] { new Integer(0), + new Integer(1) }); + + int result = tested.compare(list1, list2); + assertTrue(result < 0); + } + + public void testCompare_Greater() { + List list1 = Arrays.asList(new Object[] { new Integer(0), + new Integer(1) }); + List list2 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0) }); + + int result = tested.compare(list1, list2); + assertTrue(result > 0); + } + + public void testCompare_Longer() { + List list1 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0), new Integer(0) }); + List list2 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0) }); + + int result = tested.compare(list1, list2); + assertTrue(result > 0); + } + + public void testCompare_Shorter() { + List list1 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0) }); + List list2 = Arrays.asList(new Object[] { new Integer(0), + new Integer(0), new Integer(0) }); + + int result = tested.compare(list1, list2); + assertTrue(result < 0); + } +} diff --git a/mvn-build/core/src/test/java/org/springframework/transaction/compensating/support/CompensatingTransactionUtilsTest.java b/mvn-build/core/src/test/java/org/springframework/transaction/compensating/support/CompensatingTransactionUtilsTest.java new file mode 100644 index 00000000..e7a9b319 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/transaction/compensating/support/CompensatingTransactionUtilsTest.java @@ -0,0 +1,123 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import java.lang.reflect.Method; + +import javax.naming.directory.DirContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.transaction.compensating.manager.DirContextHolder; +import org.springframework.transaction.compensating.CompensatingTransactionOperationManager; +import org.springframework.transaction.compensating.support.CompensatingTransactionHolderSupport; +import org.springframework.transaction.compensating.support.CompensatingTransactionUtils; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class CompensatingTransactionUtilsTest extends TestCase { + + private MockControl dirContextControl; + + private DirContext dirContextMock; + + private MockControl contextSourceControl; + + private ContextSource contextSourceMock; + + private MockControl operationManagerControl; + + private CompensatingTransactionOperationManager operationManagerMock; + + protected void setUp() throws Exception { + dirContextControl = MockControl.createControl(DirContext.class); + dirContextMock = (DirContext) dirContextControl.getMock(); + + contextSourceControl = MockControl.createControl(ContextSource.class); + contextSourceMock = (ContextSource) contextSourceControl.getMock(); + + operationManagerControl = MockControl + .createControl(CompensatingTransactionOperationManager.class); + operationManagerMock = (CompensatingTransactionOperationManager) operationManagerControl + .getMock(); + + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + } + + protected void tearDown() throws Exception { + dirContextControl = null; + dirContextMock = null; + + contextSourceControl = null; + contextSourceMock = null; + + operationManagerControl = null; + operationManagerMock = null; + } + + protected void replay() { + dirContextControl.replay(); + contextSourceControl.replay(); + operationManagerControl.replay(); + } + + protected void verify() { + dirContextControl.verify(); + contextSourceControl.verify(); + operationManagerControl.verify(); + } + + public void testPerformOperation() throws Throwable { + CompensatingTransactionHolderSupport holder = new DirContextHolder( + null, dirContextMock); + holder.setTransactionOperationManager(operationManagerMock); + + TransactionSynchronizationManager.bindResource(contextSourceMock, + holder); + + Object[] expectedArgs = new Object[] { "someDn" }; + operationManagerMock.performOperation(dirContextMock, "unbind", + expectedArgs); + + replay(); + CompensatingTransactionUtils.performOperation(contextSourceMock, + dirContextMock, getUnbindMethod(), expectedArgs); + verify(); + } + + public void testPerformOperation_NoTransaction() throws Throwable { + Object[] expectedArgs = new Object[] { "someDn" }; + dirContextMock.unbind("someDn"); + + replay(); + CompensatingTransactionUtils.performOperation(contextSourceMock, + dirContextMock, getUnbindMethod(), expectedArgs); + verify(); + } + + private Method getUnbindMethod() throws NoSuchMethodException { + return DirContext.class.getMethod("unbind", + new Class[] { String.class }); + } + + public void dummyMethod() { + + } + +} diff --git a/mvn-build/core/src/test/java/org/springframework/transaction/compensating/support/DefaultCompensatingTransactionOperationManagerTest.java b/mvn-build/core/src/test/java/org/springframework/transaction/compensating/support/DefaultCompensatingTransactionOperationManagerTest.java new file mode 100644 index 00000000..b5c35185 --- /dev/null +++ b/mvn-build/core/src/test/java/org/springframework/transaction/compensating/support/DefaultCompensatingTransactionOperationManagerTest.java @@ -0,0 +1,168 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.support; + +import java.util.Stack; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.transaction.TransactionSystemException; +import org.springframework.transaction.compensating.CompensatingTransactionOperationExecutor; +import org.springframework.transaction.compensating.CompensatingTransactionOperationFactory; +import org.springframework.transaction.compensating.CompensatingTransactionOperationRecorder; +import org.springframework.transaction.compensating.support.DefaultCompensatingTransactionOperationManager; + +public class DefaultCompensatingTransactionOperationManagerTest extends + TestCase { + + private MockControl operationExecutorControl; + + private CompensatingTransactionOperationExecutor operationExecutorMock; + + private MockControl operationFactoryControl; + + private CompensatingTransactionOperationFactory operationFactoryMock; + + private MockControl operationRecorderControl; + + private CompensatingTransactionOperationRecorder operationRecorderMock; + + protected void setUp() throws Exception { + super.setUp(); + operationExecutorControl = MockControl + .createControl(CompensatingTransactionOperationExecutor.class); + operationExecutorMock = (CompensatingTransactionOperationExecutor) operationExecutorControl + .getMock(); + + operationFactoryControl = MockControl + .createControl(CompensatingTransactionOperationFactory.class); + operationFactoryMock = (CompensatingTransactionOperationFactory) operationFactoryControl + .getMock(); + + operationRecorderControl = MockControl + .createControl(CompensatingTransactionOperationRecorder.class); + operationRecorderMock = (CompensatingTransactionOperationRecorder) operationRecorderControl + .getMock(); + + } + + protected void tearDown() throws Exception { + super.tearDown(); + operationExecutorControl = null; + operationExecutorMock = null; + + operationFactoryControl = null; + operationFactoryMock = null; + + operationRecorderControl = null; + operationRecorderMock = null; + } + + protected void replay() { + operationExecutorControl.replay(); + operationFactoryControl.replay(); + operationRecorderControl.replay(); + } + + protected void verify() { + operationExecutorControl.verify(); + operationFactoryControl.verify(); + operationRecorderControl.verify(); + } + + public void testPerformOperation() { + Object[] expectedArgs = new Object[0]; + Object expectedResource = new Object(); + + operationFactoryControl.expectAndReturn(operationFactoryMock + .createRecordingOperation(expectedResource, "some method"), + operationRecorderMock); + operationRecorderControl.expectAndReturn(operationRecorderMock + .recordOperation(expectedArgs), operationExecutorMock); + operationExecutorMock.performOperation(); + + DefaultCompensatingTransactionOperationManager tested = new DefaultCompensatingTransactionOperationManager( + operationFactoryMock); + replay(); + tested.performOperation(expectedResource, "some method", expectedArgs); + verify(); + + Stack result = tested.getOperationExecutors(); + assertFalse(result.isEmpty()); + assertSame(operationExecutorMock, result.peek()); + } + + public void testRollback() { + DefaultCompensatingTransactionOperationManager tested = new DefaultCompensatingTransactionOperationManager( + operationFactoryMock); + tested.getOperationExecutors().push(operationExecutorMock); + + operationExecutorMock.rollback(); + + replay(); + tested.rollback(); + verify(); + } + + public void testRollback_Exception() { + DefaultCompensatingTransactionOperationManager tested = new DefaultCompensatingTransactionOperationManager( + operationFactoryMock); + tested.getOperationExecutors().push(operationExecutorMock); + + operationExecutorMock.rollback(); + operationExecutorControl.setThrowable(new RuntimeException()); + + replay(); + try { + tested.rollback(); + fail("TransactionSystemException expected"); + } catch (TransactionSystemException expected) { + assertTrue(true); + } + verify(); + } + + public void testCommit() { + DefaultCompensatingTransactionOperationManager tested = new DefaultCompensatingTransactionOperationManager( + operationFactoryMock); + tested.getOperationExecutors().push(operationExecutorMock); + + operationExecutorMock.commit(); + + replay(); + tested.commit(); + verify(); + } + + public void testCommit_Exception() { + DefaultCompensatingTransactionOperationManager tested = new DefaultCompensatingTransactionOperationManager( + operationFactoryMock); + tested.getOperationExecutors().push(operationExecutorMock); + + operationExecutorMock.commit(); + operationExecutorControl.setThrowable(new RuntimeException()); + + replay(); + try { + tested.commit(); + fail("TransactionSystemException expected"); + } catch (TransactionSystemException expected) { + assertTrue(true); + } + verify(); + } +} diff --git a/mvn-build/core/src/test/resources/log4j.properties b/mvn-build/core/src/test/resources/log4j.properties new file mode 100644 index 00000000..66b1c2ec --- /dev/null +++ b/mvn-build/core/src/test/resources/log4j.properties @@ -0,0 +1,4 @@ +log4j.rootCategory=warn, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout.ConversionPattern=%r [%t] %-5p\: %-15c{2} - %m%n +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout diff --git a/mvn-build/core/xdocs/navigation.xml b/mvn-build/core/xdocs/navigation.xml new file mode 100644 index 00000000..84927d4c --- /dev/null +++ b/mvn-build/core/xdocs/navigation.xml @@ -0,0 +1,43 @@ + + + + + + + LdapTemplate + + + + + +

+ + + + + + + \ No newline at end of file diff --git a/mvn-build/integration-tests/core-integration-tests/pom.xml b/mvn-build/integration-tests/core-integration-tests/pom.xml new file mode 100644 index 00000000..c972debf --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + org.springframework.ldap + spring-ldap-core-integration-tests + Spring LDAP Core Integration Tests + 1.2.2-SNAPSHOT + + + 2.0.8 + + + + + + maven-eclipse-plugin + + true + + + + + maven-compiler-plugin + + 1.4 + 1.4 + + + + + + + + org.springframework.ldap + spring-ldap-test + 1.2.2-SNAPSHOT + + + commons-pool + commons-pool + 1.3 + provided + + + com.sun + ldapbp + 1.0 + system + ${basedir}/../../lib/ldapbp.jar + + + org.acegisecurity + acegi-security + 1.0.3 + provided + + + + org.springframework + spring-dao + ${spring.version} + provided + + + org.springframework + spring-jdbc + ${spring.version} + provided + + + org.springframework + spring-aop + ${spring.version} + test + + + aspectj + aspectjrt + 1.5.3 + test + + + aspectj + aspectjweaver + 1.5.3 + test + + + gsbase + gsbase + 2.0.1 + test + + + hsqldb + hsqldb + 1.8.0.7 + test + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/Person.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/Person.java new file mode 100644 index 00000000..50cae554 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/Person.java @@ -0,0 +1,64 @@ +/* + * Copyright 2005-2007 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; + +/** + * Dummy bean to be used in the LdapTemplate integration tests. + * + * @author Mattias Arthursson + */ +public class Person { + private String fullname; + + private String lastname; + + private String description; + + private String phone; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/PersonAttributesMapper.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/PersonAttributesMapper.java new file mode 100644 index 00000000..6b0f0a4d --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/PersonAttributesMapper.java @@ -0,0 +1,47 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.AttributesMapper; + + +/** + * Dummy implementation of AttributesMapper for use in integration tests. + * + * @author Mattias Arthursson + * + */ +public class PersonAttributesMapper implements AttributesMapper { + + /** + * Maps the given attributes into a {@link Person} object. + * + * @see org.springframework.ldap.core.AttributesMapper#mapFromAttributes(javax.naming.directory.Attributes) + */ + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + Person person = new Person(); + person.setFullname((String) attributes.get("cn").get()); + person.setLastname((String) attributes.get("sn").get()); + person.setPhone((String) attributes.get("telephoneNumber").get()); + person.setDescription((String) attributes.get("description").get()); + return person; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/PersonContextMapper.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/PersonContextMapper.java new file mode 100644 index 00000000..da560d01 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/PersonContextMapper.java @@ -0,0 +1,39 @@ +/* + * Copyright 2005-2007 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; + +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.support.AbstractContextMapper; + +/** + * Dummy implemention of ContextMapper for use in the integration tests and for + * illustration purposes. + * + * @author Mattias Arthursson + */ +public class PersonContextMapper extends AbstractContextMapper { + + protected Object doMapFromContext(DirContextOperations ctx) { + Person person = new Person(); + person.setFullname(ctx.getStringAttribute("cn")); + person.setLastname(ctx.getStringAttribute("sn")); + person.setPhone(ctx.getStringAttribute("telephoneNumber")); + person.setDescription(ctx.getStringAttribute("description")); + + return person; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyDao.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyDao.java new file mode 100644 index 00000000..7d6f0e78 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyDao.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +public interface DummyDao { + void createWithException(String country, String company, String fullname, + String lastname, String description); + + void create(String country, String company, String fullname, + String lastname, String description); + + void update(String dn, String fullname, String lastname, String description); + + void updateWithException(String dn, String fullname, String lastname, + String description); + + void updateAndRename(String dn, String newDn, String description); + + void updateAndRenameWithException(String dn, String newDn, + String description); + + void modifyAttributes(String dn, String lastName, String description); + + void modifyAttributesWithException(String dn, String lastName, + String description); + + void unbind(String dn, String fullname); + + void unbindWithException(String dn, String fullname); + +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyException.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyException.java new file mode 100644 index 00000000..dacb7839 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyException.java @@ -0,0 +1,22 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +public class DummyException extends RuntimeException { + public DummyException(String message) { + super(message); + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyServiceImpl.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyServiceImpl.java new file mode 100644 index 00000000..f023fea5 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/DummyServiceImpl.java @@ -0,0 +1,24 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +public class DummyServiceImpl { + private DummyDao dummyDaoImpl; + + public void setDummyDaoImpl(DummyDao dummyDaoImpl) { + this.dummyDaoImpl = dummyDaoImpl; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/LdapAndJdbcDummyDaoImpl.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/LdapAndJdbcDummyDaoImpl.java new file mode 100644 index 00000000..ec0b43e6 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/LdapAndJdbcDummyDaoImpl.java @@ -0,0 +1,178 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; + +public class LdapAndJdbcDummyDaoImpl implements DummyDao { + private LdapTemplate ldapTemplate; + + private JdbcTemplate jdbcTemplate; + + public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#createWithException(java.lang.String, + * java.lang.String, java.lang.String, java.lang.String, + * java.lang.String) + */ + public void createWithException(String country, String company, + String fullname, String lastname, String description) { + create(country, company, fullname, lastname, description); + throw new DummyException("This method failed"); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#create(java.lang.String, + * java.lang.String, java.lang.String, java.lang.String, + * java.lang.String) + */ + public void create(String country, String company, String fullname, + String lastname, String description) { + DistinguishedName dn = new DistinguishedName(); + dn.add("c", country); + dn.add("ou", company); + dn.add("cn", fullname); + + DirContextAdapter ctx = new DirContextAdapter(); + ctx.setAttributeValues("objectclass", new String[] { "top", "person" }); + ctx.setAttributeValue("cn", fullname); + ctx.setAttributeValue("sn", lastname); + ctx.setAttributeValue("description", description); + ldapTemplate.bind(dn, ctx, null); + jdbcTemplate.update("insert into PERSON values(?, ?, ?)", new Object[] { + fullname, lastname, description }); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#update(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void update(String dn, String fullname, String lastname, + String description) { + DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn); + ctx.setAttributeValue("sn", lastname); + ctx.setAttributeValue("description", description); + ctx.update(); + + ldapTemplate.rebind(dn, ctx, null); + jdbcTemplate + .update( + "update PERSON set lastname=?, description = ? where fullname = ?", + new Object[] { lastname, description, fullname }); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#updateWithException(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void updateWithException(String dn, String fullname, + String lastname, String description) { + update(dn, fullname, lastname, description); + throw new DummyException("This method failed."); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#updateAndRename(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void updateAndRename(String dn, String newDn, String description) { + DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn); + ctx.setAttributeValue("description", description); + ctx.update(); + + ldapTemplate.rebind(dn, ctx, null); + ldapTemplate.rename(dn, newDn); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#updateAndRenameWithException(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void updateAndRenameWithException(String dn, String newDn, + String description) { + updateAndRename(dn, newDn, description); + throw new DummyException("This method failed."); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#modifyAttributes(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void modifyAttributes(String dn, String lastName, String description) { + DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn); + ctx.setAttributeValue("sn", lastName); + ctx.setAttributeValue("description", description); + + ldapTemplate.modifyAttributes(dn, ctx.getModificationItems()); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#modifyAttributesWithException(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void modifyAttributesWithException(String dn, String lastName, + String description) { + modifyAttributes(dn, lastName, description); + throw new DummyException("This method failed."); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#unbind(java.lang.String) + */ + public void unbind(String dn, String fullname) { + ldapTemplate.unbind(dn); + jdbcTemplate.update("delete from PERSON where fullname=?", + new Object[] { fullname }); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#unbindWithException(java.lang.String) + */ + public void unbindWithException(String dn, String fullname) { + unbind(dn, fullname); + throw new DummyException("This operation failed."); + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/LdapDummyDaoImpl.java b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/LdapDummyDaoImpl.java new file mode 100644 index 00000000..4db82c0a --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/main/java/org/springframework/ldap/transaction/compensating/manager/LdapDummyDaoImpl.java @@ -0,0 +1,163 @@ +/* + * Copyright 2002-2007 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.transaction.compensating.manager; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; + +public class LdapDummyDaoImpl implements DummyDao { + private LdapTemplate ldapTemplate; + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#createWithException(java.lang.String, + * java.lang.String, java.lang.String, java.lang.String, + * java.lang.String) + */ + public void createWithException(String country, String company, + String fullname, String lastname, String description) { + create(country, company, fullname, lastname, description); + throw new DummyException("This method failed"); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#create(java.lang.String, + * java.lang.String, java.lang.String, java.lang.String, + * java.lang.String) + */ + public void create(String country, String company, String fullname, + String lastname, String description) { + DistinguishedName dn = new DistinguishedName(); + dn.add("c", country); + dn.add("ou", company); + dn.add("cn", fullname); + + DirContextAdapter ctx = new DirContextAdapter(); + ctx.setAttributeValues("objectclass", new String[] { "top", "person" }); + ctx.setAttributeValue("cn", fullname); + ctx.setAttributeValue("sn", lastname); + ctx.setAttributeValue("description", description); + ldapTemplate.bind(dn, ctx, null); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#update(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void update(String dn, String fullname, String lastname, + String description) { + DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn); + ctx.setAttributeValue("sn", lastname); + ctx.setAttributeValue("description", description); + ctx.update(); + + ldapTemplate.rebind(dn, ctx, null); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#updateWithException(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void updateWithException(String dn, String fullname, + String lastname, String description) { + update(dn, fullname, lastname, description); + throw new DummyException("This method failed."); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#updateAndRename(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void updateAndRename(String dn, String newDn, String description) { + DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn); + ctx.setAttributeValue("description", description); + ctx.update(); + + ldapTemplate.rebind(dn, ctx, null); + ldapTemplate.rename(dn, newDn); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#updateAndRenameWithException(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void updateAndRenameWithException(String dn, String newDn, + String description) { + updateAndRename(dn, newDn, description); + throw new DummyException("This method failed."); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#modifyAttributes(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void modifyAttributes(String dn, String lastName, String description) { + DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn); + ctx.setAttributeValue("sn", lastName); + ctx.setAttributeValue("description", description); + + ldapTemplate.modifyAttributes(dn, ctx.getModificationItems()); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#modifyAttributesWithException(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void modifyAttributesWithException(String dn, String lastName, + String description) { + modifyAttributes(dn, lastName, description); + throw new DummyException("This method failed."); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#unbind(java.lang.String) + */ + public void unbind(String dn, String fullname) { + ldapTemplate.unbind(dn); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.transaction.support.DummyDao#unbindWithException(java.lang.String) + */ + public void unbindWithException(String dn, String fullname) { + unbind(dn, fullname); + throw new DummyException("This operation failed."); + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/log4j.properties b/mvn-build/integration-tests/core-integration-tests/src/test/java/log4j.properties new file mode 100644 index 00000000..f57302e8 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/log4j.properties @@ -0,0 +1,9 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + +#Enable debug logging +log4j.category.net.sf.ldaptemplate=INFO +log4j.category.net.sf.ldaptemplate.LdapTemplate=DEBUG diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/AbstractLdapTemplateIntegrationTest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/AbstractLdapTemplateIntegrationTest.java new file mode 100644 index 00000000..e09c0e94 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/AbstractLdapTemplateIntegrationTest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2005-2007 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; + +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +public abstract class AbstractLdapTemplateIntegrationTest extends + AbstractDependencyInjectionSpringContextTests { + + + protected void onSetUp() throws Exception { + super.onSetUp(); + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateAttributesMapperITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateAttributesMapperITest.java new file mode 100644 index 00000000..8acd316f --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateAttributesMapperITest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2005-2007 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; + +import java.util.LinkedList; +import java.util.List; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Tests the attributes mapper search method. + * + * @author Mattias Arthursson + */ +public class LdapTemplateAttributesMapperITest extends + AbstractLdapTemplateIntegrationTest { + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + public void testSearch_AttributeMapper() throws Exception { + AttributesMapper mapper = new PersonAttributesMapper(); + List result = tested.search("ou=company1,c=Sweden", + "(&(objectclass=person)(sn=Person2))", mapper); + + assertEquals(1, result.size()); + Person person = (Person) result.get(0); + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + /** + * Demonstrates how to retrieve all values of a multi-value attribute. + * + * @see LdapTemplateContextMapperITest#testSearch_ContextMapper_MultiValue() + */ + public void testSearch_AttributesMapper_MultiValue() throws Exception { + AttributesMapper mapper = new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) throws NamingException { + LinkedList list = new LinkedList(); + NamingEnumeration enumeration = attributes.get("uniqueMember").getAll(); + while (enumeration.hasMoreElements()) { + String value = (String) enumeration.nextElement(); + list.add(value); + } + String[] members = (String[]) list.toArray(new String[0]); + return members; + } + }; + List result = tested.search("ou=groups", + "(objectclass=groupOfUniqueNames)", mapper); + + assertEquals(2, result.size()); + assertEquals(1, ((String[]) result.get(0)).length); + assertEquals(5, ((String[]) result.get(1)).length); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateBindUnbindITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateBindUnbindITest.java new file mode 100644 index 00000000..42297c48 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateBindUnbindITest.java @@ -0,0 +1,119 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Tests the bind and unbind methods of LdapTemplate. The test methods in this + * class tests a little too much, but we need to clean up after binding, so the + * most efficient way to test is to do it all in one test method. Also, the + * methods in this class relies on that the lookup method works as it should - + * that should be ok, since that is verified in a separate test class. NOTE: if + * any of the tests in this class fails, it may be necessary to run the cleanup + * script as described in README.txt under /src/iutest/. + * + * @author Mattias Arthursson + */ +public class LdapTemplateBindUnbindITest extends + AbstractLdapTemplateIntegrationTest { + private LdapTemplate tested; + + private static String DN = "cn=Some Person4,ou=company1,c=Sweden"; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + public void testBindAndUnbind_Attributes_Plain() { + Attributes attributes = setupAttributes(); + tested.bind(DN, null, attributes); + verifyBoundCorrectData(); + tested.unbind(DN); + verifyCleanup(); + } + + public void testBindAndUnbind_Attributes_DIstinguishedName() { + Attributes attributes = setupAttributes(); + tested.bind(new DistinguishedName(DN), null, attributes); + verifyBoundCorrectData(); + tested.unbind(new DistinguishedName(DN)); + verifyCleanup(); + } + + public void testBindAndUnbind_DirContextAdapter_Plain() { + DirContextAdapter adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "Some Person4"); + adapter.setAttributeValue("sn", "Person4"); + + tested.bind(DN, adapter, null); + verifyBoundCorrectData(); + tested.unbind(DN); + verifyCleanup(); + } + + public void testBindAndUnbind_DirContextAdapter_DIstinguishedName() { + DirContextAdapter adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "Some Person4"); + adapter.setAttributeValue("sn", "Person4"); + + tested.bind(new DistinguishedName(DN), adapter, null); + verifyBoundCorrectData(); + tested.unbind(new DistinguishedName(DN)); + verifyCleanup(); + } + + private Attributes setupAttributes() { + Attributes attributes = new BasicAttributes(); + BasicAttribute ocattr = new BasicAttribute("objectclass"); + ocattr.add("top"); + ocattr.add("person"); + attributes.put(ocattr); + attributes.put("cn", "Some Person4"); + attributes.put("sn", "Person4"); + return attributes; + } + + private void verifyBoundCorrectData() { + DirContextAdapter result = (DirContextAdapter) tested.lookup(DN); + assertEquals("Some Person4", result.getStringAttribute("cn")); + assertEquals("Person4", result.getStringAttribute("sn")); + } + + private void verifyCleanup() { + try { + tested.lookup(DN); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateContextExecutorTest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateContextExecutorTest.java new file mode 100644 index 00000000..8561d09d --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateContextExecutorTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.springframework.ldap.core.ContextExecutor; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Tests for LdapTemplate's context executor methods. + * + * @author Mattias Arthursson + */ +public class LdapTemplateContextExecutorTest extends + AbstractLdapTemplateIntegrationTest { + + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + } + + protected void onTearDown() throws Exception { + super.onTearDown(); + } + + public void testLookupLink() { + ContextExecutor executor = new ContextExecutor(){ + public Object executeWithContext(DirContext ctx) throws NamingException { + return ctx.lookupLink("cn=Some Person,ou=company1,c=Sweden"); + } + }; + + Object object = tested.executeReadOnly(executor); + assertTrue("Should be a DirContextAdapter", object instanceof DirContextAdapter); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateContextMapperITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateContextMapperITest.java new file mode 100644 index 00000000..d2ca77b2 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateContextMapperITest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2005-2007 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; + +import java.util.List; + +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Tests the ContextMapper search method. In its way this method also + * demonstrates the use of DirContextAdapter and the DirObjectFactory. + * + * @author Mattias Arthursson + */ +public class LdapTemplateContextMapperITest extends + AbstractLdapTemplateIntegrationTest { + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.core.support.DefaultDirObjectFactory}) + * being set in the ContextSource. + */ + public void testSearch_ContextMapper() { + ContextMapper mapper = new PersonContextMapper(); + List result = tested.search("ou=company1,c=Sweden", + "(&(objectclass=person)(sn=Person2))", mapper); + + assertEquals(1, result.size()); + Person person = (Person) result.get(0); + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + /** + * Demonstrates how to retrieve all values of a multi-value attribute. + * + * @see LdapTemplateAttributesMapperITest#testSearch_AttributesMapper_MultiValue() + */ + public void testSearch_ContextMapper_MultiValue() throws Exception { + ContextMapper mapper = new ContextMapper() { + public Object mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + String[] members = adapter.getStringAttributes("uniqueMember"); + return members; + } + }; + List result = tested.search("ou=groups", + "(objectclass=groupOfUniqueNames)", mapper); + + assertEquals(2, result.size()); + assertEquals(1, ((String[]) result.get(0)).length); + assertEquals(5, ((String[]) result.get(1)).length); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateListITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateListITest.java new file mode 100644 index 00000000..58d0e27c --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateListITest.java @@ -0,0 +1,148 @@ +/* + * Copyright 2005-2007 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; + +import java.util.List; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.CountNameClassPairCallbackHandler; +import org.springframework.ldap.test.AttributeCheckContextMapper; + +/** + * Tests for LdapTemplate's list methods. + * + * @author Ulrik Sandberg + */ +public class LdapTemplateListITest extends + AbstractLdapTemplateIntegrationTest { + + private LdapTemplate tested; + + private AttributeCheckContextMapper contextMapper; + + private static final String BASE_STRING = ""; + + private static final DistinguishedName BASE_NAME = new DistinguishedName( + BASE_STRING); + + private static final String[] ALL_ATTRIBUTES = { "cn", "sn", "description", + "telephoneNumber" }; + + private static final String[] ALL_VALUES = { "Some Person", "Person", + "Sweden, Company2, Some Person", "+46 555-456321" }; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + + contextMapper = new AttributeCheckContextMapper(); + } + + protected void onTearDown() throws Exception { + super.onTearDown(); + + contextMapper = null; + } + + public void testListBindings_ContextMapper() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.listBindings("ou=company2,c=Sweden" + BASE_STRING, + contextMapper); + assertEquals(1, list.size()); + } + + public void testListBindings_ContextMapper_Name() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + DistinguishedName dn = new DistinguishedName("ou=company2,c=Sweden"); + dn.append(BASE_NAME); + List list = tested.listBindings(dn, contextMapper); + assertEquals(1, list.size()); + } + + public void testListBindings_ContextMapper_MapToPersons() { + DistinguishedName dn = new DistinguishedName("ou=company1,c=Sweden"); + dn.append(BASE_NAME); + List list = tested.listBindings(dn, new PersonContextMapper()); + assertEquals(3, list.size()); + String personClass = "org.springframework.ldap.Person"; + assertEquals(personClass, list.get(0).getClass().getName()); + assertEquals(personClass, list.get(1).getClass().getName()); + assertEquals(personClass, list.get(2).getClass().getName()); + } + + public void testList() { + List list = tested.list(BASE_STRING); + assertEquals(3, list.size()); + assertTrue(list.contains("ou=groups")); + assertTrue(list.contains("c=Norway")); + assertTrue(list.contains("c=Sweden")); + } + + public void testList_Name() { + List list = tested.list(BASE_NAME); + assertEquals(3, list.size()); + assertTrue(list.contains("ou=groups")); + assertTrue(list.contains("c=Norway")); + assertTrue(list.contains("c=Sweden")); + } + + public void testList_Handler() throws Exception { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + tested.list(BASE_STRING, handler); + assertEquals(3, handler.getNoOfRows()); + } + + public void testList_Name_Handler() throws Exception { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + tested.list(BASE_NAME, handler); + assertEquals(3, handler.getNoOfRows()); + } + + public void testListBindings() { + List list = tested.listBindings(BASE_STRING); + assertEquals(3, list.size()); + assertTrue(list.contains("ou=groups")); + assertTrue(list.contains("c=Norway")); + assertTrue(list.contains("c=Sweden")); + } + + public void testListBindings_Name() { + List list = tested.listBindings(BASE_NAME); + assertEquals(3, list.size()); + } + + public void testListBindings_Handler() throws Exception { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + tested.listBindings(BASE_STRING, handler); + assertEquals(3, handler.getNoOfRows()); + } + + public void testListBindings_Name_Handler() throws Exception { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + tested.listBindings(BASE_NAME, handler); + assertEquals(3, handler.getNoOfRows()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateLookupITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateLookupITest.java new file mode 100644 index 00000000..cb479a9b --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateLookupITest.java @@ -0,0 +1,245 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.AbstractContextSource; + +/** + * Tests the lookup methods of LdapTemplate. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapTemplateLookupITest extends + AbstractLdapTemplateIntegrationTest { + + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.core.support.DefaultDirObjectFactory}) + * being set in the ContextSource. + */ + public void testLookup_Plain() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person2, ou=company1,c=Sweden"); + + assertEquals("Some Person2", result.getStringAttribute("cn")); + assertEquals("Person2", result.getStringAttribute("sn")); + assertEquals("Sweden, Company1, Some Person2", result + .getStringAttribute("description")); + } + + public void testLookup_AttributesMapper() { + AttributesMapper mapper = new PersonAttributesMapper(); + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", mapper); + + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + public void testLookup_AttributesMapper_DistinguishedName() { + AttributesMapper mapper = new PersonAttributesMapper(); + Person person = (Person) tested.lookup(new DistinguishedName( + "cn=Some Person2, ou=company1,c=Sweden"), mapper); + + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + /** + * An {@link AttributesMapper} that only maps a subset of the full + * attributes list. Used in tests where the return attributes list has been + * limited. + * + * @author Ulrik Sandberg + */ + private final class SubsetPersonAttributesMapper implements + AttributesMapper { + /** + * Maps the cn attribute into a {@link Person} object. + * Also verifies that the other attributes haven't been set. + * + * @see org.springframework.ldap.core.AttributesMapper#mapFromAttributes(javax.naming.directory.Attributes) + */ + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + Person person = new Person(); + person.setFullname((String) attributes.get("cn").get()); + assertNull("sn should be null", attributes.get("sn")); + assertNull("description should be null", attributes + .get("description")); + return person; + } + } + + /** + * Verifies that only the subset is used when specifying a subset of the + * available attributes as return attributes. + */ + public void testLookup_ReturnAttributes_AttributesMapper() { + AttributesMapper mapper = new SubsetPersonAttributesMapper(); + + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", new String[] { "cn" }, + mapper); + + assertEquals("Some Person2", person.getFullname()); + assertNull("lastName should not be set", person.getLastname()); + assertNull("description should not be set", person.getDescription()); + } + + /** + * Verifies that only the subset is used when specifying a subset of the + * available attributes as return attributes. Uses DistinguishedName instead + * of plain string as name. + */ + public void testLookup_ReturnAttributes_AttributesMapper_DistinguishedName() { + AttributesMapper mapper = new SubsetPersonAttributesMapper(); + Person person = (Person) tested.lookup(new DistinguishedName( + "cn=Some Person2, ou=company1,c=Sweden"), + new String[] { "cn" }, mapper); + + assertEquals("Some Person2", person.getFullname()); + assertNull("lastName should not be set", person.getLastname()); + assertNull("description should not be set", person.getDescription()); + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.core.support.DefaultDirObjectFactory}) + * being set in the ContextSource. + */ + public void testLookup_ContextMapper() { + ContextMapper mapper = new PersonContextMapper(); + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", mapper); + + assertEquals("Some Person2", person.getFullname()); + assertEquals("Person2", person.getLastname()); + assertEquals("Sweden, Company1, Some Person2", person.getDescription()); + } + + /** + * Verifies that only the subset is used when specifying a subset of the + * available attributes as return attributes. + */ + public void testLookup_ReturnAttributes_ContextMapper() { + ContextMapper mapper = new PersonContextMapper(); + + Person person = (Person) tested.lookup( + "cn=Some Person2, ou=company1,c=Sweden", new String[] { "cn" }, + mapper); + + assertEquals("Some Person2", person.getFullname()); + assertNull("lastName should not be set", person.getLastname()); + assertNull("description should not be set", person.getDescription()); + } + + /** + * Verifies that we can lookup an entry that has a multi-valued rdn, which + * means more than one attribute is part of the relative DN for the entry. + * + * TODO Enable test when ApacheDS supports multi-valued rdns. + */ + public void DISABLED_testLookup_MultiValuedRdn() { + AttributesMapper mapper = new PersonAttributesMapper(); + Person person = (Person) tested.lookup( + "cn=Some Person+sn=Person, ou=company1,c=Norway", mapper); + + assertEquals("Some Person", person.getFullname()); + assertEquals("Person", person.getLastname()); + assertEquals("Norway, Company1, Some Person2", person.getDescription()); + } + + /** + * Verifies that we can lookup an entry that has a multi-valued rdn, which + * means more than one attribute is part of the relative DN for the entry. + * + * TODO Enable test when ApacheDS supports multi-valued rdns. + */ + public void DISABLED_testLookup_MultiValuedRdn_DirContextAdapter() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person+sn=Person, ou=company1,c=Norway"); + + assertEquals("Some Person", result.getStringAttribute("cn")); + assertEquals("Person", result.getStringAttribute("sn")); + assertEquals("Norway, Company1, Some Person", result + .getStringAttribute("description")); + } + + public void testLookup_GetNameInNamespace_Plain() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person2, ou=company1,c=Sweden"); + + assertEquals("cn=Some Person2, ou=company1, c=Sweden", result.getDn() + .toString()); + assertEquals( + "cn=Some Person2, ou=company1, c=Sweden, dc=jayway, dc=se", + result.getNameInNamespace()); + } + + public void testLookup_GetNameInNamespace_MultiRdn() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person+sn=Person, ou=company1,c=Norway"); + + assertEquals("cn=Some Person+sn=Person, ou=company1, c=Norway", result + .getDn().toString()); + assertEquals( + "cn=Some Person+sn=Person, ou=company1, c=Norway, dc=jayway, dc=se", + result.getNameInNamespace()); + } + + /** + * Tests bind and lookup with Java objects where the ContextSource already + * has a DirObjectFactory configured. + */ + public void testBindJavaObject() throws Exception { + AbstractContextSource contextSource = (AbstractContextSource) tested + .getContextSource(); + Class originalObjectFactory = contextSource.getDirObjectFactory(); + try { + contextSource.setDirObjectFactory(null); + contextSource.afterPropertiesSet(); + tested.bind("cn=myRandomInt", new Integer(54321), null); + Integer result = (Integer) tested.lookup("cn=myRandomInt"); + assertEquals(54321, result.intValue()); + } finally { + // reset the DirObjectFactory so as not to disturb other tests + tested.unbind("cn=myRandomInt"); + contextSource.setDirObjectFactory(originalObjectFactory); + contextSource.afterPropertiesSet(); + } + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateModifyITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateModifyITest.java new file mode 100644 index 00000000..bf5560c6 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateModifyITest.java @@ -0,0 +1,253 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Tests the modification methods (rebind and modifyAttributes) of LdapTemplate. + * It also illustrates the use of DirContextAdapter as a means of getting + * ModificationItems, in order to avoid doing a full rebind and use + * modifyAttributes() instead. We rely on that the bind, unbind and lookup + * methods work as they should - that should be ok, since that is verified in a + * separate test class. NOTE: if any of the tests in this class fails, it may be + * necessary to run the cleanup script as described in README.txt under + * /src/iutest/. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapTemplateModifyITest extends + AbstractLdapTemplateIntegrationTest { + private LdapTemplate tested; + + private static String PERSON4_DN = "cn=Some Person4,ou=company1,c=Sweden"; + + private static String PERSON5_DN = "cn=Some Person5,ou=company1,c=Sweden"; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + + DirContextAdapter adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "Some Person4"); + adapter.setAttributeValue("sn", "Person4"); + adapter.setAttributeValue("description", "Some description"); + + tested.bind(PERSON4_DN, adapter, null); + + adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "Some Person5"); + adapter.setAttributeValue("sn", "Person5"); + adapter.setAttributeValues("description", new String[] { "qwe", "123", + "rty", "uio" }); + + tested.bind(PERSON5_DN, adapter, null); + + } + + protected void onTearDown() throws Exception { + tested.unbind(PERSON4_DN); + tested.unbind(PERSON5_DN); + } + + public void testRebind_Attributes_Plain() { + Attributes attributes = setupAttributes(); + + tested.rebind(PERSON4_DN, null, attributes); + + verifyBoundCorrectData(); + } + + public void testRebind_Attributes_DistinguishedName() { + Attributes attributes = setupAttributes(); + + tested.rebind(new DistinguishedName(PERSON4_DN), null, attributes); + + verifyBoundCorrectData(); + } + + public void testModifyAttributes_MultiValueReplace() { + BasicAttribute attr = new BasicAttribute("description", + "Some other description"); + attr.add("Another description"); + ModificationItem[] mods = new ModificationItem[1]; + mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr); + + tested.modifyAttributes(PERSON4_DN, mods); + + DirContextAdapter result = (DirContextAdapter) tested + .lookup(PERSON4_DN); + String[] attributes = result.getStringAttributes("description"); + assertEquals(2, attributes.length); + assertEquals("Some other description", attributes[0]); + assertEquals("Another description", attributes[1]); + } + + public void testModifyAttributes_MultiValueAdd() { + BasicAttribute attr = new BasicAttribute("description", + "Some other description"); + attr.add("Another description"); + ModificationItem[] mods = new ModificationItem[1]; + mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr); + + tested.modifyAttributes(PERSON4_DN, mods); + + DirContextAdapter result = (DirContextAdapter) tested + .lookup(PERSON4_DN); + String[] attributes = result.getStringAttributes("description"); + assertEquals(3, attributes.length); + assertEquals("Some description", attributes[0]); + assertEquals("Some other description", attributes[1]); + assertEquals("Another description", attributes[2]); + } + + public void testModifyAttributes_MultiValueAddDuplicateToUnordered() { + BasicAttribute attr = new BasicAttribute("description", + "Some description"); + ModificationItem[] mods = new ModificationItem[1]; + mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr); + + try { + tested.modifyAttributes(PERSON4_DN, mods); + fail("AttributeInUseException expected"); + } catch (AttributeInUseException expected) { + // expected + } + } + + /** + * Test written originally to verify that duplicates are allowed on ordered + * attributes, but had to be changed since Apache DS seems to disallow + * duplicates even for ordered attributes. + */ + public void testModifyAttributes_MultiValueAddDuplicateToOrdered() { + BasicAttribute attr = new BasicAttribute("description", + "Some other description", true); // ordered + attr.add("Another description"); + // Commented out duplicate to make test work for Apache DS + // attr.add("Some description"); + ModificationItem[] mods = new ModificationItem[1]; + mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr); + + tested.modifyAttributes(PERSON4_DN, mods); + + DirContextAdapter result = (DirContextAdapter) tested + .lookup(PERSON4_DN); + String[] attributes = result.getStringAttributes("description"); + assertEquals(3, attributes.length); + assertEquals("Some description", attributes[0]); + assertEquals("Some other description", attributes[1]); + assertEquals("Another description", attributes[2]); + } + + public void testModifyAttributes_Plain() { + ModificationItem item = new ModificationItem( + DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("description", + "Some other description")); + + tested.modifyAttributes(PERSON4_DN, new ModificationItem[] { item }); + + verifyBoundCorrectData(); + } + + public void testModifyAttributes_DistinguishedName() { + ModificationItem item = new ModificationItem( + DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("description", + "Some other description")); + + tested.modifyAttributes(new DistinguishedName(PERSON4_DN), + new ModificationItem[] { item }); + + verifyBoundCorrectData(); + } + + public void testModifyAttributes_DirContextAdapter_MultiAttributes() { + DirContextAdapter adapter = (DirContextAdapter) tested + .lookup(PERSON5_DN); + adapter.setAttributeValues("description", new String[] { "qwe", "123", + "klytt", "kalle" }); + + tested.modifyAttributes(PERSON5_DN, adapter.getModificationItems()); + + // Verify + adapter = (DirContextAdapter) tested.lookup(PERSON5_DN); + String[] attributes = adapter.getStringAttributes("description"); + assertEquals(4, attributes.length); + assertEquals("qwe", attributes[0]); + assertEquals("123", attributes[1]); + assertEquals("klytt", attributes[2]); + assertEquals("kalle", attributes[3]); + } + + /** + * Demonstrates how the DirContextAdapter can be used to automatically keep + * track of changes of the attributes and deliver ModificationItems to use + * in moifyAttributes(). + */ + public void testModifyAttributes_DirContextAdapter() throws Exception { + DirContextAdapter adapter = (DirContextAdapter) tested + .lookup(PERSON4_DN); + + adapter.setAttributeValue("description", "Some other description"); + + ModificationItem[] modificationItems = adapter.getModificationItems(); + tested.modifyAttributes(PERSON4_DN, modificationItems); + + verifyBoundCorrectData(); + } + + private Attributes setupAttributes() { + Attributes attributes = new BasicAttributes(); + BasicAttribute ocattr = new BasicAttribute("objectclass"); + ocattr.add("top"); + ocattr.add("person"); + attributes.put(ocattr); + attributes.put("cn", "Some Person4"); + attributes.put("sn", "Person4"); + attributes.put("description", "Some other description"); + return attributes; + } + + private void verifyBoundCorrectData() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup(PERSON4_DN); + assertEquals("Some Person4", result.getStringAttribute("cn")); + assertEquals("Person4", result.getStringAttribute("sn")); + assertEquals("Some other description", result + .getStringAttribute("description")); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateNoBaseSuffixITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateNoBaseSuffixITest.java new file mode 100644 index 00000000..f82a2e6d --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateNoBaseSuffixITest.java @@ -0,0 +1,98 @@ +/* + * Copyright 2005-2007 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; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.CountNameClassPairCallbackHandler; + +/** + * Tests to verify that not setting a base suffix on the ContextSource (as + * defined in ldapTemplateNoBaseSuffixTestContext.xml) works as expected. + * + * NOTE: This test will not work under Java 1.4.1 or earlier. + * + * @author Mattias Arthursson + */ +public class LdapTemplateNoBaseSuffixITest extends + AbstractLdapTemplateIntegrationTest { + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateNoBaseSuffixTestContext.xml" }; + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.core.support.DefaultDirObjectFactory}) + * being set in the ContextSource. + */ + public void testLookup_Plain() { + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person2, ou=company1, c=Sweden, dc=jayway, dc=se"); + + assertEquals("Some Person2", result.getStringAttribute("cn")); + assertEquals("Person2", result.getStringAttribute("sn")); + assertEquals("Sweden, Company1, Some Person2", result + .getStringAttribute("description")); + assertEquals( + "cn=Some Person2, ou=company1, c=Sweden, dc=jayway, dc=se", + result.getDn().toString()); + assertEquals( + "cn=Some Person2, ou=company1, c=Sweden, dc=jayway, dc=se", + result.getNameInNamespace()); + } + + public void testSearch_Plain() { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + + tested.search("dc=jayway, dc=se", "(objectclass=person)", handler); + assertEquals(5, handler.getNoOfRows()); + } + + public void testBindAndUnbind_Plain() { + DirContextAdapter adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "Some Person4"); + adapter.setAttributeValue("sn", "Person4"); + tested.bind("cn=Some Person4, ou=company1, c=Sweden, dc=jayway, dc=se", + adapter, null); + + DirContextAdapter result = (DirContextAdapter) tested + .lookup("cn=Some Person4, ou=company1, c=Sweden, dc=jayway, dc=se"); + + assertEquals("Some Person4", result.getStringAttribute("cn")); + assertEquals("Person4", result.getStringAttribute("sn")); + assertEquals( + "cn=Some Person4, ou=company1, c=Sweden, dc=jayway, dc=se", + result.getDn().toString()); + + tested + .unbind("cn=Some Person4, ou=company1, c=Sweden, dc=jayway, dc=se"); + try { + tested + .lookup("cn=Some Person4, ou=company1, c=Sweden, dc=jayway, dc=se"); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateObjectBindIntegrationTest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateObjectBindIntegrationTest.java new file mode 100644 index 00000000..f4b92d3d --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateObjectBindIntegrationTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2005-2007 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; + +import java.io.Serializable; +import java.util.LinkedList; + +import javax.naming.directory.DirContext; + +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.AbstractContextSource; + +/** + * Tests bind and lookup with Java objects where the ContextSource has a + * null DirObjectFactory configured. + * + * @author Ulrik Sandberg + */ +public class LdapTemplateObjectBindIntegrationTest extends + AbstractLdapTemplateIntegrationTest { + + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateObjectBindTestContext.xml" }; + } + + public void testBindJavaObject() throws Exception { + String dn = "cn=myRandomInt"; + tested.bind(dn, new Integer(54321), null); + + Integer result = (Integer) tested.lookup(dn); + assertEquals(54321, result.intValue()); + tested.unbind(dn); + } + + public void testBindLinkedList() { + LinkedList list = new LinkedList(); + list.add(new Integer(54321)); + list.add(new Integer(67890)); + + String dn = "cn=myRandomList"; + tested.bind(dn, list, null); + + LinkedList result = (LinkedList) tested.lookup(dn); + assertEquals(2, result.size()); + assertEquals(54321, ((Integer) result.get(0)).intValue()); + assertEquals(67890, ((Integer) result.get(1)).intValue()); + tested.unbind(dn); + } + + public void testBindNonSerializableJavaObjectShouldFail() throws Exception { + NonSerializablePojo pojo = new NonSerializablePojo(); + pojo.setName("A Name"); + + try { + tested.bind("cn=myRandomObject", pojo, null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertEquals( + "can only bind Referenceable, Serializable, DirContext", + expected.getMessage()); + } + } + + /** + * Custom non-serializable class used for demonstrating bind and lookup of + * Java objects. + */ + public static class NonSerializablePojo { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public void testBindSerializableJavaObjectShouldSucceed() throws Exception { + SerializablePojo pojo = new SerializablePojo(); + pojo.setName("A Name"); + + tested.bind("cn=myRandomObject", pojo, null); + + SerializablePojo result = (SerializablePojo) tested + .lookup("cn=myRandomObject"); + assertEquals("A Name", result.getName()); + tested.unbind("cn=myRandomObject"); + } + + /** + * Custom serializable class used for demonstrating bind and lookup of Java + * objects. + */ + public static class SerializablePojo implements Serializable { + private static final long serialVersionUID = 3655768927093734908L; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * This test demonstrates that it's fully possible to perform plain JNDI + * operations from Spring LDAP. + */ + public void testPlainJndiBindJavaObject() throws Exception { + AbstractContextSource contextSource = (AbstractContextSource) tested + .getContextSource(); + DirContext ctx = contextSource.getReadWriteContext(); + + ctx.bind("cn=myRandomInt", new Integer(28420)); + + Integer result = (Integer) ctx.lookup("cn=myRandomInt"); + assertEquals(28420, result.intValue()); + tested.unbind("cn=myRandomInt"); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateRecursiveDeleteITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateRecursiveDeleteITest.java new file mode 100644 index 00000000..e43e14aa --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateRecursiveDeleteITest.java @@ -0,0 +1,127 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.Name; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Tests the recursive modification methods (unbind and the protected delete + * methods) of LdapTemplate. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapTemplateRecursiveDeleteITest extends + AbstractLdapTemplateIntegrationTest { + private LdapTemplate tested; + + private static DistinguishedName DN = new DistinguishedName( + "cn=Some Person5,ou=company1,c=Sweden"); + + private DistinguishedName firstSubDn; + + private DistinguishedName secondSubDn; + + private DistinguishedName leafDn; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + + DirContextAdapter adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "Some Person5"); + adapter.setAttributeValue("sn", "Person5"); + adapter.setAttributeValue("description", "Some description"); + tested.bind(DN, adapter, null); + + firstSubDn = new DistinguishedName("cn=subPerson"); + firstSubDn.prepend(DN); + adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "subPerson"); + adapter.setAttributeValue("sn", "subPerson"); + adapter.setAttributeValue("description", + "Should be recursively deleted"); + tested.bind(firstSubDn, adapter, null); + secondSubDn = new DistinguishedName("cn=subPerson2"); + secondSubDn.prepend(DN); + adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "subPerson2"); + adapter.setAttributeValue("sn", "subPerson2"); + adapter.setAttributeValue("description", + "Should be recursively deleted"); + tested.bind(secondSubDn, adapter, null); + + leafDn = new DistinguishedName("cn=subSubPerson"); + leafDn.prepend(firstSubDn); + adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "subSubPerson"); + adapter.setAttributeValue("sn", "subSubPerson"); + adapter.setAttributeValue("description", + "Should be recursively deleted"); + tested.bind(leafDn, adapter, null); + } + + protected void onTearDown() throws Exception { + try { + tested.unbind(DN, true); + } catch (NameNotFoundException ignore) { + // ignore + } + } + + public void testRecursiveUnbind() { + tested.unbind(DN, true); + + verifyDeleted(DN); + verifyDeleted(firstSubDn); + verifyDeleted(secondSubDn); + verifyDeleted(leafDn); + } + + public void testRecursiveUnbindOnLeaf() { + tested.unbind(leafDn, true); + verifyDeleted(leafDn); + } + + private void verifyDeleted(Name dn) { + try { + tested.lookup(dn); + fail("Expected entry '" + dn + "' to be non-existent"); + } catch (NameNotFoundException expected) { + // expected + } + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateRenameITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateRenameITest.java new file mode 100644 index 00000000..64a78c51 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateRenameITest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2005-2007 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; + +import javax.naming.Name; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Tests the rename methods of LdapTemplate. + * + * We rely on that the bind, unbind and lookup methods work as they should - + * that should be ok, since that is verified in a separate test class. * + * + * @author Ulrik Sandberg + */ +public class LdapTemplateRenameITest extends + AbstractLdapTemplateIntegrationTest { + private LdapTemplate tested; + + private static String DN = "cn=Some Person6,ou=company1,c=Sweden"; + + private static String NEWDN = "cn=Some Person6,ou=company2,c=Sweden"; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + + DirContextAdapter adapter = new DirContextAdapter(); + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", "Some Person6"); + adapter.setAttributeValue("sn", "Person6"); + adapter.setAttributeValue("description", "Some description"); + + tested.bind(DN, adapter, null); + } + + protected void onTearDown() throws Exception { + tested.unbind(NEWDN); + tested.unbind(DN); + } + + public void testRename() { + tested.rename(DN, NEWDN); + + verifyDeleted(new DistinguishedName(DN)); + verifyBoundCorrectData(); + } + + public void testRename_DistinguishedName() throws Exception { + Name oldDn = new DistinguishedName(DN); + Name newDn = new DistinguishedName(NEWDN); + tested.rename(oldDn, newDn); + + + verifyDeleted(oldDn); + verifyBoundCorrectData(); + } + + private void verifyDeleted(Name dn) { + try { + tested.lookup(dn); + fail("Expected entry '" + dn + "' to be non-existent"); + } catch (NameNotFoundException expected) { + // expected + } + } + + private void verifyBoundCorrectData() { + DirContextAdapter result = (DirContextAdapter) tested.lookup(NEWDN); + assertEquals("Some Person6", result.getStringAttribute("cn")); + assertEquals("Person6", result.getStringAttribute("sn")); + assertEquals("Some description", result + .getStringAttribute("description")); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateSearchCountITest.xls b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateSearchCountITest.xls new file mode 100644 index 00000000..a7f7e40c Binary files /dev/null and b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateSearchCountITest.xls differ diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateSearchResultITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateSearchResultITest.java new file mode 100644 index 00000000..f0f3f867 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/LdapTemplateSearchResultITest.java @@ -0,0 +1,181 @@ +/* + * Copyright 2005-2007 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; + +import java.util.List; + +import javax.naming.Name; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.test.AttributeCheckAttributesMapper; +import org.springframework.ldap.test.AttributeCheckContextMapper; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests for LdapTemplate's search methods. This test class tests all the + * different versions of the search methods except the generic ones covered in + * other tests. + * + * @author Mattias Arthursson + */ +public class LdapTemplateSearchResultITest extends + AbstractDependencyInjectionSpringContextTests { + + private LdapTemplate tested; + + private AttributeCheckAttributesMapper attributesMapper; + + private AttributeCheckContextMapper contextMapper; + + private static final String[] ALL_ATTRIBUTES = { "cn", "sn", "description", + "telephoneNumber" }; + + private static final String[] CN_SN_ATTRS = { "cn", "sn" }; + + private static final String[] ABSENT_ATTRIBUTES = { "description", + "telephoneNumber" }; + + private static final String[] CN_SN_VALUES = { "Some Person2", "Person2" }; + + private static final String[] ALL_VALUES = { "Some Person2", "Person2", + "Sweden, Company1, Some Person2", "+46 555-654321" }; + + private static final String BASE_STRING = ""; + + private static final String FILTER_STRING = "(&(objectclass=person)(sn=Person2))"; + + private static final Name BASE_NAME = new DistinguishedName(BASE_STRING); + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + + attributesMapper = new AttributeCheckAttributesMapper(); + contextMapper = new AttributeCheckContextMapper(); + } + + protected void onTearDown() throws Exception { + super.onTearDown(); + + attributesMapper = null; + contextMapper = null; + } + + public void testSearch_AttributesMapper() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_STRING, FILTER_STRING, attributesMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_AttributesMapper() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_STRING, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, attributesMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_LimitedAttrs_AttributesMapper() { + attributesMapper.setExpectedAttributes(CN_SN_ATTRS); + attributesMapper.setExpectedValues(CN_SN_VALUES); + attributesMapper.setAbsentAttributes(ABSENT_ATTRIBUTES); + List list = tested.search(BASE_STRING, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, CN_SN_ATTRS, attributesMapper); + assertEquals(1, list.size()); + } + + public void testSearch_AttributesMapper_Name() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_NAME, FILTER_STRING, attributesMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_AttributesMapper_Name() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_NAME, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, attributesMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_LimitedAttrs_AttributesMapper_Name() { + attributesMapper.setExpectedAttributes(CN_SN_ATTRS); + attributesMapper.setExpectedValues(CN_SN_VALUES); + attributesMapper.setAbsentAttributes(ABSENT_ATTRIBUTES); + List list = tested.search(BASE_NAME, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, CN_SN_ATTRS, attributesMapper); + assertEquals(1, list.size()); + } + + public void testSearch_ContextMapper() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_STRING, FILTER_STRING, contextMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_ContextMapper() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_STRING, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, contextMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_LimitedAttrs_ContextMapper() { + contextMapper.setExpectedAttributes(CN_SN_ATTRS); + contextMapper.setExpectedValues(CN_SN_VALUES); + contextMapper.setAbsentAttributes(ABSENT_ATTRIBUTES); + List list = tested.search(BASE_STRING, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, CN_SN_ATTRS, contextMapper); + assertEquals(1, list.size()); + } + + public void testSearch_ContextMapper_Name() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_NAME, FILTER_STRING, contextMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_ContextMapper_Name() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.search(BASE_NAME, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, contextMapper); + assertEquals(1, list.size()); + } + + public void testSearch_SearchScope_LimitedAttrs_ContextMapper_Name() { + contextMapper.setExpectedAttributes(CN_SN_ATTRS); + contextMapper.setExpectedValues(CN_SN_VALUES); + contextMapper.setAbsentAttributes(ABSENT_ATTRIBUTES); + List list = tested.search(BASE_NAME, FILTER_STRING, + SearchControls.SUBTREE_SCOPE, CN_SN_ATTRS, contextMapper); + assertEquals(1, list.size()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/authentication/AcegiAuthenticationSourceITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/authentication/AcegiAuthenticationSourceITest.java new file mode 100644 index 00000000..8e562008 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/authentication/AcegiAuthenticationSourceITest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2005-2007 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.authentication; + +import java.util.List; + +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + + +import org.acegisecurity.Authentication; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; +import org.acegisecurity.providers.ldap.LdapAuthenticationProvider; +import org.springframework.ldap.authentication.AcegiAuthenticationSource; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +public class AcegiAuthenticationSourceITest extends + AbstractDependencyInjectionSpringContextTests { + + private LdapAuthenticationProvider ldapAuthProvider; + + private LdapTemplate ldapTemplate; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateAcegiTestContext.xml" }; + } + + public void setLdapAuthProvider(LdapAuthenticationProvider ldapAuthProvider) { + this.ldapAuthProvider = ldapAuthProvider; + } + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public void testGetPrincipalAndCredentials() { + Authentication authentication = ldapAuthProvider + .authenticate(new UsernamePasswordAuthenticationToken( + "Some Person3", "password")); + SecurityContextHolder.getContext().setAuthentication(authentication); + + AcegiAuthenticationSource tested = new AcegiAuthenticationSource(); + assertEquals("cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se", + tested.getPrincipal()); + assertEquals("password", tested.getCredentials()); + } + + public void testSearchIndividualAuthentication() { + Authentication authentication = ldapAuthProvider + .authenticate(new UsernamePasswordAuthenticationToken( + "Some Person3", "password")); + SecurityContextHolder.getContext().setAuthentication(authentication); + + List result = ldapTemplate.search("dc=jayway,dc=se", + "(objectclass=person)", new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + return attributes.get("cn"); + } + }); + assertEquals(5, result.size()); + } + +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/control/SupportedControlsITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/control/SupportedControlsITest.java new file mode 100644 index 00000000..58880164 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/control/SupportedControlsITest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2005-2007 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.control; + +import java.util.Arrays; + +import org.springframework.ldap.AbstractLdapTemplateIntegrationTest; +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Provides tests that verify that the server supports certain controls. + * + * @author Ulrik Sandberg + */ +public class SupportedControlsITest extends AbstractLdapTemplateIntegrationTest { + /** must use a context source that has no base set */ + private LdapTemplate tested; + + private static final String SUPPORTED_CONTROL = "supportedcontrol"; + + protected String[] getConfigLocations() { + return new String[] { "/conf/rootContextSourceTestContext.xml" }; + } + + public void testExpectedControlsSupported() throws Exception { + /** + * Maps the 'supportedcontrol' attribute to a string array. + */ + ContextMapper mapper = new ContextMapper() { + + public Object mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + return adapter.getStringAttributes(SUPPORTED_CONTROL); + } + + }; + + String[] controls = (String[]) tested.lookup("", + new String[] { SUPPORTED_CONTROL }, mapper); + System.out.println(Arrays.toString(controls)); + assertEquals("Persistent Search LDAPv3 control,", + "2.16.840.1.113730.3.4.3", controls[0]); + assertEquals("Entry Change Notification LDAPv3 control,", + "2.16.840.1.113730.3.4.7", controls[1]); + assertEquals("Subentries Control,", "1.3.6.1.4.1.4203.1.10.1", + controls[2]); + assertEquals("Manage DSA IT LDAPv3 control,", + "2.16.840.1.113730.3.4.2", controls[3]); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DistinguishedNameEditorITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DistinguishedNameEditorITest.java new file mode 100644 index 00000000..4450e8f8 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DistinguishedNameEditorITest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2005-2007 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.core; + +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Integration tests for {@link DistinguishedNameEditor}. + * + * @author Mattias Arthursson + */ +public class DistinguishedNameEditorITest extends AbstractDependencyInjectionSpringContextTests { + + private DummyDistinguishedNameConsumer distinguishedNameConsumer; + + protected String[] getConfigLocations() { + return new String[] { "classpath:conf/distinguishedNameEditorTestContext.xml" }; + } + + public void testDistinguishedNameEditor() throws Exception { + assertNotNull(distinguishedNameConsumer); + DistinguishedName name = distinguishedNameConsumer.getDistinguishedName(); + assertEquals(new DistinguishedName("dc=jayway, dc=se"), name); + } + + public void setDistinguishedNameConsumer(DummyDistinguishedNameConsumer distinguishedNameConsumer) { + this.distinguishedNameConsumer = distinguishedNameConsumer; + } + +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DnParsePerformanceITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DnParsePerformanceITest.java new file mode 100644 index 00000000..6a31ff76 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DnParsePerformanceITest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2007 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.core; + +import org.springframework.ldap.core.DistinguishedName; + +import junit.framework.Test; +import junit.framework.TestCase; + +import com.clarkware.junitperf.LoadTest; + +/** + * Performance test for the {@link DistinguishedName} class. + * + * @author Ulrik Sandberg + */ +public class DnParsePerformanceITest extends TestCase { + + public DnParsePerformanceITest(String name) { + super(name); + } + + /** + * Tests parsing and toString. + */ + public void testContains() { + + DistinguishedName migpath = new DistinguishedName("OU=G,OU=I,OU=M"); + DistinguishedName path1 = new DistinguishedName( + "cn=john.doe, OU=Users,OU=SE,OU=G,OU=I,OU=M"); + DistinguishedName path2 = new DistinguishedName( + "cn=john.doe, OU=Users,OU=SE,ou=G,OU=i,OU=M, ou=foo"); + DistinguishedName path3 = new DistinguishedName( + "ou=G,OU=i,OU=M, ou=foo"); + DistinguishedName path4 = new DistinguishedName("ou=G,OU=i,ou=m"); + + DistinguishedName pathE1 = new DistinguishedName( + "cn=john.doe, OU=Users,OU=SE,ou=G,OU=L,OU=M, ou=foo"); + DistinguishedName pathE2 = new DistinguishedName( + "cn=john.doe, OU=Users,OU=SE"); + } + + public static Test suite() { + int users = 2000; + Test testCase = new DnParsePerformanceITest("testContains"); + Test loadTest = new LoadTest(testCase, users); + return loadTest; + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } +} \ No newline at end of file diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DummyDistinguishedNameConsumer.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DummyDistinguishedNameConsumer.java new file mode 100644 index 00000000..0b9c800d --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/DummyDistinguishedNameConsumer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2005-2007 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.core; + +/** + * Dummy implementation of a class that has a {@link DistinguishedName} setter, + * used for testing purposes. + * + * @author Mattias Arthursson + */ +public class DummyDistinguishedNameConsumer { + private DistinguishedName distinguishedName; + + public DistinguishedName getDistinguishedName() { + return distinguishedName; + } + + public void setDistinguishedName(DistinguishedName distinguishedName) { + this.distinguishedName = distinguishedName; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostprocessorITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostprocessorITest.java new file mode 100644 index 00000000..ea001ef6 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/BaseLdapPathBeanPostprocessorITest.java @@ -0,0 +1,97 @@ +/* + * Copyright 2005-2007 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.core.support; + +import junit.framework.TestCase; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.ldap.core.DistinguishedName; + +/** + * Integration tests for {@link BaseLdapPathBeanPostProcessor}. + * + * @author Mattias Arthursson + */ +public class BaseLdapPathBeanPostprocessorITest extends TestCase { + + protected String[] getConfigLocations() { + return new String[] { "/conf/baseLdapPathPostProcessorTestContext.xml" }; + } + + public void testPostProcessBeforeInitialization() throws Exception { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( + "/conf/baseLdapPathPostProcessorTestContext.xml"); + DummyBaseLdapPathAware tested = (DummyBaseLdapPathAware) ctx.getBean("dummyBaseContextAware"); + + DistinguishedName base = tested.getBase(); + assertNotNull(base); + assertEquals(new DistinguishedName("dc=jayway,dc=se"), base); + } + + public void testPostProcessBeforeInitializationMultipleContextSources() throws Exception { + try { + new ClassPathXmlApplicationContext("/conf/baseLdapPathPostProcessorMultiContextSourceTestContext.xml"); + fail("BeanCreationException expected"); + } + catch (BeanCreationException expected) { + Throwable cause = expected.getCause(); + assertTrue(cause instanceof NoSuchBeanDefinitionException); + } + } + + public void testPostProcessBeforeInitializationMultipleContextSourcesOneSpecified() throws Exception { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( + "/conf/baseLdapPathPostProcessorMultiContextSourceOneSpecTestContext.xml"); + DummyBaseLdapPathAware tested = (DummyBaseLdapPathAware) ctx.getBean("dummyBaseContextAware"); + + DistinguishedName base = tested.getBase(); + assertNotNull(base); + assertEquals(new DistinguishedName("cn=john doe,dc=jayway,dc=se"), base); + } + + public void testPostProcessBeforeInitializationNoContextSource() throws Exception { + try { + new ClassPathXmlApplicationContext("/conf/baseLdapPathPostProcessorNoContextSourceTestContext.xml"); + fail("BeanCreationException expected"); + } + catch (BeanCreationException expected) { + Throwable cause = expected.getCause(); + assertTrue(cause instanceof NoSuchBeanDefinitionException); + } + } + + public void testPostProcessBeforeInitializationBaseSetInProperty() throws Exception { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( + "/conf/baseLdapPathPostProcessorPropertyOverrideTestContext.xml"); + DummyBaseLdapPathAware tested = (DummyBaseLdapPathAware) ctx.getBean("dummyBaseContextAware"); + + DistinguishedName base = tested.getBase(); + assertNotNull(base); + assertEquals(new DistinguishedName("cn=john doe"), base); + } + + public void testPostProcessBeforeInitializationTransactionProxy() throws Exception { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( + "/conf/baseLdapPathPostProcessorTransactionTestContext.xml"); + DummyBaseLdapPathAware tested = (DummyBaseLdapPathAware) ctx.getBean("dummyBaseContextAware"); + + DistinguishedName base = tested.getBase(); + assertNotNull(base); + assertEquals(new DistinguishedName("dc=jayway,dc=se"), base); + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/DummyBaseLdapPathAware.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/DummyBaseLdapPathAware.java new file mode 100644 index 00000000..443980b3 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/DummyBaseLdapPathAware.java @@ -0,0 +1,37 @@ +/* + * Copyright 2005-2007 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.core.support; + +import org.springframework.ldap.core.DistinguishedName; + +/** + * Dummy implementation of {@link BaseLdapPathAware}. + * + * @author Mattias Arthursson + */ +public class DummyBaseLdapPathAware implements BaseLdapPathAware { + + private DistinguishedName base; + + public void setBaseLdapPath(DistinguishedName baseLdapPath) { + this.base = baseLdapPath; + } + + public DistinguishedName getBase() { + return base; + } + +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourcelITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourcelITest.java new file mode 100644 index 00000000..d1f5df7c --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourcelITest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2005-2007 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.core.support; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.springframework.ldap.core.ContextSource; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Integration tests for ContextSourceImpl. + * + * @author Mattias Arthursson + */ +public class LdapContextSourcelITest extends + AbstractDependencyInjectionSpringContextTests { + + private ContextSource tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + public void testGetReadOnlyContext() throws NamingException { + DirContext ctx = null; + + try { + ctx = tested.getReadOnlyContext(); + assertNotNull(ctx); + Hashtable environment = ctx.getEnvironment(); + assertTrue(environment + .containsKey(LdapContextSource.SUN_LDAP_POOLING_FLAG)); + assertTrue(environment.containsKey(Context.SECURITY_PRINCIPAL)); + assertTrue(environment.containsKey(Context.SECURITY_CREDENTIALS)); + } finally { + // Always clean up. + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this + } + } + } + } + + public void testGetReadWriteContext() throws NamingException { + DirContext ctx = null; + + try { + ctx = tested.getReadWriteContext(); + assertNotNull(ctx); + // Double check to see that we are authenticated. + Hashtable environment = ctx.getEnvironment(); + assertTrue(environment.containsKey(Context.SECURITY_PRINCIPAL)); + assertTrue(environment.containsKey(Context.SECURITY_CREDENTIALS)); + } finally { + // Always clean up. + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this + } + } + } + } + + public void setTested(ContextSource tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourcelMultiServerITest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourcelMultiServerITest.java new file mode 100644 index 00000000..caec3a68 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourcelMultiServerITest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2005-2007 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.core.support; + +import javax.naming.NamingException; + +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Integration tests for ContextSourceImpl. + * + * @author Mattias Arthursson + */ +public class LdapContextSourcelMultiServerITest extends + AbstractDependencyInjectionSpringContextTests { + + private LdapContextSource tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapContextSourceTestContext.xml" }; + } + + public void testUrls() throws NamingException { + String[] urls = tested.getUrls(); + String string = tested.assembleProviderUrlString(urls); + + assertEquals("ldap://127.0.0.1:389 ldap://127.0.0.2:389", string); + } + + public void setTested(LdapContextSource tested) { + this.tested = tested; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceAndDataSourceTransactionManagerIntegrationTest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceAndDataSourceTransactionManagerIntegrationTest.java new file mode 100644 index 00000000..ee844529 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceAndDataSourceTransactionManagerIntegrationTest.java @@ -0,0 +1,371 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.manager; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * Integration tests for {@link ContextSourceAndDataSourceTransactionManager}. + * + * @author Mattias Arthursson + */ +public class ContextSourceAndDataSourceTransactionManagerIntegrationTest extends + AbstractDependencyInjectionSpringContextTests { + + private static Log log = LogFactory + .getLog(ContextSourceAndDataSourceTransactionManagerIntegrationTest.class); + + private DummyDao dummyDao; + + private LdapTemplate ldapTemplate; + + private JdbcTemplate jdbcTemplate; + + public ContextSourceAndDataSourceTransactionManagerIntegrationTest() { + setAutowireMode(AbstractDependencyInjectionSpringContextTests.AUTOWIRE_BY_NAME); + } + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public void setDummyDao(DummyDao dummyDaoImpl) { + this.dummyDao = dummyDaoImpl; + } + + protected String[] getConfigLocations() { + return new String[] { "conf/ldapAndJdbcTransactionTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + + jdbcTemplate + .execute("create table PERSON(fullname VARCHAR, lastname VARCHAR, description VARCHAR)"); + jdbcTemplate.update("insert into PERSON values(?, ?, ?)", new Object[] { + "Some Person", "Person", "Sweden, Company1, Some Person" }); + } + + protected void onTearDown() throws Exception { + jdbcTemplate.execute("drop table PERSON"); + } + + public void testCreateWithException() { + try { + dummyDao.createWithException("Sweden", "company1", + "some testperson", "testperson", "some description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + log.debug("Verifying result"); + + // Verify that no entry was created + try { + ldapTemplate.lookup("cn=some testperson, ou=company1, c=Sweden"); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + try { + jdbcTemplate.queryForObject( + "select * from PERSON where fullname='some testperson'", + new RowMapper() { + public Object mapRow(ResultSet rs, int rowNum) + throws SQLException { + return null; + } + }); + fail("EmptyResultDataAccessException expected"); + } catch (EmptyResultDataAccessException expected) { + assertTrue(true); + } + } + + public void testCreate() { + dummyDao.create("Sweden", "company1", "some testperson", "testperson", + "some description"); + + log.debug("Verifying result"); + Object ldapResult = ldapTemplate + .lookup("cn=some testperson, ou=company1, c=Sweden"); + Object dbResult = jdbcTemplate.queryForObject( + "select * from PERSON where fullname='some testperson'", + new RowMapper() { + public Object mapRow(ResultSet rs, int rowNum) + throws SQLException { + return new Object(); + } + }); + assertNotNull(ldapResult); + assertNotNull(dbResult); + + ldapTemplate.unbind("cn=some testperson, ou=company1, c=Sweden"); + } + + public void testUpdateWithException() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + try { + dummyDao.updateWithException(dn, "Some Person", "Updated Person", + "Updated description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + log.debug("Verifying result"); + + Object ldapResult = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Person", attributes.get("sn").get()); + assertEquals("Sweden, Company1, Some Person", attributes.get( + "description").get()); + return new Object(); + } + }); + + Object jdbcResult = jdbcTemplate.queryForObject( + "select * from PERSON where fullname=?", + new Object[] { "Some Person" }, new RowMapper() { + public Object mapRow(ResultSet rs, int rowNum) + throws SQLException { + assertEquals("Person", rs.getString("lastname")); + assertEquals("Sweden, Company1, Some Person", rs + .getString("description")); + return new Object(); + } + }); + + assertNotNull(ldapResult); + assertNotNull(jdbcResult); + } + + public void testUpdate() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + dummyDao.update(dn, "Some Person", "Updated Person", + "Updated description"); + + log.debug("Verifying result"); + Object ldapResult = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Updated Person", attributes.get("sn").get()); + assertEquals("Updated description", attributes.get( + "description").get()); + return new Object(); + } + }); + + Object jdbcResult = jdbcTemplate.queryForObject( + "select * from PERSON where fullname=?", + new Object[] { "Some Person" }, new RowMapper() { + public Object mapRow(ResultSet rs, int rowNum) + throws SQLException { + assertEquals("Updated Person", rs.getString("lastname")); + assertEquals("Updated description", rs + .getString("description")); + return new Object(); + } + }); + + assertNotNull(ldapResult); + assertNotNull(jdbcResult); + dummyDao.update(dn, "Some Person", "Person", + "Sweden, Company1, Some Person"); + } + + public void testUpdateAndRenameWithException() { + String dn = "cn=Some Person2,ou=company1,c=Sweden"; + String newDn = "cn=Some Person2,ou=company2,c=Sweden"; + try { + // Perform test + dummyDao.updateAndRenameWithException(dn, newDn, + "Updated description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + // Verify that entry was not moved. + try { + ldapTemplate.lookup(newDn); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + // Verify that original entry was not updated. + Object object = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Sweden, Company1, Some Person2", attributes.get( + "description").get()); + return new Object(); + } + }); + assertNotNull(object); + } + + public void testUpdateAndRename() { + String dn = "cn=Some Person2,ou=company1,c=Sweden"; + String newDn = "cn=Some Person2,ou=company2,c=Sweden"; + // Perform test + dummyDao.updateAndRename(dn, newDn, "Updated description"); + + // Verify that entry was moved and updated. + Object object = ldapTemplate.lookup(newDn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Updated description", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(object); + dummyDao.updateAndRename(newDn, dn, "Sweden, Company1, Some Person2"); + } + + public void testModifyAttributesWithException() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + try { + // Perform test + dummyDao.modifyAttributesWithException(dn, "Updated lastname", + "Updated description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + // Verify result - check that the operation was properly rolled back + Object result = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Person", attributes.get("sn").get()); + assertEquals("Sweden, Company1, Some Person", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(result); + } + + public void testModifyAttributes() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + // Perform test + dummyDao + .modifyAttributes(dn, "Updated lastname", "Updated description"); + + // Verify result - check that the operation was not rolled back + Object result = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Updated lastname", attributes.get("sn").get()); + assertEquals("Updated description", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(result); + } + + public void testUnbindWithException() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + try { + // Perform test + dummyDao.unbindWithException(dn, "Some Person"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + // Verify result - check that the operation was properly rolled back + Object ldapResult = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + // Just verify that the entry still exists. + return new Object(); + } + }); + + Object jdbcResult = jdbcTemplate.queryForObject( + "select * from PERSON where fullname=?", + new Object[] { "Some Person" }, new RowMapper() { + public Object mapRow(ResultSet rs, int rowNum) + throws SQLException { + // Just verify that the entry still exists. + return new Object(); + } + }); + + assertNotNull(ldapResult); + assertNotNull(jdbcResult); + } + + public void testUnbind() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + // Perform test + dummyDao.unbind(dn, "Some Person"); + + try { + // Verify result - check that the operation was not rolled back + ldapTemplate.lookup(dn); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + try { + jdbcTemplate.queryForObject( + "select * from PERSON where fullname=?", + new Object[] { "Some Person" }, new RowMapper() { + public Object mapRow(ResultSet rs, int rowNum) + throws SQLException { + return null; + } + }); + fail("EmptyResultDataAccessException expected"); + } catch (EmptyResultDataAccessException expected) { + assertTrue(true); + } + } + + public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerIntegrationTest.java b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerIntegrationTest.java new file mode 100644 index 00000000..aaf94e12 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/org/springframework/ldap/transaction/compensating/manager/ContextSourceTransactionManagerIntegrationTest.java @@ -0,0 +1,281 @@ +/* + * Copyright 2005-2007 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.transaction.compensating.manager; + +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * Integration tests for {@link ContextSourceAndDataSourceTransactionManager}. + * + * @author Mattias Arthursson + */ +public class ContextSourceTransactionManagerIntegrationTest extends + AbstractDependencyInjectionSpringContextTests { + + private static Log log = LogFactory + .getLog(ContextSourceTransactionManagerIntegrationTest.class); + + public ContextSourceTransactionManagerIntegrationTest() { + setAutowireMode(AbstractDependencyInjectionSpringContextTests.AUTOWIRE_BY_NAME); + } + + private DummyDao dummyDao; + + private LdapTemplate ldapTemplate; + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public void setDummyDao(DummyDao dummyDaoImpl) { + this.dummyDao = dummyDaoImpl; + } + + protected String[] getConfigLocations() { + return new String[] { "conf/ldapTemplateTransactionTestContext.xml" }; + } + + protected void onSetUp() throws Exception { + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + } + + protected void onTearDown() throws Exception { + } + + public void testCreateWithException() { + try { + dummyDao.createWithException("Sweden", "company1", + "some testperson", "testperson", "some description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + log.debug("Verifying result"); + + // Verify that no entry was created + try { + ldapTemplate.lookup("cn=some testperson, ou=company1, c=Sweden"); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + } + + public void testCreate() { + dummyDao.create("Sweden", "company1", "some testperson", "testperson", + "some description"); + + log.debug("Verifying result"); + String expectedDn = "cn=some testperson, ou=company1, c=Sweden"; + Object ldapResult = ldapTemplate.lookup(expectedDn); + assertNotNull(ldapResult); + + ldapTemplate.unbind(expectedDn); + } + + public void testUpdateWithException() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + try { + dummyDao.updateWithException(dn, "Some Person", "Updated Person", + "Updated description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + log.debug("Verifying result"); + + Object ldapResult = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Person", attributes.get("sn").get()); + assertEquals("Sweden, Company1, Some Person", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(ldapResult); + } + + public void testUpdate() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + dummyDao.update(dn, "Some Person", "Updated Person", + "Updated description"); + + log.debug("Verifying result"); + Object ldapResult = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Updated Person", attributes.get("sn").get()); + assertEquals("Updated description", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(ldapResult); + + dummyDao.update(dn, "Some Person", "Person", + "Sweden, Company1, Some Person"); + } + + public void testUpdateAndRenameWithException() { + String dn = "cn=Some Person2,ou=company1,c=Sweden"; + String newDn = "cn=Some Person2,ou=company2,c=Sweden"; + try { + // Perform test + dummyDao.updateAndRenameWithException(dn, newDn, + "Updated description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + // Verify that entry was not moved. + try { + ldapTemplate.lookup(newDn); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + // Verify that original entry was not updated. + Object object = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Sweden, Company1, Some Person2", attributes.get( + "description").get()); + return new Object(); + } + }); + assertNotNull(object); + } + + public void testUpdateAndRename() { + String dn = "cn=Some Person2,ou=company1,c=Sweden"; + String newDn = "cn=Some Person2,ou=company2,c=Sweden"; + // Perform test + dummyDao.updateAndRename(dn, newDn, "Updated description"); + + // Verify that entry was moved and updated. + Object object = ldapTemplate.lookup(newDn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Updated description", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(object); + dummyDao.updateAndRename(newDn, dn, "Sweden, Company1, Some Person2"); + } + + public void testModifyAttributesWithException() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + try { + // Perform test + dummyDao.modifyAttributesWithException(dn, "Updated lastname", + "Updated description"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + // Verify result - check that the operation was properly rolled back + Object result = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Person", attributes.get("sn").get()); + assertEquals("Sweden, Company1, Some Person", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(result); + } + + public void testModifyAttributes() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + // Perform test + dummyDao + .modifyAttributes(dn, "Updated lastname", "Updated description"); + + // Verify result - check that the operation was not rolled back + Object result = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + assertEquals("Updated lastname", attributes.get("sn").get()); + assertEquals("Updated description", attributes.get( + "description").get()); + return new Object(); + } + }); + + assertNotNull(result); + } + + public void testUnbindWithException() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + try { + // Perform test + dummyDao.unbindWithException(dn, "Some Person"); + fail("DummyException expected"); + } catch (DummyException expected) { + assertTrue(true); + } + + // Verify result - check that the operation was properly rolled back + Object ldapResult = ldapTemplate.lookup(dn, new AttributesMapper() { + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + // Just verify that the entry still exists. + return new Object(); + } + }); + + assertNotNull(ldapResult); + } + + public void testUnbind() { + String dn = "cn=Some Person,ou=company1,c=Sweden"; + // Perform test + dummyDao.unbind(dn, "Some Person"); + + try { + // Verify result - check that the operation was not rolled back + ldapTemplate.lookup(dn); + fail("NameNotFoundException expected"); + } catch (NameNotFoundException expected) { + assertTrue(true); + } + + } +} diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/setup_data.ldif b/mvn-build/integration-tests/core-integration-tests/src/test/java/setup_data.ldif new file mode 100644 index 00000000..885a8ca4 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/setup_data.ldif @@ -0,0 +1,110 @@ +dn: ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: cn=ROLE_USER,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_USER +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person,ou=company1,c=Norway,dc=jayway,dc=se +uniqueMember: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se + +dn: cn=ROLE_ADMIN,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_ADMIN +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se + +dn: c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: country +c: Sweden +description: The country of Sweden + +dn: c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: country +c: Norway +description: The country of Norway + +dn: ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Sweden + +dn: ou=company2,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company2 +description: Second company in Sweden + +dn: ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Norway + +dn: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: +46 555-123456 + +dn: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person2 +userPassword: password +cn: Some Person2 +sn: Person2 +description: Sweden, Company1, Some Person2 +telephoneNumber: +46 555-654321 + +dn: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person3 +userPassword: password +cn: Some Person3 +sn: Person3 +description: Sweden, Company1, Some Person3 +telephoneNumber: +46 555-123654 + +dn: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person4 +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company2, Some Person +telephoneNumber: +46 555-456321 + +dn: cn=Some Person+sn=Person,ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.norwegian +userPassword: password +cn: Some Person +sn: Person +description: Norway, Company1, Some Person+Person +telephoneNumber: +45 555-654123 diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/java/teardown_data.ldif b/mvn-build/integration-tests/core-integration-tests/src/test/java/teardown_data.ldif new file mode 100644 index 00000000..f6ada1b2 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/java/teardown_data.ldif @@ -0,0 +1,5 @@ +ou=groups,dc=jayway,dc=se + +c=Sweden,dc=jayway,dc=se + +c=Norway,dc=jayway,dc=se diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/apacheDsContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/apacheDsContext.xml new file mode 100644 index 00000000..6620b80d --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/apacheDsContext.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + simple + + + ${userDn} + + + ${password} + + + + + + + + + + + + + + + + + + + + + objectClass: top + objectClass: domain + objectClass: extensibleObject + dc: jayway + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorMultiContextSourceOneSpecTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorMultiContextSourceOneSpecTestContext.xml new file mode 100644 index 00000000..2d312a68 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorMultiContextSourceOneSpecTestContext.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorMultiContextSourceTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorMultiContextSourceTestContext.xml new file mode 100644 index 00000000..1a1d104c --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorMultiContextSourceTestContext.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorNoContextSourceTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorNoContextSourceTestContext.xml new file mode 100644 index 00000000..52798e86 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorNoContextSourceTestContext.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorPropertyOverrideTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorPropertyOverrideTestContext.xml new file mode 100644 index 00000000..8b42a613 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorPropertyOverrideTestContext.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorTestContext.xml new file mode 100644 index 00000000..49a67621 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorTestContext.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorTransactionTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorTransactionTestContext.xml new file mode 100644 index 00000000..066e6a58 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/baseLdapPathPostProcessorTransactionTestContext.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/commonTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/commonTestContext.xml new file mode 100644 index 00000000..a71c074e --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/commonTestContext.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/distinguishedNameEditorTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/distinguishedNameEditorTestContext.xml new file mode 100644 index 00000000..86f40c59 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/distinguishedNameEditorTestContext.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldap.properties b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldap.properties new file mode 100644 index 00000000..8843f1f1 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1:3900 +userDn=uid=admin,ou=system +password=secret +base=dc=jayway,dc=se diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapAndJdbcTransactionTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapAndJdbcTransactionTestContext.xml new file mode 100644 index 00000000..abaaab86 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapAndJdbcTransactionTestContext.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROPAGATION_REQUIRES_NEW + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapContextSourceTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapContextSourceTestContext.xml new file mode 100644 index 00000000..2adc5219 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapContextSourceTestContext.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateAcegiTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateAcegiTestContext.xml new file mode 100644 index 00000000..a7ba9c2c --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateAcegiTestContext.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateNoBaseSuffixTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateNoBaseSuffixTestContext.xml new file mode 100644 index 00000000..312b761f --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateNoBaseSuffixTestContext.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateObjectBindTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateObjectBindTestContext.xml new file mode 100644 index 00000000..f67e95d5 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateObjectBindTestContext.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateTestContext.xml new file mode 100644 index 00000000..3c85161c --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateTestContext.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateTransactionTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateTransactionTestContext.xml new file mode 100644 index 00000000..8b111f02 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/ldapTemplateTransactionTestContext.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROPAGATION_REQUIRES_NEW + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/rootContextSourceTestContext.xml b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/rootContextSourceTestContext.xml new file mode 100644 index 00000000..13cc7894 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/conf/rootContextSourceTestContext.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/log4j.properties b/mvn-build/integration-tests/core-integration-tests/src/test/resources/log4j.properties new file mode 100644 index 00000000..12c4b1c8 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/log4j.properties @@ -0,0 +1,6 @@ +log4j.rootCategory=warn, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout.ConversionPattern=%r [%t] %-5p\: %-15c{2} - %m%n +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout + +log4j.logger.org.apache.directory.server.schema.registries.DefaultSyntaxRegistry=WARN \ No newline at end of file diff --git a/mvn-build/integration-tests/core-integration-tests/src/test/resources/setup_data.ldif b/mvn-build/integration-tests/core-integration-tests/src/test/resources/setup_data.ldif new file mode 100644 index 00000000..885a8ca4 --- /dev/null +++ b/mvn-build/integration-tests/core-integration-tests/src/test/resources/setup_data.ldif @@ -0,0 +1,110 @@ +dn: ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: cn=ROLE_USER,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_USER +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person,ou=company1,c=Norway,dc=jayway,dc=se +uniqueMember: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se + +dn: cn=ROLE_ADMIN,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_ADMIN +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se + +dn: c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: country +c: Sweden +description: The country of Sweden + +dn: c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: country +c: Norway +description: The country of Norway + +dn: ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Sweden + +dn: ou=company2,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company2 +description: Second company in Sweden + +dn: ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Norway + +dn: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: +46 555-123456 + +dn: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person2 +userPassword: password +cn: Some Person2 +sn: Person2 +description: Sweden, Company1, Some Person2 +telephoneNumber: +46 555-654321 + +dn: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person3 +userPassword: password +cn: Some Person3 +sn: Person3 +description: Sweden, Company1, Some Person3 +telephoneNumber: +46 555-123654 + +dn: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person4 +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company2, Some Person +telephoneNumber: +46 555-456321 + +dn: cn=Some Person+sn=Person,ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.norwegian +userPassword: password +cn: Some Person +sn: Person +description: Norway, Company1, Some Person+Person +telephoneNumber: +45 555-654123 diff --git a/mvn-build/integration-tests/pom.xml b/mvn-build/integration-tests/pom.xml new file mode 100644 index 00000000..a3149a37 --- /dev/null +++ b/mvn-build/integration-tests/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + org.springframework.ldap + spring-ldap-integration-tests + 1.2.2-SNAPSHOT + + pom + Spring LDAP Integration Tests + + + core-integration-tests + tiger-integration-tests + + diff --git a/mvn-build/integration-tests/tiger-integration-tests/pom.xml b/mvn-build/integration-tests/tiger-integration-tests/pom.xml new file mode 100644 index 00000000..eb9fb41c --- /dev/null +++ b/mvn-build/integration-tests/tiger-integration-tests/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + org.springframework.ldap + spring-ldap-tiger-integration-tests + Spring LDAP Tiger Integration Tests + 1.2.2-SNAPSHOT + + + 2.0.8 + + + + + + maven-eclipse-plugin + + true + + + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + + + + + org.springframework.ldap + spring-ldap-test + 1.2.2-SNAPSHOT + + + org.springframework.ldap + spring-ldap-tiger + 1.2.2-SNAPSHOT + + + commons-pool + commons-pool + 1.3 + provided + + + com.sun + ldapbp + 1.0 + system + ${basedir}/../../lib/ldapbp.jar + + + + org.springframework + spring-dao + ${spring.version} + provided + + + org.springframework + spring-jdbc + ${spring.version} + provided + + + org.springframework + spring-aop + ${spring.version} + test + + + aspectj + aspectjrt + 1.5.3 + test + + + aspectj + aspectjweaver + 1.5.3 + test + + + gsbase + gsbase + 2.0.1 + test + + + hsqldb + hsqldb + 1.8.0.7 + test + + + diff --git a/mvn-build/integration-tests/tiger-integration-tests/src/test/java/org/springframework/ldap/core/simple/SimpleLdapTemplateITest.java b/mvn-build/integration-tests/tiger-integration-tests/src/test/java/org/springframework/ldap/core/simple/SimpleLdapTemplateITest.java new file mode 100644 index 00000000..6da647cd --- /dev/null +++ b/mvn-build/integration-tests/tiger-integration-tests/src/test/java/org/springframework/ldap/core/simple/SimpleLdapTemplateITest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2005-2007 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.core.simple; + +import java.util.List; + +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +public class SimpleLdapTemplateITest extends AbstractDependencyInjectionSpringContextTests { + + private SimpleLdapTemplate ldapTemplate; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + public void setSimplLdapTemplate(SimpleLdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public void testLookup() { + String result = ldapTemplate.lookup("cn=Some Person,ou=company1,c=Sweden", new CnContextMapper()); + assertEquals("Some Person", result); + } + + public void testSearch() { + List cns = ldapTemplate.search("", "(&(objectclass=person)(sn=Person3))", new CnContextMapper()); + + assertEquals(1, cns.size()); + assertEquals("Some Person3", cns.get(0)); + } + + public void testModifyAttributes() { + DirContextOperations ctx = ldapTemplate.lookupContext("cn=Some Person,ou=company1,c=Sweden"); + + ctx.setAttributeValue("description", "updated description"); + ctx.setAttributeValue("telephoneNumber", "0000001"); + + ldapTemplate.modifyAttributes(ctx); + + // verify that the data was properly updated. + ldapTemplate.lookup("cn=Some Person,ou=company1,c=Sweden", new ParameterizedContextMapper() { + public Object mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + assertEquals("updated description", adapter.getStringAttribute("description")); + assertEquals("0000001", adapter.getStringAttribute("telephoneNumber")); + return null; + } + }); + } + + private static class CnContextMapper implements ParameterizedContextMapper { + + public String mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + + return adapter.getStringAttribute("cn"); + } + } + +} diff --git a/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/conf/ldap.properties b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/conf/ldap.properties new file mode 100644 index 00000000..6789a99b --- /dev/null +++ b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/conf/ldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1:3901 +userDn=uid=admin,ou=system +password=secret +base=dc=jayway,dc=se diff --git a/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/conf/ldapTemplateTestContext.xml b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/conf/ldapTemplateTestContext.xml new file mode 100644 index 00000000..ff552c0d --- /dev/null +++ b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/conf/ldapTemplateTestContext.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/log4j.properties b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/log4j.properties new file mode 100644 index 00000000..f57302e8 --- /dev/null +++ b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/log4j.properties @@ -0,0 +1,9 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + +#Enable debug logging +log4j.category.net.sf.ldaptemplate=INFO +log4j.category.net.sf.ldaptemplate.LdapTemplate=DEBUG diff --git a/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/setup_data.ldif b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/setup_data.ldif new file mode 100644 index 00000000..885a8ca4 --- /dev/null +++ b/mvn-build/integration-tests/tiger-integration-tests/src/test/resources/setup_data.ldif @@ -0,0 +1,110 @@ +dn: ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: cn=ROLE_USER,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_USER +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person,ou=company1,c=Norway,dc=jayway,dc=se +uniqueMember: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se +uniqueMember: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se + +dn: cn=ROLE_ADMIN,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_ADMIN +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se + +dn: c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: country +c: Sweden +description: The country of Sweden + +dn: c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: country +c: Norway +description: The country of Norway + +dn: ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Sweden + +dn: ou=company2,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company2 +description: Second company in Sweden + +dn: ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Norway + +dn: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: +46 555-123456 + +dn: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person2 +userPassword: password +cn: Some Person2 +sn: Person2 +description: Sweden, Company1, Some Person2 +telephoneNumber: +46 555-654321 + +dn: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person3 +userPassword: password +cn: Some Person3 +sn: Person3 +description: Sweden, Company1, Some Person3 +telephoneNumber: +46 555-123654 + +dn: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person4 +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company2, Some Person +telephoneNumber: +46 555-456321 + +dn: cn=Some Person+sn=Person,ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.norwegian +userPassword: password +cn: Some Person +sn: Person +description: Norway, Company1, Some Person+Person +telephoneNumber: +45 555-654123 diff --git a/mvn-build/lib/ldapbp.jar b/mvn-build/lib/ldapbp.jar new file mode 100644 index 00000000..f6ce7d68 Binary files /dev/null and b/mvn-build/lib/ldapbp.jar differ diff --git a/mvn-build/pom.xml b/mvn-build/pom.xml new file mode 100644 index 00000000..415ef246 --- /dev/null +++ b/mvn-build/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + + org.springframework.ldap + projects + 1.2.2-SNAPSHOT + + pom + Spring LDAP Master Build Project + + + core + tiger + test-support + integration-tests + + + + ${basedir}/src/docbkx + ${basedir}/target/site/guide + 2.0.8 + + + + + + + + diff --git a/mvn-build/test-support/pom.xml b/mvn-build/test-support/pom.xml new file mode 100644 index 00000000..1cc35b94 --- /dev/null +++ b/mvn-build/test-support/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + org.springframework.ldap + spring-ldap-test + Spring LDAP Automated Test Support + 1.2.2-SNAPSHOT + + + 2.0.8 + + + + + + maven-compiler-plugin + + 1.4 + 1.4 + + + + + + + org.springframework.ldap + spring-ldap + 1.2.2-SNAPSHOT + + + + org.apache.commons + commons-io + 1.3.2 + + + org.apache.directory.server + apacheds-server-main + 1.5.0 + + + org.slf4j + nlog4j + + + + + org.slf4j + slf4j-log4j12 + 1.0.1 + + + + org.springframework + spring-core + ${spring.version} + + + org.springframework + spring-beans + ${spring.version} + + + org.springframework + spring-context + ${spring.version} + + + org.springframework + spring-mock + ${spring.version} + + + log4j + log4j + 1.2.9 + + + junit + junit + 3.8.2 + + + diff --git a/mvn-build/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckAttributesMapper.java b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckAttributesMapper.java new file mode 100644 index 00000000..43b73c42 --- /dev/null +++ b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckAttributesMapper.java @@ -0,0 +1,68 @@ +/* + * Copyright 2005-2007 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.test; + +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.core.AttributesMapper; + +import junit.framework.Assert; + +/** + * Dummy AttributesMapper for testing purposes to check that the received + * Attributes are the expected ones. + * + * @author Mattias Arthursson + */ +public class AttributeCheckAttributesMapper implements AttributesMapper { + private String[] expectedAttributes = new String[0]; + + private String[] expectedValues = new String[0];; + + private String[] absentAttributes = new String[0];; + + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + Assert.assertEquals("Values and attributes need to have the same length ", + expectedAttributes.length, expectedValues.length); + for (int i = 0; i < expectedAttributes.length; i++) { + Attribute attribute = attributes.get(expectedAttributes[i]); + Assert.assertNotNull("Attribute " + expectedAttributes[i] + + " was not present", attribute); + Assert.assertEquals(expectedValues[i], attribute.get()); + } + + for (int i = 0; i < absentAttributes.length; i++) { + Assert.assertNull(attributes.get(absentAttributes[i])); + } + + return null; + } + + public void setAbsentAttributes(String[] absentAttributes) { + this.absentAttributes = absentAttributes; + } + + public void setExpectedAttributes(String[] expectedAttributes) { + this.expectedAttributes = expectedAttributes; + } + + public void setExpectedValues(String[] expectedValues) { + this.expectedValues = expectedValues; + } +} \ No newline at end of file diff --git a/mvn-build/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckContextMapper.java b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckContextMapper.java new file mode 100644 index 00000000..a12391c4 --- /dev/null +++ b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckContextMapper.java @@ -0,0 +1,66 @@ +/* + * Copyright 2005-2007 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.test; + +import junit.framework.Assert; + +import org.springframework.ldap.core.ContextMapper; +import org.springframework.ldap.core.DirContextAdapter; + +/** + * Dummy ContextMapper for testing purposes to check that the received + * Attributes are the expected ones. + * + * @author Mattias Arthursson + */ +public class AttributeCheckContextMapper implements ContextMapper { + private String[] expectedAttributes = new String[0]; + + private String[] expectedValues = new String[0]; + + private String[] absentAttributes = new String[0]; + + public Object mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + Assert.assertEquals("Values and attributes need to have the same length ", + expectedAttributes.length, expectedValues.length); + for (int i = 0; i < expectedAttributes.length; i++) { + String attributeValue = adapter + .getStringAttribute(expectedAttributes[i]); + Assert.assertNotNull("Attribute " + expectedAttributes[i] + + " was not present", attributeValue); + Assert.assertEquals(expectedValues[i], attributeValue); + } + + for (int i = 0; i < absentAttributes.length; i++) { + Assert.assertNull(adapter.getStringAttribute(absentAttributes[i])); + } + + return null; + } + + public void setAbsentAttributes(String[] absentAttributes) { + this.absentAttributes = absentAttributes; + } + + public void setExpectedAttributes(String[] expectedAttributes) { + this.expectedAttributes = expectedAttributes; + } + + public void setExpectedValues(String[] expectedValues) { + this.expectedValues = expectedValues; + } +} \ No newline at end of file diff --git a/mvn-build/test-support/src/main/java/org/springframework/ldap/test/LdapTestUtils.java b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/LdapTestUtils.java new file mode 100644 index 00000000..0386edac --- /dev/null +++ b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/LdapTestUtils.java @@ -0,0 +1,205 @@ +package org.springframework.ldap.test; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Properties; + +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.ContextNotEmptyException; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + +import org.apache.commons.io.IOUtils; +import org.apache.directory.server.configuration.MutableServerStartupConfiguration; +import org.apache.directory.server.core.configuration.ShutdownConfiguration; +import org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration; +import org.apache.directory.server.jndi.ServerContextFactory; +import org.apache.directory.server.protocol.shared.store.LdifFileLoader; +import org.springframework.core.io.Resource; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DistinguishedName; + +public class LdapTestUtils { + + /** + * Not to be instantiated. + */ + private LdapTestUtils() { + } + + public static DirContext startApacheDirectoryServer(int port, + String defaultPartitionSuffix, String defaultPartitionName) + throws NamingException { + + MutableServerStartupConfiguration cfg = new MutableServerStartupConfiguration(); + + // Determine an appropriate working directory + String tempDir = System.getProperty("java.io.tmpdir"); + cfg.setWorkingDirectory(new File(tempDir)); + + cfg.setLdapPort(port); + + MutableBTreePartitionConfiguration partitionConfiguration = new MutableBTreePartitionConfiguration(); + partitionConfiguration.setSuffix(defaultPartitionSuffix); + partitionConfiguration + .setContextEntry(getRootPartitionAttributes(defaultPartitionName)); + partitionConfiguration.setName(defaultPartitionName); + + cfg.setPartitionConfigurations(Collections + .singleton(partitionConfiguration)); + // Start the Server + + Hashtable env = createEnv(); + env.putAll(cfg.toJndiEnvironment()); + return new InitialDirContext(env); + } + + public static void destroyApacheDirectoryServer(String principal, + String credentials) throws Exception { + Properties env = new Properties(); + env.setProperty(Context.INITIAL_CONTEXT_FACTORY, + ServerContextFactory.class.getName()); + env.setProperty(Context.SECURITY_AUTHENTICATION, "simple"); + env.setProperty(Context.SECURITY_PRINCIPAL, principal); + env.setProperty(Context.SECURITY_CREDENTIALS, credentials); + + ShutdownConfiguration configuration = new ShutdownConfiguration(); + env.putAll(configuration.toJndiEnvironment()); + + new InitialContext(env); + } + + // + // public void cleanAndSetup(ContextSource contextSource, Resource ldifFile, + // DistinguishedName baseLdapPath) + // throws Exception { + // DirContext ctx = contextSource.getReadWriteContext(); + // + // // First of all, make sure the database is empty. + // Name startingPoint = null; + // + // // Different test cases have different base paths. This means that the + // // starting point will be different. + // if (baseLdapPath.size() != 0) { + // startingPoint = DistinguishedName.EMPTY_PATH; + // } else { + // startingPoint = new DistinguishedName(baseLdapPath); + // } + // + // try { + // clearSubContexts(ctx, startingPoint); + // // Load the ldif to the recently started server + // LdifFileLoader loader = new LdifFileLoader(ctx, ldifFile); + // loader.execute(); + // } finally { + // ctx.close(); + // } + // } + // + + public static void clearSubContexts(ContextSource contextSource, Name name) + throws NamingException { + DirContext ctx = null; + try { + ctx = contextSource.getReadWriteContext(); + clearSubContexts(ctx, name); + } finally { + ctx.close(); + } + } + + public static void clearSubContexts(DirContext ctx, Name name) + throws NamingException { + + NamingEnumeration enumeration = null; + try { + enumeration = ctx.listBindings(name); + while (enumeration.hasMore()) { + Binding element = (Binding) enumeration.next(); + DistinguishedName childName = new DistinguishedName(element + .getName()); + childName.prepend((DistinguishedName) name); + + try { + ctx.destroySubcontext(childName); + } catch (ContextNotEmptyException e) { + clearSubContexts(ctx, childName); + ctx.destroySubcontext(childName); + } + } + } catch (NamingException e) { + e.printStackTrace(); + } finally { + try { + enumeration.close(); + } catch (Exception e) { + // Never mind this + } + } + } + + public static void loadLdif(ContextSource contextSource, Resource ldifFile) + throws IOException { + DirContext context = contextSource.getReadWriteContext(); + try { + loadLdif(context, ldifFile); + } finally { + try { + context.close(); + } catch (Exception e) { + // This is not the exception we are interested in. + } + } + } + + private static void loadLdif(DirContext context, Resource ldifFile) + throws IOException { + File tempFile = File.createTempFile("spring_ldap_test", ".ldif"); + InputStream inputStream = ldifFile.getInputStream(); + + IOUtils.copy(inputStream, new FileOutputStream(tempFile)); + LdifFileLoader fileLoader = new LdifFileLoader(context, tempFile + .getAbsolutePath()); + fileLoader.execute(); + } + + private static Hashtable createEnv() { + Hashtable env = new Properties(); + + env.put(Context.PROVIDER_URL, ""); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "org.apache.directory.server.jndi.ServerContextFactory"); + + env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); + env.put(Context.SECURITY_CREDENTIALS, "secret"); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + + return env; + } + + private static Attributes getRootPartitionAttributes( + String defaultPartitionName) { + BasicAttributes attributes = new BasicAttributes(); + BasicAttribute objectClassAttribute = new BasicAttribute("objectClass"); + objectClassAttribute.add("top"); + objectClassAttribute.add("domain"); + objectClassAttribute.add("extensibleObject"); + attributes.put(objectClassAttribute); + attributes.put("dc", defaultPartitionName); + + return attributes; + } + +} diff --git a/mvn-build/test-support/src/main/java/org/springframework/ldap/test/TestContextSourceFactoryBean.java b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/TestContextSourceFactoryBean.java new file mode 100644 index 00000000..318ae196 --- /dev/null +++ b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/TestContextSourceFactoryBean.java @@ -0,0 +1,117 @@ +package org.springframework.ldap.test; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.core.io.Resource; +import org.springframework.ldap.core.AuthenticationSource; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.support.DefaultDirObjectFactory; +import org.springframework.ldap.core.support.LdapContextSource; + +public class TestContextSourceFactoryBean extends AbstractFactoryBean { + private int port; + + private String defaultPartitionSuffix; + + private String defaultPartitionName; + + private String principal; + + private String password; + + private boolean baseOnTarget = true; + + private Resource ldifFile; + + private Class dirObjectFactory = DefaultDirObjectFactory.class; + + private boolean pooled = true; + + private AuthenticationSource authenticationSource; + + public void setAuthenticationSource( + AuthenticationSource authenticationSource) { + this.authenticationSource = authenticationSource; + } + + public void setPooled(boolean pooled) { + this.pooled = pooled; + } + + public void setDirObjectFactory(Class dirObjectFactory) { + this.dirObjectFactory = dirObjectFactory; + } + + public void setLdifFile(Resource ldifFile) { + this.ldifFile = ldifFile; + } + + public void setBaseOnTarget(boolean baseOnTarget) { + this.baseOnTarget = baseOnTarget; + } + + public void setDefaultPartitionSuffix(String defaultPartitionSuffix) { + this.defaultPartitionSuffix = defaultPartitionSuffix; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setDefaultPartitionName(String defaultPartitionName) { + this.defaultPartitionName = defaultPartitionName; + } + + public void setPort(int port) { + this.port = port; + } + + protected Object createInstance() throws Exception { + LdapTestUtils.startApacheDirectoryServer(port, defaultPartitionSuffix, + defaultPartitionName); + + LdapContextSource targetContextSource = new LdapContextSource(); + if (baseOnTarget) { + targetContextSource.setBase(defaultPartitionSuffix); + } + + targetContextSource.setUrl("ldap://localhost:" + port); + targetContextSource.setUserDn(principal); + targetContextSource.setPassword(password); + targetContextSource.setDirObjectFactory(dirObjectFactory); + targetContextSource.setPooled(pooled); + if (authenticationSource != null) { + targetContextSource.setAuthenticationSource(authenticationSource); + } + targetContextSource.afterPropertiesSet(); + + if (baseOnTarget) { + LdapTestUtils.clearSubContexts(targetContextSource, + DistinguishedName.EMPTY_PATH); + } else { + LdapTestUtils.clearSubContexts(targetContextSource, + new DistinguishedName(defaultPartitionSuffix)); + } + + if (ldifFile != null) { + LdapTestUtils.loadLdif(targetContextSource, ldifFile); + } + + return targetContextSource; + } + + public Class getObjectType() { + return ContextSource.class; + } + + protected void destroyInstance(Object instance) throws Exception { + super.destroyInstance(instance); + + LdapTestUtils.destroyApacheDirectoryServer(principal, password); + } +} diff --git a/mvn-build/tiger/pom.xml b/mvn-build/tiger/pom.xml new file mode 100644 index 00000000..f87f048f --- /dev/null +++ b/mvn-build/tiger/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + org.springframework.ldap + spring-ldap-tiger + Spring LDAP Java 5 support + 1.2.2-SNAPSHOT + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + + + + + + org.springframework.ldap + spring-ldap + 1.2.2-SNAPSHOT + + + junit + junit + 3.8.2 + test + + + diff --git a/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/AbstractParameterizedContextMapper.java b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/AbstractParameterizedContextMapper.java new file mode 100644 index 00000000..4e56b02f --- /dev/null +++ b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/AbstractParameterizedContextMapper.java @@ -0,0 +1,53 @@ +/* + * Copyright 2005-2007 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.core.simple; + +import org.springframework.ldap.core.DirContextOperations; + +/** + * Abstract superclass that may be used instead of implementing + * {@link ParameterizedContextMapper} directly. Subclassing from this superclass, + * the supplied context will be automatically cast to + * DirContextOperations. Note that if you use your own + * DirObjectFactory, this implementation will fail with a + * ClassCastException. + * + * @author Mattias Arthursson + * + */ +public abstract class AbstractParameterizedContextMapper implements ParameterizedContextMapper { + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.ContextMapper#mapFromContext(java.lang.Object) + */ + public final T mapFromContext(Object ctx) { + return doMapFromContext((DirContextOperations) ctx); + } + + /** + * Map a single DirContextOperation to an object. The + * supplied instance is the object supplied to + * {@link #mapFromContext(Object)} cast to a + * DirContextOperations. + * + * @param ctx the context to map to an object. + * @return an object built from the data in the context. + */ + protected abstract T doMapFromContext(DirContextOperations ctx); + +} diff --git a/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/ParameterizedContextMapper.java b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/ParameterizedContextMapper.java new file mode 100644 index 00000000..f33f28bb --- /dev/null +++ b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/ParameterizedContextMapper.java @@ -0,0 +1,26 @@ +package org.springframework.ldap.core.simple; + +import javax.naming.Binding; +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.core.ContextMapper; + +/** + * Extension of the {@link ContextMapper} interface. Uses Java 5 covariant + * return types to override the return type of the + * {@link #mapFromContext(Object)} method to be the type parameter T. + * + * @param + */ +public interface ParameterizedContextMapper extends ContextMapper { + + /** + * Map a single LDAP Context to an object. The supplied Object + * ctx is the object from a single {@link SearchResult}, + * {@link Binding}, or a lookup operation. + * + * @param ctx the context to map to an object. + * @return an object built from the data in the context. + */ + public T mapFromContext(Object ctx); +} diff --git a/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/SimpleLdapOperations.java b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/SimpleLdapOperations.java new file mode 100644 index 00000000..294d390c --- /dev/null +++ b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/SimpleLdapOperations.java @@ -0,0 +1,142 @@ +/* + * Copyright 2005-2007 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.core.simple; + +import java.util.List; + +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.NamingException; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.DirContextProcessor; +import org.springframework.ldap.core.LdapOperations; + +/** + * LDAP operations interface usable on Java 5 and above, exposing a set of + * common LDAP operations. + * + * @author Mattias Arthursson + */ +public interface SimpleLdapOperations { + + /** + * Get the wrapped LdapOperations instance. + * + * @return the wrapped LdapOperations instance. + * @throws NamingException if any error occurs. + */ + LdapOperations getLdapOperations(); + + /** + * Search for a List of type T using the supplied filter and link + * ParametrizedContextMapper. + * + * @param base Base DN relative to the base of the ContextSource - where to + * start the search. + * @param filter Search filter. + * @param mapper the Mapper to supply all results to. + * @return a List of type T containing objects for all entries found, as + * mapped by the ParametrizedContextMapper. + * @throws NamingException if any error occurs. + */ + List search(String base, String filter, ParameterizedContextMapper mapper); + + /** + * Search for a List of type T using the supplied filter, SearchControls, + * DirContextProcessor and ParametrizedContextMapper. + * + * @param base Base DN relative to the base of the ContextSource - where to + * start the search. + * @param filter Search filter. + * @param controls the SearchControls. Make sure that the returningObjFlag + * is set to true. + * @param mapper the Mapper to supply all results to. + * @param processor the DirContextProcessor to be used for applying pre/post + * processing on the DirContext instance. + * @return a List of type T containing objects for all entries found, as + * mapped by the ParametrizedContextMapper. + * @throws NamingException if any error occurs. + */ + List search(String base, String filter, SearchControls controls, ParameterizedContextMapper mapper, + DirContextProcessor processor); + + /** + * Perform a lookup of the specified DN and map the result using the mapper. + * + * @param dn the Distinguished Name to look up. + * @param mapper the mapper to use. + * @return the mapped object, as received by the ParametrizedContextMapper. + * @throws NamingException if any error occurs. + */ + T lookup(String dn, ParameterizedContextMapper mapper); + + /** + * Look up the specified DN, and automatically cast it to a + * {@link DirContextOperations} instance. + * + * @param dn The Distinguished Name of the entry to look up. + * @return A {@link DirContextOperations} instance constructed from the + * found entry. + * @throws NamingException if any error occurs. + */ + DirContextOperations lookupContext(String dn); + + /** + * Create an entry in the LDAP tree. The attributes used to create the entry + * are either retrieved from the obj parameter or the + * attributes parameter (or both). One of these parameters + * may be null but not both. + * + * @param dn The distinguished name to bind the object and attributes to. + * @param obj The object to bind, may be null. Typically a DirContext + * implementation. + * @param attributes The attributes to bind, may be null. + * @throws NamingException if any error occurs. + */ + void bind(String dn, Object obj, Attributes attributes); + + /** + * Remove an entry from the LDAP tree. The entry must not have any children. + * + * @param dn The distinguished name to unbind. + * @throws NamingException if any error occurs. + */ + void unbind(String dn); + + /** + * Modify the Attributes of the entry corresponding to the supplied + * {@link DirContextOperations} instance. The instance should have been + * received from the {@link #lookupContext(String)} operation, and then modified to + * match the current state of the matching domain object, e.g.: + * + *
+	 * public void update(Person person) {
+	 * 	DirContextOperations ctx = simpleLdapOperations.lookup(person.getDn());
+	 * 
+	 * 	ctx.setAttributeValue("description", person.getDescription());
+	 * 	ctx.setAttributeValue("telephoneNumber", person.getPhone());
+	 * 	// More modifications here
+	 * 
+	 * 	simpleLdapOperations.modifyAttributes(ctx);
+	 * }
+	 * 
+ * + * @param ctx the entry to update in the LDAP tree. + * @throws NamingException if any error occurs. + */ + void modifyAttributes(DirContextOperations ctx); +} diff --git a/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/SimpleLdapTemplate.java b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/SimpleLdapTemplate.java new file mode 100644 index 00000000..bd8107e9 --- /dev/null +++ b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/SimpleLdapTemplate.java @@ -0,0 +1,137 @@ +/* + * Copyright 2005-2007 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.core.simple; + +import java.util.List; + +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.DirContextProcessor; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.core.LdapTemplate; + +/** + * Java-5-based convenience wrapper for the classic LdapTemplate, adding some + * convenient shortcuts and taking advantage of Java 5 Generics. + * + * Use the {@link #getLdapOperations()} method if you need to invoke less + * commonly used template methods. + * + * @author Mattias Arthursson + */ +public class SimpleLdapTemplate implements SimpleLdapOperations { + + private LdapOperations ldapOperations; + + /** + * Constructs a new SimpleLdapTemplate instance wrapping the supplied + * LdapOperations instance. + * + * @param ldapOperations the LdapOperations instance to wrap. + */ + public SimpleLdapTemplate(LdapOperations ldapOperations) { + this.ldapOperations = ldapOperations; + } + + /** + * Constructs a new SimpleLdapTemplate instance, automatically creating a + * wrapped LdapTemplate instance to work with. + * + * @param contextSource + */ + public SimpleLdapTemplate(ContextSource contextSource) { + this.ldapOperations = new LdapTemplate(contextSource); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#getLdapOperations() + */ + public LdapOperations getLdapOperations() { + return ldapOperations; + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#lookup(java.lang.String, + * org.springframework.ldap.core.simple.ParametrizedContextMapper) + */ + @SuppressWarnings("unchecked") + public T lookup(String dn, ParameterizedContextMapper mapper) { + return (T) ldapOperations.lookup(dn, mapper); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#search(java.lang.String, + * java.lang.String, + * org.springframework.ldap.core.simple.ParametrizedContextMapper) + */ + @SuppressWarnings("unchecked") + public List search(String base, String filter, ParameterizedContextMapper mapper) { + return ldapOperations.search(base, filter, mapper); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.core.simple.ParametrizedContextMapper, + * org.springframework.ldap.core.DirContextProcessor) + */ + @SuppressWarnings("unchecked") + public List search(String base, String filter, SearchControls controls, ParameterizedContextMapper mapper, + DirContextProcessor processor) { + return ldapOperations.search(base, filter, controls, mapper, processor); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#lookup(java.lang.String) + */ + public DirContextOperations lookupContext(String dn) { + return ldapOperations.lookupContext(dn); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#modifyAttributes(org.springframework.ldap.core.DirContextOperations) + */ + public void modifyAttributes(DirContextOperations ctx) { + ldapOperations.modifyAttributes(ctx); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#bind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(String dn, Object obj, Attributes attributes) { + ldapOperations.bind(dn, obj, attributes); + } + + /* + * (non-Javadoc) + * @see org.springframework.ldap.core.simple.SimpleLdapOperations#unbind(java.lang.String) + */ + public void unbind(String dn) { + ldapOperations.unbind(dn); + } + +} diff --git a/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/package.html b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/package.html new file mode 100644 index 00000000..56f3b7ec --- /dev/null +++ b/mvn-build/tiger/src/main/java/org/springframework/ldap/core/simple/package.html @@ -0,0 +1,7 @@ + + + +Simplification layer over LdapTemplate for Java 5 and above. + + + diff --git a/mvn-build/tiger/src/main/java/overview.html b/mvn-build/tiger/src/main/java/overview.html new file mode 100644 index 00000000..28045ca7 --- /dev/null +++ b/mvn-build/tiger/src/main/java/overview.html @@ -0,0 +1,3 @@ + +This document is the API specification for the Java5-specific parts of the Spring LDAP Framework. + \ No newline at end of file