diff --git a/build-spring-ldap/.project b/build-spring-ldap/.project new file mode 100644 index 00000000..d27922c5 --- /dev/null +++ b/build-spring-ldap/.project @@ -0,0 +1,11 @@ + + + build-spring-ldap + + + + + + + + diff --git a/build-spring-ldap/build.xml b/build-spring-ldap/build.xml new file mode 100644 index 00000000..0c3024a0 --- /dev/null +++ b/build-spring-ldap/build.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Master build for spring-ldap main and sample jars. + + Please execute + build -projecthelp + + to see a list of all relevant targets. + The default target is 'main' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + projects=${projects} + projects.names=${projects.names} + + + + + + + projects=${projects} + + + + + + + projects=${projects} + + + + + + + projects=${projects} + + + + + + + projects=${projects} + + + + + + + projects=${projects} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-spring-ldap/checklist_release.txt b/build-spring-ldap/checklist_release.txt new file mode 100644 index 00000000..5439074e --- /dev/null +++ b/build-spring-ldap/checklist_release.txt @@ -0,0 +1,64 @@ +build-spring-ldap +================= + +* Update release number in project.properties + + release.version=1.1 + +spring-ldap +=========== + +* Update release number in project.properties + + project.base.version=1.1 + project.version=${project.base.version} + ivy.status=release + +* Update release number in reference manual (docs/reference/src/index.xml) + + + Spring LDAP + Reference Documentation + Version 1.1 + +* Update release number in readme.txt + + Spring-LDAP 1.1 (Aug 2006) + +General +======= + +* Update changelog with changes, including upgraded libraries + +* Synchronize and commit changes, including .* files + +* Run clean-all from build-spring-ldap + +* Clean integration-repo + +* Clean Ivy cache + +* Run all tests, including integration tests + +* Run the sample spring-ldap-person in a web container + +* Run the sample spring-ldap-article integration tests + +* Run doc-all from spring-ldap + +* Run release from build-spring-ldap + +* Run jdiff from spring-ldap + +* Add Maven pom file for the new release to top directory. + +After release +============= + +* Store source for new release in build-spring-ldap/spring-ldap- + (used in future jdiff runs) + +* Tag release like this: + 1. Open a svn repo browser, eg Eclipse + 2. Create a new directory: spring-projects/tags/spring-ldap- + 3. Copy repository, common-build and spring-ldap to that directory diff --git a/build-spring-ldap/jdiff/lib/2000/10/XMLSchema b/build-spring-ldap/jdiff/lib/2000/10/XMLSchema new file mode 100644 index 00000000..781076e8 --- /dev/null +++ b/build-spring-ldap/jdiff/lib/2000/10/XMLSchema @@ -0,0 +1,2201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> + + + + Part 1 version: $Id: XMLSchema,v 1.1 2001/11/16 06:19:40 mdoar Exp $ + Part 2 version: $Id: XMLSchema,v 1.1 2001/11/16 06:19:40 mdoar Exp $ + + + + + + + The schema corresponding to this document is normative, + with respect to the syntactic constraints it expresses in the + XML Schema language. The documentation (within <documentation> elements) + below, is not normative, but rather highlights important aspects of + the W3C Recommendation of which this is a part + + + + + The simpleType element and all of its members are defined + towards the end of this schema document + + + + + + Get access to the xml: attribute groups for xml:lang + as declared on 'documentation' below + + + + + + + + This type is extended by almost all schema types + to allow attributes from other namespaces to be + added to user schemas. + + + + + + + + + + + + + This type is extended by all types which allow annotation + other than <schema> itself + + + + + + + + + + + + + + + + This abstract element defines an substitution group over the + elements which occur freely at the top level of schemas. These are: + simpleType, complexType, element, attribute, attributeGroup, group, notation + All of their types are based on the "annotated" type by extension. + + + + + + + This abstract element defines a substitution group for the + elements which can self-redefine (see <redefine> below). + + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for maxOccurs + + + + + + + + + + + + for all particles + + + + + + + for element, group and attributeGroup, + which both define and reference + + + + + + + + 'complexType' uses this + + + + + + + + + + + + 'topLevelGroup' uses this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This branch is short for + <complexContent> + <restriction base="anyType"> + ... + </restriction> + </complexContent> + + + + + + + + + + + + + + + Will be restricted to required or forbidden + + + + + + Not allowed if simpleContent child is chosen. + May be overriden by setting on complexContent child. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overrides any setting on complexType parent. + + + + + + + + + + + + + + + + + + + + + + + + + + No typeDefParticle group reference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + #all or (possibly empty) subset of {substitution, extension, + restriction} + + A utility type, not for public use + + + + + + + + + + + + + + + + + #all or (possibly empty) subset of {extension, restriction} + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + The element element can be used either + at the toplevel to define an element-type binding globally, + or within a content model to either reference a globally-defined + element or type or declare an element-type binding locally. + The ref form is not allowed at the top level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for explicit groups, named top-level groups and + group references + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for the three kinds of group + + + + + + + + + + + + + + + + + + + + + Only elements allowed inside + + + + + + + + + restricted max/min + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + simple type for the value of the 'namespace' attr of + 'any' and 'anyAttribute' + + + + Value is + ##any - - any non-conflicting WFXML/attribute at all + + ##other - - any non-conflicting WFXML/attribute from + namespace other than targetNS + + ##local - - any unqualified non-conflicting WFXML/attribute + + one or - - any non-conflicting WFXML/attribute from + more URI the listed namespaces + references + (space separated) + + ##targetNamespace or ##local may appear in the above list, to + refer to the targetNamespace of the enclosing + schema or an absent targetNamespace respectively + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An XPath expression + + A utility type, not for public use + + + + + + A VERY permissive definition, probably not even right + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A public identifier, per ISO 8879 + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + notations for use within XML Schema schemas + + + + + + + + + Not the real urType, but as close an approximation as we can + get in the XML representation + + + + + + + + + + First the builtin primitive datatypes. These definitions are for + information only, the real builtin definitions are magic. Note in + particular that there is no type named 'anySimpleType'. The + primitives should really be derived from no type at all, and + anySimpleType should be derived as a union of all the primitives. + + + + For each built-in datatype in this schema (both primitive and + derived) can be uniquely addressed via a URI constructed + as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is an XPointer that identifies + the name of the datatype, as an ID + + For example, to address the date datatype, the URI is: + + http://www.w3.org/2000/10/XMLSchema#xpointer(id("date")) + + Additionally, each facet definition element can be uniquely + addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is an XPointer that identifies + the name of the facet, as an ID + + For example, to address the period facet, the URI is: + + http://www.w3.org/2000/10/XMLSchema#xpointer(id("period")) + + Additionally, each facet usage in a built-in datatype definition + can be uniquely addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is an XPointer that identifies + the name of the datatype, followed by a period (".") + followed by the name of the facet, as an ID + + For example, to address the usage of the period facet in + the definition of date, the URI is: + + http://www.w3.org/2000/10/XMLSchema#xpointer(id("date.period")) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Now the derived primitive types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 33 from the XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 7 from the XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 5 from the XML spec + + + + + + + + + + + + + + + pattern matches production 4 from the Namespaces in XML spec + + + + + + + + + + + NOTATION cannot be used directly in a schema; rather a type + must be derived from it by specifying at least one enumeration + facet whose value is the name of a NOTATION declared in the + schema. + + + the value/lexical spaces of NOTATION are not the full + value/lexical spaces of NOTATION even though there are + no additional constraining facets. The true value/lexical + spaces are limited to the set of names of NOTATIONs declared + in the schema. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Can be restricted to required or forbidden + + + + + + + + + + + + + + + + + + Required at the top level + + + + + + + + + + + + + + + + + + Forbidden when nested + + + + + + + + + + + + + + + + + + + + + + + + + + + base attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + itemType attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + + + memberTypes attribute must be non-empty or there must be + at least one simpleType child + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + each (8-bit) byte is encoded as a sequence + of 2 hexidecimal digits + + + + + + + value is encoded in Base64 as defined + in the MIME RFC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-spring-ldap/jdiff/lib/2000/10/XMLSchema-instance b/build-spring-ldap/jdiff/lib/2000/10/XMLSchema-instance new file mode 100644 index 00000000..94d6d43b --- /dev/null +++ b/build-spring-ldap/jdiff/lib/2000/10/XMLSchema-instance @@ -0,0 +1,31 @@ + + + + +]> + + + +

XML Schema instance namespace

+

See The XML Schema draft recommendation for an introduction

+ + +
+
Henry S. Thompson
+ $Date: 2001/11/16 06:19:40 $
+ $Id: XMLSchema-instance,v 1.1 2001/11/16 06:19:40 mdoar Exp $ +
+
+ + + No definitions are provided here, as + this schema is never used as such + + + + + + +
diff --git a/build-spring-ldap/jdiff/lib/2001/XMLSchema b/build-spring-ldap/jdiff/lib/2001/XMLSchema new file mode 100644 index 00000000..fa1eeefa --- /dev/null +++ b/build-spring-ldap/jdiff/lib/2001/XMLSchema @@ -0,0 +1,2441 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> + + + + Part 1 version: Id: XMLSchema.xsd,v 1.49 2001/10/25 10:25:41 ht Exp + Part 2 version: Id: datatypes.xsd,v 1.53 2001/10/25 10:26:05 ht Exp + + + + + + + The schema corresponding to this document is normative, + with respect to the syntactic constraints it expresses in the + XML Schema language. The documentation (within <documentation> elements) + below, is not normative, but rather highlights important aspects of + the W3C Recommendation of which this is a part + + + + + The simpleType element and all of its members are defined + towards the end of this schema document + + + + + + Get access to the xml: attribute groups for xml:lang + as declared on 'schema' and 'documentation' below + + + + + + + + This type is extended by almost all schema types + to allow attributes from other namespaces to be + added to user schemas. + + + + + + + + + + + + + This type is extended by all types which allow annotation + other than <schema> itself + + + + + + + + + + + + + + + + This group is for the + elements which occur freely at the top level of schemas. + All of their types are based on the "annotated" type by extension. + + + + + + + + + + + + + This group is for the + elements which can self-redefine (see <redefine> below). + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {extension, restriction} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for maxOccurs + + + + + + + + + + + + for all particles + + + + + + + for element, group and attributeGroup, + which both define and reference + + + + + + + + 'complexType' uses this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This branch is short for + <complexContent> + <restriction base="xs:anyType"> + ... + </restriction> + </complexContent> + + + + + + + + + + + + + + + Will be restricted to required or forbidden + + + + + + Not allowed if simpleContent child is chosen. + May be overriden by setting on complexContent child. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overrides any setting on complexType parent. + + + + + + + + + + + + + + + + + + + + + + + + + + No typeDefParticle group reference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {substitution, extension, + restriction} + + + + + + + + + + + + + + + + + + + + + + + + + The element element can be used either + at the top level to define an element-type binding globally, + or within a content model to either reference a globally-defined + element or type or declare an element-type binding locally. + The ref form is not allowed at the top level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for explicit groups, named top-level groups and + group references + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for the three kinds of group + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + restricted max/min + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Only elements allowed inside + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + simple type for the value of the 'namespace' attr of + 'any' and 'anyAttribute' + + + + Value is + ##any - - any non-conflicting WFXML/attribute at all + + ##other - - any non-conflicting WFXML/attribute from + namespace other than targetNS + + ##local - - any unqualified non-conflicting WFXML/attribute + + one or - - any non-conflicting WFXML/attribute from + more URI the listed namespaces + references + (space separated) + + ##targetNamespace or ##local may appear in the above list, to + refer to the targetNamespace of the enclosing + schema or an absent targetNamespace respectively + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in selectors + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the following EBNF: + Selector ::= Path ( '|' Path )* + Path ::= ('.//')? Step ( '/' Step )* + Step ::= '.' | NameTest + NameTest ::= QName | '*' | NCName ':' '*' + child:: is also allowed + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in fields + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the same EBNF as for selector, + with the following change: + Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) + + + + + + + + + + + + + + + + + + + + + + + + + + + The three kinds of identity constraints, all with + type of or derived from 'keybase'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + A public identifier, per ISO 8879 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + notations for use within XML Schema schemas + + + + + + + + + Not the real urType, but as close an approximation as we can + get in the XML representation + + + + + + + + + + First the built-in primitive datatypes. These definitions are for + information only, the real built-in definitions are magic. Note in + particular that there is no type named 'anySimpleType'. The + primitives should really be derived from no type at all, and + anySimpleType should be derived as a union of all the primitives. + + + + For each built-in datatype in this schema (both primitive and + derived) can be uniquely addressed via a URI constructed + as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype + + For example, to address the int datatype, the URI is: + + http://www.w3.org/2001/XMLSchema#int + + Additionally, each facet definition element can be uniquely + addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the facet + + For example, to address the maxInclusive facet, the URI is: + + http://www.w3.org/2001/XMLSchema#maxInclusive + + Additionally, each facet usage in a built-in datatype definition + can be uniquely addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype, followed + by a period (".") followed by the name of the facet + + For example, to address the usage of the maxInclusive facet in + the definition of int, the URI is: + + http://www.w3.org/2001/XMLSchema#int.maxInclusive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOTATION cannot be used directly in a schema; rather a type + must be derived from it by specifying at least one enumeration + facet whose value is the name of a NOTATION declared in the + schema. + + + + + + + + + + Now the derived primitive types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern specifies the content of section 2.12 of XML 1.0e2 + and RFC 1766 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 7 from the XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 5 from the XML spec + + + + + + + + + + + + + + + pattern matches production 4 from the Namespaces in XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + #all or (possibly empty) subset of {restriction, union, list} + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + Can be restricted to required or forbidden + + + + + + + + + + + + + + + + + + Required at the top level + + + + + + + + + + + + + + + + + + Forbidden when nested + + + + + + + + + + + + + + + + + + We should use a substitution group for facets, but + that's ruled out because it would allow users to + add their own, which we're not ready for yet. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + base attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + itemType attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + + + memberTypes attribute must be non-empty or there must be + at least one simpleType child + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-spring-ldap/jdiff/lib/2001/XMLSchema-instance b/build-spring-ldap/jdiff/lib/2001/XMLSchema-instance new file mode 100644 index 00000000..d7e0c418 --- /dev/null +++ b/build-spring-ldap/jdiff/lib/2001/XMLSchema-instance @@ -0,0 +1,38 @@ + + + + + + + + +]> + + + +

XML Schema instance namespace

+

See the XML Schema + Recommendation for an introduction

+ + +
+ $Date: 2002/01/09 20:32:53 $
+ $Id: XMLSchema-instance,v 1.1 2002/01/09 20:32:53 mdoar Exp $ +
+
+ +

This schema should never be used as such: + the XML + Schema Recommendation forbids the declaration of + attributes in this namespace

+
+
+ + + + + +
diff --git a/build-spring-ldap/jdiff/lib/Null.java b/build-spring-ldap/jdiff/lib/Null.java new file mode 100644 index 00000000..75a57019 --- /dev/null +++ b/build-spring-ldap/jdiff/lib/Null.java @@ -0,0 +1,9 @@ +/** + * This class is used only as a "null" argument for Javadoc when comparing + * two API files. Javadoc has to have a package, .java or .class file as an + * argument, even though JDiff doesn't use it. + */ +public class Null { + public Null() { + } +} diff --git a/build-spring-ldap/jdiff/lib/ant.jar b/build-spring-ldap/jdiff/lib/ant.jar new file mode 100644 index 00000000..8ad84e34 Binary files /dev/null and b/build-spring-ldap/jdiff/lib/ant.jar differ diff --git a/build-spring-ldap/jdiff/lib/antjdiff.jar b/build-spring-ldap/jdiff/lib/antjdiff.jar new file mode 100644 index 00000000..df0c5acb Binary files /dev/null and b/build-spring-ldap/jdiff/lib/antjdiff.jar differ diff --git a/build-spring-ldap/jdiff/lib/background.gif b/build-spring-ldap/jdiff/lib/background.gif new file mode 100644 index 00000000..e6d2dda3 Binary files /dev/null and b/build-spring-ldap/jdiff/lib/background.gif differ diff --git a/build-spring-ldap/jdiff/lib/black.gif b/build-spring-ldap/jdiff/lib/black.gif new file mode 100644 index 00000000..185d95b1 Binary files /dev/null and b/build-spring-ldap/jdiff/lib/black.gif differ diff --git a/build-spring-ldap/jdiff/lib/classdoc.jar b/build-spring-ldap/jdiff/lib/classdoc.jar new file mode 100644 index 00000000..72541639 Binary files /dev/null and b/build-spring-ldap/jdiff/lib/classdoc.jar differ diff --git a/build-spring-ldap/jdiff/lib/jdiff.jar b/build-spring-ldap/jdiff/lib/jdiff.jar new file mode 100644 index 00000000..d1f9377f Binary files /dev/null and b/build-spring-ldap/jdiff/lib/jdiff.jar differ diff --git a/build-spring-ldap/jdiff/lib/jdiff_logo.gif b/build-spring-ldap/jdiff/lib/jdiff_logo.gif new file mode 100644 index 00000000..d098485d Binary files /dev/null and b/build-spring-ldap/jdiff/lib/jdiff_logo.gif differ diff --git a/build-spring-ldap/jdiff/lib/new.gif b/build-spring-ldap/jdiff/lib/new.gif new file mode 100644 index 00000000..c0ef7a47 Binary files /dev/null and b/build-spring-ldap/jdiff/lib/new.gif differ diff --git a/build-spring-ldap/jdiff/lib/xerces.jar b/build-spring-ldap/jdiff/lib/xerces.jar new file mode 100644 index 00000000..e75d486c Binary files /dev/null and b/build-spring-ldap/jdiff/lib/xerces.jar differ diff --git a/build-spring-ldap/project.properties b/build-spring-ldap/project.properties new file mode 100644 index 00000000..673614c0 --- /dev/null +++ b/build-spring-ldap/project.properties @@ -0,0 +1,5 @@ +# configurable property values, may be locally overriden by a build.properties file +release.version=1.1.2 + +# The location of the common build system +common.build.dir=${basedir}/../common-build diff --git a/build-spring-ldap/readme.txt b/build-spring-ldap/readme.txt new file mode 100644 index 00000000..04cc21d3 --- /dev/null +++ b/build-spring-ldap/readme.txt @@ -0,0 +1,16 @@ +This is where the master build to create releases of spring-ldap resides. + +To build a new release: + +1. If you have ivy installed, make sure it is version 1.4 or higher. + (check your ANT_HOME/lib for ivy-xxx.jar - if it is too old a version, + remove it or replace it with the one in ../common-build/lib). + +2. Update project.properties to contain the new release version, if necessary. + +3. From this directory, run: + ant release + The release archive will be created and placed in: + target/release + +Questions? See http://www.springframework.org/ldap \ No newline at end of file diff --git a/build-spring-ldap/release-toc.txt b/build-spring-ldap/release-toc.txt new file mode 100644 index 00000000..77f74670 --- /dev/null +++ b/build-spring-ldap/release-toc.txt @@ -0,0 +1,11 @@ +Spring-LDAP + +Release archive table of contents +--------------------------------------------------- +* common-build - contains the ant 1.6 "common build system" used by all spring-ldap projects to compile/build/test +* spring-ldap/build-spring-ldap - contains master build files to produce this release archive +* spring-ldap/spring-ldap - contains project sources +* spring-ldap/spring-ldap-person - contains a sample web project. +* spring-ldap/spring-ldap-article - contains source code for an article published on java.net. + +See the readme.txt within the above directories for additional information. \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java new file mode 100644 index 00000000..10089337 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataIntegrityViolationException; + +/** + * Exception that indicates that an invalid or missing Attribute has been + * supplied to an LDAP operation. + * + * @author Mattias Arthursson + * + */ +public class AttributesIntegrityViolationException extends + DataIntegrityViolationException { + + private static final long serialVersionUID = -6368616096960202571L; + + public AttributesIntegrityViolationException(String msg) { + super(msg); + } + + public AttributesIntegrityViolationException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AttributesMapper.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AttributesMapper.java new file mode 100644 index 00000000..bc6af080 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AttributesMapper.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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 LdapTemplate class. + *

+ * Typically used in LdapTemplate's search methods. AttributeMapper objects are + * typically stateless and thus reusable; they are ideal for implementing + * attribute-mapping logic in one place. + *

+ * Alternatively, consider using a {@link ContextMapper} in stead. + * + * @see org.springframework.ldap.LdapTemplate#search(Name, String, + * 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AuthenticationSource.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AuthenticationSource.java new file mode 100644 index 00000000..7a8874c1 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/AuthenticationSource.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap; + +/** + * An AuthenticationSource is responsible for providing the principal 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 (userName). + */ + public String getPrincipal(); + + /** + * Get the credentials to use when creating an authenticated context. + * + * @return the credentials (userName). + */ + public String getCredentials(); +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/BadLdapGrammarException.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/BadLdapGrammarException.java new file mode 100644 index 00000000..a7d55dbd --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/BadLdapGrammarException.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.InvalidDataAccessResourceUsageException; + +/** + * 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 + InvalidDataAccessResourceUsageException { + + private static final long serialVersionUID = 961612585331409470L; + + public BadLdapGrammarException(String message) { + super(message); + } + + public BadLdapGrammarException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java new file mode 100644 index 00000000..6f42103d --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +/** + * A NameClassPairCallbackHandler to collect all results in an internal List. + * + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextAssembler.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextAssembler.java new file mode 100644 index 00000000..da2ef350 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextAssembler.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap; + +/** + * 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextExecutor.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextExecutor.java new file mode 100644 index 00000000..70ac8790 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextExecutor.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +/** + * Interface for delegating an actual operation to be performed on an + * DirContext. For searches, use {@link org.springframework.ldap.SearchExecutor} in + * stead. A typical usage of this interface could be e.g.: + * + *

+ * ContextExecutor executor = new ContextExecutor(){
+ *   public Object executeWithContext(DirContext ctx) throws NamingException{
+ *     return ctx.lookup(dn);
+ *   }
+ * };
+ * 
+ * + * @see org.springframework.ldap.LdapTemplate#executeReadOnly(ContextExecutor) + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextMapper.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextMapper.java new file mode 100644 index 00000000..a9effe00 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextMapper.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.support.DefaultDirObjectFactory; +import org.springframework.ldap.support.DirContextAdapter; + +/** + * An interface used by LdapTemplate to map LDAP Contexts to beans. Responsible + * for mapping from 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}, 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 + * + * @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. + * @return an object built from the data in the context. + */ + public Object mapFromContext(Object ctx); +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextSource.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextSource.java new file mode 100644 index 00000000..fa104841 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/ContextSource.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.DirContext; + +import org.springframework.dao.DataAccessException; + +/** + * Interface used to retrieve and authenticate LDAP contexts. + * + * @see org.springframework.ldap.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 DataAccessException + * if some error occurs creating an DirContext. + */ + public DirContext getReadOnlyContext() throws DataAccessException; + + /** + * Gets a read-write DirContext. + * + * @return A DirContext instance, never null. + * @throws DataAccessException + * if some error occurs creating an DirContext. + */ + public DirContext getReadWriteContext() throws DataAccessException; +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java new file mode 100644 index 00000000..a7eba9e5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java new file mode 100644 index 00000000..6c27b5c7 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java @@ -0,0 +1,96 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.CommunicationException; +import javax.naming.ContextNotEmptyException; +import javax.naming.LimitExceededException; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.directory.InvalidAttributesException; +import javax.naming.directory.InvalidSearchControlsException; +import javax.naming.directory.InvalidSearchFilterException; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +/** + * The default implementation of NamingExceptionTranslator. + * + * @author Mattias Arthursson + */ +public class DefaultNamingExceptionTranslator implements + NamingExceptionTranslator { + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.NamingExceptionTranslator#translate(java.lang.String, + * java.lang.String, java.lang.String, javax.naming.NamingException) + */ + public DataAccessException translate(NamingException namingException) { + + if (namingException instanceof NameNotFoundException) { + return new EntryNotFoundException("Entry not found", + namingException); + } + + if (namingException instanceof InvalidSearchFilterException) { + return new BadLdapGrammarException("Invalid search filter", + namingException); + } + + if (namingException instanceof InvalidSearchControlsException) { + return new InvalidDataAccessApiUsageException( + "Invalid search controls supplied by internal API", + namingException); + } + + if (namingException instanceof NameAlreadyBoundException) { + return new DataIntegrityViolationException("Name already bound", + namingException); + } + + if (namingException instanceof ContextNotEmptyException) { + return new DataIntegrityViolationException( + "The context needs to be empty in order to be removed", + namingException); + } + + if (namingException instanceof InvalidAttributesException) { + return new AttributesIntegrityViolationException( + "Invalid attributes", namingException); + } + + if (namingException instanceof LimitExceededException) { + return new SearchLimitExceededException("Too many objects found", + namingException); + } + + if (namingException instanceof CommunicationException) { + throw new DataRetrievalFailureException( + "Unable to communicate with LDAP server", namingException); + } + + // Fallback - other type of NamingException encountered. + return new UncategorizedLdapException("Operation failed", + namingException); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DirContextProcessor.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DirContextProcessor.java new file mode 100644 index 00000000..59f873f5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/DirContextProcessor.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +/** + * Interface to be called in search by LdapTemplate before and after the actual + * search and enumeration traversal. + * + * @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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/EntryNotFoundException.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/EntryNotFoundException.java new file mode 100644 index 00000000..9bd81eb9 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/EntryNotFoundException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataRetrievalFailureException; + +/** + * Represents that an entry could not be found. + * + * @author Mattias Arthursson + */ +public class EntryNotFoundException extends DataRetrievalFailureException { + + private static final long serialVersionUID = -1268390922996332424L; + + public EntryNotFoundException(String msg) { + super(msg); + } + + public EntryNotFoundException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/LdapOperations.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/LdapOperations.java new file mode 100644 index 00000000..8cdc0829 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/LdapOperations.java @@ -0,0 +1,1350 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; + +/** + * 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 custom 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 + * SearchResult is passed to the CallbackHandler. Any encountered + * NamingException will be translated using the NamingExceptionTranslator. + * + * @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 DataAccessException + * 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 DataAccessException; + + /** + * Perform a search. 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 SearchResult is passed to the + * CallbackHandler. Any encountered NamingException will be translated using + * the NamingExceptionTranslator. + * + * @param se + * The SearchExecutor to use for performing the actual search. + * @param handler + * The NameClassPairCallbackHandler to which each found entry + * will be passed. + * @throws DataAccessException + * 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) + throws DataAccessException; + + /** + * 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 DataAccessException + * if the operation resulted in a NamingException. + */ + public Object executeReadOnly(ContextExecutor ce) + throws DataAccessException; + + /** + * 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. + * + * @param ce + * The ContextExecutor to which the actual operation on the + * DirContext will be delegated. + * @return the result from the ContextExecutor's operation. + * @throws DataAccessException + * if the operation resulted in a NamingException. + */ + public Object executeReadWrite(ContextExecutor ce) + throws DataAccessException; + + /** + * 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 SearchResults + * to. + */ + public void search(Name base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler); + + /** + * 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 SearchResults + * to. + */ + public void search(String base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler); + + /** + * 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 SearchResults + * to. + * @param processor + * The DirContextProcessor to use before and after the search. + */ + public void search(Name base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor); + + /** + * 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 DataAccessException + * 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); + + /** + * 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 DataAccessException + * 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); + + /** + * 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 DataAccessException + * 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); + + /** + * 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 DataAccessException + * 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); + + /** + * 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. + */ + public void search(String base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor); + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Each SearchResult is + * supplied to the specified NameClassPairCallbackHandler. Use the specified + * search scope and return objects flag in search controls. + * + * @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. + * @param handler + * The NameClassPairCallbackHandler to supply the SearchResults + * to. + * @throws DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 no the + * returnObjects 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Only search for the + * specified attributes. 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Only search for the + * specified attributes. 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified AttributesMapper. The + * default seach 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified AttributesMapper. The + * default seach 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified ContextMapper. Only + * look for 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified ContextMapper. Only + * look for 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Perform a non-recursive listing of the contexts bound to 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Lookup the supplied DN and return the found object. WARNING: This + * method should only be used if a DirObjectFactory has been specified on + * the ContextFactory. If this is not the case, you will get a new instance + * of the actual DirContext, which is probably not what you want. If, + * however this is what you want, be careful to close the context + * after you finished working with it. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object. + * @throws DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn) throws DataAccessException; + + /** + * Lookup the supplied DN and return the found object. WARNING: This + * method should only be used if a DirObjectFactory has been specified on + * the ContextFactory. If this is not the case, you will get a new instance + * of the actual DirContext, which is probably not what you want. If, + * however this is what you want, be careful to close the context + * after you finished working with it. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object. + * @throws DataAccessException + * if any error occurs. + */ + public Object lookup(String dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, String[] attributes, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, String[] attributes, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, String[] attributes, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, String[] attributes, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void modifyAttributes(Name dn, ModificationItem[] mods) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void modifyAttributes(String dn, ModificationItem[] mods) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void bind(Name dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void bind(String dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(Name dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(String dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(Name dn, boolean recursive) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(String dn, boolean recursive) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void rebind(Name dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void rebind(String dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataIntegrityViolationException + * if newDn is already bound + * @throws DataAccessException + * if any other error occurs. + */ + public void rename(final Name oldDn, final Name newDn) + throws DataAccessException; + + /** + * 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 DataIntegrityViolationException + * if newDn is already bound + * @throws DataAccessException + * if any other error occurs. + */ + public void rename(final String oldDn, final String newDn) + throws DataAccessException; +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/LdapTemplate.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/LdapTemplate.java new file mode 100644 index 00000000..6c462737 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/LdapTemplate.java @@ -0,0 +1,1379 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NameNotFoundException; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +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 javax.naming.directory.SearchResult; + +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.dao.DataAccessException; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; + +/** + * 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.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 NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); + + 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; + } + + /** + * 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.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, boolean, + * org.springframework.ldap.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.LdapOperations#search(java.lang.String, + * java.lang.String, int, boolean, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, int searchScope, + boolean returningObjFlag, NameClassPairCallbackHandler handler) + throws DataAccessException { + + search(base, filter, getDefaultSearchControls(searchScope, + returningObjFlag, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.NameClassPairCallbackHandler, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler, processor); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.NameClassPairCallbackHandler, + * org.springframework.ldap.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 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 DataAccessException + * 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; + try { + processor.preProcess(ctx); + results = se.executeSearch(ctx); + + while (results.hasMore()) { + NameClassPair result = (NameClassPair) results.next(); + handler.handleNameClassPair(result); + } + processor.postProcess(ctx); + } 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. + } catch (PartialResultException e) { + // Workaround for AD servers not handling referrals correctly. + if (ignorePartialResultException) { + log.debug("PartialResultException encountered and ignored", e); + } else { + throw getExceptionTranslator().translate(e); + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + closeContextAndNamingEnumeration(ctx, results); + } + } + + /** + * 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 DataAccessException + * 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.LdapOperations#search(javax.naming.Name, + * java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(Name base, String filter, + NameClassPairCallbackHandler handler) throws DataAccessException { + + search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, + NameClassPairCallbackHandler handler) throws DataAccessException { + + search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws DataAccessException { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws DataAccessException { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + AttributesMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + AttributesMapper mapper) throws DataAccessException { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, AttributesMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, AttributesMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws DataAccessException { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws DataAccessException { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + ContextMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + ContextMapper mapper) throws DataAccessException { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, ContextMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, ContextMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, SearchControls controls, + ContextMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, SearchControls controls, + ContextMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, SearchControls controls, + AttributesMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper, + * org.springframework.ldap.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.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper, + * org.springframework.ldap.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.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper, + * org.springframework.ldap.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.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper, + * org.springframework.ldap.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.LdapOperations#list(java.lang.String, + * org.springframework.ldap.ListResultCallbackHandler) + */ + public void list(final String base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.ListResultCallbackHandler) + */ + public void list(final Name base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(java.lang.String, + * org.springframework.ldap.NameClassPairMapper) + */ + public List list(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.NameClassPairMapper) + */ + public List list(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name) + */ + public List list(final Name base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(java.lang.String) + */ + public List list(final String base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void listBindings(final String base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void listBindings(final Name base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.NameClassPairMapper) + */ + public List listBindings(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.NameClassPairMapper) + */ + public List listBindings(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String) + */ + public List listBindings(final String base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name) + */ + public List listBindings(final Name base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.ContextMapper) + */ + public List listBindings(String base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.ContextMapper) + */ + public List listBindings(Name base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#executeReadOnly(org.springframework.ldap.ContextExecutor) + */ + public Object executeReadOnly(ContextExecutor ce) { + DirContext ctx = contextSource.getReadOnlyContext(); + return executeWithContext(ce, ctx); + } + + /* + * @see org.springframework.ldap.LdapOperations#executeReadWrite(org.springframework.ldap.ContextExecutor) + */ + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + closeContext(ctx); + } + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name) + */ + public Object lookup(final Name dn) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String) + */ + public Object lookup(final String dn) throws DataAccessException { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final Name dn, final AttributesMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final String dn, final AttributesMapper mapper) + throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.ContextMapper) + */ + public Object lookup(final Name dn, final ContextMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.ContextMapper) + */ + public Object lookup(final String dn, final ContextMapper mapper) + throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * java.lang.String[], org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final Name dn, final String[] attributes, + final AttributesMapper mapper) throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + return mapper.mapFromAttributes(filteredAttributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * java.lang.String[], org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final String dn, final String[] attributes, + final AttributesMapper mapper) throws DataAccessException { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + return mapper.mapFromAttributes(filteredAttributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * java.lang.String[], org.springframework.ldap.ContextMapper) + */ + public Object lookup(final Name dn, final String[] attributes, + final ContextMapper mapper) throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + DirContextAdapter contextAdapter = new DirContextAdapter( + filteredAttributes, dn); + return mapper.mapFromContext(contextAdapter); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * java.lang.String[], org.springframework.ldap.ContextMapper) + */ + public Object lookup(final String dn, final String[] attributes, + final ContextMapper mapper) throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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.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 NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#modifyAttributes(java.lang.String, + * javax.naming.directory.ModificationItem[]) + */ + public void modifyAttributes(final String dn, final ModificationItem[] mods) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.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 NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#bind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(final String dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(javax.naming.Name) + */ + public void unbind(final Name dn) { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(java.lang.String) + */ + public void unbind(final String dn) throws DataAccessException { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(javax.naming.Name, + * boolean) + */ + public void unbind(final Name dn, boolean recursive) + throws DataAccessException { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(java.lang.String, + * boolean) + */ + public void unbind(final String dn, boolean recursive) + throws DataAccessException { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + private void doUnbind(final Name dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbind(final String dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbindRecursively(final Name dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + deleteRecursively(ctx, new DistinguishedName(dn)); + return null; + } + }); + } + + private void doUnbindRecursively(final String dn) + throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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 DataAccessException + * if any error occurs + */ + protected void deleteRecursively(DirContext ctx, DistinguishedName name) + throws DataAccessException { + + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + try { + enumeration.close(); + } catch (Exception e) { + // Never mind this + } + } + } + + /* + * @see org.springframework.ldap.LdapOperations#rebind(javax.naming.Name, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final Name dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rebind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final String dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rename(javax.naming.Name, + * javax.naming.Name) + */ + public void rename(final Name oldDn, final Name newDn) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rename(oldDn, newDn); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rename(java.lang.String, + * java.lang.String) + */ + public void rename(final String oldDn, final String newDn) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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. + } + } + } + + /** + * Get the NamingExceptionTranslator that will be used by this instance. If + * no exceptionTranslator has been set, a default instance will be created. + * + * @return the NamingExceptionTranslator to be used by this instance. + */ + public NamingExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + /** + * Set the NamingExceptionTranslator to be used by this instance. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + + this.exceptionTranslator = exceptionTranslator; + } + + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } + + /** + * A CollectingNameClassPairCallbackHandler to wrap an AttributesMapper. + * That is, the found object is extracted from the {@link Attributes} of + * each {@link SearchResult}, and then passed to the specified + * AttributesMapper for translation. This class needs to be nested, since we + * want to be able to get hold of the exception translator of this instance. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ + public class AttributesMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private AttributesMapper mapper; + + public AttributesMapperCallbackHandler(AttributesMapper mapper) { + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a SearchResult and pass its attributes to + * the 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } + + /** + * 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 + */ + public class ContextMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private ContextMapper mapper; + + public ContextMapperCallbackHandler(ContextMapper mapper) { + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a {@link Binding} and pass its attributes + * to the ContextMapper. + * + * @param nameClassPair + * a SearchResult 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 EntryNotFoundException( + "SearchResult did not contain any object."); + } + return mapper.mapFromContext(object); + } + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java new file mode 100644 index 00000000..0698a9c6 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +/** + * Callback interface used by LdapTemplate's 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NameClassPairMapper.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NameClassPairMapper.java new file mode 100644 index 00000000..acf8cd14 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NameClassPairMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java new file mode 100644 index 00000000..cd6e7a4c --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.springframework.dao.DataAccessException; + +/** + * Interface to be implemented by classes that can translate between + * NamingExceptions and DataAccessExceptions. + * + * @author Mattias Arthursson + * + */ +public interface NamingExceptionTranslator { + /** + * Translate the given NamingException into a generic data access exception. + * @param namingException + * the offending NamingException. + * + * @return the DataAccessException to throw. + */ + public DataAccessException translate(NamingException namingException); +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/SearchExecutor.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/SearchExecutor.java new file mode 100644 index 00000000..34b474b5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/SearchExecutor.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +/** + * Interface for delegating an actual search operation. The typical + * implementation of executeSearch would be something like: + * + *

+ * SearchExecutor executor = new SearchExecutor(){
+ *   public NamingEnumeration executeSearch(DirContext ctx) throws NamingException{
+ *     return ctx.search(dn, filter, searchControls);
+ *   }
+ * }
+ * 
+ * + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/SearchLimitExceededException.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/SearchLimitExceededException.java new file mode 100644 index 00000000..49806334 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/SearchLimitExceededException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataRetrievalFailureException; + +/** + * Indicates that the search limit was exceeded in a search. + * + * @author Mattias Arthursson + * + */ +public class SearchLimitExceededException extends DataRetrievalFailureException { + private static final long serialVersionUID = 6899885947075235580L; + + public SearchLimitExceededException(String msg) { + super(msg); + } + + public SearchLimitExceededException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/UncategorizedLdapException.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/UncategorizedLdapException.java new file mode 100644 index 00000000..2423a3b7 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/UncategorizedLdapException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.UncategorizedDataAccessException; + +/** + * Indicates that an unknown NamingException has occurred. + * + * @author Mattias Arthursson + */ +public class UncategorizedLdapException extends + UncategorizedDataAccessException { + + private static final long serialVersionUID = -3319936235493869823L; + + public UncategorizedLdapException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/package.html b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/package.html new file mode 100644 index 00000000..f160a4a2 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/package.html @@ -0,0 +1,3 @@ + +The core package of the Spring-LDAP library. + \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AbstractContextSource.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AbstractContextSource.java new file mode 100644 index 00000000..d972ebe1 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AbstractContextSource.java @@ -0,0 +1,459 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.AuthenticationSource; +import org.springframework.ldap.ContextSource; +import org.springframework.ldap.DefaultNamingExceptionTranslator; +import org.springframework.ldap.NamingExceptionTranslator; + +/** + * Abstract implementation of the 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 AuthenticationSource is set, this will be used for getting user name + * and password for each new connection, otherwise a default one will be created + * using the specified userName 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.LdapTemplate + * @see org.springframework.ldap.support.DefaultDirObjectFactory + * @see org.springframework.ldap.support.LdapContextSource + * @see org.springframework.ldap.support.DirContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public abstract class AbstractContextSource implements ContextSource, + 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; + + protected String userName = ""; + + 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 NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); + + private static final Log log = LogFactory.getLog(LdapContextSource.class); + + public static final String SUN_LDAP_POOLING_FLAG = "com.sun.jndi.ldap.connect.pool"; + + private static final String JDK_142 = "1.4.2"; + + public DirContext getReadOnlyContext() { + if (!anonymousReadOnly) { + return createContext(getAuthenticatedEnv()); + } else { + return createContext(getAnonymousEnv()); + } + } + + public DirContext getReadWriteContext() { + return createContext(getAuthenticatedEnv()); + } + + /** + * Default implementation of setting the environment up to be authenticated. + * Override in subclass if necessary. This is needed for Active Directory + * connectivity, for example. + * + * @param env + * the environment to modify. + */ + protected void setupAuthenticatedEnvironment(Hashtable env) { + env + .put(Context.SECURITY_PRINCIPAL, authenticationSource + .getPrincipal()); + log.debug("Principal: '" + userName + "'"); + 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 (base != null) { + 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, should not) specify the + * full distinguished names in the operations performed. + * + * @param base + * the base suffix. + */ + public void setBase(String base) { + this.base = new DistinguishedName(base); + } + + /** + * 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. + */ + 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 getExceptionTranslator().translate(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; + } + + /** + * 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; + } + + /** + * 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 (base != null && 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(userName)) { + log + .warn("Property 'userName' not set - " + + "anonymous context will be used for read-write operations"); + } else if (StringUtils.isBlank(password)) { + log.warn("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 { + 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 (base != null) { + // 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 name (principal) to use for getting authenticated contexts. + * + * @param userName + * the user name. + */ + public void setUserName(String userName) { + this.userName = 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; + } + + /** + * 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. + * + * @param pooled + * whether Contexts should be pooled. + */ + public void setPooled(boolean pooled) { + this.pooled = 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; + } + + public void setAuthenticationSource( + AuthenticationSource authenticationProvider) { + this.authenticationSource = authenticationProvider; + } + + /** + * 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 and anonymous environment should be + * used for read-only operations, false otherwise. + */ + public void setAnonymousReadOnly(boolean anonymousReadOnly) { + this.anonymousReadOnly = anonymousReadOnly; + } + + /** + * Set the NamingExceptionTranslator to be used by this instance. By + * default, a {@link DefaultNamingExceptionTranslator} will be used. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + + /** + * Get the NamingExceptionTranslator used by this instance. + * + * @return the NamingExceptionTranslator. + */ + public NamingExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + /** + * 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 userName; + } + + public String getCredentials() { + return password; + } + + } + + protected String[] getUrls() { + return urls; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AggregateDirContextProcessor.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AggregateDirContextProcessor.java new file mode 100644 index 00000000..e24a88bd --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AggregateDirContextProcessor.java @@ -0,0 +1,72 @@ +package org.springframework.ldap.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.DirContextProcessor; + +/** + * Manages a sequence of 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 DirContextProcessors. + * + * @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.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.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java new file mode 100644 index 00000000..9d0a7fa1 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ModificationItem; + +/** + * Indicates that the implementor 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 or added or removed. + * + * @return an array of modification items + */ + public ModificationItem[] getModificationItems(); +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java new file mode 100644 index 00000000..8f017beb --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +import org.springframework.ldap.NameClassPairCallbackHandler; + +/** + * A 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java new file mode 100644 index 00000000..01776c2e --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java @@ -0,0 +1,93 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.directory.Attributes; +import javax.naming.spi.DirObjectFactory; + +/** + * Default implementation of the DirObjectFactory interface. Creates a + * 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. + */ + public static final String JNDI_ENV_BASE_PATH_KEY = "org.springframework.ldap.base.path"; + + /** + * Creates a DirContextAdapter from the supplied arguments. + * + * @param obj + * @param name + * @param nameCtx + * @param environment + * @param attrs + * @return a new DirContextAdapter from the attributes and name. + * @throws Exception + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment, Attributes attrs) throws Exception { + + try { + DirContextAdapter dirContextAdapter = new DirContextAdapter(attrs, + name, (Name) environment.get(JNDI_ENV_BASE_PATH_KEY)); + 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 + } + + } + } + } + + /** + * Returns null. + * + * @param obj + * @param name + * @param nameCtx + * @param environment + * @return null. + * @throws Exception + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment) throws Exception { + return null; + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DefaultDnParserFactory.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DefaultDnParserFactory.java new file mode 100644 index 00000000..e0ccbdc7 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DefaultDnParserFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.StringReader; + +/** + * 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextAdapter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextAdapter.java new file mode 100644 index 00000000..779af825 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextAdapter.java @@ -0,0 +1,1271 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; +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.collections.CollectionUtils; +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.DefaultNamingExceptionTranslator; +import org.springframework.ldap.NamingExceptionTranslator; + +/** + * 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.support.DefaultDirObjectFactory} in your + * ContextSource you may receive instances of this class from searches and + * lookups. This can be particularly useful when updating data, since this class + * implements + * {@link org.springframework.ldap.support.AttributeModificationsAware}, + * providing a {@link #getModificationItems()} method. + * + * @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 attrs; + + private DistinguishedName dn; + + private DistinguishedName base; + + private boolean updateMode = false; + + private Attributes updatedAttrs; + + private NamingExceptionTranslator exceptionTranslator; + + /** + * 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.attrs = attrs; + } else { + this.attrs = 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.attrs = (Attributes) master.attrs.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. + */ + protected 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 = attrs.getAll(); + } + + try { + while (attributesEnumeration.hasMore()) { + Attribute oneAttribute = (Attribute) attributesEnumeration + .next(); + tmpList.add(oneAttribute.getID()); + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(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 getExceptionTranslator().translate(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 = attrs.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) { + // Attribute has been removed. + modificationList.add(new ModificationItem( + DirContext.REMOVE_ATTRIBUTE, changedAttr)); + } else { + // Collect all modifications to attribute individually (this also + // covers additions to a previously non-existant attribute). + Collection oldValues = new LinkedList(); + Collection newValues = new LinkedList(); + + collectAttributeValues(oldValues, currentAttribute); + collectAttributeValues(newValues, changedAttr); + Collection myModifications = new LinkedList(); + + Collection addedValues = CollectionUtils.subtract(newValues, + oldValues); + Collection removedValues = CollectionUtils.subtract(oldValues, + newValues); + + collectModifications(DirContext.ADD_ATTRIBUTE, changedAttr, + addedValues, myModifications); + collectModifications(DirContext.REMOVE_ATTRIBUTE, changedAttr, + removedValues, 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(int modificationType, Attribute attr, + Collection values, Collection c) { + if (values.size() > 0) { + BasicAttribute modificationAttribute = new BasicAttribute(attr + .getID()); + for (Iterator iter = values.iterator(); iter.hasNext();) { + modificationAttribute.add(iter.next()); + } + c + .add(new ModificationItem(modificationType, + modificationAttribute)); + } + } + + private void collectAttributeValues(Collection valueCollection, + Attribute attribute) throws NamingException { + + if (attribute == null) { + return; + } + + NamingEnumeration attributeValues = attribute.getAll(); + while (attributeValues.hasMoreElements()) { + Object value = (Object) attributeValues.nextElement(); + valueCollection.add(value); + } + } + + /** + * Compare the existing attribute name with the value in + * value. + *

+ * Also handles the case where the value has been reset to the original + * value after a previous change. For example, changing a to + * b and then back to a again must result in + * this method returning true so the first change can be + * overwritten with the latest change. + * TODO Do the null checks on the value instead + * + * @param name + * Name of the original attribute. + * @param value + * Value to check if it has been changed. + * @return true if there has been a change compared to original attribute, + * or a previous update + */ + private boolean isChanged(String name, Object value) { + Attribute orig = attrs.get(name); + Attribute prev = updatedAttrs.get(name); + + // FALSE if both are null it is not changed + // TODO Also include prev in null check + if (orig == null && value == null) { + return false; + } + + // TRUE if existing value is null or does not contain one value + if (orig == null || orig.size() != 1) { + return true; + } + + // TRUE if existing value is not null and the new one is null + if (orig != null && value == null) { + return true; + } + + // TRUE if we can't access the value + Object obj = null; + try { + obj = orig.get(0); + } catch (NamingException e) { + return true; + } + + if (prev == null) { + // TRUE if the value is not equal + return !value.equals(obj); + } else { + // TRUE if we can't access the value + Object prevObj = null; + try { + prevObj = prev.get(0); + } catch (NamingException e) { + return true; + } + // TRUE if the value is not equal + return !value.equals(obj) || !value.equals(prevObj); + } + } + + /** + * 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 = attrs.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 attrs.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 = attrs.get(name); + if (oneAttr == null) { + return null; + } + try { + return oneAttr.get(); + } catch (NamingException e) { + throw getExceptionTranslator().translate(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) { + attrs.put(name, value); + } + + // updating entry + if (updateMode && isChanged(name, value)) { + BasicAttribute attribute = new BasicAttribute(name); + if (value != null) { + attribute.add(value); + } + updatedAttrs.put(attribute); + } + } + + /* + * @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 + attrs.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)) { + attrs.remove(a.getID()); + } else { + // Otherwise it should be set. + attrs.put(a); + } + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(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 = attrs.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 getExceptionTranslator().translate(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 = attrs.get(name); + if (attribute != null) { + for (int i = 0; i < attribute.size(); i++) { + try { + attrSet.add(attribute.get(i)); + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } else { + return null; + } + + return attrSet; + } + + /** + * Set the supplied attribute. + * + * @param attribute + * the attribute to set. + */ + public void setAttribute(Attribute attribute) { + attrs.put(attribute); + } + + /** + * Get all attributes. + * + * @return all attributes. + */ + public Attributes getAttributes() { + return attrs; + } + + /** + * @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) attrs.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 = attrs.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() { + return dn.toString(); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#getDn() + */ + public Name getDn() { + DistinguishedName retval = new DistinguishedName(dn); + retval.removeFirst(base); + return retval; + } + + /* + * (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 = attrs.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(); + } + + /** + * Get the NamingExceptionTranslator. + * + * @return the NamingExceptionTranslator to use; if none is specified, + * {@link DefaultNamingExceptionTranslator} is used. + */ + public NamingExceptionTranslator getExceptionTranslator() { + if (exceptionTranslator == null) { + exceptionTranslator = new DefaultNamingExceptionTranslator(); + } + return exceptionTranslator; + } + + /** + * Set the NamingExceptionTranslator to use. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextOperations.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextOperations.java new file mode 100644 index 00000000..c44f3fbd --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextOperations.java @@ -0,0 +1,161 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.SortedSet; + +import javax.naming.Name; +import javax.naming.directory.DirContext; + +/** + * Interface for DirContextAdapter to simplify mock testing. + * + * @author Mattias Arthursson + */ +public interface DirContextOperations extends DirContext, + AttributeModificationsAware { + + /** + * Gets the update mode. 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. + * + * @param name + * name of the attribute. + * @return the value of the attribute. + */ + public String getStringAttribute(String name); + + /** + * Get the value of an Object attribute. + * + * @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); + + /** + * Update the attributes. This will mean that the getters + * (getStringAttribute methods) will return the updated values. Remove the + * attributes to be updated. + */ + 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 DN, without 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); +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextSource.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextSource.java new file mode 100644 index 00000000..e69d6a0a --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DirContextSource.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DistinguishedName.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DistinguishedName.java new file mode 100644 index 00000000..bb5392cd --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DistinguishedName.java @@ -0,0 +1,629 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.util.ListComparator; + +/** + * Default implementation of a Name corresponding to an LDAP path. A + * DistinguishedName implementation is included in JDK1.5 (LdapName), but not in + * prior releases. + * + * An 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 Name interface specifies that the + * most significant part be in position 0, i.e. + * + * The path: uid=adam.skogman, ou=People, ou=EU Name[0]: ou=EU Name[1]: + * ou=People Name[2]: uid=adam.skogman + *

+ * Useful for parsing and building LDAP paths. + * + *

+ * DistinguishedName path = new DistinguishedName();
+ * path.addLast("cn", entry.getUid());
+ * path.addLast("ou", "users");
+ * path.append(new DistinguishedName(helpdesk.getSomeSuffix()));
+ * String dn = path.toString();
+ * 
+ * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class DistinguishedName implements Name { + 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 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 Name. The parts of + * the supplied Name must be syntactically correct LdapRdns. + * + * @param name + * the 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 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 LdapRdn at a specified position. + * + * @param index + * the LdapRdn to retrieve. + * @return the LdapRdn at the requested position. + */ + public LdapRdn getLdapRdn(int index) { + return (LdapRdn) names.get(index); + } + + /** + * Get the name list. + * + * @return the list of LdapRdns that this DistinguishedName consists of. + */ + public List getNames() { + return names; + } + + /** + * Get the String representation of this DistinguishedName. + * + * @return a syntactically correct, escaped String representation of the + * DistinguishedName. + */ + public String toString() { + return encode(); + } + + /** + * 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() { + + // 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()) + 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 a ldap 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 a LDAP path first + * + * @param path + */ + public void append(DistinguishedName path) { + getNames().addAll(path.getNames()); + } + + /** + * Add a LDAP path first + * + * @param path + */ + 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 . 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 ldap path ends with a certian path. + * + * If the argument path is empty (no names in path) this methid 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 ldast part of this DistinguishedName. + * + * @return the removed LdapRdn. + */ + public LdapRdn removeLast() { + return (LdapRdn) names.remove(names.size() - 1); + } + + /** + * Add a new LdapRdn using the supplied key and value. + * + * @param key + * the key of the LdapRdn. + * @param value + * the value of the LdapRdn. + */ + public void add(String key, String value) { + names.add(new LdapRdn(key, value)); + } + + /** + * Add the supplied LdapRdn last in the list of Rdns. + * + * @param rdn + * the LdapRdn to add. + */ + public void add(LdapRdn rdn) { + names.add(rdn); + } + + /** + * Add the supplied 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); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DnParser.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DnParser.java new file mode 100644 index 00000000..7bd29baa --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/DnParser.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + + +/** + * 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapContextSource.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapContextSource.java new file mode 100644 index 00000000..ef3e7597 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapContextSource.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Hashtable; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.ControlFactory; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; + +import com.sun.jndi.ldap.ctl.ResponseControlFactory; + +/** + * ContextSource implementation which creates an InitialLdapContext + * instance. For configuration information, see + * {@link org.springframework.ldap.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.support.AbstractContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public class LdapContextSource extends AbstractContextSource { + + private static final Class DEFAULT_RESPONSE_CONTROL_FACTORY = ResponseControlFactory.class; + + private Class responseControlFactory = DEFAULT_RESPONSE_CONTROL_FACTORY; + + protected Hashtable getAnonymousEnv() { + Hashtable env = super.getAnonymousEnv(); + env + .put(LdapContext.CONTROL_FACTORIES, responseControlFactory + .getName() + ".trasig"); + return env; + } + + /* + * @see org.springframework.ldap.support.AbstractContextSource#getDirContextInstance(java.util.Hashtable) + */ + protected DirContext getDirContextInstance(Hashtable environment) + throws NamingException { + return new InitialLdapContext(environment, null); + } + + /** + * Set the ResponseControlFactory to use. Default is + * {@link com.sun.jndi.ldap.ctl.ResponseControlFactory}. The specified + * class needs to be an implementation of + * {@link javax.naming.ldap.ControlFactory}. + * + * @param responseControlFactory + * the ResponseControlFactory to be used. Null means reset to the + * default. + */ + public void setResponseControlFactory(Class responseControlFactory) { + if (responseControlFactory == null) { + this.responseControlFactory = DEFAULT_RESPONSE_CONTROL_FACTORY; + } else if (ControlFactory.class + .isAssignableFrom(responseControlFactory)) { + this.responseControlFactory = responseControlFactory; + } else { + throw new IllegalArgumentException( + "Invalid ReponseControlFactory: " + responseControlFactory + + " is not an implementation of " + + ControlFactory.class.getName()); + } + } + + /** + * @return the current ResponseControlFactory. + */ + Class getResponseControlFactory() { + return responseControlFactory; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapEncoder.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapEncoder.java new file mode 100644 index 00000000..d6525bc2 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapEncoder.java @@ -0,0 +1,235 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.regex.Matcher; +import java.util.regex.Pattern; + + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; + +/** + * Helper class to encode and decode ldap names and values. + * + * @author Adam Skogman + */ +public class LdapEncoder { + + static private String[] nameEscapeTable = new String[96]; + + static private String[] filterEscapeTable = new String['\\' + 1]; + + /** + * Pattern for matching escaped ldap name values. + * + * Double escaping: \ -> \\ (in pattern) -> \\\\ (in java string literal) + * + * Group 1: Hex escapes = \XX -> \p{XDigit}{2} Group 2: Ordinary escapes = + * \x -> \. Group 3: Anything but \ [^\\] + * + * Note that the \ is not part of the match. + */ + static private final Pattern VALUE_DECODE_PATTERN = Pattern + .compile("(?:\\\\(\\p{XDigit}{2}))|(?:\\\\(.))|([^\\\\])"); + + 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['\"'] = "\\\""; + // nameEscapeTable['/'] = "\\" + toTwoCharHex('/'); + 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"; + + } + + static protected String toTwoCharHex(char c) { + + String raw = Integer.toHexString(c).toUpperCase(); + + if (raw.length() > 1) + return raw; + else + return "0" + raw; + } + + /** + * All static methods + */ + private LdapEncoder() { + } + + static public 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 + * @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()); + + Matcher matcher = VALUE_DECODE_PATTERN.matcher(value); + + int end = 0; + + while (matcher.find()) { + end = matcher.end(); + // group 1 + if (matcher.group(1) != null) { + // parse as hex = base 16 + try { + char c = (char) Integer.parseInt(matcher.group(1), 16); + decoded.append(c); + } catch (NumberFormatException e) { + throw new BadLdapGrammarException( + "Escaped hex value Could not be parsed. Found '\\" + + matcher.group(1) + "'"); + } + } else if (matcher.group(2) != null) { + // just add, we stripped away the \ + decoded.append(matcher.group(2)); + } else if (matcher.group(3) != null) { + // just add + decoded.append(matcher.group(3)); + } + + } + // end is match + +1 so it should be same as length, i.e. + // last + 1 + if (end < value.length()) { + throw new BadLdapGrammarException( + "RDN could not be parsed fully, remaining '" + + StringUtils.substring(value, end) + "'"); + } + + return decoded.toString(); + + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapRdn.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapRdn.java new file mode 100644 index 00000000..f5ccdd6d --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapRdn.java @@ -0,0 +1,198 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.util.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. + */ + 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 + * @param 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 no components have been added. + */ + 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 indet idx. + * @throws IndexOutOfBoundsException + * if no component exists at index idx. + */ + public LdapRdnComponent getComponent(int idx) { + return (LdapRdnComponent) components.get(idx); + } + + /** + * Get a properly rfc2253-encoded String representation to this LdapRdn. + * + * @return an encoded String corresponding to this LdapRdn. + */ + 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); + } + + 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(); + } +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapRdnComponent.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapRdnComponent.java new file mode 100644 index 00000000..52bd4e72 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/LdapRdnComponent.java @@ -0,0 +1,163 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 { + public static final boolean DONT_DECODE_VALUE = false; + + private String key; + + private String value; + + /** + * Constructs an LdapRdnComponent without decoding the value. + * + * @param key + * the Atttribute 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; + } + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + /** + * Encode key and value to ldap + * + * @return The ldap encoded 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(); + } + + 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"; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java new file mode 100644 index 00000000..1e28a087 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.authentication; + +import org.acegisecurity.Authentication; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.userdetails.ldap.LdapUserDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.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)) { + throw new IllegalArgumentException( + "The principal property of the authentication object -" + + "needs to be a LdapUserDetails."); + } else { + LdapUserDetails details = (LdapUserDetails) principal; + return details.getDn(); + } + } else { + log.warn("No Authentication object set in SecurityContext - " + + "returning empty String as Principal"); + return ""; + } + } + + /* + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java new file mode 100644 index 00000000..ed94d937 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java @@ -0,0 +1,157 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.authentication; + +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.ldap.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 defaultUser. + * + * @return the target's principal if it is not empty, the + * defaultUser 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/package.html b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/package.html new file mode 100644 index 00000000..d15fe6bc --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/authentication/package.html @@ -0,0 +1,3 @@ + +Support classes for custom authentication. + diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessor.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessor.java new file mode 100644 index 00000000..3d54d9c4 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessor.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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 into 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(); + 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/PagedResultsCookie.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/PagedResultsCookie.java new file mode 100644 index 00000000..cbd0fe9a --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/PagedResultsCookie.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.control; + +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 = cookie; + } + + /** + * Get the cookie. + * + * @return the cookie. + */ + public byte[] getCookie() { + return cookie; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/PagedResultsRequestControl.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/PagedResultsRequestControl.java new file mode 100644 index 00000000..ab85e3c6 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/PagedResultsRequestControl.java @@ -0,0 +1,200 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.UncategorizedLdapException; +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. + * + * @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; + + public PagedResultsRequestControl(int pageSize) { + this(pageSize, null); + } + + public PagedResultsRequestControl(int pageSize, PagedResultsCookie cookie) { + this.pageSize = pageSize; + this.cookie = cookie; + fallbackResponseControlClass = loadFallbackResponseControlClass(); + } + + public PagedResultsCookie getCookie() { + return cookie; + } + + public int getPageSize() { + return pageSize; + } + + 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.support.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 UncategorizedLdapException( + "Error creating PagedResultsControl", e); + } + } + + /* + * @see org.springframework.ldap.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(); + + // 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/package.html b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/package.html new file mode 100644 index 00000000..e8351194 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/control/package.html @@ -0,0 +1,3 @@ + +Support classes for custom request control context processors. + diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java new file mode 100644 index 00000000..e00f8f04 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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(); + } + + /** + * Prints the query with LDAP encoding to a stringbuffer + * + * @param buff + * The stringbuffer + * @return The very same stringbuffer + */ + public abstract StringBuffer encode(StringBuffer buff); + + /** + * Encodes the filter to a string using the (@link #encode(StringBuffer) + * method. + * + * @return The encoded filter + */ + public String encode() { + StringBuffer buff = new StringBuffer(256); + buff = encode(buff); + return buff.toString(); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return encode(); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/AndFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/AndFilter.java new file mode 100644 index 00000000..4622da1f --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/AndFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter for a logical AND. E.g.: + * + *

+ *     AndFilter filter = new AndFilter();
+ *     filter.and(new EqualsFilter("objectclass", "person");
+ *     filter.and(new EqualsFilter("cn", "Some CN");
+ *     System.out.println(filter.ecode());    
+ * 
+ * + * would result in: (&(objectclass=person)(cn=Some CN)) + * + * @see org.springframework.ldap.support.filter.EqualsFilter + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class AndFilter extends BinaryLogicalFilter { + + private static final String AMPERSAND = "&"; + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return AMPERSAND; + } + + /** + * Add a query to the and expression + * + * @param query + * The query to and with the rest of the and:ed queries. + * @return This LdapAndQuery + */ + public AndFilter and(Filter query) { + queryList.add(query); + return this; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java new file mode 100644 index 00000000..0c3ef3a2 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.filter.Filter#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.support.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.support.filter.Filter#hashCode() + */ + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java new file mode 100644 index 00000000..20fc78f0 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.springframework.ldap.support.LdapEncoder; + +/** + * Abstract superclass for filters to 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 + * @param value + */ + public CompareFilter(String attribute, int value) { + this.attribute = attribute; + this.value = String.valueOf(value); + this.encodedValue = LdapEncoder.filterEncode(this.value); + } + + /* + * @see org.springframework.ldap.support.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.support.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.support.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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java new file mode 100644 index 00000000..069d8186 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + + +/** + * A filter for 'equals'. The following code: + * + *
+ * EqualsFilter filter = new EqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + *
(cn=Some CN)
+ * + * @author Adam Skogman + */ +public class EqualsFilter extends CompareFilter { + + private static final String EQUALS_SIGN = "="; + + public EqualsFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Convenience constructor for int values. + * + * @param attribute Name of attribute in filter. + * @param value The value of the attribute in the filter. + */ + public EqualsFilter(String attribute, int value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.support.filter.CompareFilter#getCompareString() + */ + protected String getCompareString() { + return EQUALS_SIGN; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/Filter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/Filter.java new file mode 100644 index 00000000..a5ae4f69 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/Filter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * Common interface for filters. + * + * @author Adam Skogman + */ +public interface Filter { + + /** + * Encodes the filter to a string using the (@link #encode(StringBuffer) + * method. + * + * @return The encoded filter + */ + public String encode(); + + /** + * Prints the query with LDAP encoding to a stringbuffer + * + * @param buff + * The stringbuffer + * @return The very same stringbuffer + */ + public StringBuffer encode(StringBuffer buff); + + /** + * 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 hascode + */ + public int hashCode(); + +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java new file mode 100644 index 00000000..871b8dd1 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter to compare >=. LDAP RFC does not allow > comparison. The following + * code: + * + *
+ * GreaterThanOrEqualsFilter filter = new GreaterThanOrEqualsFilter("cn",
+ *         "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn>=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class GreaterThanOrEqualsFilter extends CompareFilter { + + private static final String GREATER_THAN_OR_EQUALS = ">="; + + public GreaterThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public GreaterThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + protected String getCompareString() { + return GREATER_THAN_OR_EQUALS; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java new file mode 100644 index 00000000..36b5e723 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter to compare <=. LDAP RFC does not allow < comparison. The following + * code: + * + *
+ * LessThanOrEqualsFilter filter = new LessThanOrEqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn<=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class LessThanOrEqualsFilter extends CompareFilter { + + private static final String LESS_THAN_OR_EQUALS = "<="; + + public LessThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public LessThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + protected String getCompareString() { + return LESS_THAN_OR_EQUALS; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java new file mode 100644 index 00000000..41d46ebd --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.LdapEncoder; + +/** + * This filter allows the user to specify wildcards (*) by not escaping them in + * the filter. The following code: + * + *
+ * LikeFilter filter = new LikeFilter("cn", "foo*");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ *  (cn=foo*)
+ * 
+ * + * @author Anders Henja + * @author Mattias Arthursson + */ +public class LikeFilter extends EqualsFilter { + + public LikeFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Encodes a value according to the rules for this filter. + * + * @param value + * Value to encode. + * @return Encoded value. + */ + 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/NotFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/NotFilter.java new file mode 100644 index 00000000..972edfa5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/NotFilter.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.apache.commons.lang.Validate; + +/** + * A filter for 'not'. The following code: + * + *
+ * Filter filter = new NotFilter(new EqualsFilter("cn", "foo");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
(!(cn=foo))
+ * + * @author Adam Skogman + */ +public class NotFilter extends AbstractFilter { + + private final Filter filter; + + static private final int HASH = "!".hashCode(); + + /** + * Create a filter that negates the outcome of the given filter. + * + * @param filter + * The filter that should be negated. + */ + public NotFilter(Filter filter) { + Validate.notNull(filter); + this.filter = filter; + } + + /** + * @see org.springframework.ldap.support.filter.Filter#encode(java.lang.StringBuffer) + */ + public StringBuffer encode(StringBuffer buff) { + + buff.append("(!"); + filter.encode(buff); + buff.append(')'); + + return buff; + + } + + /** + * Compares key and value before encoding + * + * @see org.springframework.ldap.support.filter.Filter#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; + } + + /** + * hash attribute and value + * + * @see org.springframework.ldap.support.filter.Filter#hashCode() + */ + public int hashCode() { + return HASH ^ filter.hashCode(); + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/OrFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/OrFilter.java new file mode 100644 index 00000000..20ff1ddf --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/OrFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * Filter for logical OR. + * + *
+ *        AndFilter filter = new AndFilter();
+ *        filter.or(new EqualsFilter("objectclass", "person");
+ *        filter.or(new EqualsFilter("objectclass", "organizationalUnit");
+ *        System.out.println(filter.ecode());    
+ * 
+ * + * would result in: + * (|(objectclass=person)(objectclass=organizationalUnit)) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class OrFilter extends BinaryLogicalFilter { + + private static final String PIPE_SIGN = "|"; + + /** + * Add a query to the OR expression + * + * @param query + * The query to or with the rest of the or:ed queries. + * @return This LdapOrQuery + */ + public OrFilter or(Filter query) { + queryList.add(query); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return PIPE_SIGN; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java new file mode 100644 index 00000000..bbd6b8d0 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.support.LdapEncoder; + +/** + * This filter automatically converts all whitespace to wildcards (*). The + * following code: + * + *
+ * WhitespaceWildcardsFilter filter = new WhitespaceWildcardsFilter("cn",
+ *         "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: (cn=*Some*CN*) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class WhitespaceWildcardsFilter extends EqualsFilter { + private static Pattern starReplacePattern = Pattern.compile("\\s+"); + + public WhitespaceWildcardsFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Encodes a value according to the rules for this filter. + * + * @param value + * Value to encode. + * @return Encoded value. + */ + protected String encodeValue(String value) { + + // blank string means just ONE star + if (StringUtils.isBlank(value)) { + return "*"; + } + + // trim value, we will add in stars first and last anywhay + value = value.trim(); + + // filter encode so that any stars etc. are preserved + String filterEncoded = LdapEncoder.filterEncode(value); + + // Now replace all whitespace with stars + Matcher m = starReplacePattern.matcher(filterEncoded); + + // possibly 2 longer (stars at ends) + StringBuffer buff = new StringBuffer(value.length() + 2); + + buff.append('*'); + + while (m.find()) { + m.appendReplacement(buff, "*"); + } + m.appendTail(buff); + + buff.append('*'); + + return buff.toString(); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/package.html b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/package.html new file mode 100644 index 00000000..e05a77ed --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/filter/package.html @@ -0,0 +1,16 @@ + +Utility classes for dynamically building LDAP +filters. Filters can be nested and wrapped around each other: +
+AndFilter andFilter = new AndFilter();
+andFilter.and(new EqualsFilter("objectclass", "person");
+andFilter.and(new EqualsFilter("cn", "Some CN");
+OrFilter orFilter = new OrFilter();
+orFilter.or(andFilter);
+orFilter.or(new EqualsFilter("objectclass", "organizationalUnit));
+System.out.println(orFilter.encode());
+
+would result in: + +
(|(&(objectclass=person)(cn=Some CN))(objectclass=organizationalUnit))
+ diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/package.html b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/package.html new file mode 100644 index 00000000..bf83afd5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/support/package.html @@ -0,0 +1,3 @@ + +Support classes for Spring-LDAP. + \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/util/ListComparator.java b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/util/ListComparator.java new file mode 100644 index 00000000..0ea9857c --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/util/ListComparator.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 either 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/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/util/package.html b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/util/package.html new file mode 100644 index 00000000..b01effef --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/org/springframework/ldap/util/package.html @@ -0,0 +1,3 @@ + +Internal utility classes. + diff --git a/build-spring-ldap/spring-ldap-1.1.1/src/main/java/overview.html b/build-spring-ldap/spring-ldap-1.1.1/src/main/java/overview.html new file mode 100644 index 00000000..fe97647c --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1.1/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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java new file mode 100644 index 00000000..10089337 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataIntegrityViolationException; + +/** + * Exception that indicates that an invalid or missing Attribute has been + * supplied to an LDAP operation. + * + * @author Mattias Arthursson + * + */ +public class AttributesIntegrityViolationException extends + DataIntegrityViolationException { + + private static final long serialVersionUID = -6368616096960202571L; + + public AttributesIntegrityViolationException(String msg) { + super(msg); + } + + public AttributesIntegrityViolationException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AttributesMapper.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AttributesMapper.java new file mode 100644 index 00000000..bc6af080 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AttributesMapper.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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 LdapTemplate class. + *

+ * Typically used in LdapTemplate's search methods. AttributeMapper objects are + * typically stateless and thus reusable; they are ideal for implementing + * attribute-mapping logic in one place. + *

+ * Alternatively, consider using a {@link ContextMapper} in stead. + * + * @see org.springframework.ldap.LdapTemplate#search(Name, String, + * 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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AuthenticationSource.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AuthenticationSource.java new file mode 100644 index 00000000..7a8874c1 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/AuthenticationSource.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap; + +/** + * An AuthenticationSource is responsible for providing the principal 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 (userName). + */ + public String getPrincipal(); + + /** + * Get the credentials to use when creating an authenticated context. + * + * @return the credentials (userName). + */ + public String getCredentials(); +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/BadLdapGrammarException.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/BadLdapGrammarException.java new file mode 100644 index 00000000..a7d55dbd --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/BadLdapGrammarException.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.InvalidDataAccessResourceUsageException; + +/** + * 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 + InvalidDataAccessResourceUsageException { + + private static final long serialVersionUID = 961612585331409470L; + + public BadLdapGrammarException(String message) { + super(message); + } + + public BadLdapGrammarException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java new file mode 100644 index 00000000..6f42103d --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +/** + * A NameClassPairCallbackHandler to collect all results in an internal List. + * + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextAssembler.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextAssembler.java new file mode 100644 index 00000000..da2ef350 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextAssembler.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap; + +/** + * 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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextExecutor.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextExecutor.java new file mode 100644 index 00000000..70ac8790 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextExecutor.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +/** + * Interface for delegating an actual operation to be performed on an + * DirContext. For searches, use {@link org.springframework.ldap.SearchExecutor} in + * stead. A typical usage of this interface could be e.g.: + * + *

+ * ContextExecutor executor = new ContextExecutor(){
+ *   public Object executeWithContext(DirContext ctx) throws NamingException{
+ *     return ctx.lookup(dn);
+ *   }
+ * };
+ * 
+ * + * @see org.springframework.ldap.LdapTemplate#executeReadOnly(ContextExecutor) + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextMapper.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextMapper.java new file mode 100644 index 00000000..a9effe00 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextMapper.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.support.DefaultDirObjectFactory; +import org.springframework.ldap.support.DirContextAdapter; + +/** + * An interface used by LdapTemplate to map LDAP Contexts to beans. Responsible + * for mapping from 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}, 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 + * + * @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. + * @return an object built from the data in the context. + */ + public Object mapFromContext(Object ctx); +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextSource.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextSource.java new file mode 100644 index 00000000..fa104841 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/ContextSource.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.DirContext; + +import org.springframework.dao.DataAccessException; + +/** + * Interface used to retrieve and authenticate LDAP contexts. + * + * @see org.springframework.ldap.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 DataAccessException + * if some error occurs creating an DirContext. + */ + public DirContext getReadOnlyContext() throws DataAccessException; + + /** + * Gets a read-write DirContext. + * + * @return A DirContext instance, never null. + * @throws DataAccessException + * if some error occurs creating an DirContext. + */ + public DirContext getReadWriteContext() throws DataAccessException; +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java new file mode 100644 index 00000000..a7eba9e5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java new file mode 100644 index 00000000..6c27b5c7 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java @@ -0,0 +1,96 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.CommunicationException; +import javax.naming.ContextNotEmptyException; +import javax.naming.LimitExceededException; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.directory.InvalidAttributesException; +import javax.naming.directory.InvalidSearchControlsException; +import javax.naming.directory.InvalidSearchFilterException; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +/** + * The default implementation of NamingExceptionTranslator. + * + * @author Mattias Arthursson + */ +public class DefaultNamingExceptionTranslator implements + NamingExceptionTranslator { + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.NamingExceptionTranslator#translate(java.lang.String, + * java.lang.String, java.lang.String, javax.naming.NamingException) + */ + public DataAccessException translate(NamingException namingException) { + + if (namingException instanceof NameNotFoundException) { + return new EntryNotFoundException("Entry not found", + namingException); + } + + if (namingException instanceof InvalidSearchFilterException) { + return new BadLdapGrammarException("Invalid search filter", + namingException); + } + + if (namingException instanceof InvalidSearchControlsException) { + return new InvalidDataAccessApiUsageException( + "Invalid search controls supplied by internal API", + namingException); + } + + if (namingException instanceof NameAlreadyBoundException) { + return new DataIntegrityViolationException("Name already bound", + namingException); + } + + if (namingException instanceof ContextNotEmptyException) { + return new DataIntegrityViolationException( + "The context needs to be empty in order to be removed", + namingException); + } + + if (namingException instanceof InvalidAttributesException) { + return new AttributesIntegrityViolationException( + "Invalid attributes", namingException); + } + + if (namingException instanceof LimitExceededException) { + return new SearchLimitExceededException("Too many objects found", + namingException); + } + + if (namingException instanceof CommunicationException) { + throw new DataRetrievalFailureException( + "Unable to communicate with LDAP server", namingException); + } + + // Fallback - other type of NamingException encountered. + return new UncategorizedLdapException("Operation failed", + namingException); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/EntryNotFoundException.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/EntryNotFoundException.java new file mode 100644 index 00000000..9bd81eb9 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/EntryNotFoundException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataRetrievalFailureException; + +/** + * Represents that an entry could not be found. + * + * @author Mattias Arthursson + */ +public class EntryNotFoundException extends DataRetrievalFailureException { + + private static final long serialVersionUID = -1268390922996332424L; + + public EntryNotFoundException(String msg) { + super(msg); + } + + public EntryNotFoundException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/LdapOperations.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/LdapOperations.java new file mode 100644 index 00000000..80653fb8 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/LdapOperations.java @@ -0,0 +1,1104 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; + +/** + * 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. 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 SearchResult is passed to the + * CallbackHandler. Any encountered NamingException will be translated using + * the NamingExceptionTranslator. + * + * @param se + * The SearchExecutor to use for performing the actual search. + * @param handler + * The NameClassPairCallbackHandler to which each found entry + * will be passed. + * @throws DataAccessException + * 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) + throws DataAccessException; + + /** + * 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 DataAccessException + * if the operation resulted in a NamingException. + */ + public Object executeReadOnly(ContextExecutor ce) + throws DataAccessException; + + /** + * 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. + * + * @param ce + * The ContextExecutor to which the actual operation on the + * DirContext will be delegated. + * @return the result from the ContextExecutor's operation. + * @throws DataAccessException + * if the operation resulted in a NamingException. + */ + public Object executeReadWrite(ContextExecutor ce) + throws DataAccessException; + + /** + * 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 SearchResults + * to. + */ + public void search(Name base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler); + + /** + * 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 SearchResults + * to. + */ + public void search(String base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler); + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Each SearchResult is + * supplied to the specified NameClassPairCallbackHandler. Use the specified + * search scope and return objects flag in search controls. + * + * @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. + * @param handler + * The NameClassPairCallbackHandler to supply the SearchResults + * to. + * @throws DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 no the + * returnObjects 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Only search for the + * specified attributes. 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Only search for the + * specified attributes. 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified AttributesMapper. The + * default seach 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified AttributesMapper. The + * default seach 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified ContextMapper. Only + * look for 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified ContextMapper. Only + * look for 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Perform a non-recursive listing of the contexts bound to 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Lookup the supplied DN and return the found object. WARNING: This + * method should only be used if a DirObjectFactory has been specified on + * the ContextFactory. If this is not the case, you will get a new instance + * of the actual DirContext, which is probably not what you want. If, + * however this is what you want, be careful to close the context + * after you finished working with it. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object. + * @throws DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn) throws DataAccessException; + + /** + * Lookup the supplied DN and return the found object. WARNING: This + * method should only be used if a DirObjectFactory has been specified on + * the ContextFactory. If this is not the case, you will get a new instance + * of the actual DirContext, which is probably not what you want. If, + * however this is what you want, be careful to close the context + * after you finished working with it. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object. + * @throws DataAccessException + * if any error occurs. + */ + public Object lookup(String dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void modifyAttributes(Name dn, ModificationItem[] mods) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void modifyAttributes(String dn, ModificationItem[] mods) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void bind(Name dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void bind(String dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(Name dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(String dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(Name dn, boolean recursive) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(String dn, boolean recursive) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void rebind(Name dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void rebind(String dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataIntegrityViolationException + * if newDn is already bound + * @throws DataAccessException + * if any other error occurs. + */ + public void rename(final Name oldDn, final Name newDn) + throws DataAccessException; + + /** + * 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 DataIntegrityViolationException + * if newDn is already bound + * @throws DataAccessException + * if any other error occurs. + */ + public void rename(final String oldDn, final String newDn) + throws DataAccessException; +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/LdapTemplate.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/LdapTemplate.java new file mode 100644 index 00000000..d423f6a4 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/LdapTemplate.java @@ -0,0 +1,1176 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NameNotFoundException; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +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 javax.naming.directory.SearchResult; + +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.dao.DataAccessException; +import org.springframework.ldap.support.DistinguishedName; + +/** + * 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.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 NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); + + 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; + } + + /** + * 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.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, boolean, + * org.springframework.ldap.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.LdapOperations#search(java.lang.String, + * java.lang.String, int, boolean, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, int searchScope, + boolean returningObjFlag, NameClassPairCallbackHandler handler) + throws DataAccessException { + + search(base, filter, getDefaultSearchControls(searchScope, + returningObjFlag, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /** + * 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 DataAccessException + * 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) { + DirContext ctx = contextSource.getReadOnlyContext(); + + NamingEnumeration results = null; + try { + 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. + } catch (PartialResultException e) { + // Workaround for AD servers not handling referrals correctly. + if (ignorePartialResultException) { + log.debug("PartialResultException encountered and ignored", e); + } else { + throw getExceptionTranslator().translate(e); + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + closeContextAndNamingEnumeration(ctx, results); + } + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(Name base, String filter, + NameClassPairCallbackHandler handler) throws DataAccessException { + + search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, + NameClassPairCallbackHandler handler) throws DataAccessException { + + search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES), handler); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws DataAccessException { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws DataAccessException { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + AttributesMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + AttributesMapper mapper) throws DataAccessException { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, AttributesMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, AttributesMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws DataAccessException { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws DataAccessException { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + ContextMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + ContextMapper mapper) throws DataAccessException { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, ContextMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, ContextMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, SearchControls controls, + ContextMapper mapper) { + + assureReturnObjFlagSet(controls); + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + search(base, filter, controls, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, SearchControls controls, + ContextMapper mapper) { + + assureReturnObjFlagSet(controls); + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + search(base, filter, controls, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, SearchControls controls, + AttributesMapper mapper) { + + AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler( + mapper); + search(base, filter, controls, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, SearchControls controls, + AttributesMapper mapper) { + + AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler( + mapper); + search(base, filter, controls, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(java.lang.String, + * org.springframework.ldap.ListResultCallbackHandler) + */ + public void list(final String base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.ListResultCallbackHandler) + */ + public void list(final Name base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.LdapOperations#list(java.lang.String, + * org.springframework.ldap.NameClassPairMapper) + */ + public List list(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.NameClassPairMapper) + */ + public List list(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name) + */ + public List list(final Name base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(java.lang.String) + */ + public List list(final String base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void listBindings(final String base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void listBindings(final Name base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.NameClassPairMapper) + */ + public List listBindings(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.NameClassPairMapper) + */ + public List listBindings(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String) + */ + public List listBindings(final String base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name) + */ + public List listBindings(final Name base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.ContextMapper) + */ + public List listBindings(String base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.ContextMapper) + */ + public List listBindings(Name base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#executeReadOnly(org.springframework.ldap.ContextExecutor) + */ + public Object executeReadOnly(ContextExecutor ce) { + DirContext ctx = contextSource.getReadOnlyContext(); + return executeWithContext(ce, ctx); + } + + /* + * @see org.springframework.ldap.LdapOperations#executeReadWrite(org.springframework.ldap.ContextExecutor) + */ + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + closeContext(ctx); + } + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name) + */ + public Object lookup(final Name dn) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String) + */ + public Object lookup(final String dn) throws DataAccessException { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final Name dn, final AttributesMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final String dn, final AttributesMapper mapper) + throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.ContextMapper) + */ + public Object lookup(final Name dn, final ContextMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.ContextMapper) + */ + public Object lookup(final String dn, final ContextMapper mapper) + throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.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 NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#modifyAttributes(java.lang.String, + * javax.naming.directory.ModificationItem[]) + */ + public void modifyAttributes(final String dn, final ModificationItem[] mods) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.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 NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#bind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(final String dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(javax.naming.Name) + */ + public void unbind(final Name dn) { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(java.lang.String) + */ + public void unbind(final String dn) throws DataAccessException { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(javax.naming.Name, + * boolean) + */ + public void unbind(final Name dn, boolean recursive) + throws DataAccessException { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(java.lang.String, + * boolean) + */ + public void unbind(final String dn, boolean recursive) + throws DataAccessException { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + private void doUnbind(final Name dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbind(final String dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbindRecursively(final Name dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + deleteRecursively(ctx, new DistinguishedName(dn)); + return null; + } + }); + } + + private void doUnbindRecursively(final String dn) + throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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 DataAccessException + * if any error occurs + */ + protected void deleteRecursively(DirContext ctx, DistinguishedName name) + throws DataAccessException { + + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + try { + enumeration.close(); + } catch (Exception e) { + // Never mind this + } + } + } + + /* + * @see org.springframework.ldap.LdapOperations#rebind(javax.naming.Name, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final Name dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rebind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final String dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rename(javax.naming.Name, + * javax.naming.Name) + */ + public void rename(final Name oldDn, final Name newDn) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rename(oldDn, newDn); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rename(java.lang.String, + * java.lang.String) + */ + public void rename(final String oldDn, final String newDn) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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. + } + } + } + + /** + * Get the NamingExceptionTranslator that will be used by this instance. If + * no exceptionTranslator has been set, a default instance will be created. + * + * @return the NamingExceptionTranslator to be used by this instance. + */ + public NamingExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + /** + * Set the NamingExceptionTranslator to be used by this instance. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + + this.exceptionTranslator = exceptionTranslator; + } + + 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); + } + } + + /** + * 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } + + /** + * A CollectingNameClassPairCallbackHandler to wrap an AttributesMapper. + * That is, the found object is extracted from the {@link Attributes} of + * each {@link SearchResult}, and then passed to the specified + * AttributesMapper for translation. This class needs to be nested, since we + * want to be able to get hold of the exception translator of this instance. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ + public class AttributesMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private AttributesMapper mapper; + + public AttributesMapperCallbackHandler(AttributesMapper mapper) { + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a SearchResult and pass its attributes to + * the 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } + + /** + * 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 + */ + public class ContextMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private ContextMapper mapper; + + public ContextMapperCallbackHandler(ContextMapper mapper) { + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a {@link Binding} and pass its attributes + * to the ContextMapper. + * + * @param nameClassPair + * a SearchResult 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 EntryNotFoundException( + "SearchResult did not contain any object."); + } + return mapper.mapFromContext(object); + } + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java new file mode 100644 index 00000000..0698a9c6 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +/** + * Callback interface used by LdapTemplate's 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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NameClassPairMapper.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NameClassPairMapper.java new file mode 100644 index 00000000..acf8cd14 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NameClassPairMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java new file mode 100644 index 00000000..cd6e7a4c --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.springframework.dao.DataAccessException; + +/** + * Interface to be implemented by classes that can translate between + * NamingExceptions and DataAccessExceptions. + * + * @author Mattias Arthursson + * + */ +public interface NamingExceptionTranslator { + /** + * Translate the given NamingException into a generic data access exception. + * @param namingException + * the offending NamingException. + * + * @return the DataAccessException to throw. + */ + public DataAccessException translate(NamingException namingException); +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/SearchExecutor.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/SearchExecutor.java new file mode 100644 index 00000000..34b474b5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/SearchExecutor.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +/** + * Interface for delegating an actual search operation. The typical + * implementation of executeSearch would be something like: + * + *

+ * SearchExecutor executor = new SearchExecutor(){
+ *   public NamingEnumeration executeSearch(DirContext ctx) throws NamingException{
+ *     return ctx.search(dn, filter, searchControls);
+ *   }
+ * }
+ * 
+ * + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/SearchLimitExceededException.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/SearchLimitExceededException.java new file mode 100644 index 00000000..49806334 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/SearchLimitExceededException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataRetrievalFailureException; + +/** + * Indicates that the search limit was exceeded in a search. + * + * @author Mattias Arthursson + * + */ +public class SearchLimitExceededException extends DataRetrievalFailureException { + private static final long serialVersionUID = 6899885947075235580L; + + public SearchLimitExceededException(String msg) { + super(msg); + } + + public SearchLimitExceededException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/UncategorizedLdapException.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/UncategorizedLdapException.java new file mode 100644 index 00000000..2423a3b7 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/UncategorizedLdapException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.UncategorizedDataAccessException; + +/** + * Indicates that an unknown NamingException has occurred. + * + * @author Mattias Arthursson + */ +public class UncategorizedLdapException extends + UncategorizedDataAccessException { + + private static final long serialVersionUID = -3319936235493869823L; + + public UncategorizedLdapException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/package.html b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/package.html new file mode 100644 index 00000000..f160a4a2 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/package.html @@ -0,0 +1,3 @@ + +The core package of the Spring-LDAP library. + \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/AbstractContextSource.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/AbstractContextSource.java new file mode 100644 index 00000000..756fc593 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/AbstractContextSource.java @@ -0,0 +1,455 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.AuthenticationSource; +import org.springframework.ldap.ContextSource; +import org.springframework.ldap.DefaultNamingExceptionTranslator; +import org.springframework.ldap.NamingExceptionTranslator; + +/** + * Abstract implementation of the 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 AuthenticationSource is set, this will be used for getting user name + * and password for each new connection, otherwise a default one will be created + * using the specified userName 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.LdapTemplate + * @see org.springframework.ldap.support.DefaultDirObjectFactory + * @see org.springframework.ldap.support.LdapContextSource + * @see org.springframework.ldap.support.DirContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public abstract class AbstractContextSource implements ContextSource, + 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; + + protected String userName = ""; + + 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 NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); + + private static final Log log = LogFactory.getLog(LdapContextSource.class); + + public static final String SUN_LDAP_POOLING_FLAG = "com.sun.jndi.ldap.connect.pool"; + + private static final String JDK_142 = "1.4.2"; + + public DirContext getReadOnlyContext() { + if (!anonymousReadOnly) { + return createContext(getAuthenticatedEnv()); + } else { + return createContext(getAnonymousEnv()); + } + } + + public DirContext getReadWriteContext() { + return createContext(getAuthenticatedEnv()); + } + + /** + * Default implementation of setting the environment up to be authenticated. + * Override in subclass if necessary. This is needed for Active Directory + * connectivity, for example. + * + * @param env + * the environment to modify. + */ + protected void setupAuthenticatedEnvironment(Hashtable env) { + env + .put(Context.SECURITY_PRINCIPAL, authenticationSource + .getPrincipal()); + log.debug("Principal: '" + userName + "'"); + 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 (base != null) { + 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, should not) specify the + * full distinguished names in the operations performed. + * + * @param base + * the base suffix. + */ + public void setBase(String base) { + this.base = new DistinguishedName(base); + } + + /** + * 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. + */ + 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 getExceptionTranslator().translate(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; + } + + /** + * 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; + } + + /** + * 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 (base != null && 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(userName)) { + log + .warn("Property 'userName' not set - " + + "anonymous context will be used for read-write operations"); + } else if (StringUtils.isBlank(password)) { + log.warn("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 { + 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 (base != null) { + // 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 name (principal) to use for getting authenticated contexts. + * + * @param userName + * the user name. + */ + public void setUserName(String userName) { + this.userName = 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; + } + + /** + * 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. + * + * @param pooled + * whether Contexts should be pooled. + */ + public void setPooled(boolean pooled) { + this.pooled = 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; + } + + public void setAuthenticationSource( + AuthenticationSource authenticationProvider) { + this.authenticationSource = authenticationProvider; + } + + /** + * 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 and anonymous environment should be + * used for read-only operations, false otherwise. + */ + public void setAnonymousReadOnly(boolean anonymousReadOnly) { + this.anonymousReadOnly = anonymousReadOnly; + } + + /** + * Set the NamingExceptionTranslator to be used by this instance. By + * default, a {@link DefaultNamingExceptionTranslator} will be used. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + + /** + * Get the NamingExceptionTranslator used by this instance. + * + * @return the NamingExceptionTranslator. + */ + public NamingExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + /** + * 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 userName; + } + + public String getCredentials() { + return password; + } + + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java new file mode 100644 index 00000000..9d0a7fa1 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ModificationItem; + +/** + * Indicates that the implementor 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 or added or removed. + * + * @return an array of modification items + */ + public ModificationItem[] getModificationItems(); +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java new file mode 100644 index 00000000..8f017beb --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +import org.springframework.ldap.NameClassPairCallbackHandler; + +/** + * A 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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java new file mode 100644 index 00000000..b46e1d55 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java @@ -0,0 +1,115 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.directory.Attributes; +import javax.naming.spi.DirObjectFactory; + +/** + * Default implementation of the DirObjectFactory interface. Creates a + * 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. + */ + public static final String JNDI_ENV_BASE_PATH_KEY = "org.springframework.ldap.base.path"; + + /** + * Creates a DirContextAdapter from the supplied arguments. + * + * @param obj + * @param name + * @param nameCtx + * @param environment + * @param attrs + * @return a new DirContextAdapter from the attributes and name. + * @throws Exception + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment, Attributes attrs) throws Exception { + + try { + DirContextAdapter dirContextAdapter = new DirContextAdapter(attrs, + stripBasePath(name, environment)); + 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 + } + + } + } + } + + /** + * Rather crude method to remove the base suffix if specified in the LDAP + * environment. This could probably be improved to increase performance. + * + * @param name + * the full distinguished name of the found object. + * @param environment + * the context environment. + * @return the DN, without the base suffix. + */ + private Name stripBasePath(Name name, Hashtable environment) { + if (environment.containsKey(JNDI_ENV_BASE_PATH_KEY)) { + DistinguishedName distinguishedName = new DistinguishedName(name + .toString()); + distinguishedName.removeFirst((Name) environment + .get(JNDI_ENV_BASE_PATH_KEY)); + return distinguishedName; + } else { + return name; + } + } + + /** + * Returns null. + * + * @param obj + * @param name + * @param nameCtx + * @param environment + * @return null. + * @throws Exception + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment) throws Exception { + return null; + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextAdapter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextAdapter.java new file mode 100644 index 00000000..e75ecef0 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextAdapter.java @@ -0,0 +1,1186 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; +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.collections.CollectionUtils; +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.DefaultNamingExceptionTranslator; +import org.springframework.ldap.NamingExceptionTranslator; + +/** + * 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.support.DefaultDirObjectFactory} in your + * ContextSource you may receive instances of this class from searches and + * lookups. This can be particularly useful when updating data, since this class + * implements {@link org.springframework.ldap.support.AttributeModificationsAware}, + * providing a {@link #getModificationItems()} method. + * + * @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 attrs; + + private Name dn; + + private boolean updateMode = false; + + private Attributes updatedAttrs; + + private NamingExceptionTranslator exceptionTranslator; + + /** + * Default constructor. + */ + public DirContextAdapter() { + attrs = new BasicAttributes(true); + dn = null; + } + + public DirContextAdapter(Name dn) { + attrs = new BasicAttributes(true); + this.dn = dn; + } + + /** + * Create a new entry from the supplied attributes and dn. + * + * @param pAttrs + * the attributes. + * @param dn + * the dn. + */ + public DirContextAdapter(Attributes pAttrs, Name dn) { + attrs = (Attributes) pAttrs.clone(); + this.dn = dn; + } + + /** + * Constructor for cloning an existing entry. + * + * @param master + * The object to be copied. + */ + protected DirContextAdapter(DirContextAdapter master) { + this.attrs = (Attributes) master.attrs.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. + */ + protected void setUpdateMode(boolean mode) { + this.updateMode = mode; + if (updateMode) { + updatedAttrs = new BasicAttributes(true); + } + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#isUpdateMode() + */ + public boolean isUpdateMode() { + return updateMode; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#getNamesOfModifiedAttributes() + */ + public String[] getNamesOfModifiedAttributes() { + + List tmpList = new ArrayList(); + + NamingEnumeration attributesEnumeration; + if (isUpdateMode()) { + attributesEnumeration = updatedAttrs.getAll(); + } else { + attributesEnumeration = attrs.getAll(); + } + + try { + while (attributesEnumeration.hasMore()) { + Attribute oneAttribute = (Attribute) attributesEnumeration + .next(); + tmpList.add(oneAttribute.getID()); + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(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 getExceptionTranslator().translate(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 = attrs.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) { + // Attribute has been removed. + modificationList.add(new ModificationItem( + DirContext.REMOVE_ATTRIBUTE, changedAttr)); + } else { + // Collect all modifications to attribute individually (this also + // covers additions to a previously non-existant attribute). + Collection oldValues = new LinkedList(); + Collection newValues = new LinkedList(); + + collectAttributeValues(oldValues, currentAttribute); + collectAttributeValues(newValues, changedAttr); + Collection myModifications = new LinkedList(); + + Collection addedValues = CollectionUtils.subtract(newValues, + oldValues); + Collection removedValues = CollectionUtils.subtract(oldValues, + newValues); + + collectModifications(DirContext.ADD_ATTRIBUTE, changedAttr, + addedValues, myModifications); + collectModifications(DirContext.REMOVE_ATTRIBUTE, changedAttr, + removedValues, 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(int modificationType, Attribute attr, + Collection values, Collection c) { + if (values.size() > 0) { + BasicAttribute modificationAttribute = new BasicAttribute(attr + .getID()); + for (Iterator iter = values.iterator(); iter.hasNext();) { + modificationAttribute.add(iter.next()); + } + c + .add(new ModificationItem(modificationType, + modificationAttribute)); + } + } + + private void collectAttributeValues(Collection valueCollection, + Attribute attribute) throws NamingException { + + if (attribute == null) { + return; + } + + NamingEnumeration attributeValues = attribute.getAll(); + while (attributeValues.hasMoreElements()) { + Object value = (Object) attributeValues.nextElement(); + valueCollection.add(value); + } + } + + /** + * Decide whether an attribute has changed or not. + * + * @param name + * Attribute name. + * @param value + * Attribute value. + * @return true if attribute has changed. + */ + private boolean isChanged(String name, Object value) { + Attribute a = attrs.get(name); + + // FALSE if both are null it is not changed + if (a == null && value == null) { + return false; + } + + // TRUE if existing value is null or does not contain one value + if (a == null || a.size() != 1) { + return true; + } + + // TRUE if existing value is not null and the new one is null + if (a != null && value == null) { + return true; + } + + // TRUE if we can't access the value + Object obj = null; + try { + obj = a.get(0); + } catch (NamingException e) { + return true; + } + + // TRUE if the value is not equal and all other tests has been performed + return !value.equals(obj); + } + + /** + * 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 an existing attribute with name pName with value pValue. The + * order of the array must be the same order as the existing multivalued + * attribute. + * + * @param name + * @param values + * @return true if it has changed + */ + private boolean isChanged(String name, Object[] values, boolean orderMatters) { + + Attribute a = attrs.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 (they both does not + // exist) + // TRUE if new value is null and old value exists (should be + // removed) + return (a != null); + } + + // NOT setting to empty ------------------- + + // TRUE if existing value is null + if (a == null) { + return true; + } + + // TRUE is different length + if (a.size() != values.length) { + return true; + } + + // Check contents of arrays + + // Order DOES matter, e.g. first names + try { + for (int i = 0; i < a.size(); i++) { + Object obj = a.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 attrs.get(attrId) != null; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#getStringAttribute(java.lang.String) + */ + public String getStringAttribute(String name) { + return (String) getObjectAttribute(name); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#getObjectAttribute(java.lang.String) + */ + public Object getObjectAttribute(String name) { + Attribute oneAttr = attrs.get(name); + if (oneAttr == null) { + return null; + } + try { + return oneAttr.get(); + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + + /* + * (non-Javadoc) + * + * @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) { + attrs.put(name, value); + } + + // updating entry + if (updateMode && isChanged(name, value)) { + BasicAttribute attribute = new BasicAttribute(name); + if (value != null) { + attribute.add(value); + } + updatedAttrs.put(attribute); + } + + } + + /* + * (non-Javadoc) + * + * @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); + } + + /* + * (non-Javadoc) + * + * @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 + attrs.put(a); + } + + // possible to set an already existing attribute to an empty array + if (updateMode && isChanged(name, values, orderMatters)) { + updatedAttrs.put(a); + } + } + + /* + * (non-Javadoc) + * + * @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)) { + attrs.remove(a.getID()); + } else { + // Otherwise it should be set. + attrs.put(a); + } + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + closeNamingEnumeration(attributesEnumeration); + } + + // Reset the attributes to be updated + updatedAttrs = new BasicAttributes(true); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#getStringAttributes(java.lang.String) + */ + public String[] getStringAttributes(String name) { + String[] attributes; + + Attribute attribute = attrs.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 getExceptionTranslator().translate(e); + } + } + } else { + return null; + } + + return attributes; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.DirContextOperations#getAttributeSortedStringSet(java.lang.String) + */ + public SortedSet getAttributeSortedStringSet(String name) { + TreeSet attrSet = new TreeSet(); + + Attribute attribute = attrs.get(name); + if (attribute != null) { + for (int i = 0; i < attribute.size(); i++) { + try { + attrSet.add(attribute.get(i)); + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } else { + return null; + } + + return attrSet; + } + + /** + * Set the supplied attribute. + * + * @param attribute + * the attribute to set. + */ + public void setAttribute(Attribute attribute) { + attrs.put(attribute); + } + + /** + * Get all attributes. + * + * @return all attributes. + */ + public Attributes getAttributes() { + return attrs; + } + + /** + * @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) attrs.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 = attrs.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() { + return dn.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 = dn; + } + } + + /** + * @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 = attrs.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(); + } + + /** + * Get the NamingExceptionTranslator. + * + * @return the NamingExceptionTranslator to use; if none is specified, + * {@link DefaultNamingExceptionTranslator} is used. + */ + public NamingExceptionTranslator getExceptionTranslator() { + if (exceptionTranslator == null) { + exceptionTranslator = new DefaultNamingExceptionTranslator(); + } + return exceptionTranslator; + } + + /** + * Set the NamingExceptionTranslator to use. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextOperations.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextOperations.java new file mode 100644 index 00000000..645b18d6 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextOperations.java @@ -0,0 +1,161 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.SortedSet; + +import javax.naming.Name; +import javax.naming.directory.DirContext; + +/** + * Interface for DirContextAdapter to simplify mock testing. + * + * @author Mattias Arthursson + */ +public interface DirContextOperations extends DirContext, + AttributeModificationsAware { + + /** + * Gets the update mode. 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. + * + * @param name + * name of the attribute. + * @return the value of the attribute. + */ + public String getStringAttribute(String name); + + /** + * Get the value of an Object attribute. + * + * @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); + + /** + * Update the attributes. This will mean that the getters + * (getStringAttribute methods) will return the updated values. Remove the + * attributes to be updated. + */ + 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 DN, for example uid=some.user,ou=People,ou=EU. + * + * @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); +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextSource.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextSource.java new file mode 100644 index 00000000..e69d6a0a --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DirContextSource.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DistinguishedName.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DistinguishedName.java new file mode 100644 index 00000000..88b26f95 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/DistinguishedName.java @@ -0,0 +1,570 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.naming.InvalidNameException; +import javax.naming.Name; + + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; + +/** + * Default implementation of a Name corresponding to an LDAP path. A + * DistinguishedName implementation is included in JDK1.5 (LdapName), but not in + * prior releases. + * + * An 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 Name interface specifies that the + * most significant part be in position 0, i.e. + * + * The path: uid=adam.skogman, ou=People, ou=EU Name[0]: ou=EU Name[1]: + * ou=People Name[2]: uid=adam.skogman + *

+ * Useful for parsing and building LDAP paths. + * + *

+ * DistinguishedName path = new DistinguishedName();
+ * path.addLast("cn", entry.getUid());
+ * path.addLast("ou", "users");
+ * path.append(new DistinguishedName(helpdesk.getSomeSuffix()));
+ * String dn = path.toString();
+ * 
+ * + * TODO: Implement compareTo(). + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class DistinguishedName implements Name { + private static final long serialVersionUID = 3514344371999042586L; + + public static final DistinguishedName EMPTY_PATH = new DistinguishedName(); + + private LinkedList names; + + protected static final Pattern NAME_PATTERN = Pattern + .compile("(.*?[^\\\\])(,|;|$)"); + + /** + * 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) { + parse(path); + } + + /** + * Construct a new DistinguishedName from the supplied List of LdapRdn + * objects. + * + * @param list + * the components that this instance will consist of. + */ + public DistinguishedName(LinkedList list) { + this.names = list; + } + + /** + * Construct a new DistinguishedName from the supplied Name. The parts of + * the supplied Name must be syntactically correct LdapRdns. + * + * @param name + * the 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) { + names = new LinkedList(); + + if (!StringUtils.isBlank(path)) { + + Matcher matcher = NAME_PATTERN.matcher(path); + + while (matcher.find()) { + String rdnString = matcher.group(1); + LdapRdn name = new LdapRdn(rdnString); + names.add(0, name); + } + } + } + + /** + * Get the LdapRdn at a specified position. + * + * @param index + * the LdapRdn to retrieve. + * @return the LdapRdn at the requested position. + */ + public LdapRdn getLdapRdn(int index) { + return (LdapRdn) names.get(index); + } + + /** + * Get the name list. + * + * @return the list of LdapRdns that this DistinguishedName consists of. + */ + public LinkedList getNames() { + return names; + } + + /** + * Get the String representation of this DistinguishedName. + * + * @return a syntactically correct, escaped String representation of the + * DistinguishedName. + */ + public String toString() { + return encode(); + } + + /** + * 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() { + + // 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()) + 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 a ldap 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 a LDAP path first + * + * @param path + */ + public void append(DistinguishedName path) { + getNames().addAll(path.getNames()); + } + + /** + * Add a LDAP path first + * + * @param path + */ + public void prepend(DistinguishedName path) { + ListIterator i = path.getNames().listIterator(path.getNames().size()); + while (i.hasPrevious()) { + getNames().addFirst(i.previous()); + } + } + + /** + * Remove the first part of this DistinguishedName. + * + * @return the removed entry. + */ + public LdapRdn removeFirst() { + return (LdapRdn) getNames().removeFirst(); + } + + /** + * Remove the supplied path from the beginning of this DistinguishedName if + * this instance starts with . 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(); + } + + public int compareTo(Object o) { + // TODO: Implement this. + return 0; + } + + 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 ldap path ends with a certian path. + * + * If the argument path is empty (no names in path) this methid 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 ldast part of this DistinguishedName. + * + * @return the removed LdapRdn. + */ + public LdapRdn removeLast() { + return (LdapRdn) getNames().removeLast(); + } + + public void add(String key, String value) { + names.add(new LdapRdn(key, value)); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapContextSource.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapContextSource.java new file mode 100644 index 00000000..cc73c4ba --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapContextSource.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.support.AbstractContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public class LdapContextSource extends AbstractContextSource { + + /* + * (non-Javadoc) + * + * @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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapEncoder.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapEncoder.java new file mode 100644 index 00000000..d6525bc2 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapEncoder.java @@ -0,0 +1,235 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.regex.Matcher; +import java.util.regex.Pattern; + + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; + +/** + * Helper class to encode and decode ldap names and values. + * + * @author Adam Skogman + */ +public class LdapEncoder { + + static private String[] nameEscapeTable = new String[96]; + + static private String[] filterEscapeTable = new String['\\' + 1]; + + /** + * Pattern for matching escaped ldap name values. + * + * Double escaping: \ -> \\ (in pattern) -> \\\\ (in java string literal) + * + * Group 1: Hex escapes = \XX -> \p{XDigit}{2} Group 2: Ordinary escapes = + * \x -> \. Group 3: Anything but \ [^\\] + * + * Note that the \ is not part of the match. + */ + static private final Pattern VALUE_DECODE_PATTERN = Pattern + .compile("(?:\\\\(\\p{XDigit}{2}))|(?:\\\\(.))|([^\\\\])"); + + 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['\"'] = "\\\""; + // nameEscapeTable['/'] = "\\" + toTwoCharHex('/'); + 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"; + + } + + static protected String toTwoCharHex(char c) { + + String raw = Integer.toHexString(c).toUpperCase(); + + if (raw.length() > 1) + return raw; + else + return "0" + raw; + } + + /** + * All static methods + */ + private LdapEncoder() { + } + + static public 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 + * @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()); + + Matcher matcher = VALUE_DECODE_PATTERN.matcher(value); + + int end = 0; + + while (matcher.find()) { + end = matcher.end(); + // group 1 + if (matcher.group(1) != null) { + // parse as hex = base 16 + try { + char c = (char) Integer.parseInt(matcher.group(1), 16); + decoded.append(c); + } catch (NumberFormatException e) { + throw new BadLdapGrammarException( + "Escaped hex value Could not be parsed. Found '\\" + + matcher.group(1) + "'"); + } + } else if (matcher.group(2) != null) { + // just add, we stripped away the \ + decoded.append(matcher.group(2)); + } else if (matcher.group(3) != null) { + // just add + decoded.append(matcher.group(3)); + } + + } + // end is match + +1 so it should be same as length, i.e. + // last + 1 + if (end < value.length()) { + throw new BadLdapGrammarException( + "RDN could not be parsed fully, remaining '" + + StringUtils.substring(value, end) + "'"); + } + + return decoded.toString(); + + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapRdn.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapRdn.java new file mode 100644 index 00000000..3cd37a8d --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/LdapRdn.java @@ -0,0 +1,217 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.net.URI; +import java.net.URISyntaxException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.BadLdapGrammarException; + +/** + * Datatype for a LDAP name, a part of a path. + * + * The name: uid=adam.skogman Key: uid Value: adam.skogman + * + * @author Adam Skogman + */ +public class LdapRdn { + + private String key; + + private String value; + + private String ldapEncoded; + + protected static final Pattern RDN_PATTERN = Pattern + .compile("\\s*(\\p{Alnum}+)\\s*=\\s*((\\\\\\p{XDigit}{2}|\\\\.|[^\\\\])+?)\\s*"); + + private static final Log log = LogFactory.getLog(LdapRdn.class); + + /** + * Constructs a RDN from an ldap encoded rdn "foo=bar". + * + * The key will be forced to lowercase. The value is kept as is. + * + * The ldapEncoded value will not be the same, but rather a reencoded value, + * so that all looks the same. + * + * @param ldapRdn + * @throws BadLdapGrammarException + * If the rdn could not be parsed. + */ + public LdapRdn(String ldapRdn) throws BadLdapGrammarException { + + parseLdap(ldapRdn); + + } + + /** + * Constructs a RDN from a key and a value. + * + * The key will be forced to lowercase. The value is kept as is. + * + * @param key + * Not blank + * @param value + * Not blank + */ + public LdapRdn(String key, String value) { + + if (StringUtils.isBlank(key)) + throw new IllegalArgumentException("Key may not be blank"); + + if (StringUtils.isBlank(value)) + throw new IllegalArgumentException("Value may not be blank"); + + this.key = key.toLowerCase(); + this.value = value; + + this.ldapEncoded = encodeLdap(); + + } + + /** + * Encode key and value to ldap + * + * @return The ldap encoded 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(); + } + + /** + * @param rdn + * The ldap rdn to parse + * @throws BadLdapGrammarException if any error occurs parsing the rdn. + */ + protected void parseLdap(String rdn) throws BadLdapGrammarException { + + Matcher matcher = RDN_PATTERN.matcher(rdn); + + if (!matcher.matches()) { + throw new BadLdapGrammarException( + "Not a proper name (such as key=value): " + rdn); + } + + String rawKey = matcher.group(1); + String rawValue = matcher.group(2); + + this.key = StringUtils.lowerCase(rawKey); + this.value = LdapEncoder.nameDecode(rawValue); + + if (key == null) { + throw new BadLdapGrammarException("Key empty in rdn '" + rdn + "'"); + } + if (value == null) { + throw new BadLdapGrammarException("Value empty in rdn '" + rdn + + "'"); + } + + this.ldapEncoded = encodeLdap(); + + } + + /** + * Parse the supplied String for a valid LdapRdn. Swallow any exceptions. + * + * @param rdn + * the String to parse. + * @return an LdapRdn corresponding to the supplied String if the format was + * valid, null otherwise. + */ + public static LdapRdn parse(String rdn) { + + try { + return new LdapRdn(rdn); + } catch (BadLdapGrammarException e) { + return null; + } + + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + /** + * + * + * @see java.lang.Object#toString() + */ + public String toString() { + return ldapEncoded; + } + + /** + * @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; + } + + LdapRdn rdn = (LdapRdn) obj; + + return StringUtils.equalsIgnoreCase(rdn.key, this.key) + && StringUtils.equalsIgnoreCase(rdn.value, this.value); + } + + public Object 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... + log.error("Failed to URL encode value " + value, e); + return key + "=" + "value"; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + /** + * @return The LdapRdn as a string where the value is LDAP-encoded. + */ + public String getLdapEncoded() { + return ldapEncoded; + } +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java new file mode 100644 index 00000000..1e28a087 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.authentication; + +import org.acegisecurity.Authentication; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.userdetails.ldap.LdapUserDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.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)) { + throw new IllegalArgumentException( + "The principal property of the authentication object -" + + "needs to be a LdapUserDetails."); + } else { + LdapUserDetails details = (LdapUserDetails) principal; + return details.getDn(); + } + } else { + log.warn("No Authentication object set in SecurityContext - " + + "returning empty String as Principal"); + return ""; + } + } + + /* + * @see org.springframework.ldap.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java new file mode 100644 index 00000000..ed94d937 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java @@ -0,0 +1,157 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.authentication; + +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.ldap.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 defaultUser. + * + * @return the target's principal if it is not empty, the + * defaultUser 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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/package.html b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/package.html new file mode 100644 index 00000000..d15fe6bc --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/authentication/package.html @@ -0,0 +1,3 @@ + +Support classes for custom authentication. + diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java new file mode 100644 index 00000000..e00f8f04 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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(); + } + + /** + * Prints the query with LDAP encoding to a stringbuffer + * + * @param buff + * The stringbuffer + * @return The very same stringbuffer + */ + public abstract StringBuffer encode(StringBuffer buff); + + /** + * Encodes the filter to a string using the (@link #encode(StringBuffer) + * method. + * + * @return The encoded filter + */ + public String encode() { + StringBuffer buff = new StringBuffer(256); + buff = encode(buff); + return buff.toString(); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return encode(); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/AndFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/AndFilter.java new file mode 100644 index 00000000..4622da1f --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/AndFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter for a logical AND. E.g.: + * + *

+ *     AndFilter filter = new AndFilter();
+ *     filter.and(new EqualsFilter("objectclass", "person");
+ *     filter.and(new EqualsFilter("cn", "Some CN");
+ *     System.out.println(filter.ecode());    
+ * 
+ * + * would result in: (&(objectclass=person)(cn=Some CN)) + * + * @see org.springframework.ldap.support.filter.EqualsFilter + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class AndFilter extends BinaryLogicalFilter { + + private static final String AMPERSAND = "&"; + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return AMPERSAND; + } + + /** + * Add a query to the and expression + * + * @param query + * The query to and with the rest of the and:ed queries. + * @return This LdapAndQuery + */ + public AndFilter and(Filter query) { + queryList.add(query); + return this; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java new file mode 100644 index 00000000..0c3ef3a2 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.filter.Filter#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.support.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.support.filter.Filter#hashCode() + */ + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java new file mode 100644 index 00000000..20fc78f0 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.springframework.ldap.support.LdapEncoder; + +/** + * Abstract superclass for filters to 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 + * @param value + */ + public CompareFilter(String attribute, int value) { + this.attribute = attribute; + this.value = String.valueOf(value); + this.encodedValue = LdapEncoder.filterEncode(this.value); + } + + /* + * @see org.springframework.ldap.support.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.support.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.support.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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java new file mode 100644 index 00000000..069d8186 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + + +/** + * A filter for 'equals'. The following code: + * + *
+ * EqualsFilter filter = new EqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + *
(cn=Some CN)
+ * + * @author Adam Skogman + */ +public class EqualsFilter extends CompareFilter { + + private static final String EQUALS_SIGN = "="; + + public EqualsFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Convenience constructor for int values. + * + * @param attribute Name of attribute in filter. + * @param value The value of the attribute in the filter. + */ + public EqualsFilter(String attribute, int value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.support.filter.CompareFilter#getCompareString() + */ + protected String getCompareString() { + return EQUALS_SIGN; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/Filter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/Filter.java new file mode 100644 index 00000000..a5ae4f69 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/Filter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * Common interface for filters. + * + * @author Adam Skogman + */ +public interface Filter { + + /** + * Encodes the filter to a string using the (@link #encode(StringBuffer) + * method. + * + * @return The encoded filter + */ + public String encode(); + + /** + * Prints the query with LDAP encoding to a stringbuffer + * + * @param buff + * The stringbuffer + * @return The very same stringbuffer + */ + public StringBuffer encode(StringBuffer buff); + + /** + * 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 hascode + */ + public int hashCode(); + +} \ No newline at end of file diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java new file mode 100644 index 00000000..871b8dd1 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter to compare >=. LDAP RFC does not allow > comparison. The following + * code: + * + *
+ * GreaterThanOrEqualsFilter filter = new GreaterThanOrEqualsFilter("cn",
+ *         "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn>=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class GreaterThanOrEqualsFilter extends CompareFilter { + + private static final String GREATER_THAN_OR_EQUALS = ">="; + + public GreaterThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public GreaterThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + protected String getCompareString() { + return GREATER_THAN_OR_EQUALS; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java new file mode 100644 index 00000000..36b5e723 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter to compare <=. LDAP RFC does not allow < comparison. The following + * code: + * + *
+ * LessThanOrEqualsFilter filter = new LessThanOrEqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn<=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class LessThanOrEqualsFilter extends CompareFilter { + + private static final String LESS_THAN_OR_EQUALS = "<="; + + public LessThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public LessThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + protected String getCompareString() { + return LESS_THAN_OR_EQUALS; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java new file mode 100644 index 00000000..41d46ebd --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.LdapEncoder; + +/** + * This filter allows the user to specify wildcards (*) by not escaping them in + * the filter. The following code: + * + *
+ * LikeFilter filter = new LikeFilter("cn", "foo*");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ *  (cn=foo*)
+ * 
+ * + * @author Anders Henja + * @author Mattias Arthursson + */ +public class LikeFilter extends EqualsFilter { + + public LikeFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Encodes a value according to the rules for this filter. + * + * @param value + * Value to encode. + * @return Encoded value. + */ + 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/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/NotFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/NotFilter.java new file mode 100644 index 00000000..972edfa5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/NotFilter.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.apache.commons.lang.Validate; + +/** + * A filter for 'not'. The following code: + * + *
+ * Filter filter = new NotFilter(new EqualsFilter("cn", "foo");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
(!(cn=foo))
+ * + * @author Adam Skogman + */ +public class NotFilter extends AbstractFilter { + + private final Filter filter; + + static private final int HASH = "!".hashCode(); + + /** + * Create a filter that negates the outcome of the given filter. + * + * @param filter + * The filter that should be negated. + */ + public NotFilter(Filter filter) { + Validate.notNull(filter); + this.filter = filter; + } + + /** + * @see org.springframework.ldap.support.filter.Filter#encode(java.lang.StringBuffer) + */ + public StringBuffer encode(StringBuffer buff) { + + buff.append("(!"); + filter.encode(buff); + buff.append(')'); + + return buff; + + } + + /** + * Compares key and value before encoding + * + * @see org.springframework.ldap.support.filter.Filter#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; + } + + /** + * hash attribute and value + * + * @see org.springframework.ldap.support.filter.Filter#hashCode() + */ + public int hashCode() { + return HASH ^ filter.hashCode(); + } + +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/OrFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/OrFilter.java new file mode 100644 index 00000000..20ff1ddf --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/OrFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * Filter for logical OR. + * + *
+ *        AndFilter filter = new AndFilter();
+ *        filter.or(new EqualsFilter("objectclass", "person");
+ *        filter.or(new EqualsFilter("objectclass", "organizationalUnit");
+ *        System.out.println(filter.ecode());    
+ * 
+ * + * would result in: + * (|(objectclass=person)(objectclass=organizationalUnit)) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class OrFilter extends BinaryLogicalFilter { + + private static final String PIPE_SIGN = "|"; + + /** + * Add a query to the OR expression + * + * @param query + * The query to or with the rest of the or:ed queries. + * @return This LdapOrQuery + */ + public OrFilter or(Filter query) { + queryList.add(query); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return PIPE_SIGN; + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java new file mode 100644 index 00000000..bbd6b8d0 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.support.LdapEncoder; + +/** + * This filter automatically converts all whitespace to wildcards (*). The + * following code: + * + *
+ * WhitespaceWildcardsFilter filter = new WhitespaceWildcardsFilter("cn",
+ *         "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: (cn=*Some*CN*) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class WhitespaceWildcardsFilter extends EqualsFilter { + private static Pattern starReplacePattern = Pattern.compile("\\s+"); + + public WhitespaceWildcardsFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Encodes a value according to the rules for this filter. + * + * @param value + * Value to encode. + * @return Encoded value. + */ + protected String encodeValue(String value) { + + // blank string means just ONE star + if (StringUtils.isBlank(value)) { + return "*"; + } + + // trim value, we will add in stars first and last anywhay + value = value.trim(); + + // filter encode so that any stars etc. are preserved + String filterEncoded = LdapEncoder.filterEncode(value); + + // Now replace all whitespace with stars + Matcher m = starReplacePattern.matcher(filterEncoded); + + // possibly 2 longer (stars at ends) + StringBuffer buff = new StringBuffer(value.length() + 2); + + buff.append('*'); + + while (m.find()) { + m.appendReplacement(buff, "*"); + } + m.appendTail(buff); + + buff.append('*'); + + return buff.toString(); + } +} diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/package.html b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/package.html new file mode 100644 index 00000000..e05a77ed --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/filter/package.html @@ -0,0 +1,16 @@ + +Utility classes for dynamically building LDAP +filters. Filters can be nested and wrapped around each other: +
+AndFilter andFilter = new AndFilter();
+andFilter.and(new EqualsFilter("objectclass", "person");
+andFilter.and(new EqualsFilter("cn", "Some CN");
+OrFilter orFilter = new OrFilter();
+orFilter.or(andFilter);
+orFilter.or(new EqualsFilter("objectclass", "organizationalUnit));
+System.out.println(orFilter.encode());
+
+would result in: + +
(|(&(objectclass=person)(cn=Some CN))(objectclass=organizationalUnit))
+ diff --git a/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/package.html b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/package.html new file mode 100644 index 00000000..bf83afd5 --- /dev/null +++ b/build-spring-ldap/spring-ldap-1.1/src/main/java/org/springframework/ldap/support/package.html @@ -0,0 +1,3 @@ + +Support classes for Spring-LDAP. + \ No newline at end of file diff --git a/common-build/.cvsignore b/common-build/.cvsignore new file mode 100644 index 00000000..8bbd83c4 --- /dev/null +++ b/common-build/.cvsignore @@ -0,0 +1,11 @@ +build.properties +*.jpx.local* +*.log +*.iws +*.tws +target +dist +gen-src +bak +mimedata +ivy-cache diff --git a/common-build/.project b/common-build/.project new file mode 100644 index 00000000..41d6903f --- /dev/null +++ b/common-build/.project @@ -0,0 +1,11 @@ + + + common-build + + + + + + + + diff --git a/common-build/clover-targets.xml b/common-build/clover-targets.xml new file mode 100644 index 00000000..50af58bb --- /dev/null +++ b/common-build/clover-targets.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/common-targets.xml b/common-build/common-targets.xml new file mode 100644 index 00000000..c384b209 --- /dev/null +++ b/common-build/common-targets.xml @@ -0,0 +1,928 @@ + + + + + + + + + + + + + + + + ANT build for ${project.name} ${project.version}. + + Please execute: + ant -projecthelp + or + ant -p + to see a list of publically executable targets. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + reading ivy config + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + copying generated resources + + + + + + + + copying generated test resources + + + + + + + + + + + + + + + + + + + + + + + + compiling main sources + + + + + + + + + compiling test sources + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/db-targets.xml b/common-build/db-targets.xml new file mode 100644 index 00000000..06aaa0ac --- /dev/null +++ b/common-build/db-targets.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + User sql.src = '${sql.src}' + + + + + + + + + + + + Admin sql.src = '${sql.src}' + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/doc-targets.xml b/common-build/doc-targets.xml new file mode 100644 index 00000000..895439fe --- /dev/null +++ b/common-build/doc-targets.xml @@ -0,0 +1,157 @@ + + + + + + + + + + +Targets for generating reference documentation. + +Please execute + ant -p + +to see a list of all relevant targets. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/ejb21-targets.xml b/common-build/ejb21-targets.xml new file mode 100644 index 00000000..6732d54b --- /dev/null +++ b/common-build/ejb21-targets.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/hibernate-targets.xml b/common-build/hibernate-targets.xml new file mode 100644 index 00000000..f82fce41 --- /dev/null +++ b/common-build/hibernate-targets.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generating database.properties from build.properties + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/ivyconf.properties b/common-build/ivyconf.properties new file mode 100644 index 00000000..f2d940b6 --- /dev/null +++ b/common-build/ivyconf.properties @@ -0,0 +1,2 @@ +repository.dir=${ivy.conf.dir}/../repository +integration.repo.dir=${ivy.conf.dir}/../integration-repo diff --git a/common-build/ivyconf.xml b/common-build/ivyconf.xml new file mode 100644 index 00000000..dcb9e570 --- /dev/null +++ b/common-build/ivyconf.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common-build/lib/ivy-1.4.1.jar b/common-build/lib/ivy-1.4.1.jar new file mode 100644 index 00000000..09b0403e Binary files /dev/null and b/common-build/lib/ivy-1.4.1.jar differ diff --git a/common-build/project.properties b/common-build/project.properties new file mode 100644 index 00000000..d76f6920 --- /dev/null +++ b/common-build/project.properties @@ -0,0 +1,135 @@ +# values in here are overriden by build.properties and project.properties in +# individual projects, and build.properties in this dir, in that order + +# default wildcard for finding JUnit tests +# the convention is that our JUnit test classes have XXXTests-style names +test.includes=**/*Test.class,**/*Tests.class + +# wildcards to exclude among JUnit tests +test.excludes=**/Abstract* + +# we define standard directory locations here +# these should not normally never have to be overriden +# we define them here instead of in the XML so that Ant nested property +# replacement can work for properties overrides by other properties files + +# root of any bin files needed during build +bin.dir=${basedir}/bin + +# libs needed for build process. ivy pulls down jars into various dirs under here +lib.dir=${basedir}/lib + +# any static libs that need to be checked in instead of being pulled down by ivy +# should be placed somehwere under here. The standard build doesn't touch this though +static.lib.dir=${basedir}/static-lib + +# root of sources hierarchy +src.dir=${basedir}/src + +# main sources +src.java.main.dir=${src.dir}/main/java + +# test sources +src.java.test.dir=${src.dir}/test/java + +# root of web-apps sources +src.web.dir=${basedir}/src/main/webapp + +# root of source doc files +src.doc.dir=${basedir}/src/doc + +# root of etc hierarchy, typically used for templated sources and config files +src.etc.dir=${basedir}/src/etc + +# resources get copied into target classes dir with filtering +src.resources.dir=${src.dir}/main/resources +src.dtd.dir=${src.etc.dir}/dtd +src.tld.dir=${src.etc.dir}/tld + +# test resources get copied into target test classes dir with filtering +src.test.resources.dir=${src.etc.dir}/test-resources + +# root of build hierarchy +target.dir=${basedir}/target + +# any generated java sources go here +target.gen.java.dir=${target.dir}/gen-java-src +target.gen.java.test.dir=${target.dir}/gen-java-test-src + +# where built source class files and resources go +target.classes.dir=${target.dir}/classes + +# where built test class files and resources go +target.testclasses.dir=${target.dir}/test-classes + +# where test results end up +target.testresults.dir=${target.dir}/test-reports + +# where JavaDoc generate files go +target.javadocs.dir=${target.dir}/javadocs + +# where J2SE/J2EE modules (.jar, .war, .ear) go +target.artifacts.dir=${target.dir}/artifacts + +# where target lib archives (.JARs) go +target.lib.dir=${target.artifacts.dir}/lib + +# where WAR archive goes +target.artifacts.war.dir=${target.artifacts.dir}/war + +# where EAR archive goes +target.artifacts.ear.dir=${target.artifacts.dir}/ear + +# where any exploded web-app goes +target.war.expanded.dir=${target.artifacts.dir}/war-expanded + +# where any exploded EAR goes +target.ear.expanded.dir=${target.artifacts.dir}/ear-expanded + +# file used to provide token filter values during text file copies +target.filter.file=${target.dir}/filter.properties + +# where all project documentation resides +docs.dir=${basedir}/docs + +# where all project reference documentation resides +docs.ref.dir=${docs.dir}/reference + +# where all project reference documentation sources reside +docs.ref.src.dir=${docs.dir}/reference/src + +# where all DocBook libraries reside +docs.ref.lib.dir=${docs.dir}/reference/lib + +# where the project reference documentation is built +docs.ref.target.dir=${docs.dir}/reference/target + +# where the dist target is saved +dist.dir=${target.dir}/dist + +# sample: set proper packages here, for javadocs +packages=stub.* + +# override default ivy retrieve pattern to include configuration name in path, and no +# revision number The main advantage of no revision number in the name is that when pulling +# down subsequent snapshots of the same jar, a newer one will just overwrite a previous +# version. This means we are not forced to clean the lib dir between retrieves. +ivy.retrieve.pattern=${lib.dir}/[conf]/[artifact].[ext] + +# To add the revision, comment out the previous, and add next 2 to build.properties +# clear.libs.before.retrieve=anything +# ivy.retrieve.pattern=${lib.dir}/[conf]/[artifact]-[revision].[ext] + +# now define patterns for special retrieve used to build repo for release +release.repo.ivy.retrieve.pattern=[organisation]/[module]/[revision]/[artifact]-[revision].[ext] +release.repo.ivy.retrieve.ivy.pattern=[organisation]/[module]/ivy-[revision].xml + +# override default ivy distrib dir to go under targets +ivy.distrib.dir=${target.dir}/dist +# override where ivy.xml files are placed, so they are hierarchical and are safe +# to merge from multiple projects +ivy.srcivypattern=${ivy.distrib.dir}/ivys/[organisation]/[module]/ivy-[revision].xml + +# Location where temporary data goes. Only used for Oracle-specific create-user script +# needs to be customized for the user if location is not appropriate, or always for unix +data.dir=c:\\temp \ No newline at end of file diff --git a/common-build/readme.txt b/common-build/readme.txt new file mode 100644 index 00000000..d94b2b73 --- /dev/null +++ b/common-build/readme.txt @@ -0,0 +1,37 @@ +Contained in this directory is the Spring Jumpstart common build system used +to build all Spring projects. + +This generic build system is ant 1.6 based and also requires Ivy 1.3 or > for dependency management. + +Projects are expected to import master build files contained within +this directory as needed for the build targets they require. + +Build targets are organized into logical files: +- common-targets.xml : core targets applicable to all projects +- clover-targets.xml : for working with clover +- tomcat-targets.xml : for deploying webapps to the tomcat servlet container +etc... + +As an example, here is Spring Web Flow's project build.xml: + + + + + + + + + + + + + + + + + + +This build.xml imports the "common-targets.xml" fragment containing +core targets for compilation, distribution unit creation, and junit +testing. It also imports "clover-targets.xml" to facilitate the +generation of test coverage reports with clover. \ No newline at end of file diff --git a/common-build/spring-targets.xml b/common-build/spring-targets.xml new file mode 100644 index 00000000..286cc37d --- /dev/null +++ b/common-build/spring-targets.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/db/data.xml b/common-build/templates/db/data.xml new file mode 100644 index 00000000..bae3fc3d --- /dev/null +++ b/common-build/templates/db/data.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/common-build/templates/jsp/includeBottom.jsp b/common-build/templates/jsp/includeBottom.jsp new file mode 100644 index 00000000..4686f911 --- /dev/null +++ b/common-build/templates/jsp/includeBottom.jsp @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/common-build/templates/jsp/includeTop.jsp b/common-build/templates/jsp/includeTop.jsp new file mode 100644 index 00000000..5911a810 --- /dev/null +++ b/common-build/templates/jsp/includeTop.jsp @@ -0,0 +1,22 @@ +<%@ page contentType="text/html" %> +<%@ page session="false" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> + + + +@TITLE@ + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/jsp/pageshell.jsp b/common-build/templates/jsp/pageshell.jsp new file mode 100644 index 00000000..c60d2bb5 --- /dev/null +++ b/common-build/templates/jsp/pageshell.jsp @@ -0,0 +1,7 @@ +<%@ include file="includeTop.jsp" %> + +
+ +
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/common-build/templates/log4j/log4j.properties b/common-build/templates/log4j/log4j.properties new file mode 100644 index 00000000..5223d5b0 --- /dev/null +++ b/common-build/templates/log4j/log4j.properties @@ -0,0 +1,19 @@ +log4j.rootCategory=WARN, stdout, logfile + +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 + +log4j.appender.logfile=org.apache.log4j.RollingFileAppender +log4j.appender.logfile.File=${@PROJECT_NAME@.root}/@PROJECT_NAME@.log +log4j.appender.logfile.MaxFileSize=512KB + +# Keep three backup files +log4j.appender.logfile.MaxBackupIndex=3 +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout + +#Pattern to output : date priority [category] - line_separator +log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n + +# Enable debug interceptor +#log4j.category.org.springframework.aop.interceptor=DEBUG \ No newline at end of file diff --git a/common-build/templates/projects/standard/.classpath b/common-build/templates/projects/standard/.classpath new file mode 100644 index 00000000..ce8eb525 --- /dev/null +++ b/common-build/templates/projects/standard/.classpath @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/common-build/templates/projects/standard/.cvsignore b/common-build/templates/projects/standard/.cvsignore new file mode 100644 index 00000000..16b87eb5 --- /dev/null +++ b/common-build/templates/projects/standard/.cvsignore @@ -0,0 +1,8 @@ +target +lib +bak +build.properties +*.log +*.jpx.local* +*.iws +*.tws diff --git a/common-build/templates/projects/standard/.project b/common-build/templates/projects/standard/.project new file mode 100644 index 00000000..6f35c775 --- /dev/null +++ b/common-build/templates/projects/standard/.project @@ -0,0 +1,17 @@ + + + @PROJECT_NAME@ + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/common-build/templates/projects/standard/build.xml b/common-build/templates/projects/standard/build.xml new file mode 100644 index 00000000..c38a109a --- /dev/null +++ b/common-build/templates/projects/standard/build.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/projects/standard/ivy.xml b/common-build/templates/projects/standard/ivy.xml new file mode 100644 index 00000000..6bb7d161 --- /dev/null +++ b/common-build/templates/projects/standard/ivy.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/projects/standard/project.properties b/common-build/templates/projects/standard/project.properties new file mode 100644 index 00000000..1fecca74 --- /dev/null +++ b/common-build/templates/projects/standard/project.properties @@ -0,0 +1,5 @@ +# to override these or other properties with local user settings, +# create a unversioned build.properties file + +# The location of the common build system +common.build.dir=${basedir}/../common-build \ No newline at end of file diff --git a/common-build/templates/projects/standard/src/etc/filter.properties b/common-build/templates/projects/standard/src/etc/filter.properties new file mode 100644 index 00000000..c54e5880 --- /dev/null +++ b/common-build/templates/projects/standard/src/etc/filter.properties @@ -0,0 +1,33 @@ +# $Header$ + +# Contains filterable project settings. Setting placeholders in filterable project text +# files will be replaced with these values when the 'statics' build target is run. +# +# You may add static settings directly to this source file in the format: +# setting=value e.g MY_SETTING=myvalue +# This is appropriate usage if you know the setting value will never change. +# +# At build time this source file is copied to the ${target.dir} where additional +# dynamic settings may be appended using the task. Use this approach +# when a setting value depends on the build or the local user's environment. +# +# An example of this approach is shown below: +# +# build.xml +# +# +# +# +# +# +# +# +# This allows for dynamic replacement values that are sourced from local properties files to facilitate +# local user settings. +# +# To refer to filterable settings within project source files like config files, JSPs, or +# other text files use the standard ant placeholder format: +# @SETTING_NAME@ e.g, @MY_SETTING@ and @MY_LOCAL_SETTING@ +# +# Your settings: diff --git a/common-build/templates/projects/standard/src/etc/test-resources/log4j.properties b/common-build/templates/projects/standard/src/etc/test-resources/log4j.properties new file mode 100644 index 00000000..c6d11b4d --- /dev/null +++ b/common-build/templates/projects/standard/src/etc/test-resources/log4j.properties @@ -0,0 +1,16 @@ +log4j.rootCategory=WARN, stdout, logfile + +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 + +log4j.appender.logfile=org.apache.log4j.RollingFileAppender +log4j.appender.logfile.File=${@PROJECT_NAME@.root}/@PROJECT_NAME@.log +log4j.appender.logfile.MaxFileSize=512KB + +# Keep three backup files +log4j.appender.logfile.MaxBackupIndex=3 +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout + +#Pattern to output : date priority [category] - line_separator +log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n diff --git a/common-build/templates/projects/standard/src/etc/test-resources/readme.txt b/common-build/templates/projects/standard/src/etc/test-resources/readme.txt new file mode 100644 index 00000000..5ae540d8 --- /dev/null +++ b/common-build/templates/projects/standard/src/etc/test-resources/readme.txt @@ -0,0 +1,5 @@ +test-resources +--------- +Test-time related configuration files (*.properties, *.xml) and other resources reside in this directory. These files are copied into the test classpath, they are not included in the project distribution unit. + +You may remove this file once you understand the project structure and the purpose of this directory: this file also serves as a "empty directory" placeholder so CVS won't prune the directory. \ No newline at end of file diff --git a/common-build/templates/projects/standard/src/main/java/readme.txt b/common-build/templates/projects/standard/src/main/java/readme.txt new file mode 100644 index 00000000..d13c5d57 --- /dev/null +++ b/common-build/templates/projects/standard/src/main/java/readme.txt @@ -0,0 +1,5 @@ +java +------ +Project source code resides in this directory. Files here are compiled into classes placed in ${project.dir}/target/classes and added to the compile classpath. + +You may remove this file once you understand the project structure and the purpose of this directory: this file also serves as a "empty directory" placeholder so CVS won't prune the directory. diff --git a/common-build/templates/projects/standard/src/main/resources/readme.txt b/common-build/templates/projects/standard/src/main/resources/readme.txt new file mode 100644 index 00000000..b354a2ba --- /dev/null +++ b/common-build/templates/projects/standard/src/main/resources/readme.txt @@ -0,0 +1,5 @@ +resources +--------- +Tweakable application configuration files (*.properties) and other resources reside in this directory. + +You may remove this file once you understand the project structure and the purpose of this directory: this file also serves as a "empty directory" placeholder so CVS won't prune the directory. \ No newline at end of file diff --git a/common-build/templates/projects/standard/src/test/java/readme.txt b/common-build/templates/projects/standard/src/test/java/readme.txt new file mode 100644 index 00000000..4a11f99e --- /dev/null +++ b/common-build/templates/projects/standard/src/test/java/readme.txt @@ -0,0 +1,5 @@ +test +------ +Project unit and integration tests reside within this directory. Files here are compiled into classes placed in ${project.dir}/target/test-classes and added to the test classpath. + +You may remove this file once you understand the project structure and the purpose of this directory: this file also serves as a "empty directory" placeholder so CVS won't prune the directory. diff --git a/common-build/templates/projects/webapp/WEB-INF/classes/log4j.properties b/common-build/templates/projects/webapp/WEB-INF/classes/log4j.properties new file mode 100644 index 00000000..5223d5b0 --- /dev/null +++ b/common-build/templates/projects/webapp/WEB-INF/classes/log4j.properties @@ -0,0 +1,19 @@ +log4j.rootCategory=WARN, stdout, logfile + +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 + +log4j.appender.logfile=org.apache.log4j.RollingFileAppender +log4j.appender.logfile.File=${@PROJECT_NAME@.root}/@PROJECT_NAME@.log +log4j.appender.logfile.MaxFileSize=512KB + +# Keep three backup files +log4j.appender.logfile.MaxBackupIndex=3 +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout + +#Pattern to output : date priority [category] - line_separator +log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n + +# Enable debug interceptor +#log4j.category.org.springframework.aop.interceptor=DEBUG \ No newline at end of file diff --git a/common-build/templates/projects/webapp/WEB-INF/dispatcher-servlet-config.xml b/common-build/templates/projects/webapp/WEB-INF/dispatcher-servlet-config.xml new file mode 100644 index 00000000..d32bf1cd --- /dev/null +++ b/common-build/templates/projects/webapp/WEB-INF/dispatcher-servlet-config.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/common-build/templates/projects/webapp/WEB-INF/web.xml b/common-build/templates/projects/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..8c924b9e --- /dev/null +++ b/common-build/templates/projects/webapp/WEB-INF/web.xml @@ -0,0 +1,41 @@ + + + + + + + contextConfigLocation + + + + + + + + org.springframework.web.context.ContextLoaderListener + + + + @PROJECT_NAME@ + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + /WEB-INF/dispatcher-servlet-config.xml + + + + + @PROJECT_NAME@ + *.htm + + + \ No newline at end of file diff --git a/common-build/templates/projects/webapp/index.html b/common-build/templates/projects/webapp/index.html new file mode 100644 index 00000000..b37b2a3f --- /dev/null +++ b/common-build/templates/projects/webapp/index.html @@ -0,0 +1,10 @@ + + +Shell + + + + + + + diff --git a/common-build/templates/readme.txt b/common-build/templates/readme.txt new file mode 100644 index 00000000..cd23c818 --- /dev/null +++ b/common-build/templates/readme.txt @@ -0,0 +1,2 @@ +Contained in this directory are useful templates for projects, configuration files, +and Java artifacts for products such as Spring, Spring Web Flow, and log4j. \ No newline at end of file diff --git a/common-build/templates/spring-webflow/FlowExecutionTestTemplate.java b/common-build/templates/spring-webflow/FlowExecutionTestTemplate.java new file mode 100644 index 00000000..a387ed55 --- /dev/null +++ b/common-build/templates/spring-webflow/FlowExecutionTestTemplate.java @@ -0,0 +1,24 @@ +package webflow.templates; + +import java.util.Map; + +import org.springframework.webflow.definition.registry.FlowDefinitionResource; +import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests; +import org.springframework.webflow.test.execution.MockFlowServiceLocator; + +public class FlowExecutionTestTemplate extends AbstractXmlFlowExecutionTests { + + public void testStartFlow() { + + } + + @Override + protected FlowDefinitionResource getFlowDefinitionResource() { + return createFlowDefinitionResource("/path/to/myflow.xml"); + } + + @Override + protected void registerMockServices(MockFlowServiceLocator serviceRegistry) { + + } +} \ No newline at end of file diff --git a/common-build/templates/spring-webflow/flow-definition-1.0-form.xml b/common-build/templates/spring-webflow/flow-definition-1.0-form.xml new file mode 100644 index 00000000..6435e161 --- /dev/null +++ b/common-build/templates/spring-webflow/flow-definition-1.0-form.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/spring-webflow/flow-definition-1.0-simplest.xml b/common-build/templates/spring-webflow/flow-definition-1.0-simplest.xml new file mode 100644 index 00000000..927a309d --- /dev/null +++ b/common-build/templates/spring-webflow/flow-definition-1.0-simplest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/spring-webflow/flow-definition-1.0.xml b/common-build/templates/spring-webflow/flow-definition-1.0.xml new file mode 100644 index 00000000..aa080bea --- /dev/null +++ b/common-build/templates/spring-webflow/flow-definition-1.0.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/spring-webflow/webflow-config-1.0-spring-1.2.xml b/common-build/templates/spring-webflow/webflow-config-1.0-spring-1.2.xml new file mode 100644 index 00000000..77c48d22 --- /dev/null +++ b/common-build/templates/spring-webflow/webflow-config-1.0-spring-1.2.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/spring-webflow/webflow-config-1.0.xml b/common-build/templates/spring-webflow/webflow-config-1.0.xml new file mode 100644 index 00000000..0cb22d0e --- /dev/null +++ b/common-build/templates/spring-webflow/webflow-config-1.0.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/spring-webflow/webflow-form.jsp b/common-build/templates/spring-webflow/webflow-form.jsp new file mode 100644 index 00000000..285f09ca --- /dev/null +++ b/common-build/templates/spring-webflow/webflow-form.jsp @@ -0,0 +1,18 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ + + + +
+ + + + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/common-build/templates/spring/bean-definitions-1.2.xml b/common-build/templates/spring/bean-definitions-1.2.xml new file mode 100644 index 00000000..7be11e1c --- /dev/null +++ b/common-build/templates/spring/bean-definitions-1.2.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/common-build/templates/spring/bean-definitions-2.0.xml b/common-build/templates/spring/bean-definitions-2.0.xml new file mode 100644 index 00000000..5ae46de2 --- /dev/null +++ b/common-build/templates/spring/bean-definitions-2.0.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/common-build/templates/tomcat/context.xml b/common-build/templates/tomcat/context.xml new file mode 100644 index 00000000..0aa6e718 --- /dev/null +++ b/common-build/templates/tomcat/context.xml @@ -0,0 +1,4 @@ + + + diff --git a/common-build/tomcat-targets.xml b/common-build/tomcat-targets.xml new file mode 100644 index 00000000..ea1129a0 --- /dev/null +++ b/common-build/tomcat-targets.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Set tomcat.dir in your USER_HOME/build.properties file (create if it doesn't exist) + + + \ No newline at end of file diff --git a/common-build/version.txt b/common-build/version.txt new file mode 100644 index 00000000..27a8d2a9 --- /dev/null +++ b/common-build/version.txt @@ -0,0 +1,13 @@ +version 20060409-1 + +Changelog: +2006-08-25: Keith: updated project and file templates inside "templates" +2006-04-09: Colin: upgrade Ivy to 1.3.1 +2006-03-26: Colin: upgrade Ivy to snapshot 20060322, for transitive dep fix. +2006-03-08: Colin: upgrade Ivy to 1.3RC3 +2006-02-17: Colin: Add retrieve-to-repo to common-targets.xml, user for release procoess +2006-02-13: Arjen: Standard docbook images are now also copied in doc-targets.xml +2006-02-08: Colin: add dir="${basedir} to junit task so it runs ok from other dirs +2006-01-04: Colin: add description element to "retrieve" since it's not an internal target +2005-11-27: Update Spring version in templates +2005-11-20: Arjen: add gen java source dir functionality for test sources too diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..41f85250 --- /dev/null +++ b/pom.xml @@ -0,0 +1,277 @@ + + + 4.0.0 + org.springframework.ldap + spring-ldap + pom + 1.1.1-SNAPSHOT + Spring LDAP + http://www.springframework.org/ldap + + 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 + 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. + + 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. + + + + + + + + JIRA + + http://opensource.atlassian.com/projects/spring/browse/LDAP + + + + + continuum + http://www.purple.se:8787/continuum/ + + + mail + +
ulrik.sandberg@jayway.se
+
+
+ + mail + +
mattias.arthursson@jayway.se
+
+
+
+
+ + + + scm:svn:https://svn.sourceforge.net/svnroot/springframework/spring-projects/trunk/spring-ldap + + + scm:svn:https://svn.sourceforge.net/svnroot/springframework/spring-projects/trunk/spring-ldap + + + http://svn.sourceforge.net/viewcvs.cgi/springframework/spring-projects/trunk/spring-ldap + + + + 2006 + + + Spring Framework + http://www.springframework.org + + + + + ulsa + Ulrik Sandberg + ulrik.sandberg at jayway.se + Jayway AB + http://www.jayway.se + + Project Admin + Developer + + +1 + + + marthursson + Mattias Arthursson + mattias.arthursson at jayway.se + Jayway AB + http://www.jayway.se + + Project Admin + Developer + + +1 + + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + +Copyright 2006 the original author or authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. +See the License for the specific language governing permissions and +limitations under the License. + + + + + + + + + + + spring-ldap + spring-ldap-article + spring-ldap-person + + + + + + junit + junit + 3.8.1 + test + + + + + + + + + + + org.apache.maven.plugins + maven-clover-plugin + + + pre-site + + instrument + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-jxr-plugin + + true + + + + org.apache.maven.plugins + maven-surefire-report-plugin + + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + Spring LDAP + org.springframework.ldap* + + + Spring LDAP Person Sample + org.springframework.ldap.samples.person.* + + + + + http://java.sun.com/j2se/1.4.2/docs/api/ + + http://static.springframework.org/spring/docs/2.0.x/api/ + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + org.apache.maven.plugins + maven-clover-plugin + + + org.apache.maven.plugins + maven-jxr-plugin + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + org.apache.maven.plugins + maven-pmd-plugin + + + + + + + + + + + + + true + + apache-snapshots + Apache Snapshots + http://cvs.apache.org/maven-snapshot-repository + + + safehaus-repository + Safehaus Repository + http://m2.safehaus.org + + + spring-ext + Spring External Dependencies Repository + + https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/ + + + + + + + + true + + apache.snapshots + Apache Snapshots + http://cvs.apache.org/maven-snapshot-repository/ + + + +
diff --git a/sandbox/.classpath b/sandbox/.classpath new file mode 100644 index 00000000..e1da7745 --- /dev/null +++ b/sandbox/.classpath @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/.project b/sandbox/.project new file mode 100644 index 00000000..d37ee382 --- /dev/null +++ b/sandbox/.project @@ -0,0 +1,34 @@ + + + sandbox + + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.ibm.etools.validation.validationbuilder + + + + + com.ibm.etools.ctc.serviceprojectbuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + com.ibm.etools.ctc.javaprojectnature + + diff --git a/sandbox/.settings/org.eclipse.jdt.core.prefs b/sandbox/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..a923f3d7 --- /dev/null +++ b/sandbox/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,248 @@ +#Sun Jul 02 11:21:34 EDT 2006 +eclipse.preferences.version=1 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=1 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false +org.eclipse.jdt.core.formatter.comment.format_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=80 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false diff --git a/sandbox/.settings/org.eclipse.jdt.ui.prefs b/sandbox/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..21ae7c07 --- /dev/null +++ b/sandbox/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,3 @@ +#Fri Apr 21 13:43:14 CEST 2006 +eclipse.preferences.version=1 +formatter_settings_version=10 diff --git a/sandbox/.springBeans b/sandbox/.springBeans new file mode 100644 index 00000000..f9aa76ca --- /dev/null +++ b/sandbox/.springBeans @@ -0,0 +1,25 @@ + + + + src/iutest/conf/ldapTemplateBaseSuffixTestContext.xml + src/iutest/conf/ldapTemplateTestContext.xml + + + + iutest with base + false + false + + src/iutest/conf/ldapTemplateBaseSuffixTestContext.xml + + + + iutest + false + false + + src/iutest/conf/ldapTemplateTestContext.xml + + + + diff --git a/sandbox/build.xml b/sandbox/build.xml new file mode 100644 index 00000000..fba37e3f --- /dev/null +++ b/sandbox/build.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Unit test report has been generated. +The HTML report is ${target.testresults.html.dir}/index.html + + + + + diff --git a/sandbox/itest-openldap-targets.xml b/sandbox/itest-openldap-targets.xml new file mode 100644 index 00000000..fcee3011 --- /dev/null +++ b/sandbox/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/sandbox/itest-targets.xml b/sandbox/itest-targets.xml new file mode 100644 index 00000000..5f534a10 --- /dev/null +++ b/sandbox/itest-targets.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + compiling itest sources + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Integration test report has been generated. +The HTML report is ${target.itestresults.html.dir}/index.html + + + + + diff --git a/sandbox/ivy.xml b/sandbox/ivy.xml new file mode 100644 index 00000000..ebb1c36d --- /dev/null +++ b/sandbox/ivy.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/project.properties b/sandbox/project.properties new file mode 100644 index 00000000..269a5449 --- /dev/null +++ b/sandbox/project.properties @@ -0,0 +1,14 @@ +# properties defined in this file are overridable by a local build.properties in this project dir + +# The location of the common build system +common.build.dir=${basedir}/../common-build + +project.base.version=1.1.2 +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/sandbox/project.xml b/sandbox/project.xml new file mode 100644 index 00000000..6d9b29f2 --- /dev/null +++ b/sandbox/project.xml @@ -0,0 +1,237 @@ + + + 3 + + ldaptemplate + LdapTemplate + ldaptemplate + ldaptemplate + 1.0.2 + http://jira.ddsteps.org/browse/LDT + 2006 + org.springframework.ldap + + <p> + LdapTemplate is a framework 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, + 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 of all 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://sourceforge.net/projects/ldaptemplate + + + + ldaptemplate-user + http://lists.sourceforge.net/mailman/listinfo/ldaptemplate-user + http://lists.sourceforge.net/mailman/listinfo/ldaptemplate-user + mailto:ldaptemplate-user@lists.sourceforge.net + http://sourceforge.net/mailarchive/forum.php?forum_id=47521 + + + + + 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/sandbox/readme.txt b/sandbox/readme.txt new file mode 100644 index 00000000..929d94f8 --- /dev/null +++ b/sandbox/readme.txt @@ -0,0 +1,2 @@ +Sandbox for adding and testing new functionality without disturbing +the production code. diff --git a/sandbox/src/itest-openldap/java/conf/ldap-openldap.properties b/sandbox/src/itest-openldap/java/conf/ldap-openldap.properties new file mode 100644 index 00000000..492339a0 --- /dev/null +++ b/sandbox/src/itest-openldap/java/conf/ldap-openldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1 +userName=cn=Directory Manager +password=netscape +base=o=ikea.com diff --git a/sandbox/src/itest-openldap/java/conf/ldap.conf b/sandbox/src/itest-openldap/java/conf/ldap.conf new file mode 100644 index 00000000..aa4372ea --- /dev/null +++ b/sandbox/src/itest-openldap/java/conf/ldap.conf @@ -0,0 +1,23 @@ +# Start slapd with this file as configuration file: -f ldap.conf +# The first time, fill the database using the load script in +# src/itest/java/setup_data.ldif, like this: +# +# cd \program\openldap +# ldap_add -f ...\src\itest\java\setup_data.ldif -d cn=Manager,dc=jayway,dc=se -W +# +# The password can be seen below + +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/sandbox/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml b/sandbox/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml new file mode 100644 index 00000000..c7c22aaa --- /dev/null +++ b/sandbox/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/src/itest-openldap/java/org/springframework/ldap/LdapTemplateVirtualListViewSearchITest.java b/sandbox/src/itest-openldap/java/org/springframework/ldap/LdapTemplateVirtualListViewSearchITest.java new file mode 100644 index 00000000..86352451 --- /dev/null +++ b/sandbox/src/itest-openldap/java/org/springframework/ldap/LdapTemplateVirtualListViewSearchITest.java @@ -0,0 +1,140 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.support.control.VirtualListViewRequestControl; +import org.springframework.ldap.support.control.VirtualListViewResultsCookie; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests the still experimental virtual list view search result capability of + * LdapTemplate. + * + * @author Ulrik Sandberg + */ +public class LdapTemplateVirtualListViewSearchITest extends + AbstractDependencyInjectionSpringContextTests { + + private static final String BASE_STRING = "o=ikea.com"; + + private static final String FILTER_STRING = "(&(objectclass=ikeaperson))"; + + 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 = tested.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_VirtualListView() { + Person person; + List list; + VirtualListViewResultsCookie cookie; + VirtualListViewRequestControl requestControl; + + // Prepare for first search + requestControl = new VirtualListViewRequestControl(3); + tested.search(BASE_STRING, FILTER_STRING, searchControls, + callbackHandler, requestControl); + cookie = requestControl.getCookie(); + int listSize = requestControl.getListSize(); + // assertNotNull("Cookie should not be null yet", cookie.getCookie()); + list = callbackHandler.getList(); + assertEquals(3, list.size()); + person = (Person) list.get(0); + assertEquals("A OPER", person.getFullname()); + person = (Person) list.get(1); + assertEquals("aaaa aaaa", person.getFullname()); + person = (Person) list.get(2); + assertEquals("aaab aaab", person.getFullname()); + + // Prepare for position 6000 + requestControl = new VirtualListViewRequestControl(3, 6000, listSize, + cookie); + tested.search(BASE_STRING, FILTER_STRING, searchControls, + callbackHandler, requestControl); + cookie = requestControl.getCookie(); + // assertNull("Cookie should be null now", cookie.getCookie()); + assertEquals(6, list.size()); + person = (Person) list.get(3); + assertEquals("Jessica Santiago Moreno", person.getFullname()); + person = (Person) list.get(4); + assertEquals("Jessica stenfeldt", person.getFullname()); + person = (Person) list.get(5); + assertEquals("Jessica Svensson", person.getFullname()); + + // Prepare for position 93% + requestControl = new VirtualListViewRequestControl(3, 93, listSize, + cookie); + requestControl.setOffsetPercentage(true); + tested.search(BASE_STRING, FILTER_STRING, searchControls, + callbackHandler, requestControl); + cookie = requestControl.getCookie(); + // assertNull("Cookie should be null now", cookie.getCookie()); + assertEquals(9, list.size()); + person = (Person) list.get(6); + assertEquals("Thomas Ronnblom", person.getFullname()); + person = (Person) list.get(7); + assertEquals("Thomas Rosenthal", person.getFullname()); + person = (Person) list.get(8); + assertEquals("Thomas Rydell", 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.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/sandbox/src/itest/java/conf/apacheDsContext.xml b/sandbox/src/itest/java/conf/apacheDsContext.xml new file mode 100644 index 00000000..f5230c1e --- /dev/null +++ b/sandbox/src/itest/java/conf/apacheDsContext.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + simple + + + ${userName} + + + ${password} + + + + + + + + + + + + + + + + + + + + + objectClass: top + objectClass: domain + objectClass: extensibleObject + dc: jayway + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sandbox/src/itest/java/conf/ldap.properties b/sandbox/src/itest/java/conf/ldap.properties new file mode 100644 index 00000000..74163056 --- /dev/null +++ b/sandbox/src/itest/java/conf/ldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1:390 +userName=uid=admin,ou=system +password=secret +base=dc=jayway,dc=se diff --git a/sandbox/src/itest/java/conf/ldapTemplateAcegiTestContext.xml b/sandbox/src/itest/java/conf/ldapTemplateAcegiTestContext.xml new file mode 100644 index 00000000..cf2af0de --- /dev/null +++ b/sandbox/src/itest/java/conf/ldapTemplateAcegiTestContext.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sandbox/src/itest/java/conf/ldapTemplateBaseSuffixTestContext.xml b/sandbox/src/itest/java/conf/ldapTemplateBaseSuffixTestContext.xml new file mode 100644 index 00000000..c716a843 --- /dev/null +++ b/sandbox/src/itest/java/conf/ldapTemplateBaseSuffixTestContext.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sandbox/src/itest/java/conf/ldapTemplateTestContext.xml b/sandbox/src/itest/java/conf/ldapTemplateTestContext.xml new file mode 100644 index 00000000..2c09f828 --- /dev/null +++ b/sandbox/src/itest/java/conf/ldapTemplateTestContext.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/src/itest/java/log4j.properties b/sandbox/src/itest/java/log4j.properties new file mode 100644 index 00000000..f57302e8 --- /dev/null +++ b/sandbox/src/itest/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/sandbox/src/itest/java/setup_data.ldif b/sandbox/src/itest/java/setup_data.ldif new file mode 100644 index 00000000..e94d8d4d --- /dev/null +++ b/sandbox/src/itest/java/setup_data.ldif @@ -0,0 +1,71 @@ +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 +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: Some Person Phone + +dn: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +cn: Some Person2 +sn: Person2 +description: Sweden, Company1, Some Person2 +telephoneNumber: Some Person2 Phone + +dn: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +cn: Some Person3 +sn: Person3 +description: Sweden, Company1, Some Person3 +telephoneNumber: Some Person3 Phone +userPassword: secretpassword + +dn: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se +objectclass: top +objectclass: person +cn: Some Person +sn: Person +description: Sweden, Company2, Some Person +telephoneNumber: Some Person Phone + +dn: cn=Some Person,ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: person +cn: Some Person +sn: Person +description: Norway, Company1, Some Person +telephoneNumber: Some Person Phone + diff --git a/sandbox/src/main/java/org/springframework/ldap/support/ContextSourceTransactionManager.java b/sandbox/src/main/java/org/springframework/ldap/support/ContextSourceTransactionManager.java new file mode 100644 index 00000000..5d27c657 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ldap/support/ContextSourceTransactionManager.java @@ -0,0 +1,88 @@ +package org.springframework.ldap.support; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.springframework.ldap.ContextSource; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.support.AbstractPlatformTransactionManager; +import org.springframework.transaction.support.DefaultTransactionStatus; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class ContextSourceTransactionManager extends + AbstractPlatformTransactionManager { + + private ContextSource contextSource; + + public void setContextSource(ContextSource contextSource) { + this.contextSource = contextSource; + } + + public ContextSource getContextSource() { + return contextSource; + } + + protected Object doGetTransaction() throws TransactionException { + DirContextHolder contextHolder = (DirContextHolder) TransactionSynchronizationManager + .getResource(this.contextSource); + ContextSourceTransactionObject txObject = new ContextSourceTransactionObject( + contextHolder); + return txObject; + } + + protected void doBegin(Object transaction, TransactionDefinition definition) + throws TransactionException { + ContextSourceTransactionObject txObject = (ContextSourceTransactionObject) transaction; + + if (txObject.getContextHolder() == null) { + DirContext newCtx = getContextSource().getReadOnlyContext(); + DirContextHolder contextHolder = new DirContextHolder(newCtx); + + txObject.setContextHolder(contextHolder); + TransactionSynchronizationManager.bindResource(getContextSource(), + contextHolder); + } + } + + protected void doCommit(DefaultTransactionStatus status) + throws TransactionException { + // Nothing much to do here. + } + + protected void doRollback(DefaultTransactionStatus status) + throws TransactionException { + // Perform compensating transaction cleanup using information stored in + // ContextHolder. + } + + protected void doCleanupAfterCompletion(Object transaction) { + ContextSourceTransactionObject txObject = (ContextSourceTransactionObject) transaction; + TransactionSynchronizationManager.unbindResource(contextSource); + DirContext ctx = txObject.getContextHolder().getCtx(); + + try { + ctx.close(); + } catch (NamingException e) { + e.printStackTrace(); + } + } + + static class ContextSourceTransactionObject { + private DirContextHolder contextHolder; + + public ContextSourceTransactionObject(DirContextHolder contextHolder) { + this.contextHolder = contextHolder; + } + + public DirContextHolder getContextHolder() { + return contextHolder; + } + + public void setContextHolder(DirContextHolder contextHolder) { + this.contextHolder = contextHolder; + } + + } + +} diff --git a/sandbox/src/main/java/org/springframework/ldap/support/DirContextHolder.java b/sandbox/src/main/java/org/springframework/ldap/support/DirContextHolder.java new file mode 100644 index 00000000..d54f2086 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ldap/support/DirContextHolder.java @@ -0,0 +1,21 @@ +package org.springframework.ldap.support; + +import javax.naming.directory.DirContext; + +import org.springframework.transaction.support.ResourceHolderSupport; + +public class DirContextHolder extends ResourceHolderSupport { + private DirContext ctx; + + public void setCtx(DirContext ctx) { + this.ctx = ctx; + } + + public DirContextHolder(DirContext ctx) { + this.ctx = ctx; + } + + public DirContext getCtx() { + return ctx; + } +} diff --git a/sandbox/src/main/java/org/springframework/ldap/support/DirContextProxy.java b/sandbox/src/main/java/org/springframework/ldap/support/DirContextProxy.java new file mode 100644 index 00000000..a694da08 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ldap/support/DirContextProxy.java @@ -0,0 +1,7 @@ +package org.springframework.ldap.support; + +import javax.naming.directory.DirContext; + +public interface DirContextProxy { + DirContext getTargetContext(); +} diff --git a/sandbox/src/main/java/org/springframework/ldap/support/TlsContextSource.java b/sandbox/src/main/java/org/springframework/ldap/support/TlsContextSource.java new file mode 100644 index 00000000..e933bf9a --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ldap/support/TlsContextSource.java @@ -0,0 +1,174 @@ +package org.springframework.ldap.support; + +import java.io.IOException; +import java.security.KeyStore; +import java.util.Hashtable; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; +import javax.naming.ldap.StartTlsRequest; +import javax.naming.ldap.StartTlsResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.support.LdapContextSource; + +/** + * ContextSource implementation which overrides setupAuthenticatedEnvironment to + * allow keystore authentication. + * + * @author Ben Surgison + */ +public class TlsContextSource extends LdapContextSource { + + private static final Log log = LogFactory.getLog(TlsContextSource.class); + + private static final String KEYSTORE = "javax.net.ssl.keyStore"; + + private static final String KEYSTORETYPE = "javax.net.ssl.keyStoreType"; + + private static final String KEYSTOREPASSWORD = "javax.net.ssl.keyStorePassword"; + + private static final String TRUSTSTORE = "javax.net.ssl.trustStore"; + + private static final String TRUSTSTORETYPE = "javax.net.ssl.trustStoreType"; + + private static final String TRUSTSTOREPASSWORD = "javax.net.ssl.trustStorePassword"; + + private String keyStore; + + private String keyStoreType; + + private String keyStorePassword; + + private String trustStore; + + private String trustStoreType; + + private String trustStorePassword; + + /** + * Overridden implementation to allow TLS to be started + */ + public DirContext getReadOnlyContext() { + LdapContext ctx = (LdapContext) super.getReadOnlyContext(); + + // only start tls if keyStore has been set. + if (this.keyStore == null && this.keyStore.length() > 0) { + // Start TLS + StartTlsResponse tls; + try { + tls = (StartTlsResponse) ctx + .extendedOperation(new StartTlsRequest()); + tls.negotiate(); + } catch (NamingException ne) { + log.error(ne.getLocalizedMessage(), ne); + } catch (IOException ioe) { + log.error(ioe.getLocalizedMessage(), ioe); + } + } + return ctx; + } + + /** + * Overridden implementation of setting the environment up to be + * authenticated. This allows a keyStore certificate to be used for SASL + * security. + * + * @param env + * the environment to modify. + */ + protected void setupAuthenticatedEnvironment(Hashtable env) { + + // revert to original if keyStore hasn't been set. + if (this.keyStore == null && this.keyStore.length() > 0) { + super.setupAuthenticatedEnvironment(env); + return; + } + + // Setup keyStore system properties. + System.setProperty(KEYSTORE, getMyQualifiedName(this.keyStore)); + System.setProperty(KEYSTOREPASSWORD, this.keyStorePassword); + System.setProperty(KEYSTORETYPE, getMyKeyStoreType(this.keyStoreType)); + + // Setup trustStore system properties. + System.setProperty(TRUSTSTORE, getMyQualifiedName(this.trustStore)); + System.setProperty(TRUSTSTOREPASSWORD, this.trustStorePassword); + System.setProperty(TRUSTSTORETYPE, + getMyKeyStoreType(this.trustStoreType)); + } + + private String getMyKeyStoreType(String keyStoreType) { + if (keyStoreType != null && keyStoreType.length() > 0) { + return keyStoreType; + } + return KeyStore.getDefaultType(); + } + + private String getMyQualifiedName(String filename) { + String qualifiedName = null; + if (filename.indexOf("/") > -1 || filename.indexOf("\\") > -1) { + qualifiedName = filename; // use full name as entered + } else { + try { + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + qualifiedName = classLoader.getResource(filename) + .toExternalForm(); // get full name from classpath + qualifiedName = qualifiedName.substring(6); + } catch (Exception e) { + log.error("Missing file: " + filename, e); + } + } + return qualifiedName; + } + + /** + * @param keyStore + * the keyStore to set + */ + public void setKeyStore(String keyStore) { + + this.keyStore = keyStore; + } + + /** + * @param passPhrase + * the keyStore password to set + */ + public void setKeyStorePassword(String passPhrase) { + this.keyStorePassword = passPhrase; + } + + /** + * @param keyStoreType + * the keyStoreType to set + */ + public void setKeyStoreType(String keyStoreType) { + this.keyStoreType = keyStoreType; + } + + /** + * @param trustStore + * the trustStore to set + */ + public void setTrustStore(String trustStore) { + this.trustStore = trustStore; + } + + /** + * @param trustStorePassword + * the trustStorePassword to set + */ + public void setTrustStorePassword(String trustStorePassword) { + this.trustStorePassword = trustStorePassword; + } + + /** + * @param trustStoreType + * the trustStoreType to set + */ + public void setTrustStoreType(String trustStoreType) { + this.trustStoreType = trustStoreType; + } +} diff --git a/sandbox/src/main/java/org/springframework/ldap/support/TransactionAwareContextSourceProxy.java b/sandbox/src/main/java/org/springframework/ldap/support/TransactionAwareContextSourceProxy.java new file mode 100644 index 00000000..9f0e8051 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ldap/support/TransactionAwareContextSourceProxy.java @@ -0,0 +1,102 @@ +package org.springframework.ldap.support; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.springframework.dao.DataAccessException; +import org.springframework.ldap.ContextSource; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class TransactionAwareContextSourceProxy implements ContextSource { + + private ContextSource target; + + public TransactionAwareContextSourceProxy(ContextSource target) { + this.target = target; + } + + public DirContext getReadOnlyContext() throws DataAccessException { + DirContextHolder contextHolder = (DirContextHolder) TransactionSynchronizationManager + .getResource(target); + DirContext ctx = null; + + if (contextHolder != null) { + ctx = contextHolder.getCtx(); + } + + if (ctx == null) { + ctx = target.getReadOnlyContext(); + if (contextHolder != null) { + contextHolder.setCtx(ctx); + } + } + return getTransactionAwareDirContextProxy(ctx, target); + } + + private DirContext getTransactionAwareDirContextProxy(DirContext context, + ContextSource target) { + return (DirContext) Proxy + .newProxyInstance(DirContextProxy.class.getClassLoader(), + new Class[] { DirContextProxy.class }, + new TransactionAwareDirContextInvocationHandler( + context, target)); + + } + + public DirContext getReadWriteContext() throws DataAccessException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + static class TransactionAwareDirContextInvocationHandler implements + InvocationHandler { + private DirContext target; + + private ContextSource contextSource; + + public TransactionAwareDirContextInvocationHandler(DirContext target, + ContextSource contextSource) { + this.target = target; + this.contextSource = contextSource; + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + + if (method.getName().equals("getTargetContext")) { + return target; + } else if (method.getName().equals("equals")) { + // Only consider equal when proxies are identical. + return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); + } else if (method.getName().equals("hashCode")) { + // Use hashCode of Connection proxy. + return new Integer(hashCode()); + } else if (method.getName().equals("close")) { + doCloseConnection(target, contextSource); + return null; + } + + try { + return method.invoke(target, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + + private void doCloseConnection(DirContext context, + ContextSource contextSource) throws NamingException { + DirContextHolder transactionContextHolder = (DirContextHolder) TransactionSynchronizationManager + .getResource(contextSource); + if (transactionContextHolder == null + || transactionContextHolder.getCtx() != context) { + // This is not the transactional context or the transaction is + // no longer active - we should close it. + context.close(); + } + } + } +} diff --git a/sandbox/src/main/java/org/springframework/ldap/support/control/VirtualListViewRequestControl.java b/sandbox/src/main/java/org/springframework/ldap/support/control/VirtualListViewRequestControl.java new file mode 100644 index 00000000..6f06ace1 --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ldap/support/control/VirtualListViewRequestControl.java @@ -0,0 +1,219 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.control; + +import java.io.IOException; +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.DirContextProcessor; +import org.springframework.ldap.UncategorizedLdapException; +import org.springframework.util.ReflectionUtils; + +import com.sun.jndi.ldap.ctl.SortControl; +import com.sun.jndi.ldap.ctl.VirtualListViewControl; +import com.sun.jndi.ldap.ctl.VirtualListViewResponseControl; + +/** + * DirContextProcessor implementation for managing a virtual list view. + * + * @author Ulrik Sandberg + */ +public class VirtualListViewRequestControl implements DirContextProcessor { + + private static final Class DEFAULT_RESPONSE_CONTROL = VirtualListViewResponseControl.class; + + private static final boolean CRITICAL_CONTROL = true; + + private int pageSize; + + private VirtualListViewResultsCookie cookie; + + private int listSize; + + private int targetOffset; + + private NamingException exception; + + private int resultCode; + + private Class responseControlClass = DEFAULT_RESPONSE_CONTROL; + + private boolean offsetPercentage; + + public VirtualListViewRequestControl(int pageSize) { + this(pageSize, 0, 0, null); + } + + public VirtualListViewRequestControl(int pageSize, int targetOffset, + int listSize, VirtualListViewResultsCookie cookie) { + this.pageSize = pageSize; + this.targetOffset = targetOffset; + this.listSize = listSize; + this.cookie = cookie; + } + + public VirtualListViewResultsCookie getCookie() { + return cookie; + } + + public int getPageSize() { + return pageSize; + } + + public int getListSize() { + return listSize; + } + + public NamingException getException() { + return exception; + } + + public int getResultCode() { + return resultCode; + } + + public int getTargetOffset() { + return targetOffset; + } + + /** + * Set the class of the expected ResponseControl for the paged results + * response. The default is {@link VirtualListViewResponseControl}. + * + * @param responseControlClass + * Class of the expected response control. + */ + public void setResponseControlClass(Class responseControlClass) { + this.responseControlClass = responseControlClass; + } + + public void setOffsetPercentage(boolean isPercentage) { + this.offsetPercentage = isPercentage; + } + + 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(); + Control newControl = createRequestControl(); + + Control[] newControls = new Control[requestControls.length + 2]; + for (int i = 0; i < requestControls.length; i++) { + newControls[i] = requestControls[i]; + } + + SortControl sortControl; + try { + sortControl = new SortControl(new String[] { "cn" }, true); + } catch (IOException e) { + throw new UncategorizedLdapException("Couldn't create SortControl", + e); + } + // Add the new Controls at the end of the array. + newControls[newControls.length - 2] = sortControl; + newControls[newControls.length - 1] = newControl; + + ldapContext.setRequestControls(newControls); + } + + /* + * @see org.springframework.ldap.support.control.AbstractRequestControlDirContextProcessor#createRequestControl() + */ + public Control createRequestControl() { + try { + VirtualListViewControl virtualListViewControl; + if (offsetPercentage) { + virtualListViewControl = new VirtualListViewControl( + targetOffset, pageSize, CRITICAL_CONTROL); + } else { + virtualListViewControl = new VirtualListViewControl( + targetOffset, listSize, 0, pageSize - 1, + CRITICAL_CONTROL); + } + if (cookie != null) { + virtualListViewControl.setContextID(cookie.getCookie()); + } + return virtualListViewControl; + } catch (IOException e) { + throw new UncategorizedLdapException( + "Error creating VirtualListViewControl", e); + } + } + + /* + * @see org.springframework.ldap.DirContextProcessor#postProcess(javax.naming.directory.DirContext) + */ + public void postProcess(DirContext ctx) throws NamingException { + LdapContext ldapContext = (LdapContext) ctx; + Control[] responseControls = ldapContext.getResponseControls(); + + // 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 + if (isVirtualListViewResponseControl(responseControl)) { + Object control = responseControl; + byte[] result = (byte[]) invokeMethod("getContextID", + responseControlClass, control); + this.cookie = new VirtualListViewResultsCookie(result); + Integer wrapper = (Integer) invokeMethod("getListSize", + responseControlClass, control); + this.listSize = wrapper.intValue(); + wrapper = (Integer) invokeMethod("getTargetOffset", + responseControlClass, control); + this.targetOffset = wrapper.intValue(); + wrapper = (Integer) invokeMethod("getResultCode", + responseControlClass, control); + this.resultCode = wrapper.intValue(); + this.exception = (NamingException) invokeMethod("getException", + responseControlClass, control); + } + } + } + + /** + * Check if the given control matches a paged results response control. + * + * @param responseControl + * the control to check for a match + * @return whether the control is a paged results response control + */ + private boolean isVirtualListViewResponseControl(Control responseControl) { + if (responseControl.getClass().isAssignableFrom(responseControlClass)) { + return true; + } + return false; + } + + private Object invokeMethod(String method, Class clazz, Object control) { + Method m = ReflectionUtils.findMethod(clazz, method, new Class[0]); + return ReflectionUtils.invokeMethod(m, control); + } +} diff --git a/sandbox/src/main/java/org/springframework/ldap/support/control/VirtualListViewResultsCookie.java b/sandbox/src/main/java/org/springframework/ldap/support/control/VirtualListViewResultsCookie.java new file mode 100644 index 00000000..d457721a --- /dev/null +++ b/sandbox/src/main/java/org/springframework/ldap/support/control/VirtualListViewResultsCookie.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.control; + +import com.sun.jndi.ldap.ctl.VirtualListViewControl; + +/** + * Wrapper class for the cookie returned when using the + * {@link VirtualListViewControl}. + * + * @author Ulrik Sandberg + */ +public class VirtualListViewResultsCookie { + + private byte[] cookie; + + /** + * Constructor. + * + * @param cookie + * the cookie returned by a VirtualListViewResponseControl. + */ + public VirtualListViewResultsCookie(byte[] cookie) { + this.cookie = cookie; + } + + /** + * Get the cookie. + * + * @return the cookie. + */ + public byte[] getCookie() { + return cookie; + } +} diff --git a/sandbox/src/test/java/org/springframework/ldap/support/ContextSourceTransactionManagerTest.java b/sandbox/src/test/java/org/springframework/ldap/support/ContextSourceTransactionManagerTest.java new file mode 100644 index 00000000..d59d8a55 --- /dev/null +++ b/sandbox/src/test/java/org/springframework/ldap/support/ContextSourceTransactionManagerTest.java @@ -0,0 +1,127 @@ +package org.springframework.ldap.support; + +import javax.naming.directory.DirContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.ContextSource; +import org.springframework.ldap.support.ContextSourceTransactionManager.ContextSourceTransactionObject; +import org.springframework.transaction.TransactionDefinition; +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 TransactionDefinition transactionDefinitionMock; + + 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(); + + tested = new ContextSourceTransactionManager(); + tested.setContextSource(contextSourceMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextControl = null; + contextMock = null; + + contextSourceControl = null; + contextSourceMock = null; + + transactionDefinitionControl = null; + transactionDefinitionMock = null; + + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + } + + public void testDoGetTransaction() { + Object result = tested.doGetTransaction(); + + assertNotNull(result); + assertTrue(result instanceof ContextSourceTransactionObject); + ContextSourceTransactionObject transactionObject = (ContextSourceTransactionObject) result; + assertNull(transactionObject.getContextHolder()); + } + + public void testDoGetTransactionTransactionActive() { + DirContextHolder expectedContextHolder = new DirContextHolder(null); + TransactionSynchronizationManager.bindResource(contextSourceMock, + expectedContextHolder); + Object result = tested.doGetTransaction(); + + assertSame(expectedContextHolder, + ((ContextSourceTransactionObject) result).getContextHolder()); + } + + public void testDoBegin() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadOnlyContext(), contextMock); + + contextSourceControl.replay(); + + ContextSourceTransactionObject expectedTransactionObject = new ContextSourceTransactionObject( + null); + tested.doBegin(expectedTransactionObject, transactionDefinitionMock); + + contextSourceControl.verify(); + + DirContextHolder foundContextHolder = (DirContextHolder) TransactionSynchronizationManager + .getResource(contextSourceMock); + assertSame(contextMock, foundContextHolder.getCtx()); + } + + public void testDoCommit() { + } + + public void testDoRollback() { + fail("Not yet implemented"); + } + + public void testDoCleanupAfterCompletion() throws Exception { + DirContextHolder expectedContextHolder = new DirContextHolder( + contextMock); + TransactionSynchronizationManager.bindResource(contextSourceMock, + expectedContextHolder); + + contextMock.close(); + contextControl.replay(); + + tested.doCleanupAfterCompletion(new ContextSourceTransactionObject( + expectedContextHolder)); + + contextControl.verify(); + assertNull(TransactionSynchronizationManager + .getResource(contextSourceMock)); + + } +} diff --git a/spring-ldap-article/.classpath b/spring-ldap-article/.classpath new file mode 100644 index 00000000..c373756d --- /dev/null +++ b/spring-ldap-article/.classpath @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-article/.project b/spring-ldap-article/.project new file mode 100644 index 00000000..91947d81 --- /dev/null +++ b/spring-ldap-article/.project @@ -0,0 +1,23 @@ + + + spring-ldap-article + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + + diff --git a/spring-ldap-article/.settings/org.eclipse.jdt.core.prefs b/spring-ldap-article/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..c9f85173 --- /dev/null +++ b/spring-ldap-article/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,260 @@ +#Fri Apr 21 13:43:15 CEST 2006 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=20 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=36 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=36 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=36 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=36 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=20 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=36 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=1 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false +org.eclipse.jdt.core.formatter.comment.format_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=3 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=3 +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=3 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=50 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=3 +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false diff --git a/spring-ldap-article/.settings/org.eclipse.jdt.ui.prefs b/spring-ldap-article/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..0aeb6ebf --- /dev/null +++ b/spring-ldap-article/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,4 @@ +#Fri Apr 21 13:43:14 CEST 2006 +eclipse.preferences.version=1 +formatter_settings_version=10 +org.eclipse.jdt.ui.text.custom_code_templates=\r\n diff --git a/spring-ldap-article/.springBeans b/spring-ldap-article/.springBeans new file mode 100644 index 00000000..4919751a --- /dev/null +++ b/spring-ldap-article/.springBeans @@ -0,0 +1,16 @@ + + + + src/iutest/config/testContext.xml + + + + default + false + false + + src/iutest/config/testContext.xml + + + + diff --git a/spring-ldap-article/build.xml b/spring-ldap-article/build.xml new file mode 100644 index 00000000..5a85b7fa --- /dev/null +++ b/spring-ldap-article/build.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-article/docs/base_data.ldif b/spring-ldap-article/docs/base_data.ldif new file mode 100644 index 00000000..f06390db --- /dev/null +++ b/spring-ldap-article/docs/base_data.ldif @@ -0,0 +1,9 @@ +dn: dc=jayway,dc=se +objectclass: dcObject +objectclass: organization +o: jayway +dc: jayway + +dn: cn=Manager,dc=jayway,dc=se +objectclass:organizationalRole +cn: Manager diff --git a/spring-ldap-article/docs/ldap.conf.example b/spring-ldap-article/docs/ldap.conf.example new file mode 100644 index 00000000..3e7570c5 --- /dev/null +++ b/spring-ldap-article/docs/ldap.conf.example @@ -0,0 +1,21 @@ +# +# 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 "[open_ldap_path]/schema/core.schema" +#include "c:/Program Files/OpenLDAP/schema/inetorgperson.schema" +database bdb +suffix "dc=jayway,dc=se" +rootdn "cn=Manager,dc=jayway,dc=se" +rootpw secret +directory "[open_ldap_path]/data" diff --git a/spring-ldap-article/docs/readme.txt b/spring-ldap-article/docs/readme.txt new file mode 100644 index 00000000..651f41f3 --- /dev/null +++ b/spring-ldap-article/docs/readme.txt @@ -0,0 +1,52 @@ +LDAP server setup for samples. + +We need to semi-manually set up the server environment. I have used +OpenLDAP (http://www.openldap.org/) with a minimal amount of data set up from an +LDIF file. + +There is a Windows version of OpenLDAP available at: + + http://lucas.bergmans.us/hacks/openldap/ + +The LDIF file and sample application both expect to find the base suffix +"dc=jayway,dc=se" in the LDAP Server. + +For help in setting up the LDAP environment, three files are supplied: +1. ldap.conf.example + Modify paths in this file to suit your installation by replacing + the placeholder [open_ldap_path]. +2. base_data.ldif + Base domain and Admin data as specified in ldap.conf. +3. setup_data.ldif + The data expected by the integration test cases. + +Start the LDAP Server: +slapd -d 1 -f ldap.conf.example + +Add the LDIF files (the default password in ldap.conf.example is "secret"): +ldapadd -x -D "cn=Manager,dc=jayway,dc=se" -W -f base_data.ldif +ldapadd -x -D "cn=Manager,dc=jayway,dc=se" -W -f setup_data.ldif + +Verify the installation by running this search: + +ldapsearch -LLL -b dc=jayway,dc=se "(objectclass=*)" dn + +It should result in a list that looks like this: + +dn: dc=jayway,dc=se + +dn: cn=Manager,dc=jayway,dc=se + +dn: c=Sweden,dc=jayway,dc=se + +dn: c=Norway,dc=jayway,dc=se +... + +You should now be all set to run the integration tests. + +If you choose to run the tests on another instance, or specifically with +another suffix than the one mentioned above, you may need to modify data in +the following files: + +setup_data.ldif +/src/iutest/config/ldap.properties diff --git a/spring-ldap-article/docs/setup_data.ldif b/spring-ldap-article/docs/setup_data.ldif new file mode 100644 index 00000000..cdf07c46 --- /dev/null +++ b/spring-ldap-article/docs/setup_data.ldif @@ -0,0 +1,70 @@ +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 +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 +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 +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 +cn: Some Person +sn: Person +description: Sweden, Company2, Some Person +telephoneNumber: +46 555-456321 + +dn: cn=Some Person,ou=company1,c=Norway,dc=jayway,dc=se +objectclass: top +objectclass: person +cn: Some Person +sn: Person +description: Norway, Company1, Some Person +telephoneNumber: +45 555-654123 + diff --git a/spring-ldap-article/docs/teardown_data.ldif b/spring-ldap-article/docs/teardown_data.ldif new file mode 100644 index 00000000..97ba7e6b --- /dev/null +++ b/spring-ldap-article/docs/teardown_data.ldif @@ -0,0 +1,3 @@ +c=Sweden,dc=jayway,dc=se + +c=Norway,dc=jayway,dc=se diff --git a/spring-ldap-article/ivy.xml b/spring-ldap-article/ivy.xml new file mode 100644 index 00000000..1426550c --- /dev/null +++ b/spring-ldap-article/ivy.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-article/pom.xml b/spring-ldap-article/pom.xml new file mode 100644 index 00000000..470bad64 --- /dev/null +++ b/spring-ldap-article/pom.xml @@ -0,0 +1,84 @@ + + + + spring-ldap + org.springframework.ldap + 1.1.1-SNAPSHOT + + 4.0.0 + org.springframework + spring-ldap-person-article + Spring LDAP Article Source Code Sample + 1.1.1-SNAPSHOT + + Example code that matches the article published on java.net + on April 18, 2006. + + + + + + commons-lang + commons-lang + 2.1 + + + commons-collections + commons-collections + 3.1 + provided + + + javax.servlet + servlet-api + 2.4 + + + + org.springframework + spring-ldap + 1.1.1-SNAPSHOT + + + org.springframework + spring-web + 2.0 + + + org.springframework + spring-webmvc + 2.0 + + + org.springframework + spring-jdbc + 2.0 + + + org.springframework + spring-remoting + 2.0 + + + org.springframework + spring-support + 2.0 + + + + org.springframework + spring-mock + 2.0 + test + + + + + src/itest/java + + + src/itest/java + + + + diff --git a/spring-ldap-article/project.properties b/spring-ldap-article/project.properties new file mode 100644 index 00000000..5e43e378 --- /dev/null +++ b/spring-ldap-article/project.properties @@ -0,0 +1,10 @@ +# 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 + +javac.source=1.3 +javac.target=1.3 + +# extend common-build with source capability +main.build.configs=global,buildtime,test,source diff --git a/spring-ldap-article/readme.txt b/spring-ldap-article/readme.txt new file mode 100644 index 00000000..93ae89cc --- /dev/null +++ b/spring-ldap-article/readme.txt @@ -0,0 +1 @@ +For setup instructions on how to run this sample project, see docs/readme.txt. \ No newline at end of file diff --git a/spring-ldap-article/src/itest/java/config/ldap.properties b/spring-ldap-article/src/itest/java/config/ldap.properties new file mode 100644 index 00000000..ddd65ea4 --- /dev/null +++ b/spring-ldap-article/src/itest/java/config/ldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1 +userName=cn=Manager,dc=jayway,dc=se +password=secret +base=dc=jayway,dc=se diff --git a/spring-ldap-article/src/itest/java/config/testContext.xml b/spring-ldap-article/src/itest/java/config/testContext.xml new file mode 100644 index 00000000..7b54db0a --- /dev/null +++ b/spring-ldap-article/src/itest/java/config/testContext.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java b/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java new file mode 100644 index 00000000..35e2cdc7 --- /dev/null +++ b/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.article.dao; + +import java.util.List; + + +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.ldap.samples.article.dao.PersonDao; +import org.springframework.ldap.samples.article.domain.Person; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Abstract base class for PersonDao integration tests. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public abstract class AbstractPersonDaoIntegrationTest + extends + AbstractDependencyInjectionSpringContextTests { + + protected Person person; + + protected PersonDao personDao; + + protected String[] getConfigLocations() { + return new String[] { "config/testContext.xml" }; + } + + protected void onSetUp() throws Exception { + super.onSetUp(); + person = new Person(); + person.setCountry("Sweden"); + person.setCompany("company1"); + person.setFullName("Some Person"); + person.setLastName("Person"); + person + .setDescription("Sweden, Company1, Some Person"); + person.setPhone("+46 555-123456"); + } + + protected void onTearDown() throws Exception { + super.onTearDown(); + person = null; + personDao = null; + } + + /** + * Having a single test method test create, update and delete is not exactly + * the ideal way of testing, since they depend on each other. A better way + * would be to separate the tests and load a test fixture before each + * operation, in order to guarantee the expected state every time. See the + * ldaptemplate-person sample for the correct way to do this. + */ + public void testCreateUpdateDelete() { + try { + person.setFullName("Another Person"); + personDao.create(person); + personDao.findByPrimaryKey( + "Sweden", "company1", + "Another Person"); + // if we got here, create succeeded + + person + .setDescription("Another description"); + personDao.update(person); + Person result = personDao + .findByPrimaryKey( + "Sweden", "company1", + "Another Person"); + assertEquals( + "Another description", result + .getDescription()); + } finally { + personDao.delete(person); + try { + personDao.findByPrimaryKey( + "Sweden", "company1", + "Another Person"); + fail("DataRetrievalFailureException expected"); + } catch (DataRetrievalFailureException expected) { + // expected + } + } + } + + public void testGetAllPersonNames() { + List result = personDao.getAllPersonNames(); + assertEquals(5, result.size()); + String first = (String) result.get(0); + assertEquals("Some Person", first); + } + + public void testFindAll() { + List result = personDao.findAll(); + assertEquals(5, result.size()); + Person first = (Person) result.get(0); + assertEquals("Some Person", first + .getFullName()); + } + + public void testFindByPrimaryKey() { + Person result = personDao.findByPrimaryKey( + "Sweden", "company1", "Some Person"); + assertEquals(person, result); + } +} diff --git a/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java b/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java new file mode 100644 index 00000000..90a42d5e --- /dev/null +++ b/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.article.dao; + +import org.springframework.ldap.samples.article.dao.PersonDaoImpl; + +/** + * Integration tests for the PersonDaoImpl class. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PersonDaoImplIntegrationTest extends + AbstractPersonDaoIntegrationTest { + + public void setPersonDao( + PersonDaoImpl personDao) { + this.personDao = personDao; + } +} diff --git a/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java b/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java new file mode 100644 index 00000000..cded43b4 --- /dev/null +++ b/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.article.dao; + +import org.springframework.ldap.samples.article.dao.TraditionalPersonDaoImpl; + +/** + * Integration tests for the TraditionalPersonDaoImpl class. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class TraditionalPersonDaoImplIntegrationTest + extends AbstractPersonDaoIntegrationTest { + + public void setPersonDao( + TraditionalPersonDaoImpl personDao) { + this.personDao = personDao; + } +} diff --git a/spring-ldap-article/src/main/java/log4j.properties b/spring-ldap-article/src/main/java/log4j.properties new file mode 100644 index 00000000..4ab57157 --- /dev/null +++ b/spring-ldap-article/src/main/java/log4j.properties @@ -0,0 +1,19 @@ +log4j.rootCategory=INFO, 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 + +log4j.appender.logfile=org.apache.log4j.RollingFileAppender +log4j.appender.logfile.File=${ldaptemplate.root}/ldaptemplate.log +log4j.appender.logfile.MaxFileSize=512KB + +# Keep three backup files +log4j.appender.logfile.MaxBackupIndex=3 +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout + +#Pattern to output : date priority [category] - line_separator +log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n + +#Enable debug logging +#log4j.category.net.sf.ldaptemplate=DEBUG diff --git a/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java new file mode 100644 index 00000000..b4351d79 --- /dev/null +++ b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.article.dao; + +import java.util.List; + +import org.springframework.ldap.samples.article.domain.Person; + + +/** + * Data Access Object interface for the Person entity. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public interface PersonDao { + void create(Person person); + + void update(Person person); + + void delete(Person person); + + List getAllPersonNames(); + + List findAll(); + + Person findByPrimaryKey(String country, + String company, String fullname); +} diff --git a/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java new file mode 100644 index 00000000..752ec260 --- /dev/null +++ b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java @@ -0,0 +1,198 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.article.dao; + +import java.util.List; + +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.springframework.ldap.samples.article.domain.Person; + +import org.springframework.ldap.AttributesMapper; +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.ldap.support.filter.EqualsFilter; + +/** + * Default implementation of PersonDao. This implementation uses + * DirContextAdapter for managing attribute values. It has been specified in the + * Spring Context that the DirObjectFactory should be used when creating objects + * from contexts, which defaults to creating DirContextAdapter objects. This + * means that we can use a ContextMapper to map from the found contexts to our + * domain objects. This is especially useful since we in this case have + * properties in our domain objects that depend on parts of the DN. + * + * We could have worked with Attributes and an AttributesMapper implementation + * instead, but working with Attributes is a bore and also, working with + * AttributesMapper objects (or, indeed Attributes) does not give us access to + * the distinguished name. However, we do use it in one method that only needs a + * single attribute: {@link #getAllPersonNames()}. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PersonDaoImpl implements PersonDao { + + private LdapTemplate ldapTemplate; + + /* + * @see PersonDao#create(Person) + */ + public void create(Person person) { + Name dn = buildDn(person); + DirContextAdapter context = new DirContextAdapter( + dn); + mapToContext(person, context); + ldapTemplate.bind(dn, context, null); + } + + /* + * @see PersonDao#update(Person) + */ + public void update(Person person) { + Name dn = buildDn(person); + DirContextAdapter context = (DirContextAdapter) ldapTemplate + .lookup(dn); + mapToContext(person, context); + ldapTemplate.modifyAttributes(dn, context + .getModificationItems()); + } + + /* + * @see PersonDao#delete(Person) + */ + public void delete(Person person) { + ldapTemplate.unbind(buildDn(person)); + } + + /* + * @see PersonDao#getAllPersonNames() + */ + public List getAllPersonNames() { + EqualsFilter filter = new EqualsFilter( + "objectclass", "person"); + return ldapTemplate.search( + DistinguishedName.EMPTY_PATH, filter + .encode(), new AttributesMapper() { + public Object mapFromAttributes( + Attributes attrs) + throws NamingException { + return attrs.get("cn").get(); + } + }); + } + + /* + * @see PersonDao#findAll() + */ + public List findAll() { + EqualsFilter filter = new EqualsFilter( + "objectclass", "person"); + return ldapTemplate.search( + DistinguishedName.EMPTY_PATH, filter + .encode(), getContextMapper()); + } + + /* + * @see PersonDao#findByPrimaryKey(java.lang.String, java.lang.String, + * java.lang.String) + */ + public Person findByPrimaryKey(String country, + String company, String fullname) { + DistinguishedName dn = buildDn( + country, company, fullname); + return (Person) ldapTemplate.lookup( + dn, getContextMapper()); + } + + private ContextMapper getContextMapper() { + return new PersonContextMapper(); + } + + private DistinguishedName buildDn(Person person) { + return buildDn(person.getCountry(), person + .getCompany(), person.getFullName()); + } + + private DistinguishedName buildDn( + String country, String company, + String fullname) { + DistinguishedName dn = new DistinguishedName(); + dn.add("c", country); + dn.add("ou", company); + dn.add("cn", fullname); + return dn; + } + + private void mapToContext(Person person, + DirContextAdapter context) { + context.setAttributeValues( + "objectclass", new String[] { "top", + "person" }); + context.setAttributeValue("cn", person + .getFullName()); + context.setAttributeValue("sn", person + .getLastName()); + context.setAttributeValue( + "description", person.getDescription()); + context.setAttributeValue( + "telephoneNumber", person.getPhone()); + } + + /** + * Maps from DirContextAdapter to Person objects. A DN for a person will be + * of the form cn=[fullname],ou=[company],c=[country], so the + * values of these attributes must be extracted from the DN. For this, we use + * the DistinguishedName. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ + private static class PersonContextMapper + implements ContextMapper { + + public Object mapFromContext(Object ctx) { + DirContextAdapter context = (DirContextAdapter) ctx; + DistinguishedName dn = new DistinguishedName( + context.getDn()); + Person person = new Person(); + person.setCountry(dn + .getLdapRdn(0).getComponent().getValue()); + person.setCompany(dn + .getLdapRdn(1).getComponent().getValue()); + person.setFullName(context + .getStringAttribute("cn")); + person.setLastName(context + .getStringAttribute("sn")); + person.setDescription(context + .getStringAttribute("description")); + person + .setPhone(context + .getStringAttribute("telephoneNumber")); + + return person; + } + } + + public void setLdapTemplate( + LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } +} diff --git a/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java new file mode 100644 index 00000000..c9a3473e --- /dev/null +++ b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java @@ -0,0 +1,385 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.article.dao; + +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; + +import javax.naming.Context; +import javax.naming.NameNotFoundException; +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.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + + +import org.apache.commons.lang.StringUtils; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.ldap.samples.article.domain.Person; + +/** + * Traditional implementation of PersonDao. This implementation uses the basic + * JNDI interfaces and classes {@link DirContext}, {@link Attributes}, + * {@link Attribute}, and {@link NamingEnumeration}. The purpose is to + * contrast this implementation with that of {@link PersonDaoImpl}. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class TraditionalPersonDaoImpl implements + PersonDao { + + private String userName; + + private String password; + + private String url; + + private String base; + + /* + * @see PersonDao#create(Person) + */ + public void create(Person person) { + DirContext ctx = createAuthenticatedContext(); + String dn = buildDn(person); + try { + Attributes attrs = getAttributesToBind(person); + ctx.bind(dn, null, attrs); + } catch (NamingException e) { + throw new RuntimeException(e); + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + } + + /* + * @see PersonDao#update(Person) + */ + public void update(Person person) { + DirContext ctx = createAuthenticatedContext(); + String dn = buildDn(person); + try { + ctx + .rebind( + dn, null, + getAttributesToBind(person)); + } catch (NamingException e) { + throw new RuntimeException(e); + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + } + + /* + * @see PersonDao#delete(Person) + */ + public void delete(Person person) { + DirContext ctx = createAuthenticatedContext(); + String dn = buildDn(person); + try { + ctx.unbind(dn); + } catch (NamingException e) { + throw new RuntimeException(e); + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + } + + /* + * @see PersonDao#getAllPersonNames() + */ + public List getAllPersonNames() { + DirContext ctx = createAnonymousContext(); + + 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; + } + + /* + * @see PersonDao#findAll() + */ + public List findAll() { + DirContext ctx = createAnonymousContext(); + + 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(); + String dn = searchResult.getName(); + Attributes attributes = searchResult + .getAttributes(); + list.add(mapToPerson(dn, attributes)); + } + } 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. + } 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; + } + + /* + * @see PersonDao#findByPrimaryKey(java.lang.String, java.lang.String, + * java.lang.String) + */ + public Person findByPrimaryKey(String country, + String company, String fullname) { + + DirContext ctx = createAnonymousContext(); + String dn = buildDn( + country, company, fullname); + try { + Attributes attributes = ctx + .getAttributes(dn); + return mapToPerson(dn, attributes); + } catch (NameNotFoundException e) { + throw new DataRetrievalFailureException( + "Did not find entry with primary key '" + + dn + "'", e); + } catch (NamingException e) { + throw new RuntimeException(e); + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + } + + private String buildDn(Person person) { + return buildDn(person.getCountry(), person + .getCompany(), person.getFullName()); + } + + private String buildDn(String country, String company, + String fullname) { + StringBuffer sb = new StringBuffer(); + sb.append("cn="); + sb.append(fullname); + sb.append(", "); + sb.append("ou="); + sb.append(company); + sb.append(", "); + sb.append("c="); + sb.append(country); + String dn = sb.toString(); + return dn; + } + + private DirContext createContext(Hashtable env) { + env.put( + Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + String tempUrl = createUrl(); + env.put(Context.PROVIDER_URL, tempUrl); + DirContext ctx; + try { + ctx = new InitialDirContext(env); + } catch (NamingException e) { + throw new RuntimeException(e); + } + return ctx; + } + + private DirContext createAnonymousContext() { + Hashtable env = new Hashtable(); + return createContext(env); + } + + private DirContext createAuthenticatedContext() { + Hashtable env = new Hashtable(); + env.put( + Context.SECURITY_AUTHENTICATION, + "simple"); + env.put( + Context.SECURITY_PRINCIPAL, userName); + env.put( + Context.SECURITY_CREDENTIALS, password); + return createContext(env); + } + + private Attributes getAttributesToBind( + Person person) { + Attributes attrs = new BasicAttributes(); + BasicAttribute ocattr = new BasicAttribute( + "objectclass"); + ocattr.add("top"); + ocattr.add("person"); + attrs.put(ocattr); + attrs.put("cn", person.getFullName()); + attrs.put("sn", person.getLastName()); + attrs.put("description", person + .getDescription()); + attrs.put("telephoneNumber", person + .getPhone()); + return attrs; + } + + private Person mapToPerson(String dn, + 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("description").get()); + person.setPhone((String) attributes.get( + "telephoneNumber").get()); + + // Remove any trailing spaces after comma + String cleanedDn = dn + .replaceAll(", *", ","); + + String countryMarker = ",c="; + int countryIndex = cleanedDn + .lastIndexOf(countryMarker); + + String companyMarker = ",ou="; + int companyIndex = cleanedDn + .lastIndexOf(companyMarker); + + String country = cleanedDn + .substring(countryIndex + + countryMarker.length()); + person.setCountry(country); + String company = cleanedDn.substring( + companyIndex + companyMarker.length(), + countryIndex); + person.setCompany(company); + return person; + } + + private String createUrl() { + String tempUrl = url; + if (!tempUrl.endsWith("/")) { + tempUrl += "/"; + } + if (StringUtils.isNotEmpty(base)) { + tempUrl += base; + } + return tempUrl; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setBase(String base) { + this.base = base; + } + + public void setPassword(String credentials) { + this.password = credentials; + } + + public void setUserName(String principal) { + this.userName = principal; + } +} diff --git a/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/domain/Person.java b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/domain/Person.java new file mode 100644 index 00000000..e6e8b8f2 --- /dev/null +++ b/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/domain/Person.java @@ -0,0 +1,104 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.article.domain; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +/** + * Simple class representing a single person. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class Person { + private String fullName; + + private String lastName; + + private String description; + + private String country; + + private String company; + + 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 getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public boolean equals(Object obj) { + return EqualsBuilder.reflectionEquals( + this, obj); + } + + public int hashCode() { + return HashCodeBuilder + .reflectionHashCode(this); + } + + public String toString() { + return ToStringBuilder.reflectionToString( + this, ToStringStyle.MULTI_LINE_STYLE); + } +} diff --git a/spring-ldap-article/src/main/java/overview.html b/spring-ldap-article/src/main/java/overview.html new file mode 100644 index 00000000..309641cf --- /dev/null +++ b/spring-ldap-article/src/main/java/overview.html @@ -0,0 +1,3 @@ + +This document is the API specification for the Spring LDAP Article sample. + \ No newline at end of file diff --git a/spring-ldap-article/src/test/java/.dummy b/spring-ldap-article/src/test/java/.dummy new file mode 100644 index 00000000..e69de29b diff --git a/spring-ldap-person/.classpath b/spring-ldap-person/.classpath new file mode 100644 index 00000000..b4a42621 --- /dev/null +++ b/spring-ldap-person/.classpath @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-person/.project b/spring-ldap-person/.project new file mode 100644 index 00000000..0361d9a0 --- /dev/null +++ b/spring-ldap-person/.project @@ -0,0 +1,23 @@ + + + spring-ldap-person + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + + diff --git a/spring-ldap-person/.settings/org.eclipse.jdt.core.prefs b/spring-ldap-person/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..5c094199 --- /dev/null +++ b/spring-ldap-person/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,260 @@ +#Fri Apr 21 13:43:15 CEST 2006 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=1 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false +org.eclipse.jdt.core.formatter.comment.format_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=80 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false diff --git a/spring-ldap-person/.settings/org.eclipse.jdt.ui.prefs b/spring-ldap-person/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..fc573119 --- /dev/null +++ b/spring-ldap-person/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,4 @@ +#Fri Apr 21 13:43:15 CEST 2006 +eclipse.preferences.version=1 +formatter_settings_version=10 +org.eclipse.jdt.ui.text.custom_code_templates=\r\n diff --git a/spring-ldap-person/.springBeans b/spring-ldap-person/.springBeans new file mode 100644 index 00000000..01fd81fa --- /dev/null +++ b/spring-ldap-person/.springBeans @@ -0,0 +1,35 @@ + + + + xml + + + src/main/webapp/WEB-INF/flows/search-group-flow-beans.xml + src/main/webapp/WEB-INF/flows/detail-group-flow-beans.xml + src/main/webapp/WEB-INF/flows/search-flow-beans.xml + src/webapp/WEB-INF/apacheDsContext.xml + src/webapp/WEB-INF/applicationContext-acegi-security.xml + src/webapp/WEB-INF/applicationContext.xml + src/webapp/WEB-INF/ldaptemplate-servlet.xml + src/main/webapp/WEB-INF/flows/detail-flow-beans.xml + src/main/webapp/WEB-INF/ldaptemplate-webflow-config.xml + + + + default + false + false + + src/webapp/WEB-INF/apacheDsContext.xml + src/webapp/WEB-INF/applicationContext.xml + src/webapp/WEB-INF/applicationContext-acegi-security.xml + src/webapp/WEB-INF/ldaptemplate-servlet.xml + src/main/webapp/WEB-INF/flows/detail-flow-beans.xml + src/main/webapp/WEB-INF/flows/detail-group-flow-beans.xml + src/main/webapp/WEB-INF/flows/search-flow-beans.xml + src/main/webapp/WEB-INF/flows/search-group-flow-beans.xml + src/main/webapp/WEB-INF/ldaptemplate-webflow-config.xml + + + + diff --git a/spring-ldap-person/build.xml b/spring-ldap-person/build.xml new file mode 100644 index 00000000..9a8300da --- /dev/null +++ b/spring-ldap-person/build.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-person/deps.txt b/spring-ldap-person/deps.txt new file mode 100644 index 00000000..0609f36e --- /dev/null +++ b/spring-ldap-person/deps.txt @@ -0,0 +1,677 @@ +Apache Ant version 1.6.5 compiled on June 2 2005 +Buildfile: build.xml +Detected Java version: 1.5 in: c:\dev\jdk1.5.0\jre +Detected OS: Windows XP +parsing buildfile D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml with URI = file:///D:/src/open/spring-colin/spring-projects/spring-ldap/spring-ldap-person/build.xml +Project base dir set to: D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person + [property] Loading D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.properties + [property] Unable to find property file: D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.properties + [property] Loading D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\project.properties + [property] Loading D:\src\open\spring-colin\spring-projects\common-build\build.properties + [property] Unable to find property file: D:\src\open\spring-colin\spring-projects\common-build\build.properties + [property] Loading D:\src\open\spring-colin\spring-projects\common-build\project.properties + [property] Loading C:\Documents and Settings\colin\build.properties + [property] Unable to find property file: C:\Documents and Settings\colin\build.properties +Importing file D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person/../../common-build/common-targets.xml from D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml +parsing buildfile D:\src\open\spring-colin\spring-projects\common-build\common-targets.xml with URI = file:///D:/src/open/spring-colin/spring-projects/common-build/common-targets.xml +parsing buildfile jar:file:/D:/src/open/spring-colin/spring-projects/common-build/lib/ivy-1.3.1.jar!/fr/jayasoft/ivy/ant/antlib.xml with URI = jar:file:/D:/src/open/spring-colin/spring-projects/common-build/lib/ivy-1.3.1.jar!/fr/jayasoft/ivy/ant/antlib.xml +Importing file D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person/../../common-build/tomcat-targets.xml from D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml +parsing buildfile D:\src\open\spring-colin\spring-projects\common-build\tomcat-targets.xml with URI = file:///D:/src/open/spring-colin/spring-projects/common-build/tomcat-targets.xml +Importing file common-targets.xml from D:\src\open\spring-colin\spring-projects\common-build\tomcat-targets.xml +Skipped already imported file: + D:\src\open\spring-colin\spring-projects\common-build\common-targets.xml + +Build sequence for target(s) `retrieve' is [force.env.load, init.cl.external.props, init.env.external.props, init.pre, eclipse.configure, init.post, init.post2, init, check.for.optional.packages, build.prepare, ivy.configure, resolve.pre, resolve.main, resolve.post, resolve, retrieve.pre, retrieve.main, retrieve.post, retrieve] +Complete build sequence is [force.env.load, init.cl.external.props, init.env.external.props, init.pre, eclipse.configure, init.post, init.post2, init, check.for.optional.packages, build.prepare, ivy.configure, resolve.pre, resolve.main, resolve.post, resolve, retrieve.pre, retrieve.main, retrieve.post, retrieve, guard.tomcatdir, tomcat.init, tomcat-clean-war, statics.pre, statics.main, statics.test, statics.web, statics.post, statics, gen.pre, gen.main, gen.post, gen, compile.pre, compile.main, compile.source, compile.tests, compile.post, compile, jar.pre, jar.main, jar.post, jar, source-zip, web.pre, web.main, web.post, web, war.pre, war.main, war.post, war, tomcat-copy-war, tomcat-refresh-war, tomcat-launch, targets, ear.stage.post, ear.stage.pre, ear.stage.main, ear-stage, ear.pre, ear.main, ear.post, ear, publish.setup, publish.do.publish, publish, dist.pre, dist.main, dist.post, dist, guard.testclass, resolve.conf, tests.prepare, tests-local, tests, tests-dist, build.prepare.make.config.dirs, resources, guard.runclass, retrieve-to-repo, clean, clean-all, tomcat-context-error, tomcat-reload, copy.test.resources, tomcat-list, resolve.all, retrieve.all, tomcat-start, copy.resources, tomcat-undeploy, tomcat-content, test-resources, run-with-arguments, test, tomcat-deploy, tomcat-redeploy, classicvm, web.copy.classes, clear.lib.dir.before.retrieve, javadoc, tomcat-stop, web.copy.libs, run, tomcat-context, ] + +force.env.load: + [property] Loading Environment myenv. + +init.cl.external.props: +Skipped because property 'build.prop.file' not set. + +init.env.external.props: +Skipped because property 'myenv.STUB.BUILD.PROP.FILE' not set. + +init.pre: +Override ignored for property DSTAMP +Override ignored for property TSTAMP +Override ignored for property TODAY +Override ignored for property project.webapp.name + [property] Loading Environment myenv. +Override ignored for property myenv.ANT_CMD_LINE_ARGS +Override ignored for property myenv.ANT_HOME +Override ignored for property myenv.JDK_HOME +Override ignored for property myenv.COMPUTERNAME +Override ignored for property myenv.PROCESSOR_ARCHITECTURE +Override ignored for property myenv.SystemDrive +Override ignored for property myenv.PATHEXT +Override ignored for property myenv.PROMPT +Override ignored for property myenv.ALLUSERSPROFILE +Override ignored for property myenv.ComSpec +Override ignored for property myenv.PROCESSOR_IDENTIFIER +Override ignored for property myenv.LOGONSERVER +Override ignored for property myenv.TEMP +Override ignored for property myenv.NUMBER_OF_PROCESSORS +Override ignored for property myenv.PERL5LIB +Override ignored for property myenv.APPDATA +Override ignored for property myenv.CLIENTNAME +Override ignored for property myenv.ProgramFiles +Override ignored for property myenv.OS +Override ignored for property myenv.Path +Override ignored for property myenv.HummPATH +Override ignored for property myenv.SESSIONNAME +Override ignored for property myenv.BitRock +Override ignored for property myenv.WF_RESOURCES +Override ignored for property myenv.USERPROFILE +Override ignored for property myenv.windir +Override ignored for property myenv.USERNAME +Override ignored for property myenv._USE_CLASSPATH +Override ignored for property myenv.HOMEDRIVE +Override ignored for property myenv.PROCESSOR_REVISION +Override ignored for property myenv._JAVACMD +Override ignored for property myenv.CommonProgramFiles +Override ignored for property myenv.MAVEN_HOME +Override ignored for property myenv.HOMEPATH +Override ignored for property myenv.USERDOMAIN +Override ignored for property myenv.RUBYOPT +Override ignored for property myenv.TMP +Override ignored for property myenv.PROCESSOR_LEVEL +Override ignored for property myenv.SystemRoot +Override ignored for property myenv.APR_ICONV_PATH +Override ignored for property myenv.FP_NO_HOST_CHECK +Override ignored for property myenv.JAVA_HOME +Override ignored for property javac.source +Override ignored for property javac.target + [echo] org.name = "springframework.org" + [echo] org.package.prefix = "org.springframework" + [echo] project.name = "spring-ldap-person-sample" + [echo] project.webapp.name = "ldap-person" + [echo] project.title = "spring-ldap-person-sample" + [echo] project.base.version = "1.0-dev" + [echo] project.version = "1.0-dev-20060702095831" + [echo] ant.file = "D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml" + [echo] basedir = "D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person" + [echo] user.dir = "D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person" + [echo] ant.java.version = "1.5" + [echo] debug = "true" + [echo] optimize = "false" + [echo] javac.source = "1.3" + [echo] javac.target = "1.3" +Property ${target.appserver} has not been set + [echo] target.appserver = "${target.appserver}" + [echo] unit test vm: vm arg="-DzzzDummyVal" + +eclipse.configure: +Skipped because property 'eclipse.running' not set. + +init.post: +Overriding previous definition of reference to image.files +Overriding previous definition of reference to html.files +Overriding previous definition of reference to scriptlet.files +Overriding previous definition of reference to web.config.files +Override ignored for property target.lib.dir + [delete] Deleting: D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\properties.tmp + +init.post2: + +init: +Skipped because property 'init.done' set. + +check.for.optional.packages: +[available] Unable to load class org.apache.xalan.xslt.XSLTProcessorFactory to set property xalan.present + +build.prepare: +Override ignored for property DSTAMP +Override ignored for property TSTAMP +Override ignored for property TODAY +Project base dir set to: D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person + [antcall] calling target(s) [build.prepare.make.config.dirs] in build file D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml +parsing buildfile D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml with URI = file:///D:/src/open/spring-colin/spring-projects/spring-ldap/spring-ldap-person/build.xml +Project base dir set to: D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person + [property] Loading D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.properties + [property] Unable to find property file: D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.properties + [property] Loading D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\project.properties +Override ignored for property common.build.dir +Override ignored for property javac.source +Override ignored for property javac.target + [property] Loading D:\src\open\spring-colin\spring-projects\common-build\build.properties + [property] Unable to find property file: D:\src\open\spring-colin\spring-projects\common-build\build.properties + [property] Loading D:\src\open\spring-colin\spring-projects\common-build\project.properties +Override ignored for property target.testclasses.dir +Override ignored for property src.web.dir +Override ignored for property target.artifacts.dir +Override ignored for property bin.dir +Override ignored for property src.tld.dir +Override ignored for property static.lib.dir +Override ignored for property data.dir +Override ignored for property src.test.resources.dir +Override ignored for property docs.ref.src.dir +Override ignored for property src.dtd.dir +Override ignored for property target.classes.dir +Override ignored for property target.gen.java.test.dir +Override ignored for property src.resources.dir +Override ignored for property target.ear.expanded.dir +Override ignored for property target.lib.dir +Override ignored for property ivy.retrieve.pattern +Override ignored for property lib.dir +Override ignored for property release.repo.ivy.retrieve.pattern +Override ignored for property src.etc.dir +Override ignored for property packages +Override ignored for property dist.dir +Override ignored for property docs.dir +Override ignored for property release.repo.ivy.retrieve.ivy.pattern +Override ignored for property test.excludes +Override ignored for property target.gen.java.dir +Override ignored for property ivy.srcivypattern +Override ignored for property target.testresults.dir +Override ignored for property target.war.expanded.dir +Override ignored for property target.javadocs.dir +Override ignored for property docs.ref.target.dir +Override ignored for property target.dir +Override ignored for property target.artifacts.ear.dir +Override ignored for property docs.ref.dir +Override ignored for property src.java.main.dir +Override ignored for property target.artifacts.war.dir +Override ignored for property docs.ref.lib.dir +Override ignored for property src.dir +Override ignored for property src.java.test.dir +Override ignored for property ivy.distrib.dir +Override ignored for property test.includes +Override ignored for property target.filter.file +Override ignored for property src.doc.dir + [property] Loading C:\Documents and Settings\colin\build.properties + [property] Unable to find property file: C:\Documents and Settings\colin\build.properties +Override ignored for property build.web +Override ignored for property project.webapp.name +Importing file D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person/../../common-build/common-targets.xml from D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml +parsing buildfile D:\src\open\spring-colin\spring-projects\common-build\common-targets.xml with URI = file:///D:/src/open/spring-colin/spring-projects/common-build/common-targets.xml +parsing buildfile jar:file:/D:/src/open/spring-colin/spring-projects/common-build/lib/ivy-1.3.1.jar!/fr/jayasoft/ivy/ant/antlib.xml with URI = jar:file:/D:/src/open/spring-colin/spring-projects/common-build/lib/ivy-1.3.1.jar!/fr/jayasoft/ivy/ant/antlib.xml +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:configure +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:resolve +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:retrieve +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:deliver +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:publish +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:extract +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:cachepath +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:cachefileset +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:report +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:var +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:check +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:artifactproperty +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:buildlist +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:install +Trying to override old definition of task antlib:fr.jayasoft.ivy.ant:convertpom +Trying to override old definition of task resolve.conf +Trying to override old definition of task resolve.all +Trying to override old definition of task retrieve.conf +Trying to override old definition of task retrieve.all +Override ignored for property test.arguments +Importing file D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person/../../common-build/tomcat-targets.xml from D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml +parsing buildfile D:\src\open\spring-colin\spring-projects\common-build\tomcat-targets.xml with URI = file:///D:/src/open/spring-colin/spring-projects/common-build/tomcat-targets.xml +Importing file common-targets.xml from D:\src\open\spring-colin\spring-projects\common-build\tomcat-targets.xml +Skipped already imported file: + D:\src\open\spring-colin\spring-projects\common-build\common-targets.xml + +Build sequence for target(s) `build.prepare.make.config.dirs' is [build.prepare.make.config.dirs] +Complete build sequence is [build.prepare.make.config.dirs, force.env.load, init.cl.external.props, init.env.external.props, init.pre, eclipse.configure, init.post, init.post2, init, guard.tomcatdir, tomcat.init, tomcat-clean-war, check.for.optional.packages, build.prepare, ivy.configure, resolve.pre, resolve.main, resolve.post, resolve, retrieve.pre, retrieve.main, retrieve.post, retrieve, statics.pre, statics.main, statics.test, statics.web, statics.post, statics, gen.pre, gen.main, gen.post, gen, compile.pre, compile.main, compile.source, compile.tests, compile.post, compile, jar.pre, jar.main, jar.post, jar, source-zip, web.pre, web.main, web.post, web, war.pre, war.main, war.post, war, tomcat-copy-war, tomcat-refresh-war, tomcat-launch, targets, ear.stage.post, ear.stage.pre, ear.stage.main, ear-stage, ear.pre, ear.main, ear.post, ear, publish.setup, publish.do.publish, publish, dist.pre, dist.main, dist.post, dist, guard.testclass, resolve.conf, tests.prepare, tests-local, tests, tests-dist, resources, guard.runclass, retrieve-to-repo, clean, clean-all, tomcat-context-error, tomcat-reload, copy.test.resources, tomcat-list, resolve.all, retrieve.all, tomcat-start, copy.resources, tomcat-undeploy, tomcat-content, test-resources, run-with-arguments, test, tomcat-deploy, tomcat-redeploy, classicvm, web.copy.classes, clear.lib.dir.before.retrieve, javadoc, tomcat-stop, web.copy.libs, run, tomcat-context, ] + [antcall] Entering D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml... +Build sequence for target(s) `build.prepare.make.config.dirs' is [build.prepare.make.config.dirs] +Complete build sequence is [build.prepare.make.config.dirs, force.env.load, init.cl.external.props, init.env.external.props, init.pre, eclipse.configure, init.post, init.post2, init, guard.tomcatdir, tomcat.init, tomcat-clean-war, check.for.optional.packages, build.prepare, ivy.configure, resolve.pre, resolve.main, resolve.post, resolve, retrieve.pre, retrieve.main, retrieve.post, retrieve, statics.pre, statics.main, statics.test, statics.web, statics.post, statics, gen.pre, gen.main, gen.post, gen, compile.pre, compile.main, compile.source, compile.tests, compile.post, compile, jar.pre, jar.main, jar.post, jar, source-zip, web.pre, web.main, web.post, web, war.pre, war.main, war.post, war, tomcat-copy-war, tomcat-refresh-war, tomcat-launch, targets, ear.stage.post, ear.stage.pre, ear.stage.main, ear-stage, ear.pre, ear.main, ear.post, ear, publish.setup, publish.do.publish, publish, dist.pre, dist.main, dist.post, dist, guard.testclass, resolve.conf, tests.prepare, tests-local, tests, tests-dist, resources, guard.runclass, retrieve-to-repo, clean, clean-all, tomcat-context-error, tomcat-reload, copy.test.resources, tomcat-list, resolve.all, retrieve.all, tomcat-start, copy.resources, tomcat-undeploy, tomcat-content, test-resources, run-with-arguments, test, tomcat-deploy, tomcat-redeploy, classicvm, web.copy.classes, clear.lib.dir.before.retrieve, javadoc, tomcat-stop, web.copy.libs, run, tomcat-context, ] + +build.prepare.make.config.dirs: + [antcall] Exiting D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\build.xml. + [copy] Warning: Could not find file D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\src\etc\filter.properties to copy. +[pathconvert] Set property target.testresults.dir.unix = D:/src/open/spring-colin/spring-projects/spring-ldap/spring-ldap-person/target/test-reports +Property ${database.driver} has not been set +Property ${database.url} has not been set +Property ${database.schema} has not been set +Property ${database.username} has not been set +Property ${database.password} has not been set +[propertyfile] Updating property file: D:\src\open\spring-colin\spring-projects\spring-ldap\spring-ldap-person\target\filter.properties + +ivy.configure: + [echo] reading ivy config + [null] Loading jar:file:/D:/src/open/spring-colin/spring-projects/common-build/lib/ivy-1.3.1.jar!/fr/jayasoft/ivy/ant/ivy.properties +Override ignored for property ivy.distrib.dir +Override ignored for property ivy.retrieve.pattern +:: Ivy 1.3.1 - 20060330160721 :: http://ivy.jayasoft.org/ :: +jakarta commons httpclient not found: no authentication will be done +:: configuring :: file = D:\src\open\spring-colin\spring-projects\common-build\ivyconf.xml +no default ivy user dir defined: set to C:\Documents and Settings\colin\.ivy +no default cache defined: set to C:\Documents and Settings\colin\.ivy\cache +loading properties: D:\src\open\spring-colin\spring-projects\common-build/ivyconf.properties +configuration done (109ms) + default cache: C:\Documents and Settings\colin\.ivy\cache + default resolver: third-party + -- 9 resolvers: + third-party [chain] [filesystem-repo, spring-repo-ext, dotsrc-maven2-repo, ivyrep] + spring-repo [url] + integration-repo [file] + ivyrep [ivyrep] + spring-repo-snapshots [url] + filesystem-repo [file] + spring-repo-ext [url] + spring-projects [chain] [integration-repo, filesystem-repo, spring-repo, spring-repo-snapshots] + dotsrc-maven2-repo [ibiblio] + +resolve.pre: + +resolve.main: +using ivy parser to parse file:/D:/src/open/spring-colin/spring-projects/spring-ldap/spring-ldap-person/ivy.xml +:: resolving dependencies :: [ org.springframework | spring-ldap-sample | working@oak ] + confs: [global, buildtime, test] + validate = true + filesystem-repo: revision in cache: [ apache | commons-logging | 1.0.4 ] + found [ apache | commons-logging | 1.0.4 ] in third-party + filesystem-repo: revision in cache: [ apache | log4j | 1.2.9 ] + found [ apache | log4j | 1.2.9 ] in third-party + filesystem-repo: revision in cache: [ apache | oro | 2.0.8 ] + found [ apache | oro | 2.0.8 ] in third-party + filesystem-repo: revision in cache: [ apache | taglibs | 1.1.2 ] + found [ apache | taglibs | 1.1.2 ] in third-party + filesystem-repo: revision in cache: [ java | jstl | 1.1.2 ] + found [ java | jstl | 1.1.2 ] in third-party + integration-repo: found md file for [ org.springframework | spring-ldap | latest.integration ] + => D:\src\open\spring-colin\spring-projects\common-build\..\integration-repo\artifacts\org.springframework\spring-ldap\ivy-1.0.3-dev.xml (1.0.3-dev) + integration-repo: revision in cache: [ org.springframework | spring-ldap | latest.integration ] + found [ org.springframework | spring-ldap | 1.0.3-dev ] in spring-projects + [1.0.3-dev] [ org.springframework | spring-ldap | latest.integration ] + filesystem-repo: revision in cache: [ apache | commons-lang | 2.1 ] + found [ apache | commons-lang | 2.1 ] in third-party + filesystem-repo: revision in cache: [ apache | commons-collections | 3.1 ] + found [ apache | commons-collections | 3.1 ] in third-party + integration-repo: revision in cache: [ org.springframework | spring-beans | 2.0-m5 ] + found [ org.springframework | spring-beans | 2.0-m5 ] in spring-projects + integration-repo: revision in cache: [ org.springframework | spring-core | 2.0-m5 ] + found [ org.springframework | spring-core | 2.0-m5 ] in spring-projects + integration-repo: revision in cache: [ org.springframework | spring-context | 2.0-m5 ] + found [ org.springframework | spring-context | 2.0-m5 ] in spring-projects + integration-repo: revision in cache: [ org.springframework | spring-dao | 2.0-m5 ] + found [ org.springframework | spring-dao | 2.0-m5 ] in spring-projects + integration-repo: revision in cache: [ org.springframework | spring-web | 2.0-m5 ] + found [ org.springframework | spring-web | 2.0-m5 ] in spring-projects + integration-repo: revision in cache: [ org.springframework | spring-webmvc | 2.0-m5 ] + found [ org.springframework | spring-webmvc | 2.0-m5 ] in spring-projects + filesystem-repo: revision in cache: [ org.acegisecurity | acegi-security | 1.0.1 ] + found [ org.acegisecurity | acegi-security | 1.0.1 ] in third-party + integration-repo: no ivy file nor artifact found for [ org.springframework | spring-remoting | 1.2.7 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-remoting/ivy-1.2.7.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + filesystem-repo: no ivy file nor artifact found for [ org.springframework | spring-remoting | 1.2.7 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-remoting/ivy-1.2.7.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo/org/springframework/spring-remoting/ivy-1.2.7.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo/org/springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + spring-repo: no ivy file nor artifact found for [ org.springframework | spring-remoting | 1.2.7 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-remoting/ivy-1.2.7.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org/springframework/spring-remoting/ivy-1.2.7.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org/springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + spring-repo-snapshots: no ivy file nor artifact found for [ org.springframework | spring-remoting | 1.2.7 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-remoting/ivy-1.2.7.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar +WARN: module not found: [ org.springframework | spring-remoting | 1.2.7 ] +WARN: integration-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-remoting/ivy-1.2.7.xml +WARN: integration-repo: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar +WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-remoting/ivy-1.2.7.xml +WARN: filesystem-repo: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar +WARN: spring-repo: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-remoting/ivy-1.2.7.xml +WARN: spring-repo: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar +WARN: spring-repo-snapshots: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-remoting/ivy-1.2.7.xml +WARN: spring-repo-snapshots: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + integration-repo: no ivy file nor artifact found for [ org.springframework | spring-jdbc | 1.2.7 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-jdbc/ivy-1.2.7.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + filesystem-repo: no ivy file nor artifact found for [ org.springframework | spring-jdbc | 1.2.7 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-jdbc/ivy-1.2.7.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo/org/springframework/spring-jdbc/ivy-1.2.7.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo/org/springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + spring-repo: no ivy file nor artifact found for [ org.springframework | spring-jdbc | 1.2.7 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-jdbc/ivy-1.2.7.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org/springframework/spring-jdbc/ivy-1.2.7.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org/springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + spring-repo-snapshots: no ivy file nor artifact found for [ org.springframework | spring-jdbc | 1.2.7 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-jdbc/ivy-1.2.7.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar +WARN: module not found: [ org.springframework | spring-jdbc | 1.2.7 ] +WARN: integration-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-jdbc/ivy-1.2.7.xml +WARN: integration-repo: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar +WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-jdbc/ivy-1.2.7.xml +WARN: filesystem-repo: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar +WARN: spring-repo: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-jdbc/ivy-1.2.7.xml +WARN: spring-repo: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar +WARN: spring-repo-snapshots: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-jdbc/ivy-1.2.7.xml +WARN: spring-repo-snapshots: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + filesystem-repo: revision in cache: [ commons-lang | commons-lang | 2.1 ] + found [ commons-lang | commons-lang | 2.1 ] in third-party + filesystem-repo: revision in cache: [ commons-logging | commons-logging | 1.0.4 ] + found [ commons-logging | commons-logging | 1.0.4 ] in third-party + filesystem-repo: revision in cache: [ commons-lang | commons-lang | 2.0 ] + found [ commons-lang | commons-lang | 2.0 ] in third-party + filesystem-repo: revision in cache: [ commons-codec | commons-codec | 1.3 ] + found [ commons-codec | commons-codec | 1.3 ] in third-party + filesystem-repo: revision in cache: [ oro | oro | 2.0.8 ] + found [ oro | oro | 2.0.8 ] in third-party + filesystem-repo: revision in cache: [ commons-collections | commons-collections | 3.1 ] + found [ commons-collections | commons-collections | 3.1 ] in third-party + integration-repo: no ivy file nor artifact found for [ org.springframework | spring-support | 1.2.7 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-support/ivy-1.2.7.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar + filesystem-repo: no ivy file nor artifact found for [ org.springframework | spring-support | 1.2.7 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-support/ivy-1.2.7.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo/org/springframework/spring-support/ivy-1.2.7.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo/org/springframework/spring-support/1.2.7/spring-support-1.2.7.jar + spring-repo: no ivy file nor artifact found for [ org.springframework | spring-support | 1.2.7 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-support/ivy-1.2.7.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org/springframework/spring-support/ivy-1.2.7.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org/springframework/spring-support/1.2.7/spring-support-1.2.7.jar + spring-repo-snapshots: no ivy file nor artifact found for [ org.springframework | spring-support | 1.2.7 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-support/ivy-1.2.7.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar +WARN: module not found: [ org.springframework | spring-support | 1.2.7 ] +WARN: integration-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-support/ivy-1.2.7.xml +WARN: integration-repo: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar +WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-support/ivy-1.2.7.xml +WARN: filesystem-repo: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar +WARN: spring-repo: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-support/ivy-1.2.7.xml +WARN: spring-repo: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar +WARN: spring-repo-snapshots: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-support/ivy-1.2.7.xml +WARN: spring-repo-snapshots: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-core | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-core | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-core-shared | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-core-shared | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-server-jndi | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-server-jndi | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-server-main | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-server-main | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-protocol-shared | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-protocol-shared | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-protocol-changepw | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-protocol-changepw | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-protocol-kerberos | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-protocol-kerberos | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-kerberos-shared | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-kerberos-shared | 1.0-RC3 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.server | apacheds-protocol-ldap | 1.0-RC3 ] + found [ org.apache.directory.server | apacheds-protocol-ldap | 1.0-RC3 ] in third-party + filesystem-repo: no ivy file nor artifact found for [ apache | directory | 0.9.5 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/ivy-0.9.5.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/0.9.5/shared-ldap-0.9.5.jar + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/0.9.5/shared-asn1-0.9.5.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/ivy-0.9.5.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-ldap-0.9.5.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-asn1-0.9.5.jar + spring-repo-ext: no ivy file nor artifact found for [ apache | directory | 0.9.5 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/ivy-0.9.5.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-ldap-0.9.5.jar + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-asn1-0.9.5.jar +CLIENT ERROR: Not Found url=http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/directory-0.9.5.pom +CLIENT ERROR: Not Found url=http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-ldap-0.9.5.jar +CLIENT ERROR: Not Found url=http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-asn1-0.9.5.jar + dotsrc-maven2-repo: no ivy file nor artifact found for [ apache | directory | 0.9.5 ] + tried http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/ivy-0.9.5.xml + tried http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-ldap-0.9.5.jar + tried http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-asn1-0.9.5.jar +CLIENT ERROR: Not Found url=http://ivyrep.jayasoft.org/apache/directory/ivy-0.9.5.xml +CLIENT ERROR: Not Found url=http://www.ibiblio.org/maven/directory/jars/shared-ldap-0.9.5.jar +CLIENT ERROR: Not Found url=http://www.ibiblio.org/maven/directory/jars/shared-asn1-0.9.5.jar + ivyrep: no ivy file nor artifact found for [ apache | directory | 0.9.5 ] + tried http://ivyrep.jayasoft.org/apache/directory/ivy-0.9.5.xml + tried http://www.ibiblio.org/maven/directory/jars/shared-ldap-0.9.5.jar + tried http://www.ibiblio.org/maven/directory/jars/shared-asn1-0.9.5.jar +WARN: module not found: [ apache | directory | 0.9.5 ] +WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/ivy-0.9.5.xml +WARN: filesystem-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/0.9.5/shared-ldap-0.9.5.jar +WARN: filesystem-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/0.9.5/shared-asn1-0.9.5.jar +WARN: spring-repo-ext: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/ivy-0.9.5.xml +WARN: spring-repo-ext: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-asn1-0.9.5.jar +WARN: spring-repo-ext: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-ldap-0.9.5.jar +WARN: dotsrc-maven2-repo: tried http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/ivy-0.9.5.xml +WARN: dotsrc-maven2-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: +WARN: http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-asn1-0.9.5.jar +WARN: dotsrc-maven2-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: +WARN: http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-ldap-0.9.5.jar +WARN: ivyrep: tried http://ivyrep.jayasoft.org/apache/directory/ivy-0.9.5.xml +WARN: ivyrep: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: +WARN: http://www.ibiblio.org/maven/directory/jars/shared-ldap-0.9.5.jar +WARN: ivyrep: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: +WARN: http://www.ibiblio.org/maven/directory/jars/shared-asn1-0.9.5.jar + filesystem-repo: revision in cache: [ antlr | antlr | 2.7.2 ] + found [ antlr | antlr | 2.7.2 ] in third-party + filesystem-repo: revision in cache: [ jdbm | jdbm | 1.0 ] + found [ jdbm | jdbm | 1.0 ] in third-party + filesystem-repo: revision in cache: [ org.slf4j | slf4j-log4j12 | 1.0 ] + found [ org.slf4j | slf4j-log4j12 | 1.0 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.mina | mina-core | 0.9.2 ] + found [ org.apache.directory.mina | mina-core | 0.9.2 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.mina | mina-filter-codec-asn1 | 0.9.2 ] + found [ org.apache.directory.mina | mina-filter-codec-asn1 | 0.9.2 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.shared | shared-asn1 | 0.9.5 ] + found [ org.apache.directory.shared | shared-asn1 | 0.9.5 ] in third-party + filesystem-repo: revision in cache: [ org.apache.directory.mina | mina-filter-ssl | 0.9.2 ] + found [ org.apache.directory.mina | mina-filter-ssl | 0.9.2 ] in third-party + integration-repo: revision in cache: [ org.springframework | spring-jdbc | 2.0-m5 ] + found [ org.springframework | spring-jdbc | 2.0-m5 ] in spring-projects + integration-repo: revision in cache: [ org.springframework | spring-remoting | 2.0-m5 ] + found [ org.springframework | spring-remoting | 2.0-m5 ] in spring-projects + integration-repo: revision in cache: [ org.springframework | spring-support | 2.0-m5 ] + found [ org.springframework | spring-support | 2.0-m5 ] in spring-projects + filesystem-repo: revision in cache: [ javax.servlet | servlet-api | 2.4 ] + found [ javax.servlet | servlet-api | 2.4 ] in third-party + filesystem-repo: revision in cache: [ cenqua | clover | 1.3.8 ] + found [ cenqua | clover | 1.3.8 ] in third-party + filesystem-repo: no ivy file nor artifact found for [ springframework | spring-mock | 2.0-m5 ] + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/springframework/spring-mock/ivy-2.0-m5.xml + tried D:\src\open\spring-colin\spring-projects\common-build/../repository/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/ivy-2.0-m5.xml +CLIENT ERROR: Not Found url=https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar + spring-repo-ext: no ivy file nor artifact found for [ springframework | spring-mock | 2.0-m5 ] + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/ivy-2.0-m5.xml + tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar +CLIENT ERROR: Not Found url=http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.pom +CLIENT ERROR: Not Found url=http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar + dotsrc-maven2-repo: no ivy file nor artifact found for [ springframework | spring-mock | 2.0-m5 ] + tried http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/ivy-2.0-m5.xml + tried http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar +CLIENT ERROR: Not Found url=http://ivyrep.jayasoft.org/springframework/spring-mock/ivy-2.0-m5.xml +CLIENT ERROR: Not Found url=http://www.ibiblio.org/maven/spring-mock/jars/spring-mock-2.0-m5.jar + ivyrep: no ivy file nor artifact found for [ springframework | spring-mock | 2.0-m5 ] + tried http://ivyrep.jayasoft.org/springframework/spring-mock/ivy-2.0-m5.xml + tried http://www.ibiblio.org/maven/spring-mock/jars/spring-mock-2.0-m5.jar +WARN: module not found: [ springframework | spring-mock | 2.0-m5 ] +WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/springframework/spring-mock/ivy-2.0-m5.xml +WARN: filesystem-repo: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: +WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar +WARN: spring-repo-ext: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/ivy-2.0-m5.xml +WARN: spring-repo-ext: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: +WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar +WARN: dotsrc-maven2-repo: tried http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/ivy-2.0-m5.xml +WARN: dotsrc-maven2-repo: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: +WARN: http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar +WARN: ivyrep: tried http://ivyrep.jayasoft.org/springframework/spring-mock/ivy-2.0-m5.xml +WARN: ivyrep: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: +WARN: http://www.ibiblio.org/maven/spring-mock/jars/spring-mock-2.0-m5.jar + filesystem-repo: revision in cache: [ junit | junit | 3.8.1 ] + found [ junit | junit | 3.8.1 ] in third-party + filesystem-repo: revision in cache: [ easymock | easymock | 1.1 ] + found [ easymock | easymock | 1.1 ] in third-party +:: downloading artifacts :: + [NOT REQUIRED] [ apache | taglibs | 1.1.2 ]/standard.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-web | 2.0-m5 ]/spring-web.jar[jar] + [NOT REQUIRED] [ easymock | easymock | 1.1 ]/easymock.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-ldap | 1.0.3-dev ]/spring-ldap.jar[jar] + [NOT REQUIRED] [ org.slf4j | slf4j-log4j12 | 1.0 ]/slf4j-log4j12.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-protocol-ldap | 1.0-RC3 ]/apacheds-protocol-ldap.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-protocol-kerberos | 1.0-RC3 ]/apacheds-protocol-kerberos.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-protocol-changepw | 1.0-RC3 ]/apacheds-protocol-changepw.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-protocol-shared | 1.0-RC3 ]/apacheds-protocol-shared.jar[jar] + [NOT REQUIRED] [ apache | commons-collections | 3.1 ]/commons-collections.jar[jar] + [NOT REQUIRED] [ apache | commons-lang | 2.1 ]/commons-lang.jar[jar] + [NOT REQUIRED] [ apache | commons-logging | 1.0.4 ]/commons-logging.jar[jar] + [NOT REQUIRED] [ java | jstl | 1.1.2 ]/jstl.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-core | 1.0-RC3 ]/apacheds-core.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-core | 2.0-m5 ]/spring-core.jar[jar] + [NOT REQUIRED] [ cenqua | clover | 1.3.8 ]/clover.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-support | 2.0-m5 ]/spring-support.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-dao | 2.0-m5 ]/spring-dao.jar[jar] + [NOT REQUIRED] [ antlr | antlr | 2.7.2 ]/antlr.jar[jar] + [NOT REQUIRED] [ apache | log4j | 1.2.9 ]/log4j.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-remoting | 2.0-m5 ]/spring-remoting.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-jdbc | 2.0-m5 ]/spring-jdbc.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-server-jndi | 1.0-RC3 ]/apacheds-server-jndi.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-server-main | 1.0-RC3 ]/apacheds-server-main.jar[jar] + [NOT REQUIRED] [ jdbm | jdbm | 1.0 ]/jdbm.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-context | 2.0-m5 ]/spring-context.jar[jar] + [NOT REQUIRED] [ org.acegisecurity | acegi-security | 1.0.1 ]/acegi-security.jar[jar] + [NOT REQUIRED] [ commons-collections | commons-collections | 3.1 ]/commons-collections.jar[jar] + [NOT REQUIRED] [ oro | oro | 2.0.8 ]/oro.jar[jar] + [NOT REQUIRED] [ commons-codec | commons-codec | 1.3 ]/commons-codec.jar[jar] + [NOT REQUIRED] [ junit | junit | 3.8.1 ]/junit.jar[jar] + [NOT REQUIRED] [ commons-logging | commons-logging | 1.0.4 ]/commons-logging.jar[jar] + [NOT REQUIRED] [ javax.servlet | servlet-api | 2.4 ]/servlet-api.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-beans | 2.0-m5 ]/spring-beans.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-core-shared | 1.0-RC3 ]/apacheds-core-shared.jar[jar] + [NOT REQUIRED] [ org.apache.directory.server | apacheds-kerberos-shared | 1.0-RC3 ]/apacheds-kerberos-shared.jar[jar] + [NOT REQUIRED] [ org.apache.directory.mina | mina-filter-codec-asn1 | 0.9.2 ]/mina-filter-codec-asn1.jar[jar] + [NOT REQUIRED] [ org.apache.directory.shared | shared-asn1 | 0.9.5 ]/shared-asn1.jar[jar] + [NOT REQUIRED] [ commons-lang | commons-lang | 2.0 ]/commons-lang.jar[jar] + [NOT REQUIRED] [ org.apache.directory.mina | mina-filter-ssl | 0.9.2 ]/mina-filter-ssl.jar[jar] + [NOT REQUIRED] [ org.apache.directory.mina | mina-core | 0.9.2 ]/mina-core.jar[jar] + [NOT REQUIRED] [ org.springframework | spring-webmvc | 2.0-m5 ]/spring-webmvc.jar[jar] + [NOT REQUIRED] [ apache | oro | 2.0.8 ]/oro.jar[jar] + resolved ivy file produced in C:\Documents and Settings\colin\.ivy\cache\resolved-org.springframework-spring-ldap-sample-working@oak.xml +:: resolution report :: + :: evicted modules: + [ commons-lang | commons-lang | 2.1 ] by [[ commons-lang | commons-lang | 2.0 ]] in [global] + in [ org.acegisecurity | acegi-security | 1.0.1 ] with latest-revision + --------------------------------------------------------------------- + | | modules || artifacts | + | conf | number| search|dwnlded|evicted|| number|dwnlded| + --------------------------------------------------------------------- + | global | 41 | 1 | 0 | 1 || 36 | 0 | + | buildtime | 4 | 0 | 0 | 0 || 4 | 0 | + | test | 4 | 0 | 0 | 0 || 3 | 0 | + --------------------------------------------------------------------- +WARN: :::::::::::::::::::::::::::::::::::::::::::::: +WARN: :: UNRESOLVED DEPENDENCIES :: +WARN: :::::::::::::::::::::::::::::::::::::::::::::: +WARN: :: [ org.springframework | spring-support | 1.2.7 ]: not found +WARN: :: [ org.springframework | spring-jdbc | 1.2.7 ]: not found +WARN: :: [ springframework | spring-mock | 2.0-m5 ]: not found +WARN: :: [ apache | directory | 0.9.5 ]: not found +WARN: :: [ org.springframework | spring-remoting | 1.2.7 ]: not found +WARN: :::::::::::::::::::::::::::::::::::::::::::::: + + report for [ org.springframework | spring-ldap-sample | null ] global produced in C:\Documents and Settings\colin\.ivy\cache\org.springframework-spring-ldap-sample-global.xml + report for [ org.springframework | spring-ldap-sample | null ] buildtime produced in C:\Documents and Settings\colin\.ivy\cache\org.springframework-spring-ldap-sample-buildtime.xml + report for [ org.springframework | spring-ldap-sample | null ] test produced in C:\Documents and Settings\colin\.ivy\cache\org.springframework-spring-ldap-sample-test.xml + resolve done (12156ms) + +:: problems summary :: + WARN: module not found: [ org.springframework | spring-remoting | 1.2.7 ] + WARN: integration-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-remoting/ivy-1.2.7.xml + WARN: integration-repo: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-remoting/ivy-1.2.7.xml + WARN: filesystem-repo: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + WARN: spring-repo: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-remoting/ivy-1.2.7.xml + WARN: spring-repo: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + WARN: spring-repo-snapshots: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-remoting/ivy-1.2.7.xml + WARN: spring-repo-snapshots: tried artifact [ org.springframework | spring-remoting | 1.2.7 ]/spring-remoting.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-remoting/1.2.7/spring-remoting-1.2.7.jar + WARN: module not found: [ org.springframework | spring-jdbc | 1.2.7 ] + WARN: integration-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-jdbc/ivy-1.2.7.xml + WARN: integration-repo: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-jdbc/ivy-1.2.7.xml + WARN: filesystem-repo: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + WARN: spring-repo: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-jdbc/ivy-1.2.7.xml + WARN: spring-repo: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + WARN: spring-repo-snapshots: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-jdbc/ivy-1.2.7.xml + WARN: spring-repo-snapshots: tried artifact [ org.springframework | spring-jdbc | 1.2.7 ]/spring-jdbc.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-jdbc/1.2.7/spring-jdbc-1.2.7.jar + WARN: module not found: [ org.springframework | spring-support | 1.2.7 ] + WARN: integration-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-support/ivy-1.2.7.xml + WARN: integration-repo: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../integration-repo/artifacts/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar + WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-support/ivy-1.2.7.xml + WARN: filesystem-repo: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar + WARN: spring-repo: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-support/ivy-1.2.7.xml + WARN: spring-repo: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar + WARN: spring-repo-snapshots: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-support/ivy-1.2.7.xml + WARN: spring-repo-snapshots: tried artifact [ org.springframework | spring-support | 1.2.7 ]/spring-support.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-snapshots/org.springframework/spring-support/1.2.7/spring-support-1.2.7.jar + WARN: module not found: [ apache | directory | 0.9.5 ] + WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/ivy-0.9.5.xml + WARN: filesystem-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/0.9.5/shared-ldap-0.9.5.jar + WARN: filesystem-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/apache/directory/0.9.5/shared-asn1-0.9.5.jar + WARN: spring-repo-ext: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/ivy-0.9.5.xml + WARN: spring-repo-ext: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-asn1-0.9.5.jar + WARN: spring-repo-ext: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/apache/directory/0.9.5/shared-ldap-0.9.5.jar + WARN: dotsrc-maven2-repo: tried http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/ivy-0.9.5.xml + WARN: dotsrc-maven2-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: + WARN: http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-asn1-0.9.5.jar + WARN: dotsrc-maven2-repo: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: + WARN: http://mirrors.dotsrc.org/maven2/apache/directory/0.9.5/shared-ldap-0.9.5.jar + WARN: ivyrep: tried http://ivyrep.jayasoft.org/apache/directory/ivy-0.9.5.xml + WARN: ivyrep: tried artifact [ apache | directory | 0.9.5 ]/shared-ldap.jar[jar]: + WARN: http://www.ibiblio.org/maven/directory/jars/shared-ldap-0.9.5.jar + WARN: ivyrep: tried artifact [ apache | directory | 0.9.5 ]/shared-asn1.jar[jar]: + WARN: http://www.ibiblio.org/maven/directory/jars/shared-asn1-0.9.5.jar + WARN: module not found: [ springframework | spring-mock | 2.0-m5 ] + WARN: filesystem-repo: tried D:\src\open\spring-colin\spring-projects\common-build/../repository/springframework/spring-mock/ivy-2.0-m5.xml + WARN: filesystem-repo: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: + WARN: D:\src\open\spring-colin\spring-projects\common-build/../repository/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar + WARN: spring-repo-ext: tried https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/ivy-2.0-m5.xml + WARN: spring-repo-ext: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: + WARN: https://svn.sourceforge.net/svnroot/springframework/repos/repo-ext/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar + WARN: dotsrc-maven2-repo: tried http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/ivy-2.0-m5.xml + WARN: dotsrc-maven2-repo: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: + WARN: http://mirrors.dotsrc.org/maven2/springframework/spring-mock/2.0-m5/spring-mock-2.0-m5.jar + WARN: ivyrep: tried http://ivyrep.jayasoft.org/springframework/spring-mock/ivy-2.0-m5.xml + WARN: ivyrep: tried artifact [ springframework | spring-mock | 2.0-m5 ]/spring-mock.jar[jar]: + WARN: http://www.ibiblio.org/maven/spring-mock/jars/spring-mock-2.0-m5.jar + WARN: :::::::::::::::::::::::::::::::::::::::::::::: + WARN: :: UNRESOLVED DEPENDENCIES :: + WARN: :::::::::::::::::::::::::::::::::::::::::::::: + WARN: :: [ org.springframework | spring-support | 1.2.7 ]: not found + WARN: :: [ org.springframework | spring-jdbc | 1.2.7 ]: not found + WARN: :: [ springframework | spring-mock | 2.0-m5 ]: not found + WARN: :: [ apache | directory | 0.9.5 ]: not found + WARN: :: [ org.springframework | spring-remoting | 1.2.7 ]: not found + WARN: :::::::::::::::::::::::::::::::::::::::::::::: + + + --- USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS --- diff --git a/spring-ldap-person/docs/base_data.ldif b/spring-ldap-person/docs/base_data.ldif new file mode 100644 index 00000000..f06390db --- /dev/null +++ b/spring-ldap-person/docs/base_data.ldif @@ -0,0 +1,9 @@ +dn: dc=jayway,dc=se +objectclass: dcObject +objectclass: organization +o: jayway +dc: jayway + +dn: cn=Manager,dc=jayway,dc=se +objectclass:organizationalRole +cn: Manager diff --git a/spring-ldap-person/docs/ldap.conf.example b/spring-ldap-person/docs/ldap.conf.example new file mode 100644 index 00000000..bec7489f --- /dev/null +++ b/spring-ldap-person/docs/ldap.conf.example @@ -0,0 +1,25 @@ +# +# 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 Files/OpenLDAP/schema/core.schema" +include "C:/Program Files/OpenLDAP/schema/cosine.schema" +include "C:/Program Files/OpenLDAP/schema/inetorgperson.schema" +database bdb +suffix "dc=jayway,dc=se" +rootdn "cn=Manager,dc=jayway,dc=se" +rootpw secret +directory "C:/Program Files/OpenLDAP/data" +access to * + by users write + by * read diff --git a/spring-ldap-person/docs/readme.txt b/spring-ldap-person/docs/readme.txt new file mode 100644 index 00000000..73d6dd15 --- /dev/null +++ b/spring-ldap-person/docs/readme.txt @@ -0,0 +1,57 @@ +LDAP server setup for samples. + +We need to semi-manually set up the server environment. I have used +OpenLDAP (http://www.openldap.org/) with a minimal amount of data set up from an +LDIF file. + +There is a Windows version of OpenLDAP available at: + + http://lucas.bergmans.us/hacks/openldap/ + +The LDIF file and sample application both expect to find the base suffix +"dc=jayway,dc=se" in the LDAP Server. + +For help in setting up the LDAP environment, three files are supplied: +1. ldap.conf.example + Modify paths in this file to suit your installation by replacing + the placeholder [open_ldap_path]. +2. base_data.ldif + Base domain and Admin data as specified in ldap.conf. +3. setup_data.ldif + The data expected by the integration test cases. + +Start the LDAP Server: +slapd -d 1 -f ldap.conf.example + +Add the LDIF files (the default password in ldap.conf.example is "secret"): +ldapadd -x -D "cn=Manager,dc=jayway,dc=se" -W -f base_data.ldif +ldapadd -x -D "cn=Manager,dc=jayway,dc=se" -W -f setup_data.ldif + +Verify the installation by running this search: + +ldapsearch -LLL -b dc=jayway,dc=se "(objectclass=*)" dn + +It should result in a list that looks like this: + +dn: dc=jayway,dc=se + +dn: cn=Manager,dc=jayway,dc=se + +dn: c=Sweden,dc=jayway,dc=se + +dn: c=Norway,dc=jayway,dc=se +... + +You should now be all set to run the examples. If you are running Trifork T4, enter this: + +C:\src\ldaptemplate\ldaptemplate-person>trifork archive deploy -inplace -appname + ldaptemplate default trifork.map + +Open a browser and go to http://localhost:8080/ldaptemplate + +If you choose to run the tests on another instance, or specifically with +another suffix than the one mentioned above, you may need to modify data in +the following files: + +setup_data.ldif +/src/webapp/WEB-INF/ldap.properties diff --git a/spring-ldap-person/docs/setup_data.ldif b/spring-ldap-person/docs/setup_data.ldif new file mode 100644 index 00000000..68d301a5 --- /dev/null +++ b/spring-ldap-person/docs/setup_data.ldif @@ -0,0 +1,90 @@ +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: USER +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: USER +description: ADMIN +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: USER +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: USER +telephoneNumber: +46 555-456321 + +dn: cn=Some 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: USER +telephoneNumber: +45 555-456321 diff --git a/spring-ldap-person/docs/teardown_data.ldif b/spring-ldap-person/docs/teardown_data.ldif new file mode 100644 index 00000000..97ba7e6b --- /dev/null +++ b/spring-ldap-person/docs/teardown_data.ldif @@ -0,0 +1,3 @@ +c=Sweden,dc=jayway,dc=se + +c=Norway,dc=jayway,dc=se diff --git a/spring-ldap-person/ivy.xml b/spring-ldap-person/ivy.xml new file mode 100644 index 00000000..6b4d1dcd --- /dev/null +++ b/spring-ldap-person/ivy.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-person/pom.xml b/spring-ldap-person/pom.xml new file mode 100644 index 00000000..3ea94a95 --- /dev/null +++ b/spring-ldap-person/pom.xml @@ -0,0 +1,151 @@ + + + + spring-ldap + org.springframework.ldap + 1.1.1-SNAPSHOT + + 4.0.0 + org.springframework + spring-ldap-person-sample + Spring LDAP Person Sample + 1.1.1-SNAPSHOT + + Example web application that demonstrates how + Spring LDAP can be used to perform CRUD (Create,Read,Update,Delete) + operations towards an LDAP database. + + + + + + commons-lang + commons-lang + 2.1 + provided + + + javax.servlet + servlet-api + 2.4 + provided + + + + org.apache.directory.server + apacheds-core + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-core-shared + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-server-jndi + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-server-main + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-protocol-shared + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-protocol-changepw + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-protocol-kerberos + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-kerberos-shared + 1.0-RC3 + provided + + + org.apache.directory.server + apacheds-protocol-ldap + 1.0-RC3 + provided + + + org.apache.directory.shared + shared-asn1 + 0.9.5.1 + provided + + + org.apache.directory.shared + shared-ldap + 0.9.5.1 + provided + + + + org.springframework + spring-ldap + 1.1.1-SNAPSHOT + provided + + + org.springframework + spring-web + 2.0 + provided + + + org.springframework + spring-webmvc + 2.0 + provided + + + org.springframework + spring-jdbc + 2.0 + provided + + + org.springframework + spring-remoting + 2.0 + provided + + + org.springframework + spring-support + 2.0 + provided + + + + easymock + easymock + 1.2_Java1.3 + test + + + org.springframework + spring-mock + 2.0 + test + + + diff --git a/spring-ldap-person/project.properties b/spring-ldap-person/project.properties new file mode 100644 index 00000000..5e43e378 --- /dev/null +++ b/spring-ldap-person/project.properties @@ -0,0 +1,10 @@ +# 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 + +javac.source=1.3 +javac.target=1.3 + +# extend common-build with source capability +main.build.configs=global,buildtime,test,source diff --git a/spring-ldap-person/readme.txt b/spring-ldap-person/readme.txt new file mode 100644 index 00000000..92f1ac30 --- /dev/null +++ b/spring-ldap-person/readme.txt @@ -0,0 +1,3 @@ +To run this sample project, run "ant dist" and copy target/artifacts/war/ldap-person.war +to the webapps directory of your tomcat server. It runs Apache-DS directory server +setup with some initial data internally. \ No newline at end of file diff --git a/spring-ldap-person/src/itest/java/.dummy b/spring-ldap-person/src/itest/java/.dummy new file mode 100644 index 00000000..e69de29b diff --git a/spring-ldap-person/src/main/java/log4j.properties b/spring-ldap-person/src/main/java/log4j.properties new file mode 100644 index 00000000..4ab57157 --- /dev/null +++ b/spring-ldap-person/src/main/java/log4j.properties @@ -0,0 +1,19 @@ +log4j.rootCategory=INFO, 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 + +log4j.appender.logfile=org.apache.log4j.RollingFileAppender +log4j.appender.logfile.File=${ldaptemplate.root}/ldaptemplate.log +log4j.appender.logfile.MaxFileSize=512KB + +# Keep three backup files +log4j.appender.logfile.MaxBackupIndex=3 +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout + +#Pattern to output : date priority [category] - line_separator +log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n + +#Enable debug logging +#log4j.category.net.sf.ldaptemplate=DEBUG diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/ConfigEnvHelper.java b/spring-ldap-person/src/main/java/org/springframework/ldap/ConfigEnvHelper.java new file mode 100644 index 00000000..3f03a19b --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/ConfigEnvHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.File; +import java.util.Hashtable; + +import javax.naming.Context; + +import org.apache.directory.server.configuration.MutableServerStartupConfiguration; +import org.apache.directory.server.jndi.ServerContextFactory; +import org.springframework.beans.factory.InitializingBean; + +/** + * Helper class to simplify Spring configuration of ApacheDS. + * + * @author Mattias Arthursson + */ +public class ConfigEnvHelper implements InitializingBean { + private final MutableServerStartupConfiguration configuration; + + private final Hashtable initialEnv; + + private final File workingDir = new File(System + .getProperty("java.io.tmpdir") + + File.separator + "ldaptemplate_apacheds"); + + public ConfigEnvHelper(Hashtable initialEnv, + MutableServerStartupConfiguration configuration) { + + this.initialEnv = initialEnv; + this.configuration = configuration; + } + + public Hashtable getEnv() { + return initialEnv; + } + + public void afterPropertiesSet() throws Exception { + initialEnv.put(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName()); + configuration.setWorkingDirectory(workingDir); + initialEnv.putAll(configuration.toJndiEnvironment()); + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/LdapServerManager.java b/spring-ldap-person/src/main/java/org/springframework/ldap/LdapServerManager.java new file mode 100644 index 00000000..928b42d2 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/LdapServerManager.java @@ -0,0 +1,132 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.File; +import java.util.Collections; +import java.util.Properties; + +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.ContextNotEmptyException; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.springframework.ldap.ContextSource; +import org.springframework.ldap.support.DefaultDirObjectFactory; +import org.springframework.ldap.support.DistinguishedName; + +import org.apache.directory.server.core.configuration.ShutdownConfiguration; +import org.apache.directory.server.jndi.ServerContextFactory; +import org.apache.directory.server.protocol.shared.store.LdifFileLoader; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; + +/** + * Utility class to initialize the apache directory server. This means clearing + * it of all entries, and initializing it with the startup ldif data. + * + * @author Mattias Arthursson + */ +public class LdapServerManager implements InitializingBean, DisposableBean { + + private ContextSource contextSource; + + private String ldifFile; + + public void setContextSource(ContextSource contextSource) { + this.contextSource = contextSource; + } + + public void setLdifFile(String ldifFile) { + this.ldifFile = ldifFile; + } + + public void destroy() throws Exception { + Properties env = new Properties(); + env.setProperty(Context.INITIAL_CONTEXT_FACTORY, + ServerContextFactory.class.getName()); + env.setProperty(Context.SECURITY_AUTHENTICATION, "simple"); + env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); + env.setProperty(Context.SECURITY_CREDENTIALS, "secret"); + + ShutdownConfiguration configuration = new ShutdownConfiguration(); + env.putAll(configuration.toJndiEnvironment()); + + new InitialContext(env); + } + + public void afterPropertiesSet() throws Exception { + DirContext ctx = contextSource.getReadWriteContext(); + + // First of all, make sure the database is empty. + Name startingPoint = null; + + // Different test cases have different base paths. This means that the + // starting point will be different. + if (ctx.getEnvironment().get( + DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY) != null) { + startingPoint = DistinguishedName.EMPTY_PATH; + } else { + startingPoint = new DistinguishedName("dc=jayway,dc=se"); + } + + try { + clearSubContexts(ctx, startingPoint); + // Load the ldif to the recently started server + System.out.println("loading file '" + ldifFile + "'"); + LdifFileLoader loader = new LdifFileLoader(ctx, new File(ldifFile), + Collections.EMPTY_LIST, this.getClass().getClassLoader()); + loader.execute(); + } finally { + ctx.close(); + } + } + + private void clearSubContexts(DirContext ctx, Name name) + throws NamingException { + + NamingEnumeration enumeration = null; + try { + enumeration = ctx.listBindings(name); + while (enumeration.hasMore()) { + Binding element = (Binding) enumeration.next(); + DistinguishedName childName = new DistinguishedName(element + .getName()); + childName.prepend((DistinguishedName) name); + + try { + ctx.destroySubcontext(childName); + } catch (ContextNotEmptyException e) { + clearSubContexts(ctx, childName); + ctx.destroySubcontext(childName); + } + } + } catch (NamingException e) { + e.printStackTrace(); + } finally { + try { + enumeration.close(); + } catch (Exception e) { + // Never mind this + } + } + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/DummyGroupDao.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/DummyGroupDao.java new file mode 100644 index 00000000..b90811a6 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/DummyGroupDao.java @@ -0,0 +1,67 @@ +package org.springframework.ldap.samples.person.dao; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.EntryNotFoundException; +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +public class DummyGroupDao implements GroupDao { + private static Map GROUPS; + + static { + Group group1 = new Group(); + group1.setName("DUMMY_ADMIN"); + Group group2 = new Group(); + group2.setName("DUMMY_USER"); + GROUPS = new HashMap(); + GROUPS.put("DUMMY_ADMIN", group1); + GROUPS.put("DUMMY_USER", group2); + } + + Log log = LogFactory.getLog(DummyGroupDao.class); + + public void create(Group group) { + log.info("create"); + } + + public void delete(Group group) { + log.info("delete"); + } + + public List find(SearchCriteria criteria) { + log.info("find"); + if (GROUPS.get(criteria.getName()) != null) { + return Collections.singletonList(GROUPS.get(criteria.getName())); + } else { + return Collections.EMPTY_LIST; + } + } + + public List findAll() { + log.info("findAll"); + return new LinkedList(GROUPS.entrySet()); + } + + public Group findByPrimaryKey(String name) { + log.info("findByPrimaryKey"); + if (GROUPS.get(name) != null) { + return (Group) GROUPS.get(name); + } else { + throw new EntryNotFoundException("Could not find group with name '" + + name + "'"); + } + } + + public void update(Group group) { + log.info("update"); + Group actualGroup = findByPrimaryKey(group.getName()); + actualGroup.setMembers(group.getMembers()); + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/DummyPersonDao.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/DummyPersonDao.java new file mode 100644 index 00000000..f2e5613c --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/DummyPersonDao.java @@ -0,0 +1,83 @@ +package org.springframework.ldap.samples.person.dao; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.EntryNotFoundException; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +public class DummyPersonDao implements PersonDao { + private static Map PERSONS; + + static { + Person person1 = new Person(); + person1.setFullName("Dummy Person1"); + person1.setCompany("Dummy Company"); + person1.setCountry("Sweden"); + person1.setDescription(new String[] { "Some dummy person" }); + person1.setLastName("Person1"); + person1.setPhone("555-12312"); + person1.setDn("cn=Dummy Person1"); + Person person2 = new Person(); + person2.setFullName("Dummy Person2"); + person2.setCompany("Dummy Company"); + person2.setCountry("Norway"); + person2.setDescription(new String[] { "Another dummy person" }); + person2.setLastName("Person2"); + person2.setPhone("555-32132"); + person2.setDn("cn=Dummy Person2"); + PERSONS = new HashMap(); + PERSONS.put("Dummy Person1", person1); + PERSONS.put("Dummy Person2", person2); + } + + Log log = LogFactory.getLog(DummyPersonDao.class); + + public void create(Person person) { + log.info("create"); + } + + public void delete(Person person) { + log.info("delete"); + } + + public List find(SearchCriteria criteria) { + log.info("find"); + if (PERSONS.get(criteria.getName()) != null) { + return Collections.singletonList(PERSONS.get(criteria.getName())); + } else { + return Collections.EMPTY_LIST; + } + } + + public List findAll() { + log.info("findAll"); + return new LinkedList(PERSONS.entrySet()); + } + + public Person findByPrimaryKey(String country, String company, + String fullname) { + log.info("findByPrimaryKey"); + if (PERSONS.get(fullname) != null) { + return (Person) PERSONS.get(fullname); + } else { + throw new EntryNotFoundException( + "Could not find person with name '" + fullname + "'"); + } + } + + public void update(Person person) { + Person actualPerson = findByPrimaryKey(null, null, person.getFullName()); + actualPerson.setCompany(person.getCompany()); + actualPerson.setCountry(person.getCountry()); + actualPerson.setDescription(person.getDescription()); + actualPerson.setDn(person.getDn()); + actualPerson.setPhone(person.getPhone()); + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupContextMapper.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupContextMapper.java new file mode 100644 index 00000000..1c7438c8 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupContextMapper.java @@ -0,0 +1,48 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.Arrays; +import java.util.List; +import java.util.TreeSet; + +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.support.DirContextOperations; + +/** + * Maps from DirContextOperations (DirContextAdapters, really) to Group objects. + * A DN for a group will be of the form cn=[name],ou=groups + * + * @author Ulrik Sandberg + */ +public class GroupContextMapper implements ContextMapper { + + /* + * @see org.springframework.ldap.ContextMapper#mapFromContext(java.lang.Object) + */ + public Object mapFromContext(Object ctx) { + DirContextOperations dirContext = (DirContextOperations) ctx; + Group group = new Group(); + group.setName(dirContext.getStringAttribute("cn")); + String[] membersArray = dirContext.getStringAttributes("uniqueMember"); + if (membersArray != null) { + List list = Arrays.asList(membersArray); + group.setMembers(new TreeSet(list)); + } + return group; + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupDao.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupDao.java new file mode 100644 index 00000000..3e39e544 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupDao.java @@ -0,0 +1,40 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.List; + +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +/** + * Data Access Object interface for the Group entity. + * + * @author Ulrik Sandberg + */ +public interface GroupDao { + public void create(Group group); + + public void update(Group group); + + public void delete(Group group); + + public Group findByPrimaryKey(String name); + + public List findAll(); + + public List find(SearchCriteria criteria); +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupDaoImpl.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupDaoImpl.java new file mode 100644 index 00000000..8a1fca68 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/GroupDaoImpl.java @@ -0,0 +1,130 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.List; + +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.LdapOperations; +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.domain.SearchCriteria; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DirContextOperations; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.ldap.support.filter.AndFilter; +import org.springframework.ldap.support.filter.EqualsFilter; +import org.springframework.ldap.support.filter.WhitespaceWildcardsFilter; + +/** + * Default implementation of GroupDao. This implementation uses + * DirContextOperations (DirContextAdapter really, but for mock testing purposes + * we use the interface) for managing attribute values. It has been specified in + * the Spring Context that the DirObjectFactory should be used when creating + * objects from contexts, which defaults to creating DirContextAdapter objects. + * This means that we can use a ContextMapper to map from the found contexts to + * our domain objects. + * + * @author Ulrik Sandberg + */ +public class GroupDaoImpl implements GroupDao { + + /** + * The template object that performs all data access work. + */ + LdapOperations ldapOperations; + + DistinguishedName buildDn(Group group) { + DistinguishedName dn = new DistinguishedName(); + dn.add("ou", "groups"); + dn.add("cn", group.getName()); + return dn; + } + + DirContextOperations setAttributes(DirContextOperations adapter, Group group) { + adapter.setAttributeValues("objectclass", new String[] { "top", + "groupOfUniqueNames" }); + adapter.setAttributeValue("cn", group.getName()); + if (group.getMembers() != null && group.getMembers().size() > 0) { + adapter.setAttributeValues("uniqueMember", group.getMembers() + .toArray(new String[0])); + } + return adapter; + } + + ContextMapper getContextMapper() { + return new GroupContextMapper(); + } + + /* + * @see org.springframework.ldap.samples.person.dao.GroupDao#create(org.springframework.ldap.samples.person.domain.Group) + */ + public void create(Group group) { + ldapOperations.bind(buildDn(group), setAttributes( + new DirContextAdapter(), group), null); + } + + /* + * @see org.springframework.ldap.samples.person.dao.GroupDao#delete(org.springframework.ldap.samples.person.domain.Group) + */ + public void delete(Group group) { + ldapOperations.unbind(buildDn(group)); + } + + /* + * @see org.springframework.ldap.samples.person.dao.GroupDao#find(org.springframework.ldap.samples.person.domain.SearchCriteria) + */ + public List find(SearchCriteria criteria) { + AndFilter filter = new AndFilter(); + filter.and(new EqualsFilter("objectclass", "groupOfUniqueNames")); + filter.and(new WhitespaceWildcardsFilter("cn", criteria.getName())); + return ldapOperations.search(DistinguishedName.EMPTY_PATH, filter + .encode(), getContextMapper()); + } + + /* + * @see org.springframework.ldap.samples.person.dao.GroupDao#findAll() + */ + public List findAll() { + EqualsFilter filter = new EqualsFilter("objectclass", + "groupOfUniqueNames"); + return ldapOperations.search(DistinguishedName.EMPTY_PATH, filter + .encode(), getContextMapper()); + } + + /* + * @see org.springframework.ldap.samples.person.dao.GroupDao#findByPrimaryKey(java.lang.String) + */ + public Group findByPrimaryKey(String name) { + DistinguishedName dn = new DistinguishedName("ou=groups"); + dn.add("cn", name); + return (Group) ldapOperations.lookup(dn, getContextMapper()); + } + + /* + * @see org.springframework.ldap.samples.person.dao.GroupDao#update(org.springframework.ldap.samples.person.domain.Group) + */ + public void update(Group group) { + DistinguishedName dn = buildDn(group); + DirContextOperations adapter = (DirContextOperations) ldapOperations + .lookup(dn); + adapter = setAttributes(adapter, group); + ldapOperations.modifyAttributes(dn, adapter.getModificationItems()); + } + + public void setLdapOperations(LdapOperations ldapOperations) { + this.ldapOperations = ldapOperations; + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonContextMapper.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonContextMapper.java new file mode 100644 index 00000000..cd4aa00b --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonContextMapper.java @@ -0,0 +1,68 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import javax.naming.NamingException; + +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.DefaultNamingExceptionTranslator; +import org.springframework.ldap.NamingExceptionTranslator; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.support.DirContextOperations; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.util.Assert; + +/** + * Maps from DirContextOperations (DirContextAdapters, really) to Person + * objects. A DN for a person will be of the form + * cn=[fullname],ou=[company],c=[country], so the values of + * these attributes must be extracted from the DN. For this, we use the + * DistinguishedName. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PersonContextMapper implements ContextMapper { + + private NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); + + public Object mapFromContext(Object ctx) { + Assert.notNull(exceptionTranslator, "exceptionTranslator must be set"); + DirContextOperations dirContext = (DirContextOperations) ctx; + DistinguishedName dn = new DistinguishedName(dirContext.getDn()); + String fullDn; + try { + fullDn = dirContext.getNameInNamespace(); + } catch (NamingException e) { + throw exceptionTranslator.translate(e); + } + Person person = new Person(); + person.setDn(fullDn); + person.setCountry(dn.getLdapRdn(0).getComponent().getValue()); + person.setCompany(dn.getLdapRdn(1).getComponent().getValue()); + person.setFullName(dirContext.getStringAttribute("cn")); + person.setLastName(dirContext.getStringAttribute("sn")); + person.setDescription(dirContext.getStringAttributes("description")); + person.setPhone(dirContext.getStringAttribute("telephoneNumber")); + + return person; + } + + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonDao.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonDao.java new file mode 100644 index 00000000..9df1076b --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonDao.java @@ -0,0 +1,42 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.List; + +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + + +/** + * Data Access Object interface for the Person entity. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public interface PersonDao { + public void create(Person person); + + public void update(Person person); + + public void delete(Person person); + + public Person findByPrimaryKey(String country, String company, String fullname); + + public List findAll(); + + public List find(SearchCriteria criteria); +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonDaoImpl.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonDaoImpl.java new file mode 100644 index 00000000..b3dd3bad --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/PersonDaoImpl.java @@ -0,0 +1,120 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.List; + +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.LdapOperations; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DirContextOperations; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.ldap.support.filter.AndFilter; +import org.springframework.ldap.support.filter.EqualsFilter; +import org.springframework.ldap.support.filter.WhitespaceWildcardsFilter; + +/** + * Default implementation of PersonDao. This implementation uses + * DirContextOperations (DirContextAdapter really, but for mock testing purposes + * we use the interface) for managing attribute values. It has been specified in + * the Spring Context that the DirObjectFactory should be used when creating + * objects from contexts, which defaults to creating DirContextAdapter objects. + * This means that we can use a ContextMapper to map from the found contexts to + * our domain objects. This is especially useful since we in this case have + * properties in our domain objects that depend on parts of the DN. + * + * We could have worked with Attributes and an AttributesMapper implementation + * instead, but working with Attributes is a bore and also, working with + * AttributesMapper objects (or, indeed Attributes) does not give us access to + * the distinguished name. + * + * @author Mattias Arthursson + */ +public class PersonDaoImpl implements PersonDao { + + private LdapOperations ldapOperations; + + DistinguishedName buildDn(Person person) { + DistinguishedName dn = new DistinguishedName(); + dn.add("c", person.getCountry()); + dn.add("ou", person.getCompany()); + dn.add("cn", person.getFullName()); + return dn; + } + + DirContextOperations setAttributes(DirContextOperations adapter, + Person person) { + adapter.setAttributeValues("objectclass", new String[] { "top", + "person" }); + adapter.setAttributeValue("cn", person.getFullName()); + adapter.setAttributeValue("sn", person.getLastName()); + adapter.setAttributeValues("description", person.getDescription()); + adapter.setAttributeValue("telephoneNumber", person.getPhone()); + return adapter; + } + + ContextMapper getContextMapper() { + PersonContextMapper personContextMapper = new PersonContextMapper(); + return personContextMapper; + } + + public void create(Person person) { + ldapOperations.bind(buildDn(person), setAttributes( + new DirContextAdapter(), person), null); + } + + public void update(Person person) { + DirContextOperations ctx = (DirContextOperations) ldapOperations + .lookup(buildDn(person)); + ldapOperations.modifyAttributes(buildDn(person), setAttributes(ctx, + person).getModificationItems()); + } + + public void delete(Person person) { + ldapOperations.unbind(buildDn(person)); + } + + public List findAll() { + EqualsFilter filter = new EqualsFilter("objectclass", "person"); + return ldapOperations.search(DistinguishedName.EMPTY_PATH, filter + .encode(), getContextMapper()); + } + + public Person findByPrimaryKey(String country, String company, + String fullname) { + + DistinguishedName dn = new DistinguishedName(); + dn.add("c", country); + dn.add("ou", company); + dn.add("cn", fullname); + + return (Person) ldapOperations.lookup(dn, getContextMapper()); + } + + public List find(SearchCriteria criteria) { + AndFilter andFilter = new AndFilter(); + andFilter.and(new EqualsFilter("objectclass", "person")); + andFilter.and(new WhitespaceWildcardsFilter("cn", criteria.getName())); + return ldapOperations.search(DistinguishedName.EMPTY_PATH, andFilter + .encode(), getContextMapper()); + } + + public void setLdapOperations(LdapOperations ldapOperations) { + this.ldapOperations = ldapOperations; + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/TraditionalPersonDaoImpl.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/TraditionalPersonDaoImpl.java new file mode 100644 index 00000000..f26fc5a0 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/dao/TraditionalPersonDaoImpl.java @@ -0,0 +1,228 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; + +import javax.naming.Context; +import javax.naming.NameNotFoundException; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DirContextOperations; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +/** + * Traditional implementation of PersonDao. This implementation uses the basic + * JNDI interfaces and classes {@link DirContext}, {@link Attributes}, + * {@link Attribute}, and {@link NamingEnumeration}. The purpose is to + * contrast this implementation with that of + * {@link org.springframework.ldap.samples.person.dao.PersonDaoImpl}. + * + * @author Ulrik Sandberg TODO Add search method so we can use a search page + */ +public class TraditionalPersonDaoImpl implements PersonDao { + + private String url; + + private String base; + + // TODO Rewrite using traditional tools + DirContextOperations getContextToBind(Person person) { + DirContextAdapter adapter = new DirContextAdapter(); + adapter.setAttributeValue("objectclass", + new String[] { "top", "person" }); + adapter.setAttributeValue("cn", person.getFullName()); + adapter.setAttributeValue("sn", person.getLastName()); + adapter.setAttributeValue("description", person.getDescription()); + return adapter; + } + + // TODO Rewrite using traditional tools + public void create(Person person) { + // ldapOperations.bind(buildDn(person), getContextToBind(person), null); + } + + // TODO Rewrite using traditional tools + public void update(Person person) { + // ldapOperations.rebind(buildDn(person), getContextToBind(person), + // null); + } + + // TODO Rewrite using traditional tools + public void delete(Person person) { + // ldapOperations.unbind(buildDn(person)); + } + + /* + * @see org.springframework.ldap.samples.person.dao.PersonDao#findAll() + */ + public List findAll() { + DirContext ctx = createContext(); + + 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(); + String dn = searchResult.getName(); + Attributes attributes = searchResult.getAttributes(); + list.add(mapToPerson(dn, attributes)); + } + } 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. + } 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; + } + + /* + * @see org.springframework.ldap.samples.person.dao.PersonDao#findByPrimaryKey(java.lang.String, + * java.lang.String, java.lang.String) + */ + public Person findByPrimaryKey(String country, String company, + String fullname) { + + DirContext ctx = createContext(); + String dn = buildDn(country, company, fullname); + try { + Attributes attributes = ctx.getAttributes(dn); + return mapToPerson(dn, attributes); + } catch (NamingException e) { + throw new RuntimeException(e); + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + // Never mind this. + } + } + } + } + + /* + * @see org.springframework.ldap.samples.person.dao.PersonDao#find(org.springframework.ldap.samples.person.domain.SearchCriteria) + */ + public List find(SearchCriteria criteria) { + return null; + } + + private String buildDn(String country, String company, String fullname) { + StringBuffer sb = new StringBuffer(); + sb.append("cn="); + sb.append(fullname); + sb.append(","); + sb.append("ou="); + sb.append(company); + sb.append(","); + sb.append("c="); + sb.append(country); + String dn = sb.toString(); + return dn; + } + + private DirContext createContext() { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + String tempUrl = createUrl(); + env.put(Context.PROVIDER_URL, tempUrl); + DirContext ctx; + try { + ctx = new InitialDirContext(env); + } catch (NamingException e) { + throw new RuntimeException(e); + } + return ctx; + } + + private Person mapToPerson(String dn, Attributes attributes) + throws NamingException { + Person person = new Person(); + person.setFullName((String) attributes.get("cn").get()); + person.setLastName((String) attributes.get("sn").get()); + Attribute descriptionAttribute = attributes.get("description"); + String[] description = new String[descriptionAttribute.size()]; + for (int i = 0; i < description.length; i++) { + description[i] = (String) descriptionAttribute.get(i); + } + person.setDescription(description); + person.setPhone((String) attributes.get("telephoneNumber").get()); + String countryMarker = ",c="; + String country = dn.substring(dn.lastIndexOf(countryMarker) + + countryMarker.length()); + person.setCountry(country); + String companyMarker = ",ou="; + String company = dn.substring(dn.lastIndexOf(companyMarker) + + companyMarker.length(), dn.lastIndexOf(countryMarker)); + person.setCompany(company); + return person; + } + + private String createUrl() { + String tempUrl = url; + if (!tempUrl.endsWith("/")) { + tempUrl += "/"; + } + if (StringUtils.isNotEmpty(base)) { + tempUrl += base; + } + return tempUrl; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setBase(String base) { + this.base = base; + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/Group.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/Group.java new file mode 100644 index 00000000..90b7de02 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/Group.java @@ -0,0 +1,63 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.domain; + +import java.io.Serializable; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +/** + * Simple class representing an access group. + * + * @author Ulrik Sandberg + */ +public class Group implements Serializable { + private static final long serialVersionUID = -360943117484570379L; + + private String name; + + private Set members = new TreeSet(); + + public Set getMembers() { + return members; + } + + public void setMembers(Set members) { + this.members = members; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return EqualsBuilder.reflectionEquals(this, obj); + } + + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/GroupValidator.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/GroupValidator.java new file mode 100644 index 00000000..42f65b0e --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/GroupValidator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.domain; + +import org.springframework.util.StringUtils; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +/** + * Validates a Group domain object. + * + * @author Ulrik Sandberg + */ +public class GroupValidator implements Validator { + + public boolean supports(Class clazz) { + return clazz.equals(Group.class); + } + + public void validate(Object obj, Errors errors) { + Group group = (Group) obj; + if (!StringUtils.hasText(group.getName())) { + errors.reject("noName", "Please provide the group name"); + } + } +} \ No newline at end of file diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/Person.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/Person.java new file mode 100644 index 00000000..a3cb07c6 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/Person.java @@ -0,0 +1,121 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.domain; + +import java.io.Serializable; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.springframework.core.style.ToStringCreator; + +/** + * Simple class representing a single person. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class Person implements Serializable { + private static final long serialVersionUID = -8953862790009843528L; + + /** + * Needed when adding persons to groups, since groups require + * full distinguished names of their members. + */ + private String dn; + + private String fullName; + + private String lastName; + + private String[] description; + + private String country; + + private String company; + + private String phone; + + public String getDn() { + return dn; + } + + public void setDn(String dn) { + this.dn = dn; + } + + 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[] getDescription() { + return description; + } + + public void setDescription(String[] description) { + this.description = description; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return EqualsBuilder.reflectionEquals(this, obj); + } + + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + public String toString() { + return new ToStringCreator(this).toString(); + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/PersonValidator.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/PersonValidator.java new file mode 100644 index 00000000..d54a7c23 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/PersonValidator.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.domain; + +import org.springframework.util.StringUtils; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +public class PersonValidator implements Validator { + + public boolean supports(Class clazz) { + return clazz.equals(Person.class); + } + + public void validate(Object obj, Errors errors) { + Person person = (Person) obj; + if (!StringUtils.hasText(person.getFullName())) { + errors.reject("noName", "Please provide the full name"); + } + if (!StringUtils.hasText(person.getCompany())) { + errors.reject("noCompany", "Please provide the company"); + } + if (!StringUtils.hasText(person.getCountry())) { + errors.reject("noCountry", "Please provide the country"); + } + if (!StringUtils.hasText(person.getPhone())) { + errors.reject("noPhone", "Please provide the telephone number"); + } + } +} \ No newline at end of file diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/SearchCriteria.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/SearchCriteria.java new file mode 100644 index 00000000..6764d44d --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/SearchCriteria.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.domain; + +import java.io.Serializable; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.springframework.core.style.ToStringCreator; + +public class SearchCriteria implements Serializable { + + private static final long serialVersionUID = -4263208255805049113L; + + private String name = ""; + + public String getName() { + return name; + } + + public void setName(String firstName) { + this.name = firstName; + } + + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) + return false; + return EqualsBuilder.reflectionEquals(this, obj); + } + + public String toString() { + return new ToStringCreator(this).append("name", name).toString(); + } +} \ No newline at end of file diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/SearchCriteriaValidator.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/SearchCriteriaValidator.java new file mode 100644 index 00000000..d297abd2 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/domain/SearchCriteriaValidator.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.domain; + +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +/** + * Validates search chriteria. + * + * @author Ulrik Sandberg + * + */ +public class SearchCriteriaValidator implements Validator { + + /* + * (non-Javadoc) + * + * @see org.springframework.validation.Validator#supports(java.lang.Class) + */ + public boolean supports(Class clazz) { + return clazz.equals(SearchCriteria.class); + } + + /* + * (non-Javadoc) + * + * @see org.springframework.validation.Validator#validate(java.lang.Object, + * org.springframework.validation.Errors) + */ + public void validate(Object obj, Errors errors) { + // SearchCriteria query = (SearchCriteria) obj; + // if (!StringUtils.hasText(query.getName())) { + // errors.reject("noCriteria", "Please provide some query criteria!"); + // } + } +} \ No newline at end of file diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/GroupService.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/GroupService.java new file mode 100644 index 00000000..42baa070 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/GroupService.java @@ -0,0 +1,42 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.service; + +import java.util.List; +import java.util.Set; + +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +/** + * Example interface for a Group service. + * + * @author Ulrik Sandberg + */ +public interface GroupService { + + public void create(String name, Set members); + + public void update(Group group); + + public void delete(Group group); + + public Group findByPrimaryKey(String name); + + public List find(SearchCriteria criteria); + + public List findAll(); +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/GroupServiceImpl.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/GroupServiceImpl.java new file mode 100644 index 00000000..c11509ed --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/GroupServiceImpl.java @@ -0,0 +1,85 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.service; + +import java.util.List; +import java.util.Set; + +import org.springframework.ldap.samples.person.dao.GroupDao; +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +/** + * Service implementation for managing the Group entity. + * + * @author Ulrik Sandberg + */ +public class GroupServiceImpl implements GroupService { + + private GroupDao groupDao; + + public void setGroupDao(GroupDao groupDao) { + this.groupDao = groupDao; + } + + /* + * @see org.springframework.ldap.samples.person.service.GroupService#create(java.lang.String, + * java.util.Set) + */ + public void create(String name, Set members) { + + Group group = new Group(); + group.setName(name); + group.setMembers(members); + + groupDao.create(group); + } + + /* + * @see org.springframework.ldap.samples.person.service.GroupService#update(org.springframework.ldap.samples.person.domain.Group) + */ + public void update(Group group) { + groupDao.update(group); + } + + /* + * @see org.springframework.ldap.samples.person.service.GroupService#delete(org.springframework.ldap.samples.person.domain.Group) + */ + public void delete(Group group) { + groupDao.delete(group); + } + + /* + * @see org.springframework.ldap.samples.person.service.GroupService#findByPrimaryKey(java.lang.String) + */ + public Group findByPrimaryKey(String name) { + return groupDao.findByPrimaryKey(name); + } + + /* + * @see org.springframework.ldap.samples.person.service.GroupService#findAll() + */ + public List findAll() { + return groupDao.findAll(); + } + + /* + * @see org.springframework.ldap.samples.person.service.GroupService#find(org.springframework.ldap.samples.person.domain.SearchCriteria) + */ + public List find(SearchCriteria criteria) { + return groupDao.find(criteria); + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/PersonService.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/PersonService.java new file mode 100644 index 00000000..e5ed1f53 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/PersonService.java @@ -0,0 +1,43 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.service; + +import java.util.List; + +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +/** + * Example interface for a Person service. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public interface PersonService { + + public void create(String country, String company, String fullname, + String lastname, String[] description); + + public void update(Person person); + + public void delete(Person person); + + public Person findByPrimaryKey(String country, String company, String name); + + public List find(SearchCriteria criteria); + + public List findAll(); +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/PersonServiceImpl.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/PersonServiceImpl.java new file mode 100644 index 00000000..67a76147 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/service/PersonServiceImpl.java @@ -0,0 +1,88 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.service; + +import java.util.List; + +import org.springframework.ldap.samples.person.dao.PersonDao; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; + +/** + * Service implementation for managing the Person entity. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PersonServiceImpl implements PersonService { + + private PersonDao personDao; + + /* + * @see org.springframework.ldap.samples.person.service.PersonService#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) { + + Person person = new Person(); + person.setCountry(country); + person.setCompany(company); + person.setFullName(fullname); + person.setLastName(lastname); + person.setDescription(description); + + personDao.create(person); + } + + /* + * @see org.springframework.ldap.samples.person.service.PersonService#update(org.springframework.ldap.samples.person.domain.Person) + */ + public void update(Person person) { + personDao.update(person); + } + + public void delete(Person person) { + personDao.delete(person); + } + + /* + * @see org.springframework.ldap.samples.person.service.PersonService#findByPrimaryKey(java.lang.String, + * java.lang.String, java.lang.String) + */ + public Person findByPrimaryKey(String country, String company, String name) { + return personDao.findByPrimaryKey(country, company, name); + } + + /* + * @see org.springframework.ldap.samples.person.service.PersonService#findAll() + */ + public List findAll() { + return personDao.findAll(); + } + + /* + * @see org.springframework.ldap.samples.person.service.PersonService#find(org.springframework.ldap.samples.person.domain.SearchCriteria) + */ + public List find(SearchCriteria criteria) { + return personDao.find(criteria); + } + + public void setPersonDao(PersonDao personDao) { + this.personDao = personDao; + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/web/EditGroupFormAction.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/web/EditGroupFormAction.java new file mode 100644 index 00000000..3cf82aa5 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/web/EditGroupFormAction.java @@ -0,0 +1,103 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.web; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.commons.collections.CollectionUtils; +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.service.GroupService; +import org.springframework.util.Assert; +import org.springframework.webflow.action.FormAction; +import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.RequestContext; + +/** + * Action implementation that handles edit of a group. This action has the + * capability to add and remove members of the current Group form object. + * + * @author Ulrik Sandberg + */ +public class EditGroupFormAction extends FormAction { + + private GroupService groupService; + + public void setGroupService(GroupService groupService) { + this.groupService = groupService; + } + + /* + * @see org.springframework.webflow.action.FormAction#initAction() + */ + protected void initAction() { + super.initAction(); + Assert.notNull(groupService, "A GroupService object is required"); + } + + /* + * @see org.springframework.webflow.action.FormAction#createFormObject(org.springframework.webflow.execution.RequestContext) + */ + protected Object createFormObject(RequestContext context) throws Exception { + String name = context.getFlowScope().getRequiredString("name"); + Group group = groupService.findByPrimaryKey(name); + return group; + } + + /** + * Add the member in the member property to the form object + * members property. + * + * @param context + * RequestContext with parameters and attributes. + * @return The success event if member was added successfully + * @throws Exception + * if an unexpected error occurs + */ + public Event add(RequestContext context) throws Exception { + Group group = (Group) getFormObject(context); + Set members = group.getMembers(); + String member = (String) context.getRequestParameters().get("member"); + members.add(member); + return success(); + } + + /** + * Removes the members in the selectedMembers property from + * the form object members property. + * + * @param context + * RequestContext with parameters and attributes. + * @return The success event if members were removed + * successfully + * @throws Exception + * if an unexpected error occurs + */ + public Event remove(RequestContext context) throws Exception { + Group group = (Group) getFormObject(context); + Set members = group.getMembers(); + String[] selectedMembers = (String[]) context.getRequestParameters() + .getArray("selectedMembers"); + List membersToRemove = Arrays.asList(selectedMembers); + Collection adjustedMembers = CollectionUtils.subtract(members, + membersToRemove); + group.setMembers(new TreeSet(adjustedMembers)); + return success(); + } +} diff --git a/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/web/EditPersonFormAction.java b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/web/EditPersonFormAction.java new file mode 100644 index 00000000..027bbbf3 --- /dev/null +++ b/spring-ldap-person/src/main/java/org/springframework/ldap/samples/person/web/EditPersonFormAction.java @@ -0,0 +1,44 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap.samples.person.web; + +import org.springframework.beans.PropertyEditorRegistry; +import org.springframework.beans.propertyeditors.StringArrayPropertyEditor; +import org.springframework.webflow.action.FormAction; +import org.springframework.webflow.execution.RequestContext; + +/** + * Action implementation that handles edit of a person. Registers appropriate + * property editors. + * + * @author Mattias Arthursson + * + */ +public class EditPersonFormAction extends FormAction { + + /* + * (non-Javadoc) + * + * @see org.springframework.webflow.action.FormAction#registerPropertyEditors(org.springframework.webflow.execution.RequestContext, + * org.springframework.beans.PropertyEditorRegistry) + */ + protected void registerPropertyEditors(RequestContext context, + PropertyEditorRegistry registry) { + registry.registerCustomEditor(String[].class, + new StringArrayPropertyEditor()); + } +} diff --git a/spring-ldap-person/src/main/java/overview.html b/spring-ldap-person/src/main/java/overview.html new file mode 100644 index 00000000..7d7ac9a3 --- /dev/null +++ b/spring-ldap-person/src/main/java/overview.html @@ -0,0 +1,3 @@ + +This document is the API specification for the Spring LDAP Person sample. + \ No newline at end of file diff --git a/spring-ldap-person/src/main/java/setup_data.ldif b/spring-ldap-person/src/main/java/setup_data.ldif new file mode 100644 index 00000000..08343e68 --- /dev/null +++ b/spring-ldap-person/src/main/java/setup_data.ldif @@ -0,0 +1,111 @@ +dn: ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: cn=USER,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: 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=ADMIN,ou=groups,dc=jayway,dc=se +objectclass: top +objectclass: groupOfUniqueNames +cn: 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: USER +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: USER +description: ADMIN +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: USER +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: USER +telephoneNumber: +46 555-456321 + +dn: cn=Some 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: USER +telephoneNumber: +45 555-654123 diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/apacheDsContext.xml b/spring-ldap-person/src/main/webapp/WEB-INF/apacheDsContext.xml new file mode 100644 index 00000000..17306fb2 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/apacheDsContext.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + simple + + + ${userName} + + + ${password} + + + + + + + + + + + + + + + + + + + + objectClass: top + objectClass: domain + objectClass: extensibleObject + dc: jayway + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/applicationContext-acegi-security.xml b/spring-ldap-person/src/main/webapp/WEB-INF/applicationContext-acegi-security.xml new file mode 100644 index 00000000..e00e9996 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/applicationContext-acegi-security.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT + /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT + /ldaptemplate.htm*=ROLE_USER + + + + + diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/applicationContext.xml b/spring-ldap-person/src/main/webapp/WEB-INF/applicationContext.xml new file mode 100644 index 00000000..0554dc73 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/applicationContext.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/classes/log4j.properties b/spring-ldap-person/src/main/webapp/WEB-INF/classes/log4j.properties new file mode 100644 index 00000000..6724c59a --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/classes/log4j.properties @@ -0,0 +1,21 @@ +log4j.rootCategory=WARN, stdout, logfile + +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 + +log4j.appender.logfile=org.apache.log4j.RollingFileAppender +log4j.appender.logfile.File=${@PROJECT_WEBAPP_NAME@.root}/@PROJECT_WEBAPP_NAME@.log +log4j.appender.logfile.MaxFileSize=512KB + +# Keep three backup files +log4j.appender.logfile.MaxBackupIndex=3 +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout + +#Pattern to output : date priority [category] - line_separator +log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n + +#Enable webflow debug logging +log4j.category.org.springframework.webflow=INFO +log4j.category.org.springframework.binding=INFO +log4j.category.org.springframework.ldap=INFO diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-flow-beans.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-flow-beans.xml new file mode 100644 index 00000000..1349b02c --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-flow-beans.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-flow.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-flow.xml new file mode 100644 index 00000000..02ccab46 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-flow.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-group-flow-beans.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-group-flow-beans.xml new file mode 100644 index 00000000..fee7209b --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-group-flow-beans.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-group-flow.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-group-flow.xml new file mode 100644 index 00000000..9e047e27 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/detail-group-flow.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/personmanager-flow.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/personmanager-flow.xml new file mode 100644 index 00000000..ab1c9d69 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/personmanager-flow.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-flow-beans.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-flow-beans.xml new file mode 100644 index 00000000..17f68c64 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-flow-beans.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-flow.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-flow.xml new file mode 100644 index 00000000..32dda2c7 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-flow.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-group-flow-beans.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-group-flow-beans.xml new file mode 100644 index 00000000..e03b74ab --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-group-flow-beans.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-group-flow.xml b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-group-flow.xml new file mode 100644 index 00000000..eb8f83c2 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/flows/search-group-flow.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/editDetails.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/editDetails.jsp new file mode 100644 index 00000000..2da82db7 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/editDetails.jsp @@ -0,0 +1,53 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Edit Person Details

+
Please check form for invalid input
+
Name
Company
Country
Phone
Description
+ + + +
+
+
+ +<%@ include file="includeBottom.jsp" %> diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/editGroupDetails.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/editGroupDetails.jsp new file mode 100644 index 00000000..9747de4b --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/editGroupDetails.jsp @@ -0,0 +1,65 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Edit Group Details

+
Please check form for invalid input
+
Name
+
+ Members: +
+ +
+ + + + + + + +
+ +
+ +
+
+ + + +
+
+
+ +<%@ include file="includeBottom.jsp" %> diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/error.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/error.jsp new file mode 100644 index 00000000..0cb50f54 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/error.jsp @@ -0,0 +1,18 @@ +<%@ page session="false" %> + + + + + + +
Error
+
+
+

+ An error has occured! +

+
+
+
+ + diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/includeBottom.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/includeBottom.jsp new file mode 100644 index 00000000..3bb24aba --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/includeBottom.jsp @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/includeTop.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/includeTop.jsp new file mode 100644 index 00000000..4500d28d --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/includeTop.jsp @@ -0,0 +1,21 @@ +<%@ page contentType="text/html" %> +<%@ page session="false" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> + + + +Search the Phonebook + + + + + + + + diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/menu.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/menu.jsp new file mode 100644 index 00000000..c3ae59fa --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/menu.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + +<%@ include file="includeTop.jsp" %> + +
+
+ +
+ + + + + + ">Find and manage persons +
+ + + + + ">Find and manage groups +
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchForm.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchForm.jsp new file mode 100644 index 00000000..bc11a06d --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchForm.jsp @@ -0,0 +1,46 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
Person Search
+
+
+
Please provide valid search criteria
+
Name + +
+
+
+ + + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchGroupForm.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchGroupForm.jsp new file mode 100644 index 00000000..90d61ba2 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchGroupForm.jsp @@ -0,0 +1,46 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
Group Search
+
+
+
Please provide valid search criteria
+
Name + +
+
+
+ + + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchGroupResult.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchGroupResult.jsp new file mode 100644 index 00000000..c4558287 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchGroupResult.jsp @@ -0,0 +1,52 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+
+ + + + + + + + + + + + + +
+ Matching Groups +
+
+
+ + + + + + + + + +
Name
+ + + + + + + ${group.name} + +
+
+ + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchResult.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchResult.jsp new file mode 100644 index 00000000..f6efc9ef --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/searchResult.jsp @@ -0,0 +1,60 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+
+ + + + + + + + + + + + + +
+ Matching Persons +
+
+
+ + + + + + + + + + + + + + + +
NameCompanyCountryPhone
+ + + + + + + + + ${person.fullName} + + ${person.company}${person.country}${person.phone}
+
+ + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showAddCandidates.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showAddCandidates.jsp new file mode 100644 index 00000000..58f6f3f3 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showAddCandidates.jsp @@ -0,0 +1,58 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+
+ + + + + + + + + + + + + +
+ Select a person to add +
+
+
+ + + + + + + + + + + + + + + +
NameCompanyCountryPhone
+ + + + + + + ${person.fullName} + + ${person.company}${person.country}${person.phone}
+
+ + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showDetails.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showDetails.jsp new file mode 100644 index 00000000..91a772ad --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showDetails.jsp @@ -0,0 +1,50 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Person Details

Name${person.fullName}
Company${person.company}
Country${person.country}
Phone${person.phone}
Description + + ${description}
+
+
+ + + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showGroupDetails.jsp b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showGroupDetails.jsp new file mode 100644 index 00000000..5be81776 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/jsp/showGroupDetails.jsp @@ -0,0 +1,40 @@ +<%@ include file="includeTop.jsp" %> + +
+
+ +
+
+ + + + + + + + + + + + + + + + + +
Group Details

Name${group.name}
+
+ Members: +
+ + ${item}
+
+
+ + + +
+
+
+ +<%@ include file="includeBottom.jsp" %> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/ldap.properties b/spring-ldap-person/src/main/webapp/WEB-INF/ldap.properties new file mode 100644 index 00000000..eff9c99f --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/ldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1 +userName=uid=admin,ou=system +password=secret +base=dc=jayway,dc=se diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/ldaptemplate-servlet.xml b/spring-ldap-person/src/main/webapp/WEB-INF/ldaptemplate-servlet.xml new file mode 100644 index 00000000..6c63c297 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/ldaptemplate-servlet.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/ldaptemplate-webflow-config.xml b/spring-ldap-person/src/main/webapp/WEB-INF/ldaptemplate-webflow-config.xml new file mode 100644 index 00000000..865daaf4 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/ldaptemplate-webflow-config.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/WEB-INF/web.xml b/spring-ldap-person/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..3c3ccb4d --- /dev/null +++ b/spring-ldap-person/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,50 @@ + + + + LdapTemplate Example + + contextConfigLocation + /WEB-INF/applicationContext.xml /WEB-INF/applicationContext-acegi-security.xml + + + + Acegi Filter Chain Proxy + org.acegisecurity.util.FilterToBeanProxy + + targetClass + org.acegisecurity.util.FilterChainProxy + + + + + Acegi Filter Chain Proxy + /* + + + + org.springframework.web.context.ContextLoaderListener + + + ldaptemplate + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + + /WEB-INF/ldaptemplate-servlet.xml + /WEB-INF/ldaptemplate-webflow-config.xml + + + 1 + + + ldaptemplate + *.htm + + + index.jsp + + \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/acegilogin.jsp b/spring-ldap-person/src/main/webapp/acegilogin.jsp new file mode 100644 index 00000000..e74d9848 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/acegilogin.jsp @@ -0,0 +1,52 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page import="org.acegisecurity.ui.AbstractProcessingFilter" %> +<%@ page import="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter" %> +<%@ page import="org.acegisecurity.AuthenticationException" %> + + + + Login + + + +

Login

+ +

Valid users: +

+

username some.person (USER), password password
+ username some.person2 (ADMIN), password password +

+ + <%-- this form-login-page form is also used as the + form-error-page to ask for a login again. + --%> + + + Your login attempt was not successful, try again.
+ Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %> +
+
+ +

+ + + + + + +
User: + + + + + + + + +
Password:
  +
+ +
+ + + diff --git a/spring-ldap-person/src/main/webapp/images/spring-logo.jpg b/spring-ldap-person/src/main/webapp/images/spring-logo.jpg new file mode 100644 index 00000000..62be3983 Binary files /dev/null and b/spring-ldap-person/src/main/webapp/images/spring-logo.jpg differ diff --git a/spring-ldap-person/src/main/webapp/images/webflow-logo.jpg b/spring-ldap-person/src/main/webapp/images/webflow-logo.jpg new file mode 100644 index 00000000..ed76bae0 Binary files /dev/null and b/spring-ldap-person/src/main/webapp/images/webflow-logo.jpg differ diff --git a/spring-ldap-person/src/main/webapp/index.jsp b/spring-ldap-person/src/main/webapp/index.jsp new file mode 100644 index 00000000..d8cc2b73 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/index.jsp @@ -0,0 +1,21 @@ +<%@ page session="true" %> <%-- make sure we have a session --%> + + + + + + +
PersonManager - A Spring LDAP Sample
+
+
+

+ Spring LDAP PersonManager +

+

+ This sample application illustrates the core features of Spring LDAP. +

+
+
+
+ + diff --git a/spring-ldap-person/src/main/webapp/logoff.jsp b/spring-ldap-person/src/main/webapp/logoff.jsp new file mode 100644 index 00000000..0231bcb9 --- /dev/null +++ b/spring-ldap-person/src/main/webapp/logoff.jsp @@ -0,0 +1,4 @@ +<% +session.invalidate(); +response.sendRedirect("index.jsp"); +%> \ No newline at end of file diff --git a/spring-ldap-person/src/main/webapp/style.css b/spring-ldap-person/src/main/webapp/style.css new file mode 100644 index 00000000..edc758ab --- /dev/null +++ b/spring-ldap-person/src/main/webapp/style.css @@ -0,0 +1,58 @@ +body { + width: 720px; + margin: 0px; + padding: 0px; +} + +div#logo { + width: 720px; + height: 73px; + background: #86AEA5; +} + +div#navigation { + width: 720px; + height: 15px; + background: #E2F3B8; + text-align: right; +} + +div#content { + width: 720px; + padding: 5px; +} + +div#insert { + width: 120; + float: right; + text-align: right; +} + +.buttonBar { + height: 1.5em; + text-align: right; +} + +div#copyright { + width: 720px; +} + +div#copyright p { + text-align: center; + font-family: Tahoma, sans-serif; + font-size: 75%; + color: div#336633; + margin-left: 5px; + font-weight: bold; + clear: both; +} + +.readOnly { + color: rgb(192, 192, 192); +} + +.error { + color: red; + font-weight: bold; + font-family: Arial, sans-serif; +} \ No newline at end of file diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/dao/GroupDaoImplTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/dao/GroupDaoImplTest.java new file mode 100644 index 00000000..b1f2dfb4 --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/dao/GroupDaoImplTest.java @@ -0,0 +1,216 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.Collections; +import java.util.List; + +import javax.naming.directory.ModificationItem; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.LdapOperations; +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.domain.SearchCriteria; +import org.springframework.ldap.support.DirContextOperations; +import org.springframework.ldap.support.DistinguishedName; + +/** + * Unit tests for the GroupDaoImpl class. + * + * @author Ulrik Sandberg + */ +public class GroupDaoImplTest extends TestCase { + + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + private MockControl dirContextOperationsControl; + + private DirContextOperations dirContextOperationsMock; + + private MockControl contextMapperControl; + + private ContextMapper contextMapperMock; + + private GroupDaoImpl tested; + + private Group group; + + protected void setUp() throws Exception { + super.setUp(); + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + dirContextOperationsControl = MockControl + .createControl(DirContextOperations.class); + dirContextOperationsMock = (DirContextOperations) dirContextOperationsControl + .getMock(); + + contextMapperControl = MockControl.createControl(ContextMapper.class); + contextMapperMock = (ContextMapper) contextMapperControl.getMock(); + + group = new Group(); + + tested = new GroupDaoImpl() { + DirContextOperations setAttributes(DirContextOperations adapter, + Group p) { + assertSame(group, p); + return dirContextOperationsMock; + } + + DistinguishedName buildDn(Group p) { + assertSame(group, p); + return DistinguishedName.EMPTY_PATH; + } + + ContextMapper getContextMapper() { + return contextMapperMock; + } + }; + tested.setLdapOperations(ldapOperationsMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + ldapOperationsControl = null; + ldapOperationsMock = null; + + dirContextOperationsControl = null; + dirContextOperationsMock = null; + + contextMapperControl = null; + contextMapperMock = null; + + group = null; + tested = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + dirContextOperationsControl.replay(); + contextMapperControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + dirContextOperationsControl.verify(); + contextMapperControl.verify(); + } + + public void testBuildDn() { + tested = new GroupDaoImpl(); + Group group = new Group(); + group.setName("Some Group"); + + DistinguishedName dn = tested.buildDn(group); + + assertEquals("cn=Some Group, ou=groups", dn.toString()); + } + + public void testCreate() { + ldapOperationsMock.bind(DistinguishedName.EMPTY_PATH, + dirContextOperationsMock, null); + + replay(); + + tested.create(group); + + verify(); + } + + public void testUpdate() { + ldapOperationsControl + .expectAndReturn(ldapOperationsMock + .lookup(DistinguishedName.EMPTY_PATH), + dirContextOperationsMock); + ModificationItem[] modificationItems = new ModificationItem[0]; + dirContextOperationsControl.expectAndReturn(dirContextOperationsMock + .getModificationItems(), modificationItems); + ldapOperationsMock.modifyAttributes(DistinguishedName.EMPTY_PATH, + modificationItems); + + replay(); + + tested.update(group); + + verify(); + + } + + public void testDelete() { + ldapOperationsMock.unbind(DistinguishedName.EMPTY_PATH); + + replay(); + + tested.delete(group); + + verify(); + } + + public void testFindAll() { + List expectedList = Collections.singletonList(null); + ldapOperationsControl.expectAndReturn(ldapOperationsMock.search( + DistinguishedName.EMPTY_PATH, "(objectclass=groupOfUniqueNames)", + contextMapperMock), expectedList); + + replay(); + + List result = tested.findAll(); + + verify(); + + assertSame(expectedList, result); + } + + public void testFindByPrimaryKey() { + DistinguishedName dn = new DistinguishedName( + "cn=Some Group, ou=groups"); + + ldapOperationsControl.expectAndReturn(ldapOperationsMock.lookup(dn, + contextMapperMock), group); + + replay(); + + Group result = tested.findByPrimaryKey("Some Group"); + + verify(); + + assertSame(group, result); + } + + public void testFind_Name() { + List expectedList = Collections.singletonList(null); + ldapOperationsControl.expectAndReturn(ldapOperationsMock.search( + DistinguishedName.EMPTY_PATH, + "(&(objectclass=groupOfUniqueNames)(cn=*some*))", contextMapperMock), + expectedList); + + replay(); + + SearchCriteria criteria = new SearchCriteria(); + criteria.setName("some"); + List result = tested.find(criteria); + + verify(); + + assertSame(expectedList, result); + } +} diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/dao/PersonDaoImplTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/dao/PersonDaoImplTest.java new file mode 100644 index 00000000..07d89105 --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/dao/PersonDaoImplTest.java @@ -0,0 +1,221 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.dao; + +import java.util.Collections; +import java.util.List; + +import javax.naming.directory.ModificationItem; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.LdapOperations; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; +import org.springframework.ldap.support.DirContextOperations; +import org.springframework.ldap.support.DistinguishedName; + +/** + * Unit tests for the PersonDaoImpl class. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PersonDaoImplTest extends TestCase { + + private MockControl ldapOperationsControl; + + private LdapOperations ldapOperationsMock; + + private MockControl dirContextOperationsControl; + + private DirContextOperations dirContextOperationsMock; + + private MockControl contextMapperControl; + + private ContextMapper contextMapperMock; + + private PersonDaoImpl tested; + + private Person person; + + protected void setUp() throws Exception { + super.setUp(); + ldapOperationsControl = MockControl.createControl(LdapOperations.class); + ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock(); + + dirContextOperationsControl = MockControl + .createControl(DirContextOperations.class); + dirContextOperationsMock = (DirContextOperations) dirContextOperationsControl + .getMock(); + + contextMapperControl = MockControl.createControl(ContextMapper.class); + contextMapperMock = (ContextMapper) contextMapperControl.getMock(); + + person = new Person(); + + tested = new PersonDaoImpl() { + DirContextOperations setAttributes(DirContextOperations adapter, + Person p) { + assertSame(person, p); + return dirContextOperationsMock; + } + + DistinguishedName buildDn(Person p) { + assertSame(person, p); + return DistinguishedName.EMPTY_PATH; + } + + ContextMapper getContextMapper() { + return contextMapperMock; + } + }; + tested.setLdapOperations(ldapOperationsMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + ldapOperationsControl = null; + ldapOperationsMock = null; + + dirContextOperationsControl = null; + dirContextOperationsMock = null; + + contextMapperControl = null; + contextMapperMock = null; + + person = null; + tested = null; + } + + protected void replay() { + ldapOperationsControl.replay(); + dirContextOperationsControl.replay(); + contextMapperControl.replay(); + } + + protected void verify() { + ldapOperationsControl.verify(); + dirContextOperationsControl.verify(); + contextMapperControl.verify(); + } + + public void testBuildDn() { + tested = new PersonDaoImpl(); + Person person = new Person(); + person.setCountry("Sweden"); + person.setCompany("Some company"); + person.setFullName("Some Person"); + + DistinguishedName dn = tested.buildDn(person); + + assertEquals("cn=Some Person, ou=Some company, c=Sweden", dn.toString()); + } + + public void testCreate() { + ldapOperationsMock.bind(DistinguishedName.EMPTY_PATH, + dirContextOperationsMock, null); + + replay(); + + tested.create(person); + + verify(); + } + + public void testUpdate() { + ldapOperationsControl + .expectAndReturn(ldapOperationsMock + .lookup(DistinguishedName.EMPTY_PATH), + dirContextOperationsMock); + ModificationItem[] modificationItems = new ModificationItem[0]; + dirContextOperationsControl.expectAndReturn(dirContextOperationsMock + .getModificationItems(), modificationItems); + ldapOperationsMock.modifyAttributes(DistinguishedName.EMPTY_PATH, + modificationItems); + + replay(); + + tested.update(person); + + verify(); + + } + + public void testDelete() { + ldapOperationsMock.unbind(DistinguishedName.EMPTY_PATH); + + replay(); + + tested.delete(person); + + verify(); + } + + public void testFindAll() { + List expectedList = Collections.singletonList(null); + ldapOperationsControl.expectAndReturn(ldapOperationsMock.search( + DistinguishedName.EMPTY_PATH, "(objectclass=person)", + contextMapperMock), expectedList); + + replay(); + + List result = tested.findAll(); + + verify(); + + assertSame(expectedList, result); + } + + public void testFindByPrimaryKey() { + DistinguishedName dn = new DistinguishedName( + "cn=Some Person, ou=Some company, c=Sweden"); + + ldapOperationsControl.expectAndReturn(ldapOperationsMock.lookup(dn, + contextMapperMock), person); + + replay(); + + Person result = tested.findByPrimaryKey("Sweden", "Some company", + "Some Person"); + + verify(); + + assertSame(person, result); + + } + + public void testFind_Name() { + List expectedList = Collections.singletonList(null); + ldapOperationsControl.expectAndReturn(ldapOperationsMock.search( + DistinguishedName.EMPTY_PATH, + "(&(objectclass=person)(cn=*some*))", contextMapperMock), + expectedList); + + replay(); + + SearchCriteria criteria = new SearchCriteria(); + criteria.setName("some"); + List result = tested.find(criteria); + + verify(); + + assertSame(expectedList, result); + } +} diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/GroupTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/GroupTest.java new file mode 100644 index 00000000..a9ab3cd4 --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/GroupTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.domain; + +import java.util.Collections; +import java.util.Set; + +import junit.framework.TestCase; + +import com.gargoylesoftware.base.testing.EqualsTester; + +/** + * Unit tests for the Group class. + * + * @author Ulrik Sandberg + */ +public class GroupTest extends TestCase { + + public void testEquals() { + Set members = Collections.singleton("some dn"); + Set differentMembers = Collections.singleton("other dn"); + Group original = new Group(); + original.setName("some name"); + original.setMembers(members); + Group identical = new Group(); + identical.setName("some name"); + identical.setMembers(members); + Group different = new Group(); + different.setName("some name"); + different.setMembers(differentMembers); + Group subclass = new Group() { + private static final long serialVersionUID = 1L; + }; + subclass.setName("some name"); + subclass.setMembers(members); + new EqualsTester(original, identical, different, subclass); + } +} diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/PersonTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/PersonTest.java new file mode 100644 index 00000000..f3f34ef7 --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/PersonTest.java @@ -0,0 +1,25 @@ +package org.springframework.ldap.samples.person.domain; + +import com.gargoylesoftware.base.testing.EqualsTester; + +import junit.framework.TestCase; + +public class PersonTest extends TestCase { + + public void testEquals() { + Person original = new Person(); + original.setFullName("some name"); + original.setCompany("some company"); + Person identical = new Person(); + identical.setFullName("some name"); + identical.setCompany("some company"); + Person different = new Person(); + different.setFullName("other name"); + different.setCompany("some company"); + Person subclass = new Person() { + private static final long serialVersionUID = 1L;}; + subclass.setFullName("some name"); + subclass.setCompany("some company"); + new EqualsTester(original, identical, different, subclass); + } +} diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/SearchCriteriaTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/SearchCriteriaTest.java new file mode 100644 index 00000000..4f98a6ab --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/domain/SearchCriteriaTest.java @@ -0,0 +1,21 @@ +package org.springframework.ldap.samples.person.domain; + +import junit.framework.TestCase; + +import com.gargoylesoftware.base.testing.EqualsTester; + +public class SearchCriteriaTest extends TestCase { + + public void testEquals() { + SearchCriteria original = new SearchCriteria(); + original.setName("some"); + SearchCriteria identical = new SearchCriteria(); + identical.setName("some"); + SearchCriteria different = new SearchCriteria(); + different.setName("other"); + SearchCriteria subclass = new SearchCriteria() { + private static final long serialVersionUID = 1L;}; + subclass.setName("some"); + new EqualsTester(original, identical, different, subclass); + } +} diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/service/PersonServiceImplTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/service/PersonServiceImplTest.java new file mode 100644 index 00000000..efae0517 --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/service/PersonServiceImplTest.java @@ -0,0 +1,162 @@ +/* + * Copyright 2005-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ldap.samples.person.service; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.samples.person.dao.PersonDao; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; +import org.springframework.ldap.samples.person.service.PersonServiceImpl; + +/** + * Unit tests for the PersonServiceImpl class. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class PersonServiceImplTest extends TestCase { + + private MockControl personDaoControl; + + private PersonDao personDaoMock; + + private PersonServiceImpl tested; + + protected void setUp() throws Exception { + super.setUp(); + personDaoControl = MockControl.createControl(PersonDao.class); + personDaoMock = (PersonDao) personDaoControl.getMock(); + + tested = new PersonServiceImpl(); + tested.setPersonDao(personDaoMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + personDaoControl = null; + personDaoMock = null; + + tested = null; + } + + protected void replay() { + personDaoControl.replay(); + } + + protected void verify() { + personDaoControl.verify(); + } + + public void testCreate() { + Person person = setupPerson(); + + personDaoMock.create(person); + + replay(); + + tested.create("Sweden", "Some company", "Some Person", "Person", + new String[] { "Some description" }); + + verify(); + } + + private Person setupPerson() { + Person person = new Person(); + person.setCountry("Sweden"); + person.setCompany("Some company"); + person.setFullName("Some Person"); + person.setLastName("Person"); + person.setDescription(new String[] { "Some description" }); + return person; + } + + public void testUpdate() { + Person person = setupPerson(); + + personDaoMock.update(person); + + replay(); + + tested.update(person); + + verify(); + } + + public void testDelete() { + Person person = setupPerson(); + + personDaoMock.update(person); + + replay(); + + tested.update(person); + + verify(); + } + + public void testFindByPrimaryKey() { + Person person = new Person(); + + personDaoControl.expectAndReturn(personDaoMock.findByPrimaryKey( + "Sweden", "Some company", "Some Person"), person); + + replay(); + + Person result = tested.findByPrimaryKey("Sweden", "Some company", + "Some Person"); + + verify(); + + assertSame(person, result); + } + + public void testFindAll() { + List expected = Collections.singletonList(null); + + personDaoControl.expectAndReturn(personDaoMock.findAll(), expected); + + replay(); + + List actual = tested.findAll(); + + verify(); + + assertSame(expected, actual); + } + + public void testFind() { + List expected = Collections.singletonList(null); + + SearchCriteria criteria = new SearchCriteria(); + criteria.setName("some"); + personDaoControl + .expectAndReturn(personDaoMock.find(criteria), expected); + + replay(); + + List actual = tested.find(criteria); + + verify(); + + assertSame(expected, actual); + } +} diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/DetailFlowExecutionTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/DetailFlowExecutionTest.java new file mode 100644 index 00000000..23b97f84 --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/DetailFlowExecutionTest.java @@ -0,0 +1,152 @@ +package org.springframework.ldap.samples.person.web; + +import org.easymock.MockControl; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.service.PersonService; +import org.springframework.webflow.core.collection.LocalAttributeMap; +import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.definition.registry.FlowDefinitionResource; +import org.springframework.webflow.execution.support.ApplicationView; +import org.springframework.webflow.test.MockFlowServiceLocator; +import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests; + +public class DetailFlowExecutionTest extends AbstractXmlFlowExecutionTests { + + private MockControl personServiceControl; + + private PersonService personServiceMock; + + private MutableAttributeMap attributeMap; + + protected void setUp() throws Exception { + super.setUp(); + + personServiceControl = MockControl.createControl(PersonService.class); + personServiceMock = (PersonService) personServiceControl.getMock(); + + attributeMap = new LocalAttributeMap(); + attributeMap.put("country", "Sweden"); + attributeMap.put("company", "company1"); + attributeMap.put("name", "Keith Donald"); + } + + protected void tearDown() throws Exception { + super.tearDown(); + personServiceControl = null; + personServiceMock = null; + } + + protected void replay() { + personServiceControl.replay(); + } + + protected void verify() { + personServiceControl.verify(); + } + + public void testStartFlow() { + expectFindByPrimaryKey(); + replay(); + + ApplicationView view = applicationView(startFlow(attributeMap)); + + verify(); + assertCurrentStateEquals("displayDetails"); + assertViewNameEquals("showDetails", view); + assertModelAttributeNotNull("person", view); + } + + public void testBack() { + expectFindByPrimaryKey(); + replay(); + + startFlow(attributeMap); + signalEvent("back"); + + verify(); + assertFlowExecutionEnded(); + } + + public void testEdit() { + expectFindByPrimaryKey(2); + replay(); + + startFlow(attributeMap); + ApplicationView view = applicationView(signalEvent("edit")); + + verify(); + assertCurrentStateEquals("editDetails"); + assertViewNameEquals("editDetails", view); + assertModelAttributeNotNull("person", view); + } + + public void testCancel() { + expectFindByPrimaryKey(3); + replay(); + + startFlow(attributeMap); + signalEvent("edit"); + ApplicationView view = applicationView(signalEvent("cancel")); + + verify(); + assertCurrentStateEquals("displayDetails"); + assertViewNameEquals("showDetails", view); + assertModelAttributeNotNull("person", view); + } + + public void testSubmit() { + expectFindByPrimaryKey(3); + expectUpdate(); + replay(); + + startFlow(attributeMap); + signalEvent("edit"); + ApplicationView view = applicationView(signalEvent("submit")); + + verify(); + assertCurrentStateEquals("displayDetails"); + assertViewNameEquals("showDetails", view); + assertModelAttributeNotNull("person", view); + } + + private void expectFindByPrimaryKey() { + expectFindByPrimaryKey(1); + } + + private void expectFindByPrimaryKey(int times) { + Person person = setupPerson(); + + personServiceControl.expectAndReturn(personServiceMock + .findByPrimaryKey("Sweden", "company1", "Keith Donald"), + person, times); + } + + private void expectUpdate() { + Person person = setupPerson(); + personServiceMock.update(person); + } + + private Person setupPerson() { + Person person = new Person(); + person.setFullName("Keith Donald"); + person.setCompany("company1"); + person.setCountry("Sweden"); + person.setPhone("some phone"); + person.setLastName("Donald"); + return person; + } + + /* + * @see org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests#getFlowDefinitionResource() + */ + protected FlowDefinitionResource getFlowDefinitionResource() { + return createFlowDefinitionResource("src/main/webapp/WEB-INF/flows/detail-flow.xml"); + } + + /* + * @see org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests#registerMockServices(org.springframework.webflow.test.MockFlowServiceLocator) + */ + protected void registerMockServices(MockFlowServiceLocator serviceRegistry) { + serviceRegistry.registerBean("personService", personServiceMock); + } +} \ No newline at end of file diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/PersonManagerFlowExecutionTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/PersonManagerFlowExecutionTest.java new file mode 100644 index 00000000..0d781cfe --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/PersonManagerFlowExecutionTest.java @@ -0,0 +1,180 @@ +package org.springframework.ldap.samples.person.web; + +import java.util.Collections; + +import org.easymock.MockControl; +import org.springframework.binding.mapping.AttributeMapper; +import org.springframework.binding.mapping.MappingContext; +import org.springframework.ldap.samples.person.domain.Group; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.service.GroupService; +import org.springframework.ldap.samples.person.service.PersonService; +import org.springframework.webflow.core.collection.AttributeMap; +import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.definition.registry.FlowDefinitionResource; +import org.springframework.webflow.engine.EndState; +import org.springframework.webflow.engine.Flow; +import org.springframework.webflow.execution.support.ApplicationView; +import org.springframework.webflow.test.MockFlowServiceLocator; +import org.springframework.webflow.test.MockParameterMap; +import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests; + +public class PersonManagerFlowExecutionTest extends + AbstractXmlFlowExecutionTests { + + private MockControl personServiceControl; + + private PersonService personServiceMock; + + private MockControl groupServiceControl; + + private GroupService groupServiceMock; + + protected void setUp() throws Exception { + super.setUp(); + + personServiceControl = MockControl.createControl(PersonService.class); + personServiceMock = (PersonService) personServiceControl.getMock(); + + groupServiceControl = MockControl.createControl(GroupService.class); + groupServiceMock = (GroupService) groupServiceControl.getMock(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + personServiceControl = null; + personServiceMock = null; + groupServiceControl = null; + groupServiceMock = null; + } + + protected void replay() { + personServiceControl.replay(); + groupServiceControl.replay(); + } + + protected void verify() { + personServiceControl.verify(); + groupServiceControl.verify(); + } + + public void testStartFlow() { + ApplicationView view = applicationView(startFlow()); + assertCurrentStateEquals("displayMenu"); + assertViewNameEquals("menu", view); + } + + public void testFindPerson() { + startFlow(); + ApplicationView view = applicationView(signalEvent("findPerson")); + assertCurrentStateEquals("displayResults"); + assertViewNameEquals("searchResult", view); + assertModelAttributeCollectionSize(1, "results", view); + } + + public void testNewSearch() { + startFlow(); + signalEvent("findPerson"); + ApplicationView view = applicationView(signalEvent("newSearch")); + assertCurrentStateEquals("displayResults"); + assertViewNameEquals("searchResult", view); + assertModelAttributeCollectionSize(1, "results", view); + } + + public void testSelectValidResult() { + startFlow(); + signalEvent("findPerson"); + + MockParameterMap parameters = new MockParameterMap(); + parameters = new MockParameterMap(); + parameters.put("name", "Keith Donald"); + parameters.put("company", "company1"); + parameters.put("country", "Sweden"); + ApplicationView view = applicationView(signalEvent("select", parameters)); + + assertCurrentStateEquals("displayResults"); + assertViewNameEquals("searchResult", view); + assertModelAttributeCollectionSize(1, "results", view); + } + + /* + * @see org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests#getFlowDefinitionResource() + */ + protected FlowDefinitionResource getFlowDefinitionResource() { + return createFlowDefinitionResource("src/main/webapp/WEB-INF/flows/personmanager-flow.xml"); + } + + /* + * @see org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests#registerMockServices(org.springframework.webflow.test.MockFlowServiceLocator) + */ + protected void registerMockServices(MockFlowServiceLocator serviceRegistry) { + Flow mockSearchFlow = new Flow("search-flow"); + + // test responding to finish result + EndState endState = new EndState(mockSearchFlow, "finish"); + // Simulate a search result of one item + endState.setOutputMapper(new AttributeMapper() { + public void map(Object source, Object target, MappingContext context) { + MutableAttributeMap outputMap = (MutableAttributeMap) target; + outputMap.put("results", Collections + .singletonList(new Person())); + } + }); + + serviceRegistry.registerSubflow(mockSearchFlow); + + Flow mockDetailFlow = new Flow("detail-flow"); + mockDetailFlow.setInputMapper(new AttributeMapper() { + public void map(Object source, Object target, MappingContext context) { + String name = "Keith Donald"; + String company = "company1"; + String country = "Sweden"; + assertEquals("name '" + name + + "' not provided as input by calling search flow", + name, ((AttributeMap) source).get("name")); + assertEquals("company '" + company + + "' not provided as input by calling search flow", + company, ((AttributeMap) source).get("company")); + assertEquals("company '" + country + + "' not provided as input by calling search flow", + country, ((AttributeMap) source).get("country")); + } + }); + // test responding to finish result + new EndState(mockDetailFlow, "finish"); + + serviceRegistry.registerSubflow(mockDetailFlow); + + Flow mockSearchGroupFlow = new Flow("search-group-flow"); + + // test responding to finish result + EndState endStateSearchGroup = new EndState(mockSearchGroupFlow, "finish"); + // Simulate a search result of one item + endStateSearchGroup.setOutputMapper(new AttributeMapper() { + public void map(Object source, Object target, MappingContext context) { + MutableAttributeMap outputMap = (MutableAttributeMap) target; + outputMap.put("groups", Collections + .singletonList(new Group())); + } + }); + + serviceRegistry.registerSubflow(mockSearchGroupFlow); + + Flow mockDetailGroupFlow = new Flow("detail-group-flow"); + mockDetailGroupFlow.setInputMapper(new AttributeMapper() { + public void map(Object source, Object target, MappingContext context) { + String name = "Some Group"; + assertEquals("name '" + name + + "' not provided as input by calling search group flow", + name, ((AttributeMap) source).get("name")); + } + }); + // test responding to finish result + new EndState(mockDetailGroupFlow, "finish"); + + serviceRegistry.registerSubflow(mockDetailGroupFlow); + + serviceRegistry.registerBean("groupService", groupServiceMock); + serviceRegistry.registerBean("personService", personServiceMock); + } +} \ No newline at end of file diff --git a/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/SearchFlowExecutionTest.java b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/SearchFlowExecutionTest.java new file mode 100644 index 00000000..46a5cd4e --- /dev/null +++ b/spring-ldap-person/src/test/java/org/springframework/ldap/samples/person/web/SearchFlowExecutionTest.java @@ -0,0 +1,79 @@ +package org.springframework.ldap.samples.person.web; + +import java.util.Collections; + +import org.easymock.MockControl; +import org.springframework.ldap.samples.person.domain.Person; +import org.springframework.ldap.samples.person.domain.SearchCriteria; +import org.springframework.ldap.samples.person.service.PersonService; +import org.springframework.webflow.definition.registry.FlowDefinitionResource; +import org.springframework.webflow.execution.support.ApplicationView; +import org.springframework.webflow.test.MockFlowServiceLocator; +import org.springframework.webflow.test.MockParameterMap; +import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests; + +public class SearchFlowExecutionTest extends AbstractXmlFlowExecutionTests { + + private MockControl personServiceControl; + + private PersonService personServiceMock; + + protected void setUp() throws Exception { + super.setUp(); + + personServiceControl = MockControl.createControl(PersonService.class); + personServiceMock = (PersonService) personServiceControl.getMock(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + personServiceControl = null; + personServiceMock = null; + } + + protected void replay() { + personServiceControl.replay(); + } + + protected void verify() { + personServiceControl.verify(); + } + + public void testStartFlow() { + ApplicationView view = applicationView(startFlow()); + assertCurrentStateEquals("enterCriteria"); + assertViewNameEquals("searchForm", view); + assertModelAttributeNotNull("searchCriteria", view); + } + + public void testCriteriaSubmitSuccess() { + SearchCriteria searchCriteria = new SearchCriteria(); + searchCriteria.setName("Keith"); + personServiceControl.expectAndReturn(personServiceMock + .find(searchCriteria), Collections.singletonList(new Person())); + replay(); + + startFlow(); + MockParameterMap parameters = new MockParameterMap(); + parameters.put("name", "Keith"); + signalEvent("search", parameters); + + verify(); + + assertFlowExecutionEnded(); + } + + /* + * @see org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests#getFlowDefinitionResource() + */ + protected FlowDefinitionResource getFlowDefinitionResource() { + return createFlowDefinitionResource("src/main/webapp/WEB-INF/flows/search-flow.xml"); + } + + /* + * @see org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests#registerMockServices(org.springframework.webflow.test.MockFlowServiceLocator) + */ + protected void registerMockServices(MockFlowServiceLocator serviceRegistry) { + serviceRegistry.registerBean("personService", personServiceMock); + } +} \ No newline at end of file diff --git a/spring-ldap-person/trifork.map b/spring-ldap-person/trifork.map new file mode 100644 index 00000000..da11cac9 --- /dev/null +++ b/spring-ldap-person/trifork.map @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/spring-ldap/.checkstyle b/spring-ldap/.checkstyle new file mode 100644 index 00000000..c046ce20 --- /dev/null +++ b/spring-ldap/.checkstyle @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/spring-ldap/.classpath b/spring-ldap/.classpath new file mode 100644 index 00000000..cdaaac28 --- /dev/null +++ b/spring-ldap/.classpath @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap/.project b/spring-ldap/.project new file mode 100644 index 00000000..33c7cb5a --- /dev/null +++ b/spring-ldap/.project @@ -0,0 +1,34 @@ + + + spring-ldap + + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.ibm.etools.validation.validationbuilder + + + + + com.ibm.etools.ctc.serviceprojectbuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + com.ibm.etools.ctc.javaprojectnature + + diff --git a/spring-ldap/.settings/org.eclipse.jdt.core.prefs b/spring-ldap/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..a923f3d7 --- /dev/null +++ b/spring-ldap/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,248 @@ +#Sun Jul 02 11:21:34 EDT 2006 +eclipse.preferences.version=1 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=1 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false +org.eclipse.jdt.core.formatter.comment.format_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=80 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false diff --git a/spring-ldap/.settings/org.eclipse.jdt.ui.prefs b/spring-ldap/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..21ae7c07 --- /dev/null +++ b/spring-ldap/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,3 @@ +#Fri Apr 21 13:43:14 CEST 2006 +eclipse.preferences.version=1 +formatter_settings_version=10 diff --git a/spring-ldap/.springBeans b/spring-ldap/.springBeans new file mode 100644 index 00000000..f9aa76ca --- /dev/null +++ b/spring-ldap/.springBeans @@ -0,0 +1,25 @@ + + + + src/iutest/conf/ldapTemplateBaseSuffixTestContext.xml + src/iutest/conf/ldapTemplateTestContext.xml + + + + iutest with base + false + false + + src/iutest/conf/ldapTemplateBaseSuffixTestContext.xml + + + + iutest + false + false + + src/iutest/conf/ldapTemplateTestContext.xml + + + + diff --git a/spring-ldap/build.xml b/spring-ldap/build.xml new file mode 100644 index 00000000..d9d767cb --- /dev/null +++ b/spring-ldap/build.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Unit test report has been generated. +The HTML report is ${target.testresults.html.dir}/index.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap/changelog.txt b/spring-ldap/changelog.txt new file mode 100644 index 00000000..760efdc3 --- /dev/null +++ b/spring-ldap/changelog.txt @@ -0,0 +1,212 @@ +Spring LDAP CHANGELOG +===================== +http://www.springframework.org/ldap + +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.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. \ No newline at end of file diff --git a/spring-ldap/docs/quickstart/quickstart.pdf b/spring-ldap/docs/quickstart/quickstart.pdf new file mode 100644 index 00000000..c9e37ade Binary files /dev/null and b/spring-ldap/docs/quickstart/quickstart.pdf differ diff --git a/spring-ldap/docs/reference/images/admons/blank.png b/spring-ldap/docs/reference/images/admons/blank.png new file mode 100644 index 00000000..764bf4f0 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/blank.png differ diff --git a/spring-ldap/docs/reference/images/admons/caution.gif b/spring-ldap/docs/reference/images/admons/caution.gif new file mode 100644 index 00000000..d9f5e5b1 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/caution.gif differ diff --git a/spring-ldap/docs/reference/images/admons/caution.png b/spring-ldap/docs/reference/images/admons/caution.png new file mode 100644 index 00000000..5b7809ca Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/caution.png differ diff --git a/spring-ldap/docs/reference/images/admons/caution.tif b/spring-ldap/docs/reference/images/admons/caution.tif new file mode 100644 index 00000000..4a282948 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/caution.tif differ diff --git a/spring-ldap/docs/reference/images/admons/draft.png b/spring-ldap/docs/reference/images/admons/draft.png new file mode 100644 index 00000000..0084708c Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/draft.png differ diff --git a/spring-ldap/docs/reference/images/admons/home.gif b/spring-ldap/docs/reference/images/admons/home.gif new file mode 100644 index 00000000..6784f5bb Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/home.gif differ diff --git a/spring-ldap/docs/reference/images/admons/home.png b/spring-ldap/docs/reference/images/admons/home.png new file mode 100644 index 00000000..cbb711de Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/home.png differ diff --git a/spring-ldap/docs/reference/images/admons/important.gif b/spring-ldap/docs/reference/images/admons/important.gif new file mode 100644 index 00000000..6795d9a8 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/important.gif differ diff --git a/spring-ldap/docs/reference/images/admons/important.png b/spring-ldap/docs/reference/images/admons/important.png new file mode 100644 index 00000000..12c90f60 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/important.png differ diff --git a/spring-ldap/docs/reference/images/admons/important.tif b/spring-ldap/docs/reference/images/admons/important.tif new file mode 100644 index 00000000..184de637 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/important.tif differ diff --git a/spring-ldap/docs/reference/images/admons/next.gif b/spring-ldap/docs/reference/images/admons/next.gif new file mode 100644 index 00000000..aa1516e6 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/next.gif differ diff --git a/spring-ldap/docs/reference/images/admons/next.png b/spring-ldap/docs/reference/images/admons/next.png new file mode 100644 index 00000000..45835bf8 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/next.png differ diff --git a/spring-ldap/docs/reference/images/admons/note.gif b/spring-ldap/docs/reference/images/admons/note.gif new file mode 100644 index 00000000..f329d359 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/note.gif differ diff --git a/spring-ldap/docs/reference/images/admons/note.png b/spring-ldap/docs/reference/images/admons/note.png new file mode 100644 index 00000000..d0c3c645 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/note.png differ diff --git a/spring-ldap/docs/reference/images/admons/note.tif b/spring-ldap/docs/reference/images/admons/note.tif new file mode 100644 index 00000000..08644d6b Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/note.tif differ diff --git a/spring-ldap/docs/reference/images/admons/prev.gif b/spring-ldap/docs/reference/images/admons/prev.gif new file mode 100644 index 00000000..64ca8f3c Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/prev.gif differ diff --git a/spring-ldap/docs/reference/images/admons/prev.png b/spring-ldap/docs/reference/images/admons/prev.png new file mode 100644 index 00000000..cf24654f Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/prev.png differ diff --git a/spring-ldap/docs/reference/images/admons/tip.gif b/spring-ldap/docs/reference/images/admons/tip.gif new file mode 100644 index 00000000..823f2b41 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/tip.gif differ diff --git a/spring-ldap/docs/reference/images/admons/tip.png b/spring-ldap/docs/reference/images/admons/tip.png new file mode 100644 index 00000000..5c4aab3b Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/tip.png differ diff --git a/spring-ldap/docs/reference/images/admons/tip.tif b/spring-ldap/docs/reference/images/admons/tip.tif new file mode 100644 index 00000000..4a3d8c75 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/tip.tif differ diff --git a/spring-ldap/docs/reference/images/admons/toc-blank.png b/spring-ldap/docs/reference/images/admons/toc-blank.png new file mode 100644 index 00000000..6ffad17a Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/toc-blank.png differ diff --git a/spring-ldap/docs/reference/images/admons/toc-minus.png b/spring-ldap/docs/reference/images/admons/toc-minus.png new file mode 100644 index 00000000..abbb020c Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/toc-minus.png differ diff --git a/spring-ldap/docs/reference/images/admons/toc-plus.png b/spring-ldap/docs/reference/images/admons/toc-plus.png new file mode 100644 index 00000000..941312ce Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/toc-plus.png differ diff --git a/spring-ldap/docs/reference/images/admons/up.gif b/spring-ldap/docs/reference/images/admons/up.gif new file mode 100644 index 00000000..aabc2d01 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/up.gif differ diff --git a/spring-ldap/docs/reference/images/admons/up.png b/spring-ldap/docs/reference/images/admons/up.png new file mode 100644 index 00000000..07634de2 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/up.png differ diff --git a/spring-ldap/docs/reference/images/admons/warning.gif b/spring-ldap/docs/reference/images/admons/warning.gif new file mode 100644 index 00000000..3adf1912 Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/warning.gif differ diff --git a/spring-ldap/docs/reference/images/admons/warning.png b/spring-ldap/docs/reference/images/admons/warning.png new file mode 100644 index 00000000..1c33db8f Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/warning.png differ diff --git a/spring-ldap/docs/reference/images/admons/warning.tif b/spring-ldap/docs/reference/images/admons/warning.tif new file mode 100644 index 00000000..7b6611ec Binary files /dev/null and b/spring-ldap/docs/reference/images/admons/warning.tif differ diff --git a/spring-ldap/docs/reference/images/callouts/1.gif b/spring-ldap/docs/reference/images/callouts/1.gif new file mode 100644 index 00000000..0d669771 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/1.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/1.png b/spring-ldap/docs/reference/images/callouts/1.png new file mode 100644 index 00000000..7d473430 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/1.png differ diff --git a/spring-ldap/docs/reference/images/callouts/10.gif b/spring-ldap/docs/reference/images/callouts/10.gif new file mode 100644 index 00000000..fb50b06d Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/10.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/10.png b/spring-ldap/docs/reference/images/callouts/10.png new file mode 100644 index 00000000..997bbc82 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/10.png differ diff --git a/spring-ldap/docs/reference/images/callouts/11.gif b/spring-ldap/docs/reference/images/callouts/11.gif new file mode 100644 index 00000000..9f5dba4f Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/11.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/11.png b/spring-ldap/docs/reference/images/callouts/11.png new file mode 100644 index 00000000..ce47dac3 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/11.png differ diff --git a/spring-ldap/docs/reference/images/callouts/12.gif b/spring-ldap/docs/reference/images/callouts/12.gif new file mode 100644 index 00000000..a373d0b4 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/12.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/12.png b/spring-ldap/docs/reference/images/callouts/12.png new file mode 100644 index 00000000..31daf4e2 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/12.png differ diff --git a/spring-ldap/docs/reference/images/callouts/13.gif b/spring-ldap/docs/reference/images/callouts/13.gif new file mode 100644 index 00000000..b00b1637 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/13.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/13.png b/spring-ldap/docs/reference/images/callouts/13.png new file mode 100644 index 00000000..14021a89 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/13.png differ diff --git a/spring-ldap/docs/reference/images/callouts/14.gif b/spring-ldap/docs/reference/images/callouts/14.gif new file mode 100644 index 00000000..6d6642ee Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/14.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/14.png b/spring-ldap/docs/reference/images/callouts/14.png new file mode 100644 index 00000000..64014b75 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/14.png differ diff --git a/spring-ldap/docs/reference/images/callouts/15.gif b/spring-ldap/docs/reference/images/callouts/15.gif new file mode 100644 index 00000000..cdd7072d Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/15.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/15.png b/spring-ldap/docs/reference/images/callouts/15.png new file mode 100644 index 00000000..0d65765f Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/15.png differ diff --git a/spring-ldap/docs/reference/images/callouts/2.gif b/spring-ldap/docs/reference/images/callouts/2.gif new file mode 100644 index 00000000..100ff79f Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/2.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/2.png b/spring-ldap/docs/reference/images/callouts/2.png new file mode 100644 index 00000000..5d09341b Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/2.png differ diff --git a/spring-ldap/docs/reference/images/callouts/3.gif b/spring-ldap/docs/reference/images/callouts/3.gif new file mode 100644 index 00000000..5008ca7d Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/3.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/3.png b/spring-ldap/docs/reference/images/callouts/3.png new file mode 100644 index 00000000..ef7b7004 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/3.png differ diff --git a/spring-ldap/docs/reference/images/callouts/4.gif b/spring-ldap/docs/reference/images/callouts/4.gif new file mode 100644 index 00000000..0e5617d2 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/4.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/4.png b/spring-ldap/docs/reference/images/callouts/4.png new file mode 100644 index 00000000..adb8364e Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/4.png differ diff --git a/spring-ldap/docs/reference/images/callouts/5.gif b/spring-ldap/docs/reference/images/callouts/5.gif new file mode 100644 index 00000000..9bc75ada Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/5.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/5.png b/spring-ldap/docs/reference/images/callouts/5.png new file mode 100644 index 00000000..4d7eb460 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/5.png differ diff --git a/spring-ldap/docs/reference/images/callouts/6.gif b/spring-ldap/docs/reference/images/callouts/6.gif new file mode 100644 index 00000000..d3964070 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/6.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/6.png b/spring-ldap/docs/reference/images/callouts/6.png new file mode 100644 index 00000000..0ba694af Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/6.png differ diff --git a/spring-ldap/docs/reference/images/callouts/7.gif b/spring-ldap/docs/reference/images/callouts/7.gif new file mode 100644 index 00000000..c90b2f3d Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/7.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/7.png b/spring-ldap/docs/reference/images/callouts/7.png new file mode 100644 index 00000000..472e96f8 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/7.png differ diff --git a/spring-ldap/docs/reference/images/callouts/8.gif b/spring-ldap/docs/reference/images/callouts/8.gif new file mode 100644 index 00000000..6fe3287d Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/8.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/8.png b/spring-ldap/docs/reference/images/callouts/8.png new file mode 100644 index 00000000..5e60973c Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/8.png differ diff --git a/spring-ldap/docs/reference/images/callouts/9.gif b/spring-ldap/docs/reference/images/callouts/9.gif new file mode 100644 index 00000000..bc5c8125 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/9.gif differ diff --git a/spring-ldap/docs/reference/images/callouts/9.png b/spring-ldap/docs/reference/images/callouts/9.png new file mode 100644 index 00000000..a0676d26 Binary files /dev/null and b/spring-ldap/docs/reference/images/callouts/9.png differ diff --git a/spring-ldap/docs/reference/readme.txt b/spring-ldap/docs/reference/readme.txt new file mode 100644 index 00000000..6280723d --- /dev/null +++ b/spring-ldap/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/spring-ldap/docs/reference/src/basic.xml b/spring-ldap/docs/reference/src/basic.xml new file mode 100644 index 00000000..ed0efa86 --- /dev/null +++ b/spring-ldap/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.support.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.support.DistinguishedName; +import javax.naming.Name; + +public class PersonDaoImpl implements PersonDao { + public static final String BASE_DN = "dc=example,dc=com"; + ... + protected Name buildDn(Person p) { + DistinguishedName dn = new DistinguishedName(BASE_DN); + dn.add("c", p.getCountry()); + dn.add("ou", p.getCompany()); + dn.add("cn", p.getFullname()); + return dn; + } +} + + + Assuming that a Person has the following attributes: + + + + + + country + + Sweden + + + + company + + Some Company + + + + fullname + + Some Person + + + + + + The code above would then result in the following distinguished + name: + + cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com + + In Java 5, there is an implementation of the Name interface: LdapName. + If you are in the Java 5 world, you might as well use + LdapName. However, you may still use + DistinguishedName if you so wish. + + + + Binding and Unbinding + + + Binding Data + + Inserting data in Java LDAP is called binding. In order to do + that, a distinguished name that uniquely identifies the new entry is + required. The following example shows how data is bound using + LdapTemplate: + + + Binding data using Attributes + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void create(Person p) { + Name dn = buildDn(p); + ldapTemplate.bind(dn, null, buildAttributes(p)); + } + + private Attributes buildAttributes(Person p) { + Attributes attrs = new BasicAttributes(); + BasicAttribute ocattr = new BasicAttribute("objectclass"); + ocattr.add("top"); + ocattr.add("person"); + attrs.put(ocattr); + attrs.put("cn", "Some Person"); + attrs.put("sn", "Person"); + return attrs; + } +} + + + The Attributes building is--while dull and verbose--sufficient for + many purposes. It is, however, possible to simplify the binding + operation further, which will be described in . + + + + Unbinding Data + + Removing data in Java LDAP is called unbinding. A distinguished + name (dn) is required to identify the entry, just as in the binding + operation. The following example shows how data is unbound using + LdapTemplate: + + + Unbinding data + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void delete(Person p) { + Name dn = buildDn(p); + ldapTemplate.unbind(dn); + } +} + + + + + + Modifying + + In Java LDAP, data can be modified in two ways: either using + rebind or + modifyAttributes. + + + Modifying using <literal>rebind</literal> + + A rebind is a very crude way to modify data. + It's basically an unbind followed by a + bind. It looks like this: + + + Modifying using rebind + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void update(Person p) { + Name dn = buildDn(p); + ldapTemplate.rebind(dn, null, buildAttributes(p)); + } +} + + + + + Modifying using <literal>modifyAttributes</literal> + + If only the modified attributes should be replaced, there is a + method called modifyAttributes that takes an array of + modifications: + + + Modifying using modifyAttributes + + package com.example.dao; + +public class PersonDaoImpl implements PersonDao { + private LdapTemplate ldapTemplate; + ... + public void updateDescription(Person p) { + Name dn = buildDn(p); + Attribute attr = new BasicAttribute("description", p.getDescription()) + ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr); + ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item}); + } +} + + + Building Attributes and + ModificationItem arrays is a lot of work, but as you + will see in , the update operations + can be simplified. + + + + + Sample applications + + It is recommended that you review the Spring LDAP sample + applications included in the release distribution for best-practice + illustrations of the features of this library. A description of each + sample is provided below: + + + + spring-ldap-person - the sample demonstrating most + features. + + + + spring-ldap-article - the sample application that was written + to accompany a java.net + article about Spring LDAP. + + + + \ No newline at end of file diff --git a/spring-ldap/docs/reference/src/configuration.xml b/spring-ldap/docs/reference/src/configuration.xml new file mode 100644 index 00000000..09f1a45b --- /dev/null +++ b/spring-ldap/docs/reference/src/configuration.xml @@ -0,0 +1,195 @@ + + + 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. + + + + Authentication + + Authenticated contexts are created for both read-only and + read-write operations by default. You specify + userName and password of the LDAP + user to be used for authentication on the + ContextSource. + + + The userName 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 userName 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.support.LdapContextSource"> + <property name="url" value="ldap://localhost:389" /> + <property name="base" value="dc=example,dc=com" /> + <property name="acegiAuthenticationSource" ref="authenticationSource" /> + </bean> + + <bean id="acegiAuthenticationSource" + class="org.springframework.ldap.support.authentication.AcegiAuthenticationSource" /> + ... +</beans> + + + + We don't specify any userName 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.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.support.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.support.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. + + + \ No newline at end of file diff --git a/spring-ldap/docs/reference/src/contextprocessor.xml b/spring-ldap/docs/reference/src/contextprocessor.xml new file mode 100644 index 00000000..dae84b91 --- /dev/null +++ b/spring-ldap/docs/reference/src/contextprocessor.xml @@ -0,0 +1,226 @@ + + + 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(); +} + + This is what it can look like when you implement your own request + control DirContextProcessor: + + + + 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. + + This is an example of how the paged search results functionality can + be used: + + + Example of an integration test for paged search results + + public class LdapTemplatePagedSearchITest extends TestCase { + private LdapTemplate tested; + ... + // LDAP contains 5 persons matching the filter. Page size is 3. + // Expects two batches of 3 and 2 persons respectively. + public void testPagedResult() { + SearchControls searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + String base = "dc=example,dc=com"; + String filter = "(&(objectclass=person)(cn=Some Person*))"; + PersonAttributesMapper mapper = new PersonAttributesMapper(); + CollectingNameClassPairCallbackHandler handler = + tested.new AttributesMapperCallbackHandler(mapper); + + PagedResultsRequestControl requestControl; + requestControl = new PagedResultsRequestControl(3); + tested.search(base, filter, searchControls, handler, requestControl); + PagedResultsCookie cookie = requestControl.getCookie(); + assertNotNull("Cookie should not be null yet", cookie.getCookie()); + assertEquals(3, callbackHandler.getList().size()); + + // Prepare for second and last search + requestControl = new PagedResultsRequestControl(3, cookie); + tested.search(base, filter, searchControls, handler, requestControl); + cookie = requestControl.getCookie(); + assertNull("Cookie should be null now", cookie.getCookie()); + assertEquals(5, callbackHandler.getList().size()); + } + + + + Important to note here is that we use the same + CollectingNameClassPairCallbackHandler for both + searches. This means that the results are appended to the same list. The + second batch of two are added to the first three, giving a total of five + after the second search. + + When using the PagedResultsRequestControl it is imperative + that you keep track of the cookie returned from an operation and supply the same + instance to subsequent calls. This means that your Dao method will typically need to + wrap the result list together with the cookie in the value returned to the higher tiers. + + + + \ No newline at end of file diff --git a/spring-ldap/docs/reference/src/dirobjectfactory.xml b/spring-ldap/docs/reference/src/dirobjectfactory.xml new file mode 100644 index 00000000..5c5edbc8 --- /dev/null +++ b/spring-ldap/docs/reference/src/dirobjectfactory.xml @@ -0,0 +1,286 @@ + + + DirObjectFactory and 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. + + + + Binding and Modifying Using ContextMapper + + The DirContextAdapter can also be used to hide the Attributes 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 retrieved + DirContextAdapter as the second parameter to bind, + which should be a Context. The third parameter is + null, since we're not using any + Attributes. + + + + Modifying + + The code for a rebind would be pretty much + identical to , except + that the method called would be rebind. However, + let's say that you don't want to remove and re-create the entry, but + instead update only the attributes that have changed. 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); + DirContextAdapter context = (DirContextAdapter)ldapTemplate.lookup(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(dn, context.getModificationItems()); + } +} + + + 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); + DirContextAdapter context = (DirContextAdapter)ldapTemplate.lookup(dn); + mapToContext(person, context); + ldapTemplate.modifyAttributes(dn, context.getModificationItems()); + } + + protected void mapToContext (Person p, DirContextAdapter 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.AttributesMapper; +import org.springframework.ldap.ContextMapper; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.ldap.support.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); + DirContextAdapter context = (DirContextAdapter)ldapTemplate.lookup(dn); + mapToContext(person, context); + ldapTemplate.modifyAttributes(dn, context.getModificationItems()); + } + + 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, DirContextAdapter 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 implements ContextMapper { + public Object mapFromContext(Object ctx) { + DirContextAdapter context = (DirContextAdapter)ctx; + 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/spring-ldap/docs/reference/src/executors.xml b/spring-ldap/docs/reference/src/executors.xml new file mode 100644 index 00000000..237fa3a1 --- /dev/null +++ b/spring-ldap/docs/reference/src/executors.xml @@ -0,0 +1,133 @@ + + + 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 overloaded method you want. Let's say, for + example, that you want to use the following method: + + NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls ctls) + + The way to do this is to use a custom + SearchExecutor implementation: + + + public interface SearchExecutor { + public NamingEnumeration executeSearch(DirContext ctx) throws NamingException; +} + + + + 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); + } + }; + + NameClassPairCallbackHandler handler = + ldapTemplate.new AttributesMapperCallbackHandler(new PersonAttributesMapper()); + + return ldapTemplate.search(executor, handler); + } +} + + + 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); + } + }; + + NameClassPairCallbackHandler handler = + ldapTemplate.new ContextMapperCallbackHandler(new PersonContextMapper()); + + return ldapTemplate.search(executor, handler); + } +} + + + + When using the + LdapTemplate.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/spring-ldap/docs/reference/src/images/banner4.jpg b/spring-ldap/docs/reference/src/images/banner4.jpg new file mode 100644 index 00000000..ef33d2ae Binary files /dev/null and b/spring-ldap/docs/reference/src/images/banner4.jpg differ diff --git a/spring-ldap/docs/reference/src/images/bannerR.gif b/spring-ldap/docs/reference/src/images/bannerR.gif new file mode 100644 index 00000000..548ab884 Binary files /dev/null and b/spring-ldap/docs/reference/src/images/bannerR.gif differ diff --git a/spring-ldap/docs/reference/src/images/logo.png b/spring-ldap/docs/reference/src/images/logo.png new file mode 100644 index 00000000..31396b8a Binary files /dev/null and b/spring-ldap/docs/reference/src/images/logo.png differ diff --git a/spring-ldap/docs/reference/src/index.xml b/spring-ldap/docs/reference/src/index.xml new file mode 100644 index 00000000..b313a684 --- /dev/null +++ b/spring-ldap/docs/reference/src/index.xml @@ -0,0 +1,52 @@ + + + + + + + + +]> + + + + + + Spring LDAP + Reference Documentation + Version 1.1.2 + December 2006 + + + Mattias + Arthursson + + + Ulrik + Sandberg + + + + + 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; + &executors; + &contextprocessor; + &configuration; + + \ No newline at end of file diff --git a/spring-ldap/docs/reference/src/overview.xml b/spring-ldap/docs/reference/src/overview.xml new file mode 100644 index 00000000..67bd5172 --- /dev/null +++ b/spring-ldap/docs/reference/src/overview.xml @@ -0,0 +1,325 @@ + + + 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). + + 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: + + + 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 AttributesMapper, 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(); + } + }); + } +} + + + + + 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) + + + + spring-dao (exception hierarchy enabling sophisticated error + handling independent of the data access approach in use) + + + + commons-logging (a simple logging facade, used + internally) + + + + commons-lang (misc utilities, used internally) + + + + commons-collections (tools for working with collections, used + internally) + + + + If your application is wired up using the Spring ApplicationContext, + you also need: + + + + spring-context (adds the ability for application objects to + obtain resources using a consistent API) + + + + If you use the AcegiAuthenticationSource, you + also need: + + + + acegi-security + + + + Set up the required beans in your Spring context file and inject the + LdapTemplate into your data access object: + + + <beans> + ... + <bean id="contextSource" class="org.springframework.ldap.support.LdapContextSource"> + <property name="url" value="ldap://localhost:389" /> + <property name="base" value="dc=example,dc=com" /> + <property name="userName" value="cn=Manager" /> + <property name="password" value="secret" /> + </bean> + + <bean id="ldapTemplate" class="org.springframework.ldap.LdapTemplate"> + <constructor-arg ref="contextSource" /> + </bean> + + <bean id="myDataAccessObject" class="com.example.MyDataAccessObject"> + <property name="ldapTemplate" ref="ldapTemplate" /> + </bean> + ... +</beans> + + + + + 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. Cyclic package dependencies + are noted with (cycle). These are candidates for + removal in future releases. + + + org.springframework.ldap + + The ldap 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.support (cycle), spring-beans, spring-dao, + commons-lang, commons-logging + + + + + + org.springframework.ldap.support + + The support package contains supporting + implementations of the central interfaces as well as the + DirContextAdapter abstraction. + + + + Dependencies: ldap, + ldap.util, spring-core, spring-beans, commons-lang, + commons-collections, commons-logging + + + + + + org.springframework.ldap.support.authentication + + The support.authentication package contains + an implementation of the AuthenticationSource interface that can be used + with Acegi + Security. + + + + Dependencies: ldap, acegi-security (optional), spring-beans, + commons-lang, commons-logging + + + + + + org.springframework.ldap.support.control + + The support.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 booster pack, spring-core, + commons-logging + + + + + + org.springframework.ldap.support.filter + + The support.filter package contains the + Filter abstraction and several implementations of it. + + + + Dependencies: ldap.support, commons-lang + + + + + + org.springframework.ldap.util + + The util package contains utility + classes that are used internally. + + + + Dependencies: none + + + + + 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 is supported on Spring 1.2.8 or later. + + 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/spring-ldap/docs/reference/src/preface.xml b/spring-ldap/docs/reference/src/preface.xml new file mode 100644 index 00000000..b0a97605 --- /dev/null +++ b/spring-ldap/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/spring-ldap/docs/reference/styles/fopdf.xsl b/spring-ldap/docs/reference/styles/fopdf.xsl new file mode 100644 index 00000000..d47be44b --- /dev/null +++ b/spring-ldap/docs/reference/styles/fopdf.xsl @@ -0,0 +1,475 @@ + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright ©right; 2005-2006 + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -5em + -5em + + + + + + + + + + + Spring LDAP () + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bold + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 0 + 1 + + 1 + + + + + + book toc + + + + 2 + + + + + + + + + + 0 + 0 + 0 + + + 5mm + 10mm + 10mm + + 15mm + 10mm + 0mm + + 18mm + 18mm + + + 0pc + + + + + justify + false + + + 11 + 8 + + + 1.4 + + + + + + + 0.8em + + + + + + 17.4cm + + + + 4pt + 4pt + 4pt + 4pt + + + + 0.1pt + 0.1pt + + + + + 1 + + + + + + + + left + bold + + + pt + + + + + + + + + + + + + + + 0.8em + 0.8em + 0.8em + + + pt + + 0.1em + 0.1em + 0.1em + + + 0.6em + 0.6em + 0.6em + + + pt + + 0.1em + 0.1em + 0.1em + + + 0.4em + 0.4em + 0.4em + + + pt + + 0.1em + 0.1em + 0.1em + + + + + bold + + + pt + + false + 0.4em + 0.6em + 0.8em + + + + + + + + + pt + + + + + 1em + 1em + 1em + #444444 + solid + 0.1pt + 0.5em + 0.5em + 0.5em + 0.5em + 0.5em + 0.5em + + + + 1 + + #F0F0F0 + + + + + + 0 + 1 + + + 90 + + + + + '1' + &admon_gfx_path; + + + + + + figure after + example before + equation before + table before + procedure before + + + + 1 + + + + 0.8em + 0.8em + 0.8em + 0.1em + 0.1em + 0.1em + + + + + + + + + + + + + + + + + diff --git a/spring-ldap/docs/reference/styles/html.css b/spring-ldap/docs/reference/styles/html.css new file mode 100644 index 00000000..c603be90 --- /dev/null +++ b/spring-ldap/docs/reference/styles/html.css @@ -0,0 +1,270 @@ +BODY { + text-align: justify; + margin-right: 2em; + margin-left: 2em; +} + +A[accesskey^="p"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A[accesskey^="u"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A[accesskey^="h"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A[accesskey^="n"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +A { + color: #003399; + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; +} + +A:active { + color: #003399; +} + +A:visited { + color: #888888; +} + +P { + font-family: Verdana, Arial; +} + +DT { + font-family: Verdana, Arial; + font-size: 12px; +} + +P, DL, DT, DD, BLOCKQUOTE { + color: #000000; + margin-bottom: 3px; + margin-top: 3px; + padding-top: 0px; +} + +OL, UL, P { + margin-top: 6px; + margin-bottom: 6px; +} + +P, BLOCKQUOTE { + font-size: 90%; +} + +P.releaseinfo { + font-size: 100%; + font-weight: bold; + font-family: Verdana, Arial, helvetica, sans-serif; + padding-top: 10px; +} + +P.pubdate { + font-size: 120%; font-weight: bold; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +td { + font-size: 80%; +} + +TD, TH, SPAN { + color: #000000; +} + +TD[width^="40%"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +TABLE[summary^="Navigation header"] tbody tr th[colspan^="3"] { + font-family: Verdana, Arial, helvetica, sans-serif; +} + +BLOCKQUOTE { + margin-right: 0px; +} + +H1, H2, H3, H4, H5, H6 { + color: #000000; + font-weight:500; + margin-top:0px; + padding-top:14px; + font-family: Verdana, Arial, helvetica, sans-serif; + margin-bottom: 0px; +} + +H2.title { + font-weight:800; + margin-bottom: 8px; +} + +H2.subtitle { + font-weight:800; + margin-bottom: 20px; +} + +.firstname, .surname { + font-size: 12px; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +TABLE { + border-collapse: collapse; + border-spacing:0; + border: 1px thin black; + empty-cells: hide; + margin: 10px 0px 30px 50px; + width: 90%; +} + +div.table { + margin: 30px 0 30 0; + border: 1px dashed gray; + padding: 10px; +} + +div.table > p.title { + padding-left: 10px; +} + +table[summary^="Navigation footer"] { + border-collapse: collapse; + border-spacing:0; + border: 1px thin black; + empty-cells: hide; + margin: 0px; + width: 100%; +} + +table[summary^="Note"], table[summary^="Warning"], table[summary^="Tip"] { + border-collapse: collapse; + border-spacing:0; + border: 1px thin black; + empty-cells: hide; + margin: 10px 0 10 -20; + width: 100%; +} + +TD { + padding: 4pt; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +div.warning TD { + text-align: justify; +} + +H1 { + font-size: 150%; +} +H2 { + font-size: 110%; +} +H3 { + font-size: 100%; font-weight: bold; +} +H4 { + font-size: 90%; font-weight: bold; +} +H5 { + font-size: 90%; font-style: italic; +} +H6 { + font-size: 100%; font-style: italic; +} + +TT { + font-size: 110%; + font-family: "Courier New", Courier, monospace; + color: #000000; +} + +.navheader, .navfooter { + background-color: #e4eff3; +} + +PRE { + font-size: 110%; + padding: 5px; + border-style: solid; + border-width: 1px; + border-color: #CCCCCC; + background-color: #F4F4F4; +} + +UL, OL, LI { + list-style: disc; +} + +HR { + width: 100%; + height: 1px; + background-color: #CCCCCC; + border-width: 0px; + padding: 0px; + color: #CCCCCC; +} + +.variablelist { + padding-top: 10; + padding-bottom:10; + margin:0; +} + +.term { + font-weight:bold; +} + +.mediaobject { + padding-top: 30px; + padding-bottom: 30px; +} + +.legalnotice { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + font-style: italic; +} + +.sidebar { + float: right; + margin: 10px 0px 10px 30px; + padding: 10px 20px 20px 20px; + width: 33%; + border: 1px solid black; + background-color: #F4F4F4; + font-size: 14px; +} + +.programlisting * .classname { + font-size: 95%; +} + +.programlisting * .interfacename { + font-size: 95%; +} + +.programlisting * .literal { + font-size: 95%; +} + +.property { + font-family: "Courier New", Courier, monospace; +} diff --git a/spring-ldap/docs/reference/styles/html.xsl b/spring-ldap/docs/reference/styles/html.xsl new file mode 100644 index 00000000..0b35acad --- /dev/null +++ b/spring-ldap/docs/reference/styles/html.xsl @@ -0,0 +1,100 @@ + + + + + +]> + + + + + + + + ../styles/html.css + + + 1 + 0 + 1 + 0 + + + + + + book toc + + + + 3 + + + + + 1 + + + + + + + 1 + &callout_gfx_path; + + + 90 + + + + + '1' + &admon_gfx_path; + + + + + figure after + example before + equation before + table before + procedure before + + + + , + + + + + + + + +
+

Authors

+

+ +

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

Authors

+

+ +

+
+ + + + + + + + + + 1 + +
diff --git a/spring-ldap/docs/reference/styles/tld.to.docbook.xsl b/spring-ldap/docs/reference/styles/tld.to.docbook.xsl new file mode 100644 index 00000000..b0823a14 --- /dev/null +++ b/spring-ldap/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/spring-ldap/docs/reference/styles/xsd.to.docbook.xsl b/spring-ldap/docs/reference/styles/xsd.to.docbook.xsl new file mode 100644 index 00000000..4a9bb86d --- /dev/null +++ b/spring-ldap/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/spring-ldap/itest-openldap-targets.xml b/spring-ldap/itest-openldap-targets.xml new file mode 100644 index 00000000..7edf57ab --- /dev/null +++ b/spring-ldap/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/spring-ldap/itest-targets.xml b/spring-ldap/itest-targets.xml new file mode 100644 index 00000000..d8775077 --- /dev/null +++ b/spring-ldap/itest-targets.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + compiling itest sources + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Integration test report has been generated. +The HTML report is ${target.itestresults.html.dir}/index.html + + + + + diff --git a/spring-ldap/ivy.xml b/spring-ldap/ivy.xml new file mode 100644 index 00000000..774def0a --- /dev/null +++ b/spring-ldap/ivy.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap/license.txt b/spring-ldap/license.txt new file mode 100644 index 00000000..0c8a028d --- /dev/null +++ b/spring-ldap/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/spring-ldap/notice.txt b/spring-ldap/notice.txt new file mode 100644 index 00000000..4e701033 --- /dev/null +++ b/spring-ldap/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/spring-ldap/pom.xml b/spring-ldap/pom.xml new file mode 100644 index 00000000..334113e1 --- /dev/null +++ b/spring-ldap/pom.xml @@ -0,0 +1,176 @@ + + + + spring-ldap + org.springframework.ldap + 1.1.1-SNAPSHOT + + 4.0.0 + org.springframework + spring-ldap + Spring LDAP Core + 1.1.1-SNAPSHOT + + + + + org.codehaus.mojo + javacc-maven-plugin + 2.1 + + + + javacc + + + + + + + + + + + commons-logging + commons-logging + 1.0.4 + provided + + + commons-lang + commons-lang + 2.1 + provided + + + commons-collections + commons-collections + 3.1 + provided + + + com.sun + ldapbp + 1.0 + provided + + + org.acegisecurity + acegi-security + 1.0.1 + true + provided + + + + org.springframework + spring-beans + 2.0 + provided + + + org.springframework + spring-context + 2.0 + provided + + + org.springframework + spring-core + 2.0 + provided + + + org.springframework + spring-dao + 2.0 + provided + + + + junit + junit + 3.8.1 + test + + + easymock + easymock + 1.2_Java1.3 + test + + + gsbase + gsbase + 2.0.1 + test + + + + org.apache.directory.server + apacheds-core + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-core-shared + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-server-jndi + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-server-main + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-protocol-shared + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-protocol-changepw + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-protocol-kerberos + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-kerberos-shared + 1.0-RC3 + test + + + org.apache.directory.server + apacheds-protocol-ldap + 1.0-RC3 + test + + + org.apache.directory.shared + shared-asn1 + 0.9.5.1 + test + + + org.apache.directory.shared + shared-ldap + 0.9.5.1 + test + + + diff --git a/spring-ldap/project.properties b/spring-ldap/project.properties new file mode 100644 index 00000000..ea1803a9 --- /dev/null +++ b/spring-ldap/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-dev +#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/spring-ldap/project.xml b/spring-ldap/project.xml new file mode 100644 index 00000000..683ca71c --- /dev/null +++ b/spring-ldap/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/spring-ldap/readme.txt b/spring-ldap/readme.txt new file mode 100644 index 00000000..94092187 --- /dev/null +++ b/spring-ldap/readme.txt @@ -0,0 +1,82 @@ +Spring-LDAP 1.1.2 (Dec 2006) +----------------------------- +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 DataAccessExceptions, as well as several utilities for working with filters, LDAP +paths and Attributes. + +The DirContextProcessor interface provides the programmer with the possibility to +get callbacks before and after a search, which enables things like processing of +the LDAPv3 feature of RequestControls and ResponseControls. An abstract base class +that simplifies this is provided, as is a full implementation of PagedSearchResult. + +2. RELEASE INFO + +Spring-LDAP requires J2SE 1.4. J2SE 1.4, and javacc version 4.0 is required for building. +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 + +* 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 + +* spring-ldap-buildable-x.x.zip + ** readme.txt - this readme file. + ** license.txt - terms and conditions for use, reproduction and distribution + ** common-build - the ant 1.6 "common build system" used by all spring-ldap projects to compile/build/test + ** repository - build artifacts that are not available from public repositories + ** spring-ldap/build-spring-ldap - master build files to produce this release archive + ** spring-ldap/spring-ldap - project sources + ** spring-ldap/spring-ldap-person - a sample web project. + ** spring-ldap/spring-ldap-article - source code for an article published on java.net. + + +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, Commons Collections, spring-beans, + spring-core, spring-context, spring-dao + +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/spring-ldap/src/itest-openldap/java/conf/ldap-openldap.properties b/spring-ldap/src/itest-openldap/java/conf/ldap-openldap.properties new file mode 100644 index 00000000..57aa1478 --- /dev/null +++ b/spring-ldap/src/itest-openldap/java/conf/ldap-openldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1 +userName=cn=manager,dc=jayway,dc=se +password=secret +base=dc=jayway,dc=se diff --git a/spring-ldap/src/itest-openldap/java/conf/ldap.conf b/spring-ldap/src/itest-openldap/java/conf/ldap.conf new file mode 100644 index 00000000..aa4372ea --- /dev/null +++ b/spring-ldap/src/itest-openldap/java/conf/ldap.conf @@ -0,0 +1,23 @@ +# Start slapd with this file as configuration file: -f ldap.conf +# The first time, fill the database using the load script in +# src/itest/java/setup_data.ldif, like this: +# +# cd \program\openldap +# ldap_add -f ...\src\itest\java\setup_data.ldif -d cn=Manager,dc=jayway,dc=se -W +# +# The password can be seen below + +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/spring-ldap/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml b/spring-ldap/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml new file mode 100644 index 00000000..e905b1c3 --- /dev/null +++ b/spring-ldap/src/itest-openldap/java/conf/ldapTemplateTestContext-openldap.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap/src/itest-openldap/java/org/springframework/ldap/LdapTemplateLookupOpenLdapITest.java b/spring-ldap/src/itest-openldap/java/org/springframework/ldap/LdapTemplateLookupOpenLdapITest.java new file mode 100644 index 00000000..08e2f968 --- /dev/null +++ b/spring-ldap/src/itest-openldap/java/org/springframework/ldap/LdapTemplateLookupOpenLdapITest.java @@ -0,0 +1,217 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +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.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.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.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/spring-ldap/src/itest-openldap/java/org/springframework/ldap/LdapTemplatePagedSearchITest.java b/spring-ldap/src/itest-openldap/java/org/springframework/ldap/LdapTemplatePagedSearchITest.java new file mode 100644 index 00000000..9891ac45 --- /dev/null +++ b/spring-ldap/src/itest-openldap/java/org/springframework/ldap/LdapTemplatePagedSearchITest.java @@ -0,0 +1,153 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; + +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.ldap.support.control.PagedResultsCookie; +import org.springframework.ldap.support.control.PagedResultsRequestControl; +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 = tested.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/spring-ldap/src/itest/java/conf/apacheDsContext.xml b/spring-ldap/src/itest/java/conf/apacheDsContext.xml new file mode 100644 index 00000000..f5230c1e --- /dev/null +++ b/spring-ldap/src/itest/java/conf/apacheDsContext.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + simple + + + ${userName} + + + ${password} + + + + + + + + + + + + + + + + + + + + + objectClass: top + objectClass: domain + objectClass: extensibleObject + dc: jayway + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap/src/itest/java/conf/ldap.properties b/spring-ldap/src/itest/java/conf/ldap.properties new file mode 100644 index 00000000..74163056 --- /dev/null +++ b/spring-ldap/src/itest/java/conf/ldap.properties @@ -0,0 +1,4 @@ +urls=ldap://127.0.0.1:390 +userName=uid=admin,ou=system +password=secret +base=dc=jayway,dc=se diff --git a/spring-ldap/src/itest/java/conf/ldapContextSourceTestContext.xml b/spring-ldap/src/itest/java/conf/ldapContextSourceTestContext.xml new file mode 100644 index 00000000..1d898922 --- /dev/null +++ b/spring-ldap/src/itest/java/conf/ldapContextSourceTestContext.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/spring-ldap/src/itest/java/conf/ldapTemplateAcegiTestContext.xml b/spring-ldap/src/itest/java/conf/ldapTemplateAcegiTestContext.xml new file mode 100644 index 00000000..cf2af0de --- /dev/null +++ b/spring-ldap/src/itest/java/conf/ldapTemplateAcegiTestContext.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap/src/itest/java/conf/ldapTemplateNoBaseSuffixTestContext.xml b/spring-ldap/src/itest/java/conf/ldapTemplateNoBaseSuffixTestContext.xml new file mode 100644 index 00000000..1b033d2f --- /dev/null +++ b/spring-ldap/src/itest/java/conf/ldapTemplateNoBaseSuffixTestContext.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap/src/itest/java/conf/ldapTemplateTestContext.xml b/spring-ldap/src/itest/java/conf/ldapTemplateTestContext.xml new file mode 100644 index 00000000..92408451 --- /dev/null +++ b/spring-ldap/src/itest/java/conf/ldapTemplateTestContext.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-ldap/src/itest/java/log4j.properties b/spring-ldap/src/itest/java/log4j.properties new file mode 100644 index 00000000..f57302e8 --- /dev/null +++ b/spring-ldap/src/itest/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/spring-ldap/src/itest/java/org/springframework/ldap/AttributeCheckAttributesMapper.java b/spring-ldap/src/itest/java/org/springframework/ldap/AttributeCheckAttributesMapper.java new file mode 100644 index 00000000..bd30624b --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/AttributeCheckAttributesMapper.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Attribute; +import javax.naming.directory.Attributes; + +import junit.framework.Assert; + +/** + * Dummy AttributesMapper for testing purposes to check that the received + * Attributes are the expected ones. + * + * @author Mattias Arthursson + */ +class AttributeCheckAttributesMapper implements AttributesMapper { + private String[] expectedAttributes = new String[0]; + + private String[] expectedValues = new String[0];; + + private String[] absentAttributes = new String[0];; + + public Object mapFromAttributes(Attributes attributes) + throws NamingException { + Assert.assertEquals("Values and attributes need to have the same length ", + expectedAttributes.length, expectedValues.length); + for (int i = 0; i < expectedAttributes.length; i++) { + Attribute attribute = attributes.get(expectedAttributes[i]); + Assert.assertNotNull("Attribute " + expectedAttributes[i] + + " was not present", attribute); + Assert.assertEquals(expectedValues[i], attribute.get()); + } + + for (int i = 0; i < absentAttributes.length; i++) { + Assert.assertNull(attributes.get(absentAttributes[i])); + } + + return null; + } + + public void setAbsentAttributes(String[] absentAttributes) { + this.absentAttributes = absentAttributes; + } + + public void setExpectedAttributes(String[] expectedAttributes) { + this.expectedAttributes = expectedAttributes; + } + + public void setExpectedValues(String[] expectedValues) { + this.expectedValues = expectedValues; + } +} \ No newline at end of file diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/AttributeCheckContextMapper.java b/spring-ldap/src/itest/java/org/springframework/ldap/AttributeCheckContextMapper.java new file mode 100644 index 00000000..179606d0 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/AttributeCheckContextMapper.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.Assert; + +import org.springframework.ldap.support.DirContextAdapter; + +/** + * Dummy ContextMapper for testing purposes to check that the received + * Attributes are the expected ones. + * + * @author Mattias Arthursson + */ +class AttributeCheckContextMapper implements ContextMapper { + private String[] expectedAttributes = new String[0]; + + private String[] expectedValues = new String[0]; + + private String[] absentAttributes = new String[0]; + + public Object mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + Assert.assertEquals("Values and attributes need to have the same length ", + expectedAttributes.length, expectedValues.length); + for (int i = 0; i < expectedAttributes.length; i++) { + String attributeValue = adapter + .getStringAttribute(expectedAttributes[i]); + Assert.assertNotNull("Attribute " + expectedAttributes[i] + + " was not present", attributeValue); + Assert.assertEquals(expectedValues[i], attributeValue); + } + + for (int i = 0; i < absentAttributes.length; i++) { + Assert.assertNull(adapter.getStringAttribute(absentAttributes[i])); + } + + return null; + } + + public void setAbsentAttributes(String[] absentAttributes) { + this.absentAttributes = absentAttributes; + } + + public void setExpectedAttributes(String[] expectedAttributes) { + this.expectedAttributes = expectedAttributes; + } + + public void setExpectedValues(String[] expectedValues) { + this.expectedValues = expectedValues; + } +} \ No newline at end of file diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/ConfigEnvHelper.java b/spring-ldap/src/itest/java/org/springframework/ldap/ConfigEnvHelper.java new file mode 100644 index 00000000..20710fae --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/ConfigEnvHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.File; +import java.util.Hashtable; + +import javax.naming.Context; + +import org.apache.directory.server.configuration.MutableServerStartupConfiguration; +import org.apache.directory.server.jndi.ServerContextFactory; +import org.springframework.beans.factory.InitializingBean; + +/** + * Helper class to simplify Spring configuration of ApacheDS. + * + * @author Mattias Arthursson + */ +public class ConfigEnvHelper implements InitializingBean { + private final MutableServerStartupConfiguration configuration; + + private final Hashtable initialEnv; + + private final File workingDir = new File(System + .getProperty("java.io.tmpdir") + + File.separator + "ldaptemplate_apacheds"); + + public ConfigEnvHelper(Hashtable initialEnv, + MutableServerStartupConfiguration configuration) { + + this.initialEnv = initialEnv; + this.configuration = configuration; + } + + public Hashtable getEnv() { + return initialEnv; + } + + public void afterPropertiesSet() throws Exception { + initialEnv.put(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName()); + configuration.setWorkingDirectory(workingDir); + initialEnv.putAll(configuration.toJndiEnvironment()); + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapServerManager.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapServerManager.java new file mode 100644 index 00000000..70101b1a --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapServerManager.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Properties; + +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.ContextNotEmptyException; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + + +import org.apache.directory.server.core.configuration.ShutdownConfiguration; +import org.apache.directory.server.jndi.ServerContextFactory; +import org.apache.directory.server.protocol.shared.store.LdifFileLoader; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.ldap.ContextSource; +import org.springframework.ldap.support.DefaultDirObjectFactory; +import org.springframework.ldap.support.DistinguishedName; + +/** + * Utility class to initialize the apache directory server for use in the + * integration tests. + * + * @author Mattias Arthursson + * + */ +public class LdapServerManager implements InitializingBean, DisposableBean { + + private ContextSource contextSource; + + public void setContextSource(ContextSource contextSource) { + this.contextSource = contextSource; + } + + public void destroy() throws Exception { + Properties env = new Properties(); + env.setProperty(Context.INITIAL_CONTEXT_FACTORY, + ServerContextFactory.class.getName()); + env.setProperty(Context.SECURITY_AUTHENTICATION, "simple"); + env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); + env.setProperty(Context.SECURITY_CREDENTIALS, "secret"); + + ShutdownConfiguration configuration = new ShutdownConfiguration(); + env.putAll(configuration.toJndiEnvironment()); + + new InitialContext(env); + } + + public void afterPropertiesSet() throws Exception { + DirContext ctx = contextSource.getReadWriteContext(); + + // First of all, make sure the database is empty. + Name startingPoint = null; + + // Different test cases have different base paths. This means that the + // starting point will be different. + if (ctx.getEnvironment().get( + DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY) != null) { + startingPoint = DistinguishedName.EMPTY_PATH; + } else { + startingPoint = new DistinguishedName("dc=jayway,dc=se"); + } + + try { + clearSubContexts(ctx, startingPoint); + // Load the ldif to the recently started server + LdifFileLoader loader = new LdifFileLoader(ctx, "setup_data.ldif"); + loader.execute(); + } finally { + ctx.close(); + } + } + + private void clearSubContexts(DirContext ctx, Name name) + throws NamingException { + + NamingEnumeration enumeration = null; + try { + enumeration = ctx.listBindings(name); + while (enumeration.hasMore()) { + Binding element = (Binding) enumeration.next(); + DistinguishedName childName = new DistinguishedName(element + .getName()); + childName.prepend((DistinguishedName) name); + + try { + ctx.destroySubcontext(childName); + } catch (ContextNotEmptyException e) { + clearSubContexts(ctx, childName); + ctx.destroySubcontext(childName); + } + } + } catch (NamingException e) { + e.printStackTrace(); + } finally { + try { + enumeration.close(); + } catch (Exception e) { + // Never mind this + } + } + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateAttributesMapperITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateAttributesMapperITest.java new file mode 100644 index 00000000..53c7e2fd --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateAttributesMapperITest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.AttributesMapper; +import org.springframework.ldap.LdapTemplate; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests the attributes mapper search method. + * + * @author Mattias Arthursson + */ +public class LdapTemplateAttributesMapperITest extends + AbstractDependencyInjectionSpringContextTests { + 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()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateBindUnbindITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateBindUnbindITest.java new file mode 100644 index 00000000..2762f764 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateBindUnbindITest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.EntryNotFoundException; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * 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 + AbstractDependencyInjectionSpringContextTests { + 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("EntryNotFoundException expected"); + } catch (EntryNotFoundException expected) { + assertTrue(true); + } + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateContextExecutorTest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateContextExecutorTest.java new file mode 100644 index 00000000..3e5e6548 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateContextExecutorTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.DirContextAdapter; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests for LdapTemplate's context executor methods. + * + * @author Mattias Arthursson + */ +public class LdapTemplateContextExecutorTest extends + AbstractDependencyInjectionSpringContextTests { + + 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/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateContextMapperITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateContextMapperITest.java new file mode 100644 index 00000000..55eda910 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateContextMapperITest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ContextMapper; +import org.springframework.ldap.LdapTemplate; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * 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 + AbstractDependencyInjectionSpringContextTests { + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.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()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateListITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateListITest.java new file mode 100644 index 00000000..d3a67c9b --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateListITest.java @@ -0,0 +1,150 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.CountNameClassPairCallbackHandler; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests for LdapTemplate's list methods. + * + * @author Ulrik Sandberg + */ +public class LdapTemplateListITest extends + AbstractDependencyInjectionSpringContextTests { + + 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()); + assertEquals("ou=groups", list.get(0)); + assertEquals("c=Sweden", list.get(1)); + assertEquals("c=Norway", list.get(2)); + } + + public void testList_Name() { + List list = tested.list(BASE_NAME); + assertEquals(3, list.size()); + assertEquals("ou=groups", list.get(0)); + assertEquals("c=Sweden", list.get(1)); + assertEquals("c=Norway", list.get(2)); + } + + 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()); + assertEquals("ou=groups", list.get(0)); + assertEquals("c=Sweden", list.get(1)); + assertEquals("c=Norway", list.get(2)); + } + + public void testListBindings_Name() { + List list = tested.listBindings(BASE_NAME); + assertEquals(3, list.size()); + assertEquals("ou=groups", list.get(0)); + assertEquals("c=Sweden", list.get(1)); + assertEquals("c=Norway", list.get(2)); + } + + 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/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateLookupITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateLookupITest.java new file mode 100644 index 00000000..ebbd224a --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateLookupITest.java @@ -0,0 +1,220 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests the lookup methods of LdapTemplate. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapTemplateLookupITest extends + AbstractDependencyInjectionSpringContextTests { + + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateTestContext.xml" }; + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.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.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.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()); + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateModifyITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateModifyITest.java new file mode 100644 index 00000000..0e511e3f --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateModifyITest.java @@ -0,0 +1,251 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; + + +import org.springframework.dao.DataAccessException; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.UncategorizedLdapException; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * 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 + AbstractDependencyInjectionSpringContextTests { + 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 { + 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("UncategorizedLdapException expected"); + } catch (UncategorizedLdapException 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(). + * + * @throws NamingException + * @throws DataAccessException + */ + public void testModifyAttributes_DirContextAdapter() + throws DataAccessException, NamingException { + 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/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateNoBaseSuffixITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateNoBaseSuffixITest.java new file mode 100644 index 00000000..98e1076d --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateNoBaseSuffixITest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.EntryNotFoundException; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.support.CountNameClassPairCallbackHandler; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * 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 + AbstractDependencyInjectionSpringContextTests { + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateNoBaseSuffixTestContext.xml" }; + } + + /** + * This method depends on a DirObjectFactory ({@link org.springframework.ldap.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("EntryNotFoundException expected"); + } catch (EntryNotFoundException expected) { + assertTrue(true); + } + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateRecursiveDeleteITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateRecursiveDeleteITest.java new file mode 100644 index 00000000..a793ffb1 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateRecursiveDeleteITest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Tests the recursive modification methods (unbind and the protected delete + * methods) of LdapTemplate. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ +public class LdapTemplateRecursiveDeleteITest extends + AbstractDependencyInjectionSpringContextTests { + 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 { + 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 (EntryNotFoundException 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 (EntryNotFoundException expected) { + // expected + } + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateRenameITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateRenameITest.java new file mode 100644 index 00000000..cb871519 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateRenameITest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.EntryNotFoundException; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * 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 + AbstractDependencyInjectionSpringContextTests { + 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 { + 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 (EntryNotFoundException 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/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchCountITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchCountITest.java new file mode 100644 index 00000000..b631d071 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchCountITest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ddsteps.spring.DDStepsSpringTestCase; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.support.CountNameClassPairCallbackHandler; +import org.springframework.ldap.support.DistinguishedName; + +/** + * Tests for LdapTemplate's search methods. This test class tests the generic + * search methods - the ones that take a SearchResultCallbackHandler. + * + * NOTE: The tests in this class expects an LDAP Server to be running with + * correct data in it. See Readme.TXT under /src/iutest/ for information on how + * to set up LDAP Server and data. + * + * Use DDSteps in order to get data-driven tests - use the same method over and + * over with data from Excel. The instance variables base, filter and + * expectedResults are automatically filled with new data for each row specified + * in LdapTemplateSearchCountITest.xls. See ddsteps.org for more information. + * + * @author Mattias Arthursson + */ +public class LdapTemplateSearchCountITest extends DDStepsSpringTestCase { + + private static final boolean DONT_RETURN_OBJECTS = false; + + private String base; + + private String filter; + + private int expectedResults; + + private int searchScope; + + private LdapTemplate tested; + + protected String[] getConfigLocations() { + return new String[] { "/conf/ldapTemplateNoBaseSuffixTestContext.xml" }; + } + + public void testSearch_Count() { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + tested.search(base, filter, handler); + assertEquals(expectedResults, handler.getNoOfRows()); + } + + public void testSearch_Count_DN() { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + tested.search(new DistinguishedName(base), filter, handler); + assertEquals(expectedResults, handler.getNoOfRows()); + } + + public void testSearch_Count_SearchScope() { + CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler(); + tested.search(base, filter, searchScope, DONT_RETURN_OBJECTS, handler); + assertEquals(expectedResults, handler.getNoOfRows()); + } + + public void setBase(String base) { + this.base = base; + } + + public void setExpectedResults(int expectedResults) { + this.expectedResults = expectedResults; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public void setSearchScope(int searchScope) { + this.searchScope = searchScope; + } + + public void setTested(LdapTemplate tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchCountITest.xls b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchCountITest.xls new file mode 100644 index 00000000..576261b9 Binary files /dev/null and b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchCountITest.xls differ diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchResultITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchResultITest.java new file mode 100644 index 00000000..f450ae9a --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/LdapTemplateSearchResultITest.java @@ -0,0 +1,178 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.DistinguishedName; +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/spring-ldap/src/itest/java/org/springframework/ldap/Person.java b/spring-ldap/src/itest/java/org/springframework/ldap/Person.java new file mode 100644 index 00000000..bfe7b75f --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/Person.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/spring-ldap/src/itest/java/org/springframework/ldap/PersonAttributesMapper.java b/spring-ldap/src/itest/java/org/springframework/ldap/PersonAttributesMapper.java new file mode 100644 index 00000000..caa58e51 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/PersonAttributesMapper.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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/spring-ldap/src/itest/java/org/springframework/ldap/PersonContextMapper.java b/spring-ldap/src/itest/java/org/springframework/ldap/PersonContextMapper.java new file mode 100644 index 00000000..f93c726a --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/PersonContextMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ContextMapper; +import org.springframework.ldap.support.DirContextAdapter; + +/** + * Dummy implemention of ContextMapper for use in the integration tests and for + * illustration purposes. + * + * @author Mattias Arthursson + */ +public class PersonContextMapper implements ContextMapper { + + /* (non-Javadoc) + * @see org.springframework.ldap.ContextMapper#mapFromContext(java.lang.Object) + */ + public Object mapFromContext(Object ctx) { + DirContextAdapter adapter = (DirContextAdapter) ctx; + Person person = new Person(); + person.setFullname(adapter.getStringAttribute("cn")); + person.setLastname(adapter.getStringAttribute("sn")); + person.setPhone(adapter.getStringAttribute("telephoneNumber")); + person.setDescription(adapter.getStringAttribute("description")); + return person; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/support/DnParsePerformanceITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/support/DnParsePerformanceITest.java new file mode 100644 index 00000000..c9a69dd9 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/support/DnParsePerformanceITest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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"); + + 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 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/spring-ldap/src/itest/java/org/springframework/ldap/support/LdapContextSourcelITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/support/LdapContextSourcelITest.java new file mode 100644 index 00000000..10fb775c --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/support/LdapContextSourcelITest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Hashtable; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.springframework.ldap.support.LdapContextSource; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Integration tests for ContextSourceImpl. + * + * @author Mattias Arthursson + */ +public class LdapContextSourcelITest extends + AbstractDependencyInjectionSpringContextTests { + + private LdapContextSource 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(LdapContextSource tested) { + this.tested = tested; + } +} diff --git a/spring-ldap/src/itest/java/org/springframework/ldap/support/LdapContextSourcelMultiServerITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/support/LdapContextSourcelMultiServerITest.java new file mode 100644 index 00000000..743d1174 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/support/LdapContextSourcelMultiServerITest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/spring-ldap/src/itest/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSourceITest.java b/spring-ldap/src/itest/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSourceITest.java new file mode 100644 index 00000000..a11a4a74 --- /dev/null +++ b/spring-ldap/src/itest/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSourceITest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.AttributesMapper; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.support.authentication.AcegiAuthenticationSource; +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 testSearchIndiviualAuthentication() { + 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/spring-ldap/src/itest/java/setup_data.ldif b/spring-ldap/src/itest/java/setup_data.ldif new file mode 100644 index 00000000..885a8ca4 --- /dev/null +++ b/spring-ldap/src/itest/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/spring-ldap/src/itest/java/teardown_data.ldif b/spring-ldap/src/itest/java/teardown_data.ldif new file mode 100644 index 00000000..f6ada1b2 --- /dev/null +++ b/spring-ldap/src/itest/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/spring-ldap/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java b/spring-ldap/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java new file mode 100644 index 00000000..10089337 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/AttributesIntegrityViolationException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataIntegrityViolationException; + +/** + * Exception that indicates that an invalid or missing Attribute has been + * supplied to an LDAP operation. + * + * @author Mattias Arthursson + * + */ +public class AttributesIntegrityViolationException extends + DataIntegrityViolationException { + + private static final long serialVersionUID = -6368616096960202571L; + + public AttributesIntegrityViolationException(String msg) { + super(msg); + } + + public AttributesIntegrityViolationException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/AttributesMapper.java b/spring-ldap/src/main/java/org/springframework/ldap/AttributesMapper.java new file mode 100644 index 00000000..bc6af080 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/AttributesMapper.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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 LdapTemplate class. + *

+ * Typically used in LdapTemplate's search methods. AttributeMapper objects are + * typically stateless and thus reusable; they are ideal for implementing + * attribute-mapping logic in one place. + *

+ * Alternatively, consider using a {@link ContextMapper} in stead. + * + * @see org.springframework.ldap.LdapTemplate#search(Name, String, + * 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/spring-ldap/src/main/java/org/springframework/ldap/AuthenticationSource.java b/spring-ldap/src/main/java/org/springframework/ldap/AuthenticationSource.java new file mode 100644 index 00000000..7a8874c1 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/AuthenticationSource.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap; + +/** + * An AuthenticationSource is responsible for providing the principal 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 (userName). + */ + public String getPrincipal(); + + /** + * Get the credentials to use when creating an authenticated context. + * + * @return the credentials (userName). + */ + public String getCredentials(); +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/BadLdapGrammarException.java b/spring-ldap/src/main/java/org/springframework/ldap/BadLdapGrammarException.java new file mode 100644 index 00000000..a7d55dbd --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/BadLdapGrammarException.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.InvalidDataAccessResourceUsageException; + +/** + * 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 + InvalidDataAccessResourceUsageException { + + private static final long serialVersionUID = 961612585331409470L; + + public BadLdapGrammarException(String message) { + super(message); + } + + public BadLdapGrammarException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java b/spring-ldap/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java new file mode 100644 index 00000000..6f42103d --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/CollectingNameClassPairCallbackHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +/** + * A NameClassPairCallbackHandler to collect all results in an internal List. + * + * @see org.springframework.ldap.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/spring-ldap/src/main/java/org/springframework/ldap/ContextAssembler.java b/spring-ldap/src/main/java/org/springframework/ldap/ContextAssembler.java new file mode 100644 index 00000000..da2ef350 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/ContextAssembler.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap; + +/** + * 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/spring-ldap/src/main/java/org/springframework/ldap/ContextExecutor.java b/spring-ldap/src/main/java/org/springframework/ldap/ContextExecutor.java new file mode 100644 index 00000000..70ac8790 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/ContextExecutor.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +/** + * Interface for delegating an actual operation to be performed on an + * DirContext. For searches, use {@link org.springframework.ldap.SearchExecutor} in + * stead. A typical usage of this interface could be e.g.: + * + *

+ * ContextExecutor executor = new ContextExecutor(){
+ *   public Object executeWithContext(DirContext ctx) throws NamingException{
+ *     return ctx.lookup(dn);
+ *   }
+ * };
+ * 
+ * + * @see org.springframework.ldap.LdapTemplate#executeReadOnly(ContextExecutor) + * @see org.springframework.ldap.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/spring-ldap/src/main/java/org/springframework/ldap/ContextMapper.java b/spring-ldap/src/main/java/org/springframework/ldap/ContextMapper.java new file mode 100644 index 00000000..a9effe00 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/ContextMapper.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.directory.SearchResult; + +import org.springframework.ldap.support.DefaultDirObjectFactory; +import org.springframework.ldap.support.DirContextAdapter; + +/** + * An interface used by LdapTemplate to map LDAP Contexts to beans. Responsible + * for mapping from 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}, 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 + * + * @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. + * @return an object built from the data in the context. + */ + public Object mapFromContext(Object ctx); +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/ContextSource.java b/spring-ldap/src/main/java/org/springframework/ldap/ContextSource.java new file mode 100644 index 00000000..fa104841 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/ContextSource.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.DirContext; + +import org.springframework.dao.DataAccessException; + +/** + * Interface used to retrieve and authenticate LDAP contexts. + * + * @see org.springframework.ldap.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 DataAccessException + * if some error occurs creating an DirContext. + */ + public DirContext getReadOnlyContext() throws DataAccessException; + + /** + * Gets a read-write DirContext. + * + * @return A DirContext instance, never null. + * @throws DataAccessException + * if some error occurs creating an DirContext. + */ + public DirContext getReadWriteContext() throws DataAccessException; +} \ No newline at end of file diff --git a/spring-ldap/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java b/spring-ldap/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java new file mode 100644 index 00000000..a7eba9e5 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/DefaultNameClassPairMapper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/spring-ldap/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java b/spring-ldap/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java new file mode 100644 index 00000000..6c27b5c7 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/DefaultNamingExceptionTranslator.java @@ -0,0 +1,96 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.CommunicationException; +import javax.naming.ContextNotEmptyException; +import javax.naming.LimitExceededException; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.directory.InvalidAttributesException; +import javax.naming.directory.InvalidSearchControlsException; +import javax.naming.directory.InvalidSearchFilterException; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +/** + * The default implementation of NamingExceptionTranslator. + * + * @author Mattias Arthursson + */ +public class DefaultNamingExceptionTranslator implements + NamingExceptionTranslator { + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.NamingExceptionTranslator#translate(java.lang.String, + * java.lang.String, java.lang.String, javax.naming.NamingException) + */ + public DataAccessException translate(NamingException namingException) { + + if (namingException instanceof NameNotFoundException) { + return new EntryNotFoundException("Entry not found", + namingException); + } + + if (namingException instanceof InvalidSearchFilterException) { + return new BadLdapGrammarException("Invalid search filter", + namingException); + } + + if (namingException instanceof InvalidSearchControlsException) { + return new InvalidDataAccessApiUsageException( + "Invalid search controls supplied by internal API", + namingException); + } + + if (namingException instanceof NameAlreadyBoundException) { + return new DataIntegrityViolationException("Name already bound", + namingException); + } + + if (namingException instanceof ContextNotEmptyException) { + return new DataIntegrityViolationException( + "The context needs to be empty in order to be removed", + namingException); + } + + if (namingException instanceof InvalidAttributesException) { + return new AttributesIntegrityViolationException( + "Invalid attributes", namingException); + } + + if (namingException instanceof LimitExceededException) { + return new SearchLimitExceededException("Too many objects found", + namingException); + } + + if (namingException instanceof CommunicationException) { + throw new DataRetrievalFailureException( + "Unable to communicate with LDAP server", namingException); + } + + // Fallback - other type of NamingException encountered. + return new UncategorizedLdapException("Operation failed", + namingException); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/DirContextProcessor.java b/spring-ldap/src/main/java/org/springframework/ldap/DirContextProcessor.java new file mode 100644 index 00000000..59f873f5 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/DirContextProcessor.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +/** + * Interface to be called in search by LdapTemplate before and after the actual + * search and enumeration traversal. + * + * @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/spring-ldap/src/main/java/org/springframework/ldap/EntryNotFoundException.java b/spring-ldap/src/main/java/org/springframework/ldap/EntryNotFoundException.java new file mode 100644 index 00000000..9bd81eb9 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/EntryNotFoundException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataRetrievalFailureException; + +/** + * Represents that an entry could not be found. + * + * @author Mattias Arthursson + */ +public class EntryNotFoundException extends DataRetrievalFailureException { + + private static final long serialVersionUID = -1268390922996332424L; + + public EntryNotFoundException(String msg) { + super(msg); + } + + public EntryNotFoundException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/LdapOperations.java b/spring-ldap/src/main/java/org/springframework/ldap/LdapOperations.java new file mode 100644 index 00000000..8cdc0829 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/LdapOperations.java @@ -0,0 +1,1350 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; + +/** + * 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 custom 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 + * SearchResult is passed to the CallbackHandler. Any encountered + * NamingException will be translated using the NamingExceptionTranslator. + * + * @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 DataAccessException + * 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 DataAccessException; + + /** + * Perform a search. 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 SearchResult is passed to the + * CallbackHandler. Any encountered NamingException will be translated using + * the NamingExceptionTranslator. + * + * @param se + * The SearchExecutor to use for performing the actual search. + * @param handler + * The NameClassPairCallbackHandler to which each found entry + * will be passed. + * @throws DataAccessException + * 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) + throws DataAccessException; + + /** + * 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 DataAccessException + * if the operation resulted in a NamingException. + */ + public Object executeReadOnly(ContextExecutor ce) + throws DataAccessException; + + /** + * 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. + * + * @param ce + * The ContextExecutor to which the actual operation on the + * DirContext will be delegated. + * @return the result from the ContextExecutor's operation. + * @throws DataAccessException + * if the operation resulted in a NamingException. + */ + public Object executeReadWrite(ContextExecutor ce) + throws DataAccessException; + + /** + * 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 SearchResults + * to. + */ + public void search(Name base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler); + + /** + * 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 SearchResults + * to. + */ + public void search(String base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler); + + /** + * 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 SearchResults + * to. + * @param processor + * The DirContextProcessor to use before and after the search. + */ + public void search(Name base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor); + + /** + * 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 DataAccessException + * 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); + + /** + * 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 DataAccessException + * 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); + + /** + * 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 DataAccessException + * 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); + + /** + * 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 DataAccessException + * 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); + + /** + * 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. + */ + public void search(String base, String filter, SearchControls controls, + NameClassPairCallbackHandler handler, DirContextProcessor processor); + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Each SearchResult is + * supplied to the specified NameClassPairCallbackHandler. Use the specified + * search scope and return objects flag in search controls. + * + * @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. + * @param handler + * The NameClassPairCallbackHandler to supply the SearchResults + * to. + * @throws DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 no the + * returnObjects 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Only search for the + * specified attributes. 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. Only search for the + * specified attributes. 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified AttributesMapper. The + * default seach 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Attributes in + * each SearchResult is supplied to the specified AttributesMapper. The + * default seach 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified ContextMapper. Only + * look for 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object returned + * in each SearchResult is supplied to the specified ContextMapper. Only + * look for 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object 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 DataAccessException + * 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 DataAccessException; + + /** + * Search for all objects matching the supplied filter. The Object 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Perform a non-recursive listing of the contexts bound to 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * 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 DataAccessException + * 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 DataAccessException; + + /** + * Lookup the supplied DN and return the found object. WARNING: This + * method should only be used if a DirObjectFactory has been specified on + * the ContextFactory. If this is not the case, you will get a new instance + * of the actual DirContext, which is probably not what you want. If, + * however this is what you want, be careful to close the context + * after you finished working with it. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object. + * @throws DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn) throws DataAccessException; + + /** + * Lookup the supplied DN and return the found object. WARNING: This + * method should only be used if a DirObjectFactory has been specified on + * the ContextFactory. If this is not the case, you will get a new instance + * of the actual DirContext, which is probably not what you want. If, + * however this is what you want, be careful to close the context + * after you finished working with it. + * + * @param dn + * The distinguished name of the object to find. + * @return the found object. + * @throws DataAccessException + * if any error occurs. + */ + public Object lookup(String dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, String[] attributes, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, String[] attributes, AttributesMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(Name dn, String[] attributes, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public Object lookup(String dn, String[] attributes, ContextMapper mapper) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void modifyAttributes(Name dn, ModificationItem[] mods) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void modifyAttributes(String dn, ModificationItem[] mods) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void bind(Name dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void bind(String dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(Name dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(String dn) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(Name dn, boolean recursive) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void unbind(String dn, boolean recursive) throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void rebind(Name dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataAccessException + * if any error occurs. + */ + public void rebind(String dn, Object obj, Attributes attributes) + throws DataAccessException; + + /** + * 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 DataIntegrityViolationException + * if newDn is already bound + * @throws DataAccessException + * if any other error occurs. + */ + public void rename(final Name oldDn, final Name newDn) + throws DataAccessException; + + /** + * 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 DataIntegrityViolationException + * if newDn is already bound + * @throws DataAccessException + * if any other error occurs. + */ + public void rename(final String oldDn, final String newDn) + throws DataAccessException; +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/LdapTemplate.java b/spring-ldap/src/main/java/org/springframework/ldap/LdapTemplate.java new file mode 100644 index 00000000..6c462737 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/LdapTemplate.java @@ -0,0 +1,1379 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NameNotFoundException; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +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 javax.naming.directory.SearchResult; + +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.dao.DataAccessException; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; + +/** + * 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.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 NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); + + 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; + } + + /** + * 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.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, boolean, + * org.springframework.ldap.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.LdapOperations#search(java.lang.String, + * java.lang.String, int, boolean, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, int searchScope, + boolean returningObjFlag, NameClassPairCallbackHandler handler) + throws DataAccessException { + + search(base, filter, getDefaultSearchControls(searchScope, + returningObjFlag, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.NameClassPairCallbackHandler, + * org.springframework.ldap.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 NamingException { + return ctx.search(base, filter, controls); + } + }; + + search(se, handler, processor); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.NameClassPairCallbackHandler, + * org.springframework.ldap.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 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 DataAccessException + * 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; + try { + processor.preProcess(ctx); + results = se.executeSearch(ctx); + + while (results.hasMore()) { + NameClassPair result = (NameClassPair) results.next(); + handler.handleNameClassPair(result); + } + processor.postProcess(ctx); + } 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. + } catch (PartialResultException e) { + // Workaround for AD servers not handling referrals correctly. + if (ignorePartialResultException) { + log.debug("PartialResultException encountered and ignored", e); + } else { + throw getExceptionTranslator().translate(e); + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + closeContextAndNamingEnumeration(ctx, results); + } + } + + /** + * 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 DataAccessException + * 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.LdapOperations#search(javax.naming.Name, + * java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(Name base, String filter, + NameClassPairCallbackHandler handler) throws DataAccessException { + + search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void search(String base, String filter, + NameClassPairCallbackHandler handler) throws DataAccessException { + + search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, + DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES), handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws DataAccessException { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, AttributesMapper mapper) throws DataAccessException { + return search(base, filter, getDefaultSearchControls(searchScope, + DONT_RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, int searchScope, + AttributesMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, int searchScope, + AttributesMapper mapper) throws DataAccessException { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, AttributesMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.AttributesMapper) + */ + public List search(String base, String filter, AttributesMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws DataAccessException { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, java.lang.String[], + * org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + String[] attrs, ContextMapper mapper) throws DataAccessException { + + return search(base, filter, getDefaultSearchControls(searchScope, + RETURN_OBJ_FLAG, attrs), mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, int, org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, int searchScope, + ContextMapper mapper) { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, int, org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, int searchScope, + ContextMapper mapper) throws DataAccessException { + + return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, ContextMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, ContextMapper mapper) + throws DataAccessException { + + return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper) + */ + public List search(String base, String filter, SearchControls controls, + ContextMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper) + */ + public List search(Name base, String filter, SearchControls controls, + ContextMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper) + */ + public List search(Name base, String filter, SearchControls controls, + AttributesMapper mapper) { + + return search(base, filter, controls, mapper, + new NullDirContextProcessor()); + } + + /* + * @see org.springframework.ldap.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.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.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper, + * org.springframework.ldap.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.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.AttributesMapper, + * org.springframework.ldap.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.LdapOperations#search(java.lang.String, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper, + * org.springframework.ldap.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.LdapOperations#search(javax.naming.Name, + * java.lang.String, javax.naming.directory.SearchControls, + * org.springframework.ldap.ContextMapper, + * org.springframework.ldap.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.LdapOperations#list(java.lang.String, + * org.springframework.ldap.ListResultCallbackHandler) + */ + public void list(final String base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.ListResultCallbackHandler) + */ + public void list(final Name base, NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.list(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(java.lang.String, + * org.springframework.ldap.NameClassPairMapper) + */ + public List list(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name, + * org.springframework.ldap.NameClassPairMapper) + */ + public List list(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + list(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(javax.naming.Name) + */ + public List list(final Name base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#list(java.lang.String) + */ + public List list(final String base) { + return list(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void listBindings(final String base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.NameClassPairCallbackHandler) + */ + public void listBindings(final Name base, + NameClassPairCallbackHandler handler) { + SearchExecutor searchExecutor = new SearchExecutor() { + public NamingEnumeration executeSearch(DirContext ctx) + throws NamingException { + return ctx.listBindings(base); + } + }; + + search(searchExecutor, handler); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.NameClassPairMapper) + */ + public List listBindings(String base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.NameClassPairMapper) + */ + public List listBindings(Name base, NameClassPairMapper mapper) { + CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler( + mapper); + listBindings(base, handler); + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String) + */ + public List listBindings(final String base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name) + */ + public List listBindings(final Name base) { + return listBindings(base, new DefaultNameClassPairMapper()); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(java.lang.String, + * org.springframework.ldap.ContextMapper) + */ + public List listBindings(String base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#listBindings(javax.naming.Name, + * org.springframework.ldap.ContextMapper) + */ + public List listBindings(Name base, ContextMapper mapper) { + + ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( + mapper); + listBindings(base, handler); + + return handler.getList(); + } + + /* + * @see org.springframework.ldap.LdapOperations#executeReadOnly(org.springframework.ldap.ContextExecutor) + */ + public Object executeReadOnly(ContextExecutor ce) { + DirContext ctx = contextSource.getReadOnlyContext(); + return executeWithContext(ce, ctx); + } + + /* + * @see org.springframework.ldap.LdapOperations#executeReadWrite(org.springframework.ldap.ContextExecutor) + */ + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + closeContext(ctx); + } + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name) + */ + public Object lookup(final Name dn) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String) + */ + public Object lookup(final String dn) throws DataAccessException { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + return ctx.lookup(dn); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final Name dn, final AttributesMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final String dn, final AttributesMapper mapper) + throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes attributes = ctx.getAttributes(dn); + return mapper.mapFromAttributes(attributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * org.springframework.ldap.ContextMapper) + */ + public Object lookup(final Name dn, final ContextMapper mapper) { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * org.springframework.ldap.ContextMapper) + */ + public Object lookup(final String dn, final ContextMapper mapper) + throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Object object = ctx.lookup(dn); + return mapper.mapFromContext(object); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * java.lang.String[], org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final Name dn, final String[] attributes, + final AttributesMapper mapper) throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + return mapper.mapFromAttributes(filteredAttributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * java.lang.String[], org.springframework.ldap.AttributesMapper) + */ + public Object lookup(final String dn, final String[] attributes, + final AttributesMapper mapper) throws DataAccessException { + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + return mapper.mapFromAttributes(filteredAttributes); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(javax.naming.Name, + * java.lang.String[], org.springframework.ldap.ContextMapper) + */ + public Object lookup(final Name dn, final String[] attributes, + final ContextMapper mapper) throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + Attributes filteredAttributes = ctx.getAttributes(dn, + attributes); + DirContextAdapter contextAdapter = new DirContextAdapter( + filteredAttributes, dn); + return mapper.mapFromContext(contextAdapter); + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#lookup(java.lang.String, + * java.lang.String[], org.springframework.ldap.ContextMapper) + */ + public Object lookup(final String dn, final String[] attributes, + final ContextMapper mapper) throws DataAccessException { + + return executeReadOnly(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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.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 NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#modifyAttributes(java.lang.String, + * javax.naming.directory.ModificationItem[]) + */ + public void modifyAttributes(final String dn, final ModificationItem[] mods) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.modifyAttributes(dn, mods); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.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 NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#bind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void bind(final String dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.bind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(javax.naming.Name) + */ + public void unbind(final Name dn) { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(java.lang.String) + */ + public void unbind(final String dn) throws DataAccessException { + doUnbind(dn); + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(javax.naming.Name, + * boolean) + */ + public void unbind(final Name dn, boolean recursive) + throws DataAccessException { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + /* + * @see org.springframework.ldap.LdapOperations#unbind(java.lang.String, + * boolean) + */ + public void unbind(final String dn, boolean recursive) + throws DataAccessException { + if (!recursive) { + doUnbind(dn); + } else { + doUnbindRecursively(dn); + } + } + + private void doUnbind(final Name dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbind(final String dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.unbind(dn); + return null; + } + }); + } + + private void doUnbindRecursively(final Name dn) throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + deleteRecursively(ctx, new DistinguishedName(dn)); + return null; + } + }); + } + + private void doUnbindRecursively(final String dn) + throws DataAccessException { + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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 DataAccessException + * if any error occurs + */ + protected void deleteRecursively(DirContext ctx, DistinguishedName name) + throws DataAccessException { + + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } finally { + try { + enumeration.close(); + } catch (Exception e) { + // Never mind this + } + } + } + + /* + * @see org.springframework.ldap.LdapOperations#rebind(javax.naming.Name, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final Name dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rebind(java.lang.String, + * java.lang.Object, javax.naming.directory.Attributes) + */ + public void rebind(final String dn, final Object obj, + final Attributes attributes) throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rebind(dn, obj, attributes); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rename(javax.naming.Name, + * javax.naming.Name) + */ + public void rename(final Name oldDn, final Name newDn) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws NamingException { + ctx.rename(oldDn, newDn); + return null; + } + }); + } + + /* + * @see org.springframework.ldap.LdapOperations#rename(java.lang.String, + * java.lang.String) + */ + public void rename(final String oldDn, final String newDn) + throws DataAccessException { + + executeReadWrite(new ContextExecutor() { + public Object executeWithContext(DirContext ctx) + throws 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. + } + } + } + + /** + * Get the NamingExceptionTranslator that will be used by this instance. If + * no exceptionTranslator has been set, a default instance will be created. + * + * @return the NamingExceptionTranslator to be used by this instance. + */ + public NamingExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + /** + * Set the NamingExceptionTranslator to be used by this instance. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + + this.exceptionTranslator = exceptionTranslator; + } + + 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } + + /** + * A CollectingNameClassPairCallbackHandler to wrap an AttributesMapper. + * That is, the found object is extracted from the {@link Attributes} of + * each {@link SearchResult}, and then passed to the specified + * AttributesMapper for translation. This class needs to be nested, since we + * want to be able to get hold of the exception translator of this instance. + * + * @author Mattias Arthursson + * @author Ulrik Sandberg + */ + public class AttributesMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private AttributesMapper mapper; + + public AttributesMapperCallbackHandler(AttributesMapper mapper) { + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a SearchResult and pass its attributes to + * the 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 (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } + + /** + * 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 + */ + public class ContextMapperCallbackHandler extends + CollectingNameClassPairCallbackHandler { + private ContextMapper mapper; + + public ContextMapperCallbackHandler(ContextMapper mapper) { + this.mapper = mapper; + } + + /** + * Cast the NameClassPair to a {@link Binding} and pass its attributes + * to the ContextMapper. + * + * @param nameClassPair + * a SearchResult 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 EntryNotFoundException( + "SearchResult did not contain any object."); + } + return mapper.mapFromContext(object); + } + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java b/spring-ldap/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java new file mode 100644 index 00000000..0698a9c6 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/NameClassPairCallbackHandler.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +/** + * Callback interface used by LdapTemplate's 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/spring-ldap/src/main/java/org/springframework/ldap/NameClassPairMapper.java b/spring-ldap/src/main/java/org/springframework/ldap/NameClassPairMapper.java new file mode 100644 index 00000000..acf8cd14 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/NameClassPairMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/spring-ldap/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java b/spring-ldap/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java new file mode 100644 index 00000000..cd6e7a4c --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/NamingExceptionTranslator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.springframework.dao.DataAccessException; + +/** + * Interface to be implemented by classes that can translate between + * NamingExceptions and DataAccessExceptions. + * + * @author Mattias Arthursson + * + */ +public interface NamingExceptionTranslator { + /** + * Translate the given NamingException into a generic data access exception. + * @param namingException + * the offending NamingException. + * + * @return the DataAccessException to throw. + */ + public DataAccessException translate(NamingException namingException); +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/SearchExecutor.java b/spring-ldap/src/main/java/org/springframework/ldap/SearchExecutor.java new file mode 100644 index 00000000..34b474b5 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/SearchExecutor.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +/** + * Interface for delegating an actual search operation. The typical + * implementation of executeSearch would be something like: + * + *

+ * SearchExecutor executor = new SearchExecutor(){
+ *   public NamingEnumeration executeSearch(DirContext ctx) throws NamingException{
+ *     return ctx.search(dn, filter, searchControls);
+ *   }
+ * }
+ * 
+ * + * @see org.springframework.ldap.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/spring-ldap/src/main/java/org/springframework/ldap/SearchLimitExceededException.java b/spring-ldap/src/main/java/org/springframework/ldap/SearchLimitExceededException.java new file mode 100644 index 00000000..49806334 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/SearchLimitExceededException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.DataRetrievalFailureException; + +/** + * Indicates that the search limit was exceeded in a search. + * + * @author Mattias Arthursson + * + */ +public class SearchLimitExceededException extends DataRetrievalFailureException { + private static final long serialVersionUID = 6899885947075235580L; + + public SearchLimitExceededException(String msg) { + super(msg); + } + + public SearchLimitExceededException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/UncategorizedLdapException.java b/spring-ldap/src/main/java/org/springframework/ldap/UncategorizedLdapException.java new file mode 100644 index 00000000..2423a3b7 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/UncategorizedLdapException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.dao.UncategorizedDataAccessException; + +/** + * Indicates that an unknown NamingException has occurred. + * + * @author Mattias Arthursson + */ +public class UncategorizedLdapException extends + UncategorizedDataAccessException { + + private static final long serialVersionUID = -3319936235493869823L; + + public UncategorizedLdapException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/package.html b/spring-ldap/src/main/java/org/springframework/ldap/package.html new file mode 100644 index 00000000..f160a4a2 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/package.html @@ -0,0 +1,3 @@ + +The core package of the Spring-LDAP library. + \ No newline at end of file diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/AbstractContextSource.java b/spring-ldap/src/main/java/org/springframework/ldap/support/AbstractContextSource.java new file mode 100644 index 00000000..d972ebe1 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/AbstractContextSource.java @@ -0,0 +1,459 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.AuthenticationSource; +import org.springframework.ldap.ContextSource; +import org.springframework.ldap.DefaultNamingExceptionTranslator; +import org.springframework.ldap.NamingExceptionTranslator; + +/** + * Abstract implementation of the 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 AuthenticationSource is set, this will be used for getting user name + * and password for each new connection, otherwise a default one will be created + * using the specified userName 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.LdapTemplate + * @see org.springframework.ldap.support.DefaultDirObjectFactory + * @see org.springframework.ldap.support.LdapContextSource + * @see org.springframework.ldap.support.DirContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public abstract class AbstractContextSource implements ContextSource, + 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; + + protected String userName = ""; + + 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 NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); + + private static final Log log = LogFactory.getLog(LdapContextSource.class); + + public static final String SUN_LDAP_POOLING_FLAG = "com.sun.jndi.ldap.connect.pool"; + + private static final String JDK_142 = "1.4.2"; + + public DirContext getReadOnlyContext() { + if (!anonymousReadOnly) { + return createContext(getAuthenticatedEnv()); + } else { + return createContext(getAnonymousEnv()); + } + } + + public DirContext getReadWriteContext() { + return createContext(getAuthenticatedEnv()); + } + + /** + * Default implementation of setting the environment up to be authenticated. + * Override in subclass if necessary. This is needed for Active Directory + * connectivity, for example. + * + * @param env + * the environment to modify. + */ + protected void setupAuthenticatedEnvironment(Hashtable env) { + env + .put(Context.SECURITY_PRINCIPAL, authenticationSource + .getPrincipal()); + log.debug("Principal: '" + userName + "'"); + 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 (base != null) { + 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, should not) specify the + * full distinguished names in the operations performed. + * + * @param base + * the base suffix. + */ + public void setBase(String base) { + this.base = new DistinguishedName(base); + } + + /** + * 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. + */ + 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 getExceptionTranslator().translate(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; + } + + /** + * 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; + } + + /** + * 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 (base != null && 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(userName)) { + log + .warn("Property 'userName' not set - " + + "anonymous context will be used for read-write operations"); + } else if (StringUtils.isBlank(password)) { + log.warn("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 { + 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 (base != null) { + // 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 name (principal) to use for getting authenticated contexts. + * + * @param userName + * the user name. + */ + public void setUserName(String userName) { + this.userName = 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; + } + + /** + * 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. + * + * @param pooled + * whether Contexts should be pooled. + */ + public void setPooled(boolean pooled) { + this.pooled = 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; + } + + public void setAuthenticationSource( + AuthenticationSource authenticationProvider) { + this.authenticationSource = authenticationProvider; + } + + /** + * 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 and anonymous environment should be + * used for read-only operations, false otherwise. + */ + public void setAnonymousReadOnly(boolean anonymousReadOnly) { + this.anonymousReadOnly = anonymousReadOnly; + } + + /** + * Set the NamingExceptionTranslator to be used by this instance. By + * default, a {@link DefaultNamingExceptionTranslator} will be used. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + + /** + * Get the NamingExceptionTranslator used by this instance. + * + * @return the NamingExceptionTranslator. + */ + public NamingExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + /** + * 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 userName; + } + + public String getCredentials() { + return password; + } + + } + + protected String[] getUrls() { + return urls; + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/AggregateDirContextProcessor.java b/spring-ldap/src/main/java/org/springframework/ldap/support/AggregateDirContextProcessor.java new file mode 100644 index 00000000..e24a88bd --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/AggregateDirContextProcessor.java @@ -0,0 +1,72 @@ +package org.springframework.ldap.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.DirContextProcessor; + +/** + * Manages a sequence of 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 DirContextProcessors. + * + * @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.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.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/spring-ldap/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java b/spring-ldap/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java new file mode 100644 index 00000000..9d0a7fa1 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/AttributeModificationsAware.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ModificationItem; + +/** + * Indicates that the implementor 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 or added or removed. + * + * @return an array of modification items + */ + public ModificationItem[] getModificationItems(); +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java b/spring-ldap/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java new file mode 100644 index 00000000..8f017beb --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/CountNameClassPairCallbackHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +import org.springframework.ldap.NameClassPairCallbackHandler; + +/** + * A 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/spring-ldap/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java b/spring-ldap/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java new file mode 100644 index 00000000..f860cbfb --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/DefaultDirObjectFactory.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.directory.Attributes; +import javax.naming.spi.DirObjectFactory; + +/** + * Default implementation of the DirObjectFactory interface. Creates a + * 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. + */ + 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/spring-ldap/src/main/java/org/springframework/ldap/support/DefaultDnParserFactory.java b/spring-ldap/src/main/java/org/springframework/ldap/support/DefaultDnParserFactory.java new file mode 100644 index 00000000..e0ccbdc7 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/DefaultDnParserFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.StringReader; + +/** + * 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/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextAdapter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextAdapter.java new file mode 100644 index 00000000..f4fcd6f7 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextAdapter.java @@ -0,0 +1,1271 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; +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.collections.CollectionUtils; +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.DefaultNamingExceptionTranslator; +import org.springframework.ldap.NamingExceptionTranslator; + +/** + * 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.support.DefaultDirObjectFactory} in your + * ContextSource you may receive instances of this class from searches and + * lookups. This can be particularly useful when updating data, since this class + * implements + * {@link org.springframework.ldap.support.AttributeModificationsAware}, + * providing a {@link #getModificationItems()} method. + * + * @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 attrs; + + private DistinguishedName dn; + + private DistinguishedName base; + + private boolean updateMode = false; + + private Attributes updatedAttrs; + + private NamingExceptionTranslator exceptionTranslator; + + /** + * 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.attrs = attrs; + } else { + this.attrs = 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.attrs = (Attributes) master.attrs.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. + */ + protected 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 = attrs.getAll(); + } + + try { + while (attributesEnumeration.hasMore()) { + Attribute oneAttribute = (Attribute) attributesEnumeration + .next(); + tmpList.add(oneAttribute.getID()); + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(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 getExceptionTranslator().translate(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 = attrs.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) { + // Attribute has been removed. + modificationList.add(new ModificationItem( + DirContext.REMOVE_ATTRIBUTE, changedAttr)); + } else { + // Collect all modifications to attribute individually (this also + // covers additions to a previously non-existant attribute). + Collection oldValues = new LinkedList(); + Collection newValues = new LinkedList(); + + collectAttributeValues(oldValues, currentAttribute); + collectAttributeValues(newValues, changedAttr); + Collection myModifications = new LinkedList(); + + Collection addedValues = CollectionUtils.subtract(newValues, + oldValues); + Collection removedValues = CollectionUtils.subtract(oldValues, + newValues); + + collectModifications(DirContext.ADD_ATTRIBUTE, changedAttr, + addedValues, myModifications); + collectModifications(DirContext.REMOVE_ATTRIBUTE, changedAttr, + removedValues, 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(int modificationType, Attribute attr, + Collection values, Collection c) { + if (values.size() > 0) { + BasicAttribute modificationAttribute = new BasicAttribute(attr + .getID()); + for (Iterator iter = values.iterator(); iter.hasNext();) { + modificationAttribute.add(iter.next()); + } + c + .add(new ModificationItem(modificationType, + modificationAttribute)); + } + } + + private void collectAttributeValues(Collection valueCollection, + Attribute attribute) throws NamingException { + + if (attribute == null) { + return; + } + + NamingEnumeration attributeValues = attribute.getAll(); + while (attributeValues.hasMoreElements()) { + Object value = (Object) attributeValues.nextElement(); + valueCollection.add(value); + } + } + + /** + * Compare the existing attribute name with the value in + * value. + *

+ * Also handles the case where the value has been reset to the original + * value after a previous change. For example, changing a to + * b and then back to a again must result in + * this method returning true so the first change can be + * overwritten with the latest change. TODO Do the null checks on the value + * instead + * + * @param name + * Name of the original attribute. + * @param value + * Value to check if it has been changed. + * @return true if there has been a change compared to original attribute, + * or a previous update + */ + private boolean isChanged(String name, Object value) { + Attribute orig = attrs.get(name); + Attribute prev = updatedAttrs.get(name); + + // FALSE if both are null it is not changed + // TODO Also include prev in null check + if (orig == null && value == null) { + return false; + } + + // TRUE if existing value is null or does not contain one value + if (orig == null || orig.size() != 1) { + return true; + } + + // TRUE if existing value is not null and the new one is null + if (orig != null && value == null) { + return true; + } + + // TRUE if we can't access the value + Object obj = null; + try { + obj = orig.get(0); + } catch (NamingException e) { + return true; + } + + if (prev == null) { + // TRUE if the value is not equal + return !value.equals(obj); + } else { + // TRUE if we can't access the value + Object prevObj = null; + try { + prevObj = prev.get(0); + } catch (NamingException e) { + return true; + } + // TRUE if the value is not equal + return !value.equals(obj) || !value.equals(prevObj); + } + } + + /** + * 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 = attrs.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 attrs.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 = attrs.get(name); + if (oneAttr == null) { + return null; + } + try { + return oneAttr.get(); + } catch (NamingException e) { + throw getExceptionTranslator().translate(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) { + attrs.put(name, value); + } + + // updating entry + if (updateMode && isChanged(name, value)) { + BasicAttribute attribute = new BasicAttribute(name); + if (value != null) { + attribute.add(value); + } + updatedAttrs.put(attribute); + } + } + + /* + * @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 + attrs.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)) { + attrs.remove(a.getID()); + } else { + // Otherwise it should be set. + attrs.put(a); + } + } + } catch (NamingException e) { + throw getExceptionTranslator().translate(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 = attrs.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 getExceptionTranslator().translate(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 = attrs.get(name); + if (attribute != null) { + for (int i = 0; i < attribute.size(); i++) { + try { + attrSet.add(attribute.get(i)); + } catch (NamingException e) { + throw getExceptionTranslator().translate(e); + } + } + } else { + return null; + } + + return attrSet; + } + + /** + * Set the supplied attribute. + * + * @param attribute + * the attribute to set. + */ + public void setAttribute(Attribute attribute) { + attrs.put(attribute); + } + + /** + * Get all attributes. + * + * @return all attributes. + */ + public Attributes getAttributes() { + return attrs; + } + + /** + * @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) attrs.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 = attrs.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 = attrs.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(); + } + + /** + * Get the NamingExceptionTranslator. + * + * @return the NamingExceptionTranslator to use; if none is specified, + * {@link DefaultNamingExceptionTranslator} is used. + */ + public NamingExceptionTranslator getExceptionTranslator() { + if (exceptionTranslator == null) { + exceptionTranslator = new DefaultNamingExceptionTranslator(); + } + return exceptionTranslator; + } + + /** + * Set the NamingExceptionTranslator to use. + * + * @param exceptionTranslator + * the NamingExceptionTranslator to use. + */ + public void setExceptionTranslator( + NamingExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextOperations.java b/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextOperations.java new file mode 100644 index 00000000..c44f3fbd --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextOperations.java @@ -0,0 +1,161 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.SortedSet; + +import javax.naming.Name; +import javax.naming.directory.DirContext; + +/** + * Interface for DirContextAdapter to simplify mock testing. + * + * @author Mattias Arthursson + */ +public interface DirContextOperations extends DirContext, + AttributeModificationsAware { + + /** + * Gets the update mode. 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. + * + * @param name + * name of the attribute. + * @return the value of the attribute. + */ + public String getStringAttribute(String name); + + /** + * Get the value of an Object attribute. + * + * @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); + + /** + * Update the attributes. This will mean that the getters + * (getStringAttribute methods) will return the updated values. Remove the + * attributes to be updated. + */ + 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 DN, without 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); +} \ No newline at end of file diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextSource.java b/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextSource.java new file mode 100644 index 00000000..e69d6a0a --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/DirContextSource.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.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/spring-ldap/src/main/java/org/springframework/ldap/support/DistinguishedName.java b/spring-ldap/src/main/java/org/springframework/ldap/support/DistinguishedName.java new file mode 100644 index 00000000..050ea203 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/DistinguishedName.java @@ -0,0 +1,647 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.util.ListComparator; + +/** + * Default implementation of a Name corresponding to an LDAP path. A + * DistinguishedName implementation is included in JDK1.5 (LdapName), but not in + * prior releases. + * + * An 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 Name interface specifies that the + * most significant part be in position 0, i.e. + * + * The path: uid=adam.skogman, ou=People, ou=EU Name[0]: ou=EU Name[1]: + * ou=People Name[2]: uid=adam.skogman + *

+ * Useful for parsing and building LDAP paths. + * + *

+ * DistinguishedName path = new DistinguishedName();
+ * path.addLast("cn", entry.getUid());
+ * path.addLast("ou", "users");
+ * path.append(new DistinguishedName(helpdesk.getSomeSuffix()));
+ * String dn = path.toString();
+ * 
+ * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class DistinguishedName implements Name { + 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 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 Name. The parts of + * the supplied Name must be syntactically correct LdapRdns. + * + * @param name + * the 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 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 LdapRdn at a specified position. + * + * @param index + * the LdapRdn to retrieve. + * @return the LdapRdn at the requested position. + */ + public LdapRdn getLdapRdn(int index) { + return (LdapRdn) names.get(index); + } + + /** + * Get the name list. + * + * @return the list of LdapRdns that this DistinguishedName consists of. + */ + public List getNames() { + return names; + } + + /** + * Get the String representation of this DistinguishedName. + * + * @return a syntactically correct, escaped String representation of the + * DistinguishedName. + */ + public String toString() { + return encode(); + } + + /** + * 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() { + + // 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()) + 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 a ldap path contains another path. + * + * @param path + * the path to check. + * @return true if the supplied path is conained in this instance, false + * otherwise. + */ + public boolean contains(DistinguishedName path) { + + List shortlist = path.getNames(); + + // this path must be at least as long + if (getNames().size() < shortlist.size()) + return false; + + // must have names + if (shortlist.size() == 0) + return false; + + Iterator longiter = getNames().iterator(); + Iterator shortiter = shortlist.iterator(); + + LdapRdn longname = (LdapRdn) longiter.next(); + LdapRdn shortname = (LdapRdn) shortiter.next(); + + // find first match + while (!longname.equals(shortname) && longiter.hasNext()) { + longname = (LdapRdn) longiter.next(); + } + + // Done? + if (!shortiter.hasNext() && longname.equals(shortname)) + return true; + if (!longiter.hasNext()) + return false; + + // compare + while (longname.equals(shortname) && longiter.hasNext() + && shortiter.hasNext()) { + longname = (LdapRdn) longiter.next(); + shortname = (LdapRdn) shortiter.next(); + } + + // Done + if (!shortiter.hasNext() && longname.equals(shortname)) + return true; + else + return false; + + } + + /** + * Add an LDAP path last in this DistinguishedName. E.g.: + * + *
+     * DistinguishedName name1 = new DistinguishedName("c=SE, dc=jayway, dc=se");
+     * DistinguishedName name2 = new DistinguishedName("ou=people");
+     * name1.append(name2);
+     * 
+ * + * will result in ou=people, c=SE, dc=jayway, dc=se + * + * @param path + * the path to append. + */ + public void append(DistinguishedName path) { + getNames().addAll(path.getNames()); + } + + /** + * Add an LDAP path first in this DistinguishedName. E.g.: + * + *
+     * DistinguishedName name1 = new DistinguishedName("ou=people");
+     * DistinguishedName name2 = new DistinguishedName("c=SE, dc=jayway, dc=se");
+     * name1.prepend(name2);
+     * 
+ * + * will result in ou=people, c=SE, dc=jayway, dc=se + * + * @param path + * the path to prepend. + */ + public void prepend(DistinguishedName path) { + ListIterator i = path.getNames().listIterator(path.getNames().size()); + while (i.hasPrevious()) { + names.add(0, i.previous()); + } + } + + /** + * Remove the first part of this DistinguishedName. + * + * @return the removed entry. + */ + public LdapRdn removeFirst() { + return (LdapRdn) names.remove(0); + } + + /** + * Remove the supplied path from the beginning of this DistinguishedName if + * this instance starts with . 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 ldap path ends with a certian path. + * + * If the argument path is empty (no names in path) this methid 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 ldast part of this DistinguishedName. + * + * @return the removed LdapRdn. + */ + public LdapRdn removeLast() { + return (LdapRdn) names.remove(names.size() - 1); + } + + /** + * Add a new LdapRdn using the supplied key and value. + * + * @param key + * the key of the LdapRdn. + * @param value + * the value of the LdapRdn. + */ + public void add(String key, String value) { + names.add(new LdapRdn(key, value)); + } + + /** + * Add the supplied LdapRdn last in the list of Rdns. + * + * @param rdn + * the LdapRdn to add. + */ + public void add(LdapRdn rdn) { + names.add(rdn); + } + + /** + * Add the supplied 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); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/DnParser.java b/spring-ldap/src/main/java/org/springframework/ldap/support/DnParser.java new file mode 100644 index 00000000..7bd29baa --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/DnParser.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + + +/** + * 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/spring-ldap/src/main/java/org/springframework/ldap/support/LdapContextSource.java b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapContextSource.java new file mode 100644 index 00000000..ef3e7597 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapContextSource.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Hashtable; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.ControlFactory; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; + +import com.sun.jndi.ldap.ctl.ResponseControlFactory; + +/** + * ContextSource implementation which creates an InitialLdapContext + * instance. For configuration information, see + * {@link org.springframework.ldap.support.AbstractContextSource AbstractContextSource}. + * + * @see org.springframework.ldap.support.AbstractContextSource + * + * @author Mattias Arthursson + * @author Adam Skogman + * @author Ulrik Sandberg + */ +public class LdapContextSource extends AbstractContextSource { + + private static final Class DEFAULT_RESPONSE_CONTROL_FACTORY = ResponseControlFactory.class; + + private Class responseControlFactory = DEFAULT_RESPONSE_CONTROL_FACTORY; + + protected Hashtable getAnonymousEnv() { + Hashtable env = super.getAnonymousEnv(); + env + .put(LdapContext.CONTROL_FACTORIES, responseControlFactory + .getName() + ".trasig"); + return env; + } + + /* + * @see org.springframework.ldap.support.AbstractContextSource#getDirContextInstance(java.util.Hashtable) + */ + protected DirContext getDirContextInstance(Hashtable environment) + throws NamingException { + return new InitialLdapContext(environment, null); + } + + /** + * Set the ResponseControlFactory to use. Default is + * {@link com.sun.jndi.ldap.ctl.ResponseControlFactory}. The specified + * class needs to be an implementation of + * {@link javax.naming.ldap.ControlFactory}. + * + * @param responseControlFactory + * the ResponseControlFactory to be used. Null means reset to the + * default. + */ + public void setResponseControlFactory(Class responseControlFactory) { + if (responseControlFactory == null) { + this.responseControlFactory = DEFAULT_RESPONSE_CONTROL_FACTORY; + } else if (ControlFactory.class + .isAssignableFrom(responseControlFactory)) { + this.responseControlFactory = responseControlFactory; + } else { + throw new IllegalArgumentException( + "Invalid ReponseControlFactory: " + responseControlFactory + + " is not an implementation of " + + ControlFactory.class.getName()); + } + } + + /** + * @return the current ResponseControlFactory. + */ + Class getResponseControlFactory() { + return responseControlFactory; + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/LdapEncoder.java b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapEncoder.java new file mode 100644 index 00000000..d6525bc2 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapEncoder.java @@ -0,0 +1,235 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.regex.Matcher; +import java.util.regex.Pattern; + + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.BadLdapGrammarException; + +/** + * Helper class to encode and decode ldap names and values. + * + * @author Adam Skogman + */ +public class LdapEncoder { + + static private String[] nameEscapeTable = new String[96]; + + static private String[] filterEscapeTable = new String['\\' + 1]; + + /** + * Pattern for matching escaped ldap name values. + * + * Double escaping: \ -> \\ (in pattern) -> \\\\ (in java string literal) + * + * Group 1: Hex escapes = \XX -> \p{XDigit}{2} Group 2: Ordinary escapes = + * \x -> \. Group 3: Anything but \ [^\\] + * + * Note that the \ is not part of the match. + */ + static private final Pattern VALUE_DECODE_PATTERN = Pattern + .compile("(?:\\\\(\\p{XDigit}{2}))|(?:\\\\(.))|([^\\\\])"); + + 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['\"'] = "\\\""; + // nameEscapeTable['/'] = "\\" + toTwoCharHex('/'); + 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"; + + } + + static protected String toTwoCharHex(char c) { + + String raw = Integer.toHexString(c).toUpperCase(); + + if (raw.length() > 1) + return raw; + else + return "0" + raw; + } + + /** + * All static methods + */ + private LdapEncoder() { + } + + static public 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 + * @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()); + + Matcher matcher = VALUE_DECODE_PATTERN.matcher(value); + + int end = 0; + + while (matcher.find()) { + end = matcher.end(); + // group 1 + if (matcher.group(1) != null) { + // parse as hex = base 16 + try { + char c = (char) Integer.parseInt(matcher.group(1), 16); + decoded.append(c); + } catch (NumberFormatException e) { + throw new BadLdapGrammarException( + "Escaped hex value Could not be parsed. Found '\\" + + matcher.group(1) + "'"); + } + } else if (matcher.group(2) != null) { + // just add, we stripped away the \ + decoded.append(matcher.group(2)); + } else if (matcher.group(3) != null) { + // just add + decoded.append(matcher.group(3)); + } + + } + // end is match + +1 so it should be same as length, i.e. + // last + 1 + if (end < value.length()) { + throw new BadLdapGrammarException( + "RDN could not be parsed fully, remaining '" + + StringUtils.substring(value, end) + "'"); + } + + return decoded.toString(); + + } + +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/LdapRdn.java b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapRdn.java new file mode 100644 index 00000000..f5ccdd6d --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapRdn.java @@ -0,0 +1,198 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.util.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. + */ + 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 + * @param 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 no components have been added. + */ + 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 indet idx. + * @throws IndexOutOfBoundsException + * if no component exists at index idx. + */ + public LdapRdnComponent getComponent(int idx) { + return (LdapRdnComponent) components.get(idx); + } + + /** + * Get a properly rfc2253-encoded String representation to this LdapRdn. + * + * @return an encoded String corresponding to this LdapRdn. + */ + 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); + } + + 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(); + } +} \ No newline at end of file diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/LdapRdnComponent.java b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapRdnComponent.java new file mode 100644 index 00000000..52bd4e72 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/LdapRdnComponent.java @@ -0,0 +1,163 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 { + public static final boolean DONT_DECODE_VALUE = false; + + private String key; + + private String value; + + /** + * Constructs an LdapRdnComponent without decoding the value. + * + * @param key + * the Atttribute 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; + } + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + /** + * Encode key and value to ldap + * + * @return The ldap encoded 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(); + } + + 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"; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + 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/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java b/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java new file mode 100644 index 00000000..1e28a087 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSource.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.authentication; + +import org.acegisecurity.Authentication; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.userdetails.ldap.LdapUserDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.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)) { + throw new IllegalArgumentException( + "The principal property of the authentication object -" + + "needs to be a LdapUserDetails."); + } else { + LdapUserDetails details = (LdapUserDetails) principal; + return details.getDn(); + } + } else { + log.warn("No Authentication object set in SecurityContext - " + + "returning empty String as Principal"); + return ""; + } + } + + /* + * @see org.springframework.ldap.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/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java b/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java new file mode 100644 index 00000000..ed94d937 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecorator.java @@ -0,0 +1,157 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.authentication; + +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.ldap.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 defaultUser. + * + * @return the target's principal if it is not empty, the + * defaultUser 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/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/package.html b/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/package.html new file mode 100644 index 00000000..d15fe6bc --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/authentication/package.html @@ -0,0 +1,3 @@ + +Support classes for custom authentication. + diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessor.java b/spring-ldap/src/main/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessor.java new file mode 100644 index 00000000..3d54d9c4 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessor.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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 into 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(); + 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/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResult.java b/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResult.java new file mode 100644 index 00000000..ccb22460 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResult.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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}. + */ +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/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResultsCookie.java b/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResultsCookie.java new file mode 100644 index 00000000..46d179c2 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResultsCookie.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResultsRequestControl.java b/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResultsRequestControl.java new file mode 100644 index 00000000..ab85e3c6 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/control/PagedResultsRequestControl.java @@ -0,0 +1,200 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.UncategorizedLdapException; +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. + * + * @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; + + public PagedResultsRequestControl(int pageSize) { + this(pageSize, null); + } + + public PagedResultsRequestControl(int pageSize, PagedResultsCookie cookie) { + this.pageSize = pageSize; + this.cookie = cookie; + fallbackResponseControlClass = loadFallbackResponseControlClass(); + } + + public PagedResultsCookie getCookie() { + return cookie; + } + + public int getPageSize() { + return pageSize; + } + + 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.support.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 UncategorizedLdapException( + "Error creating PagedResultsControl", e); + } + } + + /* + * @see org.springframework.ldap.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(); + + // 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/spring-ldap/src/main/java/org/springframework/ldap/support/control/package.html b/spring-ldap/src/main/java/org/springframework/ldap/support/control/package.html new file mode 100644 index 00000000..e8351194 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/control/package.html @@ -0,0 +1,3 @@ + +Support classes for custom request control context processors. + diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java new file mode 100644 index 00000000..e00f8f04 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/AbstractFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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(); + } + + /** + * Prints the query with LDAP encoding to a stringbuffer + * + * @param buff + * The stringbuffer + * @return The very same stringbuffer + */ + public abstract StringBuffer encode(StringBuffer buff); + + /** + * Encodes the filter to a string using the (@link #encode(StringBuffer) + * method. + * + * @return The encoded filter + */ + public String encode() { + StringBuffer buff = new StringBuffer(256); + buff = encode(buff); + return buff.toString(); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return encode(); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/AndFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/AndFilter.java new file mode 100644 index 00000000..4622da1f --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/AndFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter for a logical AND. E.g.: + * + *

+ *     AndFilter filter = new AndFilter();
+ *     filter.and(new EqualsFilter("objectclass", "person");
+ *     filter.and(new EqualsFilter("cn", "Some CN");
+ *     System.out.println(filter.ecode());    
+ * 
+ * + * would result in: (&(objectclass=person)(cn=Some CN)) + * + * @see org.springframework.ldap.support.filter.EqualsFilter + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class AndFilter extends BinaryLogicalFilter { + + private static final String AMPERSAND = "&"; + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return AMPERSAND; + } + + /** + * Add a query to the and expression + * + * @param query + * The query to and with the rest of the and:ed queries. + * @return This LdapAndQuery + */ + public AndFilter and(Filter query) { + queryList.add(query); + return this; + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java new file mode 100644 index 00000000..0c3ef3a2 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/BinaryLogicalFilter.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.filter.Filter#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.support.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.support.filter.Filter#hashCode() + */ + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java new file mode 100644 index 00000000..20fc78f0 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/CompareFilter.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.springframework.ldap.support.LdapEncoder; + +/** + * Abstract superclass for filters to 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 + * @param value + */ + public CompareFilter(String attribute, int value) { + this.attribute = attribute; + this.value = String.valueOf(value); + this.encodedValue = LdapEncoder.filterEncode(this.value); + } + + /* + * @see org.springframework.ldap.support.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.support.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.support.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/spring-ldap/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java new file mode 100644 index 00000000..069d8186 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/EqualsFilter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + + +/** + * A filter for 'equals'. The following code: + * + *
+ * EqualsFilter filter = new EqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + *
(cn=Some CN)
+ * + * @author Adam Skogman + */ +public class EqualsFilter extends CompareFilter { + + private static final String EQUALS_SIGN = "="; + + public EqualsFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Convenience constructor for int values. + * + * @param attribute Name of attribute in filter. + * @param value The value of the attribute in the filter. + */ + public EqualsFilter(String attribute, int value) { + super(attribute, value); + } + + /* + * @see org.springframework.ldap.support.filter.CompareFilter#getCompareString() + */ + protected String getCompareString() { + return EQUALS_SIGN; + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/Filter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/Filter.java new file mode 100644 index 00000000..a5ae4f69 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/Filter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * Common interface for filters. + * + * @author Adam Skogman + */ +public interface Filter { + + /** + * Encodes the filter to a string using the (@link #encode(StringBuffer) + * method. + * + * @return The encoded filter + */ + public String encode(); + + /** + * Prints the query with LDAP encoding to a stringbuffer + * + * @param buff + * The stringbuffer + * @return The very same stringbuffer + */ + public StringBuffer encode(StringBuffer buff); + + /** + * 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 hascode + */ + public int hashCode(); + +} \ No newline at end of file diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java new file mode 100644 index 00000000..871b8dd1 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter to compare >=. LDAP RFC does not allow > comparison. The following + * code: + * + *
+ * GreaterThanOrEqualsFilter filter = new GreaterThanOrEqualsFilter("cn",
+ *         "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn>=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class GreaterThanOrEqualsFilter extends CompareFilter { + + private static final String GREATER_THAN_OR_EQUALS = ">="; + + public GreaterThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public GreaterThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + protected String getCompareString() { + return GREATER_THAN_OR_EQUALS; + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java new file mode 100644 index 00000000..36b5e723 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * A filter to compare <=. LDAP RFC does not allow < comparison. The following + * code: + * + *
+ * LessThanOrEqualsFilter filter = new LessThanOrEqualsFilter("cn", "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ * (cn<=Some CN)
+ * 
+ * + * @author Mattias Arthursson + */ +public class LessThanOrEqualsFilter extends CompareFilter { + + private static final String LESS_THAN_OR_EQUALS = "<="; + + public LessThanOrEqualsFilter(String attribute, String value) { + super(attribute, value); + } + + public LessThanOrEqualsFilter(String attribute, int value) { + super(attribute, value); + } + + protected String getCompareString() { + return LESS_THAN_OR_EQUALS; + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java new file mode 100644 index 00000000..41d46ebd --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/LikeFilter.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.LdapEncoder; + +/** + * This filter allows the user to specify wildcards (*) by not escaping them in + * the filter. The following code: + * + *
+ * LikeFilter filter = new LikeFilter("cn", "foo*");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
+ *  (cn=foo*)
+ * 
+ * + * @author Anders Henja + * @author Mattias Arthursson + */ +public class LikeFilter extends EqualsFilter { + + public LikeFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Encodes a value according to the rules for this filter. + * + * @param value + * Value to encode. + * @return Encoded value. + */ + 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/spring-ldap/src/main/java/org/springframework/ldap/support/filter/NotFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/NotFilter.java new file mode 100644 index 00000000..972edfa5 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/NotFilter.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.apache.commons.lang.Validate; + +/** + * A filter for 'not'. The following code: + * + *
+ * Filter filter = new NotFilter(new EqualsFilter("cn", "foo");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: + * + *
(!(cn=foo))
+ * + * @author Adam Skogman + */ +public class NotFilter extends AbstractFilter { + + private final Filter filter; + + static private final int HASH = "!".hashCode(); + + /** + * Create a filter that negates the outcome of the given filter. + * + * @param filter + * The filter that should be negated. + */ + public NotFilter(Filter filter) { + Validate.notNull(filter); + this.filter = filter; + } + + /** + * @see org.springframework.ldap.support.filter.Filter#encode(java.lang.StringBuffer) + */ + public StringBuffer encode(StringBuffer buff) { + + buff.append("(!"); + filter.encode(buff); + buff.append(')'); + + return buff; + + } + + /** + * Compares key and value before encoding + * + * @see org.springframework.ldap.support.filter.Filter#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; + } + + /** + * hash attribute and value + * + * @see org.springframework.ldap.support.filter.Filter#hashCode() + */ + public int hashCode() { + return HASH ^ filter.hashCode(); + } + +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/OrFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/OrFilter.java new file mode 100644 index 00000000..20ff1ddf --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/OrFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +/** + * Filter for logical OR. + * + *
+ *        AndFilter filter = new AndFilter();
+ *        filter.or(new EqualsFilter("objectclass", "person");
+ *        filter.or(new EqualsFilter("objectclass", "organizationalUnit");
+ *        System.out.println(filter.ecode());    
+ * 
+ * + * would result in: + * (|(objectclass=person)(objectclass=organizationalUnit)) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class OrFilter extends BinaryLogicalFilter { + + private static final String PIPE_SIGN = "|"; + + /** + * Add a query to the OR expression + * + * @param query + * The query to or with the rest of the or:ed queries. + * @return This LdapOrQuery + */ + public OrFilter or(Filter query) { + queryList.add(query); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.support.filter.BinaryLogicalFilter#getLogicalOperator() + */ + protected String getLogicalOperator() { + return PIPE_SIGN; + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java new file mode 100644 index 00000000..bbd6b8d0 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.springframework.ldap.support.LdapEncoder; + +/** + * This filter automatically converts all whitespace to wildcards (*). The + * following code: + * + *
+ * WhitespaceWildcardsFilter filter = new WhitespaceWildcardsFilter("cn",
+ *         "Some CN");
+ * System.out.println(filter.ecode());
+ * 
+ * + * would result in: (cn=*Some*CN*) + * + * @author Adam Skogman + * @author Mattias Arthursson + */ +public class WhitespaceWildcardsFilter extends EqualsFilter { + private static Pattern starReplacePattern = Pattern.compile("\\s+"); + + public WhitespaceWildcardsFilter(String attribute, String value) { + super(attribute, value); + } + + /** + * Encodes a value according to the rules for this filter. + * + * @param value + * Value to encode. + * @return Encoded value. + */ + protected String encodeValue(String value) { + + // blank string means just ONE star + if (StringUtils.isBlank(value)) { + return "*"; + } + + // trim value, we will add in stars first and last anywhay + value = value.trim(); + + // filter encode so that any stars etc. are preserved + String filterEncoded = LdapEncoder.filterEncode(value); + + // Now replace all whitespace with stars + Matcher m = starReplacePattern.matcher(filterEncoded); + + // possibly 2 longer (stars at ends) + StringBuffer buff = new StringBuffer(value.length() + 2); + + buff.append('*'); + + while (m.find()) { + m.appendReplacement(buff, "*"); + } + m.appendTail(buff); + + buff.append('*'); + + return buff.toString(); + } +} diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/filter/package.html b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/package.html new file mode 100644 index 00000000..e05a77ed --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/filter/package.html @@ -0,0 +1,16 @@ + +Utility classes for dynamically building LDAP +filters. Filters can be nested and wrapped around each other: +
+AndFilter andFilter = new AndFilter();
+andFilter.and(new EqualsFilter("objectclass", "person");
+andFilter.and(new EqualsFilter("cn", "Some CN");
+OrFilter orFilter = new OrFilter();
+orFilter.or(andFilter);
+orFilter.or(new EqualsFilter("objectclass", "organizationalUnit));
+System.out.println(orFilter.encode());
+
+would result in: + +
(|(&(objectclass=person)(cn=Some CN))(objectclass=organizationalUnit))
+ diff --git a/spring-ldap/src/main/java/org/springframework/ldap/support/package.html b/spring-ldap/src/main/java/org/springframework/ldap/support/package.html new file mode 100644 index 00000000..bf83afd5 --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/support/package.html @@ -0,0 +1,3 @@ + +Support classes for Spring-LDAP. + \ No newline at end of file diff --git a/spring-ldap/src/main/java/org/springframework/ldap/util/ListComparator.java b/spring-ldap/src/main/java/org/springframework/ldap/util/ListComparator.java new file mode 100644 index 00000000..0ea9857c --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/util/ListComparator.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 either 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/spring-ldap/src/main/java/org/springframework/ldap/util/package.html b/spring-ldap/src/main/java/org/springframework/ldap/util/package.html new file mode 100644 index 00000000..b01effef --- /dev/null +++ b/spring-ldap/src/main/java/org/springframework/ldap/util/package.html @@ -0,0 +1,3 @@ + +Internal utility classes. + diff --git a/spring-ldap/src/main/java/overview.html b/spring-ldap/src/main/java/overview.html new file mode 100644 index 00000000..fe97647c --- /dev/null +++ b/spring-ldap/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/spring-ldap/src/main/javacc/DnParserImpl.jj b/spring-ldap/src/main/javacc/DnParserImpl.jj new file mode 100644 index 00000000..3660fcc3 --- /dev/null +++ b/spring-ldap/src/main/javacc/DnParserImpl.jj @@ -0,0 +1,165 @@ +/* + * Copyright © 2002 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * California 95054, U.S.A. All rights reserved. Sun Microsystems, Inc. has + * intellectual property rights relating to technology embodied in the product + * that is described in this document. In particular, and without limitation, + * these intellectual property rights may include one or more of the U.S. + * patents listed at http://www.sun.com/patents and one or more additional + * patents or pending patent applications in the U.S. and in other countries. + * U.S. Government Rights - Commercial software. Government users are subject + * to the Sun Microsystems, Inc. standard license agreement and applicable + * provisions of the FAR and its supplements. Use is subject to license terms. + * Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered + * trademarks of Sun Microsystems, Inc. in the U.S. and other countries. This + * product is covered and controlled by U.S. Export Control laws and may be + * subject to the export or import laws in other countries. Nuclear, missile, + * chemical biological weapons or nuclear maritime end uses or end users, + * whether direct or indirect, are strictly prohibited. Export or reexport + * to countries subject to U.S. embargo or to entities identified on U.S. + * export exclusion lists, including, but not limited to, the denied persons + * and specially designated nationals lists is strictly prohibited. + */ + + +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.support; + +import org.springframework.ldap.support.LdapRdn; +import org.springframework.ldap.support.LdapRdnComponent; +import org.springframework.ldap.support.DistinguishedName; + +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/spring-ldap/src/test/java/org/springframework/ldap/CollectingNameClassPairCallbackHandlerTest.java b/spring-ldap/src/test/java/org/springframework/ldap/CollectingNameClassPairCallbackHandlerTest.java new file mode 100644 index 00000000..2328454e --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/CollectingNameClassPairCallbackHandlerTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameClassPair; + +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/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateListTest.java b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateListTest.java new file mode 100644 index 00000000..d1e3b942 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateListTest.java @@ -0,0 +1,439 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.PartialResultException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import junit.framework.TestCase; + +import org.easymock.MockControl; + +/** + * 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 MockControl exceptionTranslatorControl; + + private NamingExceptionTranslator exceptionTranslatorMock; + + 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(); + + exceptionTranslatorControl = MockControl + .createControl(NamingExceptionTranslator.class); + exceptionTranslatorMock = (NamingExceptionTranslator) exceptionTranslatorControl + .getMock(); + + tested = new LdapTemplate(contextSourceMock); + tested.setExceptionTranslator(exceptionTranslatorMock); + } + + 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; + + exceptionTranslatorControl = null; + exceptionTranslatorMock = null; + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + namingEnumerationControl.replay(); + nameControl.replay(); + handlerControl.replay(); + contextMapperControl.replay(); + exceptionTranslatorControl.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + namingEnumerationControl.verify(); + nameControl.verify(); + handlerControl.verify(); + contextMapperControl.verify(); + exceptionTranslatorControl.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(); + + PartialResultException pre = new PartialResultException(); + dirContextControl.expectAndThrow(dirContextMock.list(NAME), pre); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(pre), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.list(NAME); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testList_PartialResultException_Ignore() throws NamingException { + expectGetReadOnlyContext(); + + PartialResultException pre = new 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(); + + NamingException ne = new NamingException(); + dirContextControl.expectAndThrow(dirContextMock.list(NAME), ne); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.list(NAME); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateLookupTest.java b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateLookupTest.java new file mode 100644 index 00000000..8f151426 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateLookupTest.java @@ -0,0 +1,453 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 javax.naming.NameNotFoundException; +import javax.naming.NamingException; +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.support.DirContextAdapter; +import org.springframework.ldap.support.DistinguishedName; + +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 MockControl exceptionTranslatorControl; + + private NamingExceptionTranslator exceptionTranslatorMock; + + 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(); + + exceptionTranslatorControl = MockControl + .createControl(NamingExceptionTranslator.class); + exceptionTranslatorMock = (NamingExceptionTranslator) exceptionTranslatorControl + .getMock(); + + tested = new LdapTemplate(contextSourceMock); + tested.setExceptionTranslator(exceptionTranslatorMock); + } + + 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; + + exceptionTranslatorControl = null; + exceptionTranslatorMock = null; + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + nameControl.replay(); + contextMapperControl.replay(); + attributesMapperControl.replay(); + exceptionTranslatorControl.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + nameControl.verify(); + contextMapperControl.verify(); + attributesMapperControl.verify(); + exceptionTranslatorControl.verify(); + } + + private void expectGetReadOnlyContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadOnlyContext(), dirContextMock); + } + + // Tests for lookup(name) + + public void testLookup() throws NamingException { + 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 NamingException { + 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 NamingException { + expectGetReadOnlyContext(); + + NamingException ne = new NamingException(); + dirContextControl.expectAndThrow(dirContextMock.lookup(nameMock), ne); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.lookup(nameMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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(); + + NamingException ne = new NamingException(); + dirContextControl.expectAndThrow( + dirContextMock.getAttributes(nameMock), ne); + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.lookup(nameMock, attributesMapperMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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(); + + NameNotFoundException ne = new NameNotFoundException(); + dirContextControl.expectAndThrow(dirContextMock.lookup(nameMock), ne); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.lookup(nameMock, contextMapperMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateRenameTest.java b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateRenameTest.java new file mode 100644 index 00000000..459c9be7 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateRenameTest.java @@ -0,0 +1,198 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 javax.naming.NameAlreadyBoundException; +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.dao.DataIntegrityViolationException; +import org.springframework.ldap.ContextSource; +import org.springframework.ldap.LdapTemplate; +import org.springframework.ldap.NamingExceptionTranslator; +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 MockControl exceptionTranslatorControl; + + private NamingExceptionTranslator exceptionTranslatorMock; + + 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(); + + exceptionTranslatorControl = MockControl + .createControl(NamingExceptionTranslator.class); + exceptionTranslatorMock = (NamingExceptionTranslator) exceptionTranslatorControl + .getMock(); + + tested = new LdapTemplate(contextSourceMock); + tested.setExceptionTranslator(exceptionTranslatorMock); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + contextSourceControl = null; + contextSourceMock = null; + + dirContextControl = null; + dirContextMock = null; + + oldNameControl = null; + newNameMock = null; + + exceptionTranslatorControl = null; + exceptionTranslatorMock = null; + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + oldNameControl.replay(); + exceptionTranslatorControl.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + oldNameControl.verify(); + exceptionTranslatorControl.verify(); + } + + private void expectGetReadWriteContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadWriteContext(), dirContextMock); + } + + public void testRename() throws NamingException { + expectGetReadWriteContext(); + + dirContextMock.rename(oldNameMock, newNameMock); + dirContextMock.close(); + + replay(); + + tested.rename(oldNameMock, newNameMock); + + verify(); + } + + public void testRename_NameAlreadyBoundException() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.rename(oldNameMock, newNameMock); + NameAlreadyBoundException ne = new NameAlreadyBoundException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new DataIntegrityViolationException("dummy")); + + replay(); + + try { + tested.rename(oldNameMock, newNameMock); + fail("DataIntegrityViolationException expected"); + } catch (DataIntegrityViolationException expected) { + assertTrue(true); + } + + verify(); + } + + public void testRename_NamingException() throws Exception { + expectGetReadWriteContext(); + + dirContextMock.rename(oldNameMock, newNameMock); + NamingException ne = new NamingException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new UncategorizedLdapException("dummy", ne)); + + replay(); + + try { + tested.rename(oldNameMock, newNameMock); + fail("UncategorizedLdapException expected"); + } catch (UncategorizedLdapException expected) { + assertTrue(true); + } + + verify(); + } + + public void testRename_String() throws NamingException { + expectGetReadWriteContext(); + + dirContextMock.rename("o=example.com", "o=somethingelse.com"); + dirContextMock.close(); + + replay(); + + tested.rename("o=example.com", "o=somethingelse.com"); + + verify(); + } +} diff --git a/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateTest.java b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateTest.java new file mode 100644 index 00000000..e7c78259 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/LdapTemplateTest.java @@ -0,0 +1,1708 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Binding; +import javax.naming.Name; +import javax.naming.NameNotFoundException; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.PartialResultException; +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.support.DistinguishedName; + +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 exceptionTranslatorControl; + + private NamingExceptionTranslator exceptionTranslatorMock; + + private MockControl contextExecutorControl; + + private ContextExecutor contextExecutorMock; + + private MockControl searchExecutorControl; + + private SearchExecutor searchExecutorMock; + + private LdapTemplate tested; + + private MockControl dirContextProcessorControl; + + private DirContextProcessor dirContextProcessorMock; + + 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(); + + exceptionTranslatorControl = MockControl + .createControl(NamingExceptionTranslator.class); + exceptionTranslatorMock = (NamingExceptionTranslator) exceptionTranslatorControl + .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(); + + tested = new LdapTemplate(contextSourceMock); + tested.setExceptionTranslator(exceptionTranslatorMock); + } + + 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; + + exceptionTranslatorControl = null; + exceptionTranslatorMock = null; + + contextExecutorControl = null; + contextExecutorMock = null; + + searchExecutorControl = null; + searchExecutorMock = null; + + dirContextProcessorControl = null; + dirContextProcessorMock = null; + } + + protected void replay() { + contextSourceControl.replay(); + dirContextControl.replay(); + namingEnumerationControl.replay(); + nameControl.replay(); + handlerControl.replay(); + contextMapperControl.replay(); + attributesMapperControl.replay(); + exceptionTranslatorControl.replay(); + contextExecutorControl.replay(); + searchExecutorControl.replay(); + dirContextProcessorControl.replay(); + } + + protected void verify() { + contextSourceControl.verify(); + dirContextControl.verify(); + namingEnumerationControl.verify(); + nameControl.verify(); + handlerControl.verify(); + contextMapperControl.verify(); + attributesMapperControl.verify(); + exceptionTranslatorControl.verify(); + contextExecutorControl.verify(); + searchExecutorControl.verify(); + dirContextProcessorControl.verify(); + } + + private void expectGetReadWriteContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadWriteContext(), dirContextMock); + } + + private void expectGetReadOnlyContext() { + contextSourceControl.expectAndReturn(contextSourceMock + .getReadOnlyContext(), dirContextMock); + } + + public void testSearch_CallbackHandler() throws NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + dirContextControl.setDefaultMatcher(new SearchControlsMatcher()); + dirContextControl.expectAndThrow(dirContextMock.search(nameMock, + "(ou=somevalue)", controls), new NameNotFoundException()); + + dirContextMock.close(); + + replay(); + + tested.search(nameMock, "(ou=somevalue)", handlerMock); + + verify(); + } + + public void testSearch_NamingException() throws NamingException { + expectGetReadOnlyContext(); + + SearchControls controls = new SearchControls(); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + controls.setReturningObjFlag(false); + + dirContextControl.setDefaultMatcher(new SearchControlsMatcher()); + NamingException ne = new NamingException(); + dirContextControl.expectAndThrow(dirContextMock.search(nameMock, + "(ou=somevalue)", controls), ne); + + dirContextMock.close(); + + EntryNotFoundException expectedException = new EntryNotFoundException( + "dummy"); + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), expectedException); + + replay(); + + try { + tested.search(nameMock, "(ou=somevalue)", handlerMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException expected) { + assertSame(expectedException, expected); + } + + verify(); + } + + public void testSearch_CallbackHandler_DirContextProcessor() + throws NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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 NamingException { + 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); + NamingException ne = new NamingException(); + dirContextControl.setThrowable(ne); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + dirContextMock.close(); + + replay(); + + try { + tested.modifyAttributes(nameMock, mods); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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); + NamingException ne = new NamingException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.bind(nameMock, expectedObject, expectedAttributes); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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 NamingException { + 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 NamingException { + 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); + NamingException ne = new NamingException(); + dirContextControl.setThrowable(ne); + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.unbind(nameMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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(); + + NamingException ne = new NamingException(); + contextExecutorControl.expectAndThrow(contextExecutorMock + .executeWithContext(dirContextMock), ne); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.executeReadOnly(contextExecutorMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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(); + + NamingException ne = new NamingException(); + contextExecutorControl.expectAndThrow(contextExecutorMock + .executeWithContext(dirContextMock), ne); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.executeReadWrite(contextExecutorMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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); + + NamingException ne = new NamingException(); + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), ne); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock, + dirContextProcessorMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException 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(); + + NamingException ne = new NamingException(); + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), ne); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testDoSearch_NamingException_NamingEnumeration() + throws Exception { + expectGetReadOnlyContext(); + + searchExecutorControl.expectAndReturn(searchExecutorMock + .executeSearch(dirContextMock), namingEnumerationMock); + + NamingException ne = new NamingException(); + namingEnumerationControl.expectAndThrow( + namingEnumerationMock.hasMore(), ne); + namingEnumerationMock.close(); + + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ne), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testDoSearch_NameNotFoundException() throws Exception { + expectGetReadOnlyContext(); + + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), new NameNotFoundException()); + dirContextMock.close(); + + replay(); + + tested.search(searchExecutorMock, handlerMock); + + verify(); + } + + public void testSearch_PartialResult_IgnoreNotSet() throws NamingException { + expectGetReadOnlyContext(); + + PartialResultException ex = new PartialResultException(); + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), ex); + dirContextMock.close(); + + exceptionTranslatorControl.expectAndReturn(exceptionTranslatorMock + .translate(ex), new EntryNotFoundException("dummy")); + + replay(); + + try { + tested.search(searchExecutorMock, handlerMock); + fail("EntryNotFoundException expected"); + } catch (EntryNotFoundException expected) { + assertTrue(true); + } + + verify(); + } + + public void testSearch_PartialResult_IgnoreSet() throws NamingException { + tested.setIgnorePartialResultException(true); + + expectGetReadOnlyContext(); + + searchExecutorControl.expectAndThrow(searchExecutorMock + .executeSearch(dirContextMock), new PartialResultException()); + dirContextMock.close(); + + replay(); + + tested.search(searchExecutorMock, handlerMock); + + 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/spring-ldap/src/test/java/org/springframework/ldap/RequestControlMatcher.java b/spring-ldap/src/test/java/org/springframework/ldap/RequestControlMatcher.java new file mode 100644 index 00000000..b430fd96 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/RequestControlMatcher.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/spring-ldap/src/test/java/org/springframework/ldap/support/AggregateDirContextProcessorTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/AggregateDirContextProcessorTest.java new file mode 100644 index 00000000..0bf5b136 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/AggregateDirContextProcessorTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.NamingException; + +import junit.framework.TestCase; + +import org.easymock.MockControl; +import org.springframework.ldap.DirContextProcessor; + +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/spring-ldap/src/test/java/org/springframework/ldap/support/CountNameClassPairResultCallbackHandlerTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/CountNameClassPairResultCallbackHandlerTest.java new file mode 100644 index 00000000..0eacbeb0 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/CountNameClassPairResultCallbackHandlerTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.SearchResult; + +import org.springframework.ldap.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/spring-ldap/src/test/java/org/springframework/ldap/support/DefaultDirObjectFactoryTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/DefaultDirObjectFactoryTest.java new file mode 100644 index 00000000..c0f4e499 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/DefaultDirObjectFactoryTest.java @@ -0,0 +1,157 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.support.DefaultDirObjectFactory; +import org.springframework.ldap.support.DirContextAdapter; +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/DirContextAdapterBugTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/DirContextAdapterBugTest.java new file mode 100644 index 00000000..e57c2d07 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/DirContextAdapterBugTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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/spring-ldap/src/test/java/org/springframework/ldap/support/DirContextAdapterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/DirContextAdapterTest.java new file mode 100644 index 00000000..e25fc1e6 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/DirContextAdapterTest.java @@ -0,0 +1,664 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Name; +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 org.springframework.ldap.support.DirContextAdapter; + +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 classUnderTest; + + protected void setUp() throws Exception { + super.setUp(); + classUnderTest = new DirContextAdapter(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testSetUpdateMode() throws Exception { + assertFalse(classUnderTest.isUpdateMode()); + classUnderTest.setUpdateMode(true); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setUpdateMode(false); + assertFalse(classUnderTest.isUpdateMode()); + } + + public void testGetModificationItems() throws Exception { + ModificationItem[] items = classUnderTest.getModificationItems(); + assertEquals(0, items.length); + classUnderTest.setUpdateMode(true); + assertEquals(0, items.length); + } + + public void testAlwaysReplace() throws Exception { + ModificationItem[] items = classUnderTest.getModificationItems(); + assertEquals(0, items.length); + classUnderTest.setUpdateMode(true); + assertEquals(0, items.length); + } + + public void testGetStringAttributeNotExists() throws Exception { + String s = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + String s = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + String s[] = classUnderTest.getStringAttributes("abc"); + assertEquals("123", s[0]); + assertEquals("234", s[1]); + assertEquals(2, s.length); + } + + public void testGetStringAttributesNotExists() throws Exception { + String s[] = classUnderTest.getStringAttributes("abc"); + assertNull(s); + } + + public void testSetStringAttribute() throws Exception { + assertFalse(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValue("abc", "123"); + Attributes attrs = classUnderTest.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + } + + public void testSetStringAttributeNull() throws Exception { + assertFalse(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValue("abc", null); + Attributes attrs = classUnderTest.getAttributes(); + Attribute attr = attrs.get("abc"); + assertNull(attr); + } + + public void testAddAttribute() throws Exception { + classUnderTest.setUpdateMode(true); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValue("abc", "123"); + Attributes attrs = classUnderTest.getAttributes(); + Attribute attr = attrs.get("abc"); + assertNull(attr); + + ModificationItem[] mods = classUnderTest.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.ADD_ATTRIBUTE, mods[0].getModificationOp()); + attr = mods[0].getAttribute(); + assertEquals("123", (String) attr.get()); + + String[] modNames = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(1, modNames.length); + assertEquals("abc", modNames[0]); + + classUnderTest.update(); + mods = classUnderTest.getModificationItems(); + assertEquals(0, mods.length); + modNames = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + attrs = classUnderTest.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 { + classUnderTest.setUpdateMode(true); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "123", "456" }); + Attributes attrs = classUnderTest.getAttributes(); + Attribute attr = attrs.get("abc"); + assertNull(attr); + ModificationItem[] mods = classUnderTest.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 = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(1, modNames.length); + assertEquals("abc", modNames[0]); + + classUnderTest.update(); + mods = classUnderTest.getModificationItems(); + assertEquals(0, mods.length); + modNames = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + attrs = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + + classUnderTest.setUpdateMode(true); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValue("abc", null); + Attributes attrs = classUnderTest.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + + ModificationItem[] mods = classUnderTest.getModificationItems(); + assertEquals(1, mods.length); + assertEquals(DirContext.REMOVE_ATTRIBUTE, mods[0].getModificationOp()); + attr = mods[0].getAttribute(); + assertEquals("abc", (String) attr.getID()); + String[] modNames = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(1, modNames.length); + assertEquals("abc", modNames[0]); + + classUnderTest.update(); + mods = classUnderTest.getModificationItems(); + assertEquals(0, mods.length); + modNames = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + attrs = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + + classUnderTest.setUpdateMode(true); + classUnderTest.setAttributeValues("abc", new String[] {}); + + ModificationItem[] mods = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + classUnderTest.setAttributeValue("abc", "234"); // change + + ModificationItem[] mods = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValue("abc", "123"); // change + + ModificationItem[] mods = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "123", "qwe" }); + + ModificationItem[] mods = classUnderTest.getModificationItems(); + assertEquals(0, mods.length); + String[] modNames = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + classUnderTest.setAttributeValues("abc", new String[] { "qwe", "123" }); + + ModificationItem[] mods = classUnderTest.getModificationItems(); + assertEquals(0, mods.length); + String[] modNames = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "qwe", "123" }, + true); + + // change + ModificationItem[] mods = classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "123", "qwe", + "klytt" }); + + ModificationItem[] modificationItems = classUnderTest + .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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "123" }); + + ModificationItem[] modificationItems = classUnderTest + .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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "123" }); + + ModificationItem[] modificationItems = classUnderTest + .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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", null); + + ModificationItem[] modificationItems = classUnderTest + .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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "123", "qwe" }); + + ModificationItem[] modificationItems = classUnderTest + .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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("abc", new String[] { "123", "qwe", + "klytt", "kalle" }); + + ModificationItem[] modificationItems = classUnderTest + .getModificationItems(); + assertEquals(2, modificationItems.length); + + assertEquals(DirContext.ADD_ATTRIBUTE, modificationItems[0] + .getModificationOp()); + Attribute modifiedAttribute = modificationItems[0].getAttribute(); + assertEquals("abc", modifiedAttribute.getID()); + assertEquals(2, modifiedAttribute.size()); + assertEquals("klytt", modifiedAttribute.get(0)); + assertEquals("kalle", modifiedAttribute.get(1)); + + modifiedAttribute = modificationItems[1].getAttribute(); + assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItems[1] + .getModificationOp()); + assertEquals("abc", modifiedAttribute.getID()); + assertEquals(2, modifiedAttribute.size()); + assertEquals("rty", modifiedAttribute.get(0)); + assertEquals("uio", 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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValues("def", new String[] { "kalle", + "klytt" }); + + ModificationItem[] modificationItems = classUnderTest + .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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValue("abc", "234"); // change + classUnderTest.setAttributeValue("abc", "987"); + // change a second time + ModificationItem[] mods = classUnderTest.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()); + + classUnderTest.update(); + mods = classUnderTest.getModificationItems(); + assertEquals(0, mods.length); + String[] modNames = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + Attributes attrs = classUnderTest.getAttributes(); + attr = attrs.get("abc"); + assertEquals("987", (String) attr.get()); + assertEquals("987", classUnderTest.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); + } + } + classUnderTest = new TestableDirContextAdapter(); + assertTrue(classUnderTest.isUpdateMode()); + classUnderTest.setAttributeValue("abc", "234"); // change + classUnderTest.setAttributeValue("qwe", null); // remove + classUnderTest.setAttributeValue("zzz", "new"); // new + Attributes attrs = classUnderTest.getAttributes(); + Attribute attr = attrs.get("abc"); + assertEquals("123", (String) attr.get()); + assertEquals(2, attrs.size()); + + ModificationItem[] mods = classUnderTest.getModificationItems(); + assertEquals(3, mods.length); + String[] modNames = classUnderTest.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()); + + classUnderTest.update(); + mods = classUnderTest.getModificationItems(); + assertEquals(0, mods.length); + modNames = classUnderTest.getNamesOfModifiedAttributes(); + assertEquals(0, modNames.length); + + attrs = classUnderTest.getAttributes(); + assertEquals(2, attrs.size()); + attr = attrs.get("abc"); + assertEquals("234", (String) attr.get()); + assertEquals("new", classUnderTest.getStringAttribute("zzz")); + } + + 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; + } +} diff --git a/spring-ldap/src/test/java/org/springframework/ldap/support/DistinguishedNameTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/DistinguishedNameTest.java new file mode 100644 index 00000000..3ae5a59d --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/DistinguishedNameTest.java @@ -0,0 +1,448 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Enumeration; + +import javax.naming.CompositeName; +import javax.naming.InvalidNameException; +import javax.naming.Name; + +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); + } + +} \ No newline at end of file diff --git a/spring-ldap/src/test/java/org/springframework/ldap/support/LdapContextSourceTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapContextSourceTest.java new file mode 100644 index 00000000..231b24a6 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapContextSourceTest.java @@ -0,0 +1,220 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Hashtable; + +import javax.naming.Context; +import javax.naming.ldap.Control; +import javax.naming.ldap.ControlFactory; + +import junit.framework.TestCase; + +import org.springframework.ldap.AuthenticationSource; + +import com.sun.jndi.ldap.ctl.ResponseControlFactory; + +/** + * 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.setUserName("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)); + + // 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)); + } + + public void testGetAuthenticatedEnv() throws Exception { + tested.setBase("dc=example,dc=se"); + tested.setUrl("ldap://ldap.example.com:389"); + tested.setPooled(true); + tested.setUserName("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)); + } + + 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 testGetAnonymousEnv_DontCacheEnv() throws Exception { + tested.setBase("dc=example,dc=se"); + tested.setUrl("ldap://ldap.example.com:389"); + tested.setPooled(true); + tested.setUserName("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)); + } + + public void testsetResponseControlFactory_Null() throws Exception { + tested.setResponseControlFactory(null); + assertNotNull(tested.getResponseControlFactory()); + assertEquals(ResponseControlFactory.class, tested + .getResponseControlFactory()); + } + + public void testsetResponseControlFactory_Valid() throws Exception { + tested.setResponseControlFactory(ControlFactory.class); + assertEquals(ControlFactory.class, tested.getResponseControlFactory()); + } + + public void testsetResponseControlFactory_Invalid() throws Exception { + try { + tested.setResponseControlFactory(Control.class); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + 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/spring-ldap/src/test/java/org/springframework/ldap/support/LdapEncoderTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapEncoderTest.java new file mode 100644 index 00000000..3afbd3c5 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapEncoderTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.springframework.ldap.BadLdapGrammarException; +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/LdapRdnComponentTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapRdnComponentTest.java new file mode 100644 index 00000000..5d93cbf3 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapRdnComponentTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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/spring-ldap/src/test/java/org/springframework/ldap/support/LdapRdnTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapRdnTest.java new file mode 100644 index 00000000..5994ad3c --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/LdapRdnTest.java @@ -0,0 +1,214 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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()); + } + + 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()); + } + + 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/spring-ldap/src/test/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSourceTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSourceTest.java new file mode 100644 index 00000000..abfc4daa --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/authentication/AcegiAuthenticationSourceTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.authentication; + +import junit.framework.TestCase; + + +import org.acegisecurity.Authentication; +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.userdetails.User; +import org.acegisecurity.userdetails.ldap.LdapUserDetails; +import org.easymock.MockControl; +import org.springframework.ldap.support.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(); + } +} diff --git a/spring-ldap/src/test/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecoratorTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecoratorTest.java new file mode 100644 index 00000000..3fdfbcea --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/authentication/DefaultValuesAuthenticationSourceDecoratorTest.java @@ -0,0 +1,119 @@ +package org.springframework.ldap.support.authentication; + +import org.easymock.MockControl; +import org.springframework.ldap.AuthenticationSource; +import org.springframework.ldap.support.authentication.DefaultValuesAuthenticationSourceDecorator; + +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/spring-ldap/src/test/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessorTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessorTest.java new file mode 100644 index 00000000..07a904d5 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/control/AbstractRequestControlDirContextProcessorTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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; + +public class AbstractRequestControlDirContextProcessorTest 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_NotLdapContext() throws Exception { + try { + tested.preProcess(dirContextMock); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } +} diff --git a/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultTest.java new file mode 100644 index 00000000..1401b74c --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.control; + +import java.util.LinkedList; +import java.util.List; + +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/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultsCookieTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultsCookieTest.java new file mode 100644 index 00000000..39ea3c52 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultsCookieTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.control; + +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/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultsRequestControlTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultsRequestControlTest.java new file mode 100644 index 00000000..2cf0e4dc --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/control/PagedResultsRequestControlTest.java @@ -0,0 +1,188 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.control; + +import java.io.IOException; + +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; + +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.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 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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/AbstractFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/AbstractFilterTest.java new file mode 100644 index 00000000..7fad9e46 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/AbstractFilterTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/AndFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/AndFilterTest.java new file mode 100644 index 00000000..c1feccbc --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/AndFilterTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.filter.AndFilter; +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/EqualsFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/EqualsFilterTest.java new file mode 100644 index 00000000..58122f1e --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/EqualsFilterTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilterTest.java new file mode 100644 index 00000000..6a483973 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/GreaterThanOrEqualsFilterTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilterTest.java new file mode 100644 index 00000000..3a152f45 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/LessThanOrEqualsFilterTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/LikeFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/LikeFilterTest.java new file mode 100644 index 00000000..af3d9d1d --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/LikeFilterTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/NotFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/NotFilterTest.java new file mode 100644 index 00000000..58788e7c --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/NotFilterTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.filter.EqualsFilter; +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/OrFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/OrFilterTest.java new file mode 100644 index 00000000..f5f5360d --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/OrFilterTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.filter.EqualsFilter; +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilterTest.java b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilterTest.java new file mode 100644 index 00000000..d781c281 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/support/filter/WhitespaceWildcardsFilterTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.springframework.ldap.support.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/spring-ldap/src/test/java/org/springframework/ldap/util/ListComparatorTest.java b/spring-ldap/src/test/java/org/springframework/ldap/util/ListComparatorTest.java new file mode 100644 index 00000000..ac456537 --- /dev/null +++ b/spring-ldap/src/test/java/org/springframework/ldap/util/ListComparatorTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.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/spring-ldap/src/test/resources/log4j.properties b/spring-ldap/src/test/resources/log4j.properties new file mode 100644 index 00000000..66b1c2ec --- /dev/null +++ b/spring-ldap/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/spring-ldap/xdocs/navigation.xml b/spring-ldap/xdocs/navigation.xml new file mode 100644 index 00000000..84927d4c --- /dev/null +++ b/spring-ldap/xdocs/navigation.xml @@ -0,0 +1,43 @@ + + + + + + + LdapTemplate + + + + + + + + + + + + + \ No newline at end of file