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 rebind
+
+ 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 modifyAttributes
+
+ 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 BaseLdapPathAware
+
+ 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 BaseLdapPathBeanPostProcessor in your ApplicationContext
+
+ <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 PagedResultsRequestControl
+
+ 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 getStringAttributes()
+
+ 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 DirContextAdapter
+
+ 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 DirContextAdapter
+
+ 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 ParameterizedContextMapper
+ 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.
+
+
+
+
+
+
+
+
+ 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.:
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ * 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.:
+ *
+ *
+ *
+ * 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.:
+ *
+ *
+ *
+ * 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:
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ *
+ * 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:
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ *
+ * 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:
+ *
+ *
+
+
+
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:
+ *
+ *
Property
+ *
Description
+ *
Required
+ *
Default
+ *
+ *
+ *
contextSource
+ *
The {@link ContextSource} to get {@link DirContext}s from
+ * for adding to the pool.
+ *
Yes
+ *
null
+ *
+ *
+ *
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.
+ *
No
+ *
null
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ *
Property
+ *
Description
+ *
Required
+ *
Default
+ *
+ *
+ *
contextSource
+ *
+ * The {@link ContextSource} to get {@link DirContext}s from for adding to the pool.
+ *
+ *
Yes
+ *
null
+ *
+ *
+ *
dirContextValidator
+ *
+ * The {@link DirContextValidator} to use for validating {@link DirContext}s. Required
+ * if any of the test/validate options are enabled.
+ *
+ *
+ * @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