diff --git a/mvn-build/samples/samples-utils/pom.xml b/mvn-build/samples/samples-utils/pom.xml
new file mode 100644
index 00000000..84918002
--- /dev/null
+++ b/mvn-build/samples/samples-utils/pom.xml
@@ -0,0 +1,50 @@
+
+ 4.0.0
+ org.springframework.ldap
+ spring-ldap-samples-utils
+ jar
+ 1.2.2-SNAPSHOT
+ Spring LDAP utilities for samples
+
+ org.springframework.ldap
+ master
+ 1.2.2-SNAPSHOT
+
+
+
+
+
+ maven-compiler-plugin
+
+ 1.5
+ 1.5
+
+
+
+
+
+
+ org.springframework.ldap
+ spring-ldap-tiger
+ 1.2.2-SNAPSHOT
+
+
+ org.springframework.ldap
+ spring-ldap-test
+ 1.2.2-SNAPSHOT
+
+
+ org.springframework
+ spring-test
+ ${spring.version}
+
+
+ junit
+ junit
+ 4.4
+ test
+
+
+
diff --git a/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/HtmlRowLdapTreeVisitor.java b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/HtmlRowLdapTreeVisitor.java
new file mode 100644
index 00000000..704ee425
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/HtmlRowLdapTreeVisitor.java
@@ -0,0 +1,27 @@
+package org.springframework.ldap.samples.utils;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.ldap.core.DirContextOperations;
+
+public class HtmlRowLdapTreeVisitor implements LdapTreeVisitor {
+
+ private List rows = new LinkedList();
+
+ public void visit(DirContextOperations node, int currentDepth) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < currentDepth; i++) {
+ sb.append(" ");
+ }
+
+ sb.append(node.getDn()).append("
\n");
+
+ rows.add(sb.toString());
+ }
+
+ public List getRows() {
+ return rows;
+ }
+
+}
diff --git a/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTree.java b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTree.java
new file mode 100644
index 00000000..2bbe7b44
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTree.java
@@ -0,0 +1,45 @@
+package org.springframework.ldap.samples.utils;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.ldap.core.DirContextOperations;
+
+public class LdapTree {
+ private final DirContextOperations node;
+
+ private List subContexts = new LinkedList();
+
+ public LdapTree(DirContextOperations node) {
+ this.node = node;
+ }
+
+ public DirContextOperations getNode() {
+ return node;
+ }
+
+ public List getSubContexts() {
+ return subContexts;
+ }
+
+ public void setSubContexts(List subContexts) {
+ this.subContexts = subContexts;
+ }
+
+ public void addSubTree(LdapTree ldapTree) {
+ subContexts.add(ldapTree);
+ }
+
+
+
+ public void traverse(LdapTreeVisitor visitor) {
+ traverse(visitor, 0);
+ }
+
+ private void traverse(LdapTreeVisitor visitor, int currentDepth) {
+ visitor.visit(node, currentDepth);
+ for (LdapTree subContext : subContexts) {
+ subContext.traverse(visitor, currentDepth + 1);
+ }
+ }
+}
diff --git a/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTreeBuilder.java b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTreeBuilder.java
new file mode 100644
index 00000000..8359faa9
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTreeBuilder.java
@@ -0,0 +1,36 @@
+package org.springframework.ldap.samples.utils;
+
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.AbstractContextMapper;
+
+public class LdapTreeBuilder {
+
+ private LdapTemplate ldapTemplate;
+
+ public LdapTreeBuilder(LdapTemplate ldapTemplate) {
+ this.ldapTemplate = ldapTemplate;
+ }
+
+ public LdapTree getLdapTree(DistinguishedName root) {
+ DirContextOperations context = ldapTemplate.lookupContext(root.toString());
+ return getLdapTree(context);
+ }
+
+ private LdapTree getLdapTree(final DirContextOperations rootContext) {
+ final LdapTree ldapTree = new LdapTree(rootContext);
+ ldapTemplate.listBindings(rootContext.getDn(), new AbstractContextMapper() {
+ @Override
+ protected Object doMapFromContext(DirContextOperations ctx) {
+ DistinguishedName dn = (DistinguishedName) ctx.getDn();
+ dn.prepend((DistinguishedName) rootContext.getDn());
+
+ ldapTree.addSubTree(getLdapTree(ctx));
+ return null;
+ }
+ });
+
+ return ldapTree;
+ }
+}
diff --git a/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTreeVisitor.java b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTreeVisitor.java
new file mode 100644
index 00000000..9d4457af
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/main/java/org/springframework/ldap/samples/utils/LdapTreeVisitor.java
@@ -0,0 +1,8 @@
+package org.springframework.ldap.samples.utils;
+
+import org.springframework.ldap.core.DirContextOperations;
+
+public interface LdapTreeVisitor {
+
+ public void visit(DirContextOperations node, int currentDepth);
+}
diff --git a/mvn-build/samples/samples-utils/src/test/java/org/springframework/ldap/samples/utils/LdapTreeBuilderIntegrationTest.java b/mvn-build/samples/samples-utils/src/test/java/org/springframework/ldap/samples/utils/LdapTreeBuilderIntegrationTest.java
new file mode 100644
index 00000000..ab4e4c48
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/test/java/org/springframework/ldap/samples/utils/LdapTreeBuilderIntegrationTest.java
@@ -0,0 +1,57 @@
+package org.springframework.ldap.samples.utils;
+
+import static junit.framework.Assert.assertEquals;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
+
+@ContextConfiguration(locations = { "/conf/testContext.xml" })
+public class LdapTreeBuilderIntegrationTest extends AbstractJUnit4SpringContextTests {
+
+ @Autowired
+ private LdapTreeBuilder tested;
+
+ @Test
+ public void testGetLdapTree() {
+ LdapTree ldapTree = tested.getLdapTree(new DistinguishedName("c=Sweden"));
+ ldapTree.traverse(new TestVisitor());
+ }
+
+ private static final class TestVisitor implements LdapTreeVisitor {
+ private static final DistinguishedName DN_1 = new DistinguishedName("c=Sweden");
+
+ private static final DistinguishedName DN_2 = new DistinguishedName("ou=company1,c=Sweden");
+
+ private static final DistinguishedName DN_3 = new DistinguishedName("cn=Some Person,ou=company1,c=Sweden");
+
+ private static final DistinguishedName DN_4 = new DistinguishedName("cn=Some Person2,ou=company1,c=Sweden");
+
+ private Map names = new LinkedHashMap();
+
+ private Iterator keyIterator;
+
+ public TestVisitor() {
+ names.put(DN_1, 0);
+ names.put(DN_2, 1);
+ names.put(DN_3, 2);
+ names.put(DN_4, 2);
+
+ keyIterator = names.keySet().iterator();
+ }
+
+ public void visit(DirContextOperations node, int currentDepth) {
+ DistinguishedName next = keyIterator.next();
+ assertEquals(next, node.getDn());
+ assertEquals(names.get(next).intValue(), currentDepth);
+ }
+ }
+
+}
diff --git a/mvn-build/samples/samples-utils/src/test/resources/conf/commonTestContext.xml b/mvn-build/samples/samples-utils/src/test/resources/conf/commonTestContext.xml
new file mode 100644
index 00000000..a71c074e
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/test/resources/conf/commonTestContext.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/mvn-build/samples/samples-utils/src/test/resources/conf/ldap.properties b/mvn-build/samples/samples-utils/src/test/resources/conf/ldap.properties
new file mode 100644
index 00000000..8843f1f1
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/test/resources/conf/ldap.properties
@@ -0,0 +1,4 @@
+urls=ldap://127.0.0.1:3900
+userDn=uid=admin,ou=system
+password=secret
+base=dc=jayway,dc=se
diff --git a/mvn-build/samples/samples-utils/src/test/resources/conf/testContext.xml b/mvn-build/samples/samples-utils/src/test/resources/conf/testContext.xml
new file mode 100644
index 00000000..67b2d314
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/test/resources/conf/testContext.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mvn-build/samples/samples-utils/src/test/resources/setup_data.ldif b/mvn-build/samples/samples-utils/src/test/resources/setup_data.ldif
new file mode 100644
index 00000000..0ce6455a
--- /dev/null
+++ b/mvn-build/samples/samples-utils/src/test/resources/setup_data.ldif
@@ -0,0 +1,36 @@
+dn: c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: country
+c: Sweden
+description: The country of Sweden
+
+dn: ou=company1,c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: organizationalUnit
+ou: company1
+description: First company in Sweden
+
+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
+
diff --git a/mvn-build/samples/spring-ldap-article/.springBeans b/mvn-build/samples/spring-ldap-article/.springBeans
new file mode 100644
index 00000000..4a47426b
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/.springBeans
@@ -0,0 +1,13 @@
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
diff --git a/mvn-build/samples/spring-ldap-article/build.xml b/mvn-build/samples/spring-ldap-article/build.xml
new file mode 100644
index 00000000..5a85b7fa
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/build.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mvn-build/samples/spring-ldap-article/docs/base_data.ldif b/mvn-build/samples/spring-ldap-article/docs/base_data.ldif
new file mode 100644
index 00000000..f06390db
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/docs/ldap.conf.example b/mvn-build/samples/spring-ldap-article/docs/ldap.conf.example
new file mode 100644
index 00000000..3e7570c5
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/docs/readme.txt b/mvn-build/samples/spring-ldap-article/docs/readme.txt
new file mode 100644
index 00000000..651f41f3
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/docs/setup_data.ldif b/mvn-build/samples/spring-ldap-article/docs/setup_data.ldif
new file mode 100644
index 00000000..cdf07c46
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/docs/teardown_data.ldif b/mvn-build/samples/spring-ldap-article/docs/teardown_data.ldif
new file mode 100644
index 00000000..97ba7e6b
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/ivy.xml b/mvn-build/samples/spring-ldap-article/ivy.xml
new file mode 100644
index 00000000..7ffd8d8b
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/ivy.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mvn-build/samples/spring-ldap-article/pom.xml b/mvn-build/samples/spring-ldap-article/pom.xml
new file mode 100644
index 00000000..f3fbe486
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/pom.xml
@@ -0,0 +1,89 @@
+
+
+
+ master
+ org.springframework.ldap
+ 1.2.2-SNAPSHOT
+
+ 4.0.0
+ org.springframework
+ spring-ldap-person-article
+ Spring LDAP Article Source Code Sample
+ 1.2
+ war
+
+ Example code that matches the article published on java.net on
+ April 18, 2006.
+
+
+
+
+
+ maven-compiler-plugin
+
+ 1.5
+ 1.5
+
+
+
+ org.mortbay.jetty
+ maven-jetty-plugin
+
+
+
+
+
+
+
+
+ org.springframework.ldap
+ spring-ldap-test
+ 1.2.2-SNAPSHOT
+
+
+ org.springframework.ldap
+ spring-ldap-samples-utils
+ 1.2.2-SNAPSHOT
+
+
+ org.springframework
+ spring-jdbc
+ ${spring.version}
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
+
+ javax.servlet
+ servlet-api
+ 2.4
+ provided
+
+
+
+ org.springframework
+ spring-test
+ ${spring.version}
+ test
+
+
+
+
+
diff --git a/mvn-build/samples/spring-ldap-article/project.properties b/mvn-build/samples/spring-ldap-article/project.properties
new file mode 100644
index 00000000..5e43e378
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/readme.txt b/mvn-build/samples/spring-ldap-article/readme.txt
new file mode 100644
index 00000000..93ae89cc
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java b/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java
new file mode 100644
index 00000000..e8e4238e
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java b/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java
new file mode 100644
index 00000000..8c9af813
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ldap.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/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java b/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java
new file mode 100644
index 00000000..b8c09166
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/itest/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ldap.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/mvn-build/samples/spring-ldap-article/src/main/java/log4j.properties b/mvn-build/samples/spring-ldap-article/src/main/java/log4j.properties
new file mode 100644
index 00000000..4ab57157
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java
new file mode 100644
index 00000000..65e5205a
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java
new file mode 100644
index 00000000..4c17e2e5
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.core.AttributesMapper;
+import org.springframework.ldap.core.ContextMapper;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.filter.EqualsFilter;
+import org.springframework.ldap.samples.article.domain.Person;
+
+/**
+ * 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/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java
new file mode 100644
index 00000000..3b9a03a9
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 setUserDn(String principal) {
+ this.userName = principal;
+ }
+}
diff --git a/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/domain/Person.java b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/domain/Person.java
new file mode 100644
index 00000000..57ddc99a
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/domain/Person.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/web/DefaultController.java b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/web/DefaultController.java
new file mode 100644
index 00000000..cf18d709
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/java/org/springframework/ldap/samples/article/web/DefaultController.java
@@ -0,0 +1,62 @@
+package org.springframework.ldap.samples.article.web;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.samples.article.dao.PersonDao;
+import org.springframework.ldap.samples.article.domain.Person;
+import org.springframework.ldap.samples.utils.HtmlRowLdapTreeVisitor;
+import org.springframework.ldap.samples.utils.LdapTree;
+import org.springframework.ldap.samples.utils.LdapTreeBuilder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+@Controller
+public class DefaultController {
+
+ @Autowired
+ private LdapTreeBuilder ldapTreeBuilder;
+
+ @Autowired
+ private PersonDao personDao;
+
+ @RequestMapping("/welcome.do")
+ public void welcomeHandler() {
+ }
+
+ @RequestMapping("/doStuff.do")
+ public ModelAndView showTree() {
+ LdapTree ldapTree = ldapTreeBuilder.getLdapTree(DistinguishedName.EMPTY_PATH);
+ HtmlRowLdapTreeVisitor visitor = new HtmlRowLdapTreeVisitor();
+ ldapTree.traverse(visitor);
+ return new ModelAndView("showTree", "rows", visitor.getRows());
+ }
+
+ @RequestMapping("/addPerson.do")
+ public ModelAndView addPerson() {
+ Person person = getPerson();
+
+ personDao.create(person);
+ return showTree();
+ }
+
+ @RequestMapping("/removePerson.do")
+ public ModelAndView removePerson() {
+ Person person = getPerson();
+
+ personDao.delete(person);
+ return showTree();
+ }
+
+
+ private Person getPerson() {
+ Person person = new Person();
+ person.setFullName("John Doe");
+ person.setLastName("Doe");
+ person.setCompany("company1");
+ person.setCountry("Sweden");
+ person.setDescription("Test user");
+ return person;
+ }
+
+}
diff --git a/mvn-build/samples/spring-ldap-article/src/main/java/overview.html b/mvn-build/samples/spring-ldap-article/src/main/java/overview.html
new file mode 100644
index 00000000..309641cf
--- /dev/null
+++ b/mvn-build/samples/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/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/applicationContext.xml b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/applicationContext.xml
new file mode 100644
index 00000000..1cc7472c
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/applicationContext.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/basic-servlet.xml b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/basic-servlet.xml
new file mode 100644
index 00000000..e3b71874
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/basic-servlet.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/jsp/showTree.jsp b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/jsp/showTree.jsp
new file mode 100644
index 00000000..58271cc2
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/jsp/showTree.jsp
@@ -0,0 +1,11 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
+
+
+Add new test person
+Remove test person
+
+
+ ${row}
+
+
+
diff --git a/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/jsp/welcome.jsp b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/jsp/welcome.jsp
new file mode 100644
index 00000000..9c99a2d7
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/jsp/welcome.jsp
@@ -0,0 +1,5 @@
+
+
+Hello
+
+
\ No newline at end of file
diff --git a/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/ldap.properties b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/ldap.properties
new file mode 100644
index 00000000..8843f1f1
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/ldap.properties
@@ -0,0 +1,4 @@
+urls=ldap://127.0.0.1:3900
+userDn=uid=admin,ou=system
+password=secret
+base=dc=jayway,dc=se
diff --git a/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/setup_data.ldif b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/setup_data.ldif
new file mode 100644
index 00000000..885a8ca4
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/setup_data.ldif
@@ -0,0 +1,110 @@
+dn: ou=groups,dc=jayway,dc=se
+objectclass: top
+objectclass: organizationalUnit
+ou: groups
+
+dn: cn=ROLE_USER,ou=groups,dc=jayway,dc=se
+objectclass: top
+objectclass: groupOfUniqueNames
+cn: ROLE_USER
+uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se
+uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se
+uniqueMember: cn=Some Person,ou=company1,c=Norway,dc=jayway,dc=se
+uniqueMember: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se
+uniqueMember: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se
+
+dn: cn=ROLE_ADMIN,ou=groups,dc=jayway,dc=se
+objectclass: top
+objectclass: groupOfUniqueNames
+cn: ROLE_ADMIN
+uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se
+
+dn: c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: country
+c: Sweden
+description: The country of Sweden
+
+dn: c=Norway,dc=jayway,dc=se
+objectclass: top
+objectclass: country
+c: Norway
+description: The country of Norway
+
+dn: ou=company1,c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: organizationalUnit
+ou: company1
+description: First company in Sweden
+
+dn: ou=company2,c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: organizationalUnit
+ou: company2
+description: Second company in Sweden
+
+dn: ou=company1,c=Norway,dc=jayway,dc=se
+objectclass: top
+objectclass: organizationalUnit
+ou: company1
+description: First company in Norway
+
+dn: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: some.person
+userPassword: password
+cn: Some Person
+sn: Person
+description: Sweden, Company1, Some Person
+telephoneNumber: +46 555-123456
+
+dn: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: some.person2
+userPassword: password
+cn: Some Person2
+sn: Person2
+description: Sweden, Company1, Some Person2
+telephoneNumber: +46 555-654321
+
+dn: cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: some.person3
+userPassword: password
+cn: Some Person3
+sn: Person3
+description: Sweden, Company1, Some Person3
+telephoneNumber: +46 555-123654
+
+dn: cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: some.person4
+userPassword: password
+cn: Some Person
+sn: Person
+description: Sweden, Company2, Some Person
+telephoneNumber: +46 555-456321
+
+dn: cn=Some Person+sn=Person,ou=company1,c=Norway,dc=jayway,dc=se
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: some.norwegian
+userPassword: password
+cn: Some Person
+sn: Person
+description: Norway, Company1, Some Person+Person
+telephoneNumber: +45 555-654123
diff --git a/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/web.xml b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..f48d838c
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,30 @@
+
+
+
+ Spring LDAP Basic Example
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+
+ basic
+
+ org.springframework.web.servlet.DispatcherServlet
+
+
+ contextConfigLocation
+ /WEB-INF/basic-servlet.xml
+
+ 1
+
+
+
+ basic
+ *.do
+
+
diff --git a/mvn-build/samples/spring-ldap-article/src/test/java/.dummy b/mvn-build/samples/spring-ldap-article/src/test/java/.dummy
new file mode 100644
index 00000000..e69de29b
diff --git a/mvn-build/samples/spring-ldap-article/src/test/resources/config/ldap.properties b/mvn-build/samples/spring-ldap-article/src/test/resources/config/ldap.properties
new file mode 100644
index 00000000..270d8f72
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/test/resources/config/ldap.properties
@@ -0,0 +1,4 @@
+urls=ldap://127.0.0.1
+userDn=cn=Manager,dc=jayway,dc=se
+password=secret
+base=dc=jayway,dc=se
diff --git a/mvn-build/samples/spring-ldap-article/src/test/resources/config/testContext.xml b/mvn-build/samples/spring-ldap-article/src/test/resources/config/testContext.xml
new file mode 100644
index 00000000..7f3b07f3
--- /dev/null
+++ b/mvn-build/samples/spring-ldap-article/src/test/resources/config/testContext.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mvn-build/test-support/pom.xml b/mvn-build/test-support/pom.xml
index 15386390..0b6b71c9 100644
--- a/mvn-build/test-support/pom.xml
+++ b/mvn-build/test-support/pom.xml
@@ -22,8 +22,8 @@
maven-compiler-plugin
- 1.4
- 1.4
+ 1.5
+ 1.5
@@ -35,6 +35,16 @@
1.2.2-SNAPSHOT
+
+ org.opends
+ openDS
+ 1.0.0-build005
+
+
+ commons-io
+ commons-io
+ 1.4
+
org.apache.commons
commons-io
@@ -43,7 +53,7 @@
org.apache.directory.server
apacheds-server-main
- 1.5.0
+ 1.0.2
org.slf4j
diff --git a/mvn-build/test-support/src/main/java/org/springframework/ldap/test/LdapTestUtils.java b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/LdapTestUtils.java
index 40e29e5f..cffba3d0 100644
--- a/mvn-build/test-support/src/main/java/org/springframework/ldap/test/LdapTestUtils.java
+++ b/mvn-build/test-support/src/main/java/org/springframework/ldap/test/LdapTestUtils.java
@@ -45,7 +45,6 @@ import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
import org.springframework.core.io.Resource;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DistinguishedName;
-import org.springframework.ldap.core.support.AbstractContextSource;
/**
* Utilities for starting, stopping and populating an in-process Apache
@@ -96,7 +95,7 @@ public class LdapTestUtils {
partitionConfiguration.setContextEntry(getRootPartitionAttributes(defaultPartitionName));
partitionConfiguration.setName(defaultPartitionName);
- cfg.setPartitionConfigurations(Collections.singleton(partitionConfiguration));
+ cfg.setContextPartitionConfigurations(Collections.singleton(partitionConfiguration));
// Start the Server
Hashtable env = createEnv(principal, credentials);
@@ -213,7 +212,7 @@ public class LdapTestUtils {
public static void cleanAndSetup(ContextSource contextSource, DistinguishedName rootNode, Resource ldifFile)
throws NamingException, IOException {
-
+
clearSubContexts(contextSource, rootNode);
loadLdif(contextSource, ldifFile);
}