From 18397362a3b58c12cf0f74e433da0ea222b6f2ba Mon Sep 17 00:00:00 2001
From: Ulrik Sandberg
Date: Wed, 28 Jan 2009 20:03:02 +0000
Subject: [PATCH] Added article project that uses Spring 2.0 in order to verify
backwards compatibility.
---
samples/article-spring20/.springBeans | 25 ++
samples/article-spring20/pom.xml | 189 +++++++++
samples/article-spring20/readme.txt | 21 +
.../ldap/samples/article/dao/PersonDao.java | 42 ++
.../samples/article/dao/PersonDaoImpl.java | 163 ++++++++
.../article/dao/TraditionalPersonDaoImpl.java | 377 ++++++++++++++++++
.../ldap/samples/article/domain/Person.java | 104 +++++
.../article/web/DefaultController.java | 132 ++++++
.../src/main/java/overview.html | 3 +
.../src/main/resources/log4j.properties | 7 +
.../webapp/WEB-INF/applicationContext.xml | 36 ++
.../src/main/webapp/WEB-INF/basic-servlet.xml | 35 ++
.../main/webapp/WEB-INF/jsp/showPerson.jsp | 20 +
.../src/main/webapp/WEB-INF/jsp/showTree.jsp | 17 +
.../src/main/webapp/WEB-INF/ldap.properties | 4 +
.../src/main/webapp/WEB-INF/setup_data.ldif | 35 ++
.../src/main/webapp/WEB-INF/web.xml | 34 ++
.../src/main/webapp/index.htm | 5 +
.../dao/AbstractPersonDaoIntegrationTest.java | 122 ++++++
.../dao/PersonDaoImplIntegrationTest.java | 33 ++
...aditionalPersonDaoImplIntegrationTest.java | 31 ++
.../src/test/resources/config/ldap.properties | 4 +
.../src/test/resources/config/testContext.xml | 37 ++
samples/pom.xml | 1 +
24 files changed, 1477 insertions(+)
create mode 100644 samples/article-spring20/.springBeans
create mode 100644 samples/article-spring20/pom.xml
create mode 100644 samples/article-spring20/readme.txt
create mode 100644 samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java
create mode 100644 samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java
create mode 100644 samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java
create mode 100644 samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/domain/Person.java
create mode 100644 samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/web/DefaultController.java
create mode 100644 samples/article-spring20/src/main/java/overview.html
create mode 100644 samples/article-spring20/src/main/resources/log4j.properties
create mode 100644 samples/article-spring20/src/main/webapp/WEB-INF/applicationContext.xml
create mode 100644 samples/article-spring20/src/main/webapp/WEB-INF/basic-servlet.xml
create mode 100755 samples/article-spring20/src/main/webapp/WEB-INF/jsp/showPerson.jsp
create mode 100644 samples/article-spring20/src/main/webapp/WEB-INF/jsp/showTree.jsp
create mode 100644 samples/article-spring20/src/main/webapp/WEB-INF/ldap.properties
create mode 100644 samples/article-spring20/src/main/webapp/WEB-INF/setup_data.ldif
create mode 100644 samples/article-spring20/src/main/webapp/WEB-INF/web.xml
create mode 100644 samples/article-spring20/src/main/webapp/index.htm
create mode 100644 samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java
create mode 100644 samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java
create mode 100644 samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java
create mode 100644 samples/article-spring20/src/test/resources/config/ldap.properties
create mode 100644 samples/article-spring20/src/test/resources/config/testContext.xml
diff --git a/samples/article-spring20/.springBeans b/samples/article-spring20/.springBeans
new file mode 100644
index 00000000..fcb9ecc0
--- /dev/null
+++ b/samples/article-spring20/.springBeans
@@ -0,0 +1,25 @@
+
+
+ 1
+
+
+
+
+
+
+ src/main/webapp/WEB-INF/applicationContext.xml
+ src/main/webapp/WEB-INF/basic-servlet.xml
+ src/test/resources/config/testContext.xml
+
+
+
+
+ true
+ false
+
+ src/main/webapp/WEB-INF/applicationContext.xml
+ src/main/webapp/WEB-INF/basic-servlet.xml
+
+
+
+
diff --git a/samples/article-spring20/pom.xml b/samples/article-spring20/pom.xml
new file mode 100644
index 00000000..95a0c608
--- /dev/null
+++ b/samples/article-spring20/pom.xml
@@ -0,0 +1,189 @@
+
+
+
+ org.springframework.ldap
+ spring-ldap-parent
+ 1.3.0.CI-SNAPSHOT
+
+ 4.0.0
+ spring-ldap-article-spring20
+ Spring LDAP Article Sample (Spring 2.0)
+ war
+ Example code that matches the article published on
+ java.net on April 18, 2006. Uses Spring 2.0, rather than 2.5
+
+
+
+ maven-compiler-plugin
+
+ 1.5
+ 1.5
+
+
+
+ org.mortbay.jetty
+ maven-jetty-plugin
+
+
+
+
+
+
+ org.springframework.ldap
+ spring-ldap-test
+
+
+ log4j
+ log4j
+ 1.2.9
+
+
+ javax.servlet
+ jstl
+ 1.2
+
+
+ org.springframework.ldap
+ spring-ldap-samples-utils
+
+
+ org.springframework
+ spring-test
+
+
+
+
+ org.springframework
+ spring-beans
+ 2.0.8
+
+
+ org.springframework
+ spring-core
+ 2.0.8
+
+
+ org.springframework
+ spring-dao
+ 2.0.8
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-core
+
+
+
+
+ org.springframework
+ spring-jdbc
+ 2.0.8
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-core
+
+
+
+
+ org.springframework
+ spring-context
+ 2.0.8
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-core
+
+
+
+
+ org.springframework
+ spring-web
+ 2.0.8
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework
+ spring-core
+
+
+
+
+ org.springframework
+ spring-support
+ 2.0.8
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework
+ spring-core
+
+
+
+
+ org.springframework
+ spring-webmvc
+ 2.0.8
+
+
+ org.springframework
+ spring-web
+
+
+ org.springframework
+ spring-support
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework
+ spring-core
+
+
+
+
+ javax.servlet
+ servlet-api
+ provided
+
+
+
+ org.springframework
+ spring-mock
+ 2.0.8
+ test
+
+
+
diff --git a/samples/article-spring20/readme.txt b/samples/article-spring20/readme.txt
new file mode 100644
index 00000000..e603ea31
--- /dev/null
+++ b/samples/article-spring20/readme.txt
@@ -0,0 +1,21 @@
+Uses Spring 2.0.
+Sample application demonstrating how to do the most basic stuff in Spring LDAP.
+A very simple dao implementation is provided in
+org.springframework.ldap.samples.article.dao.PersonDaoImpl
+It demonstrates some basic operations using Spring LDAP. For reference purposes,
+a corresponding implementation using ordinary Java LDAP/JNDI implementation is
+available in TraditionalPersonDaoImpl.
+
+How to use:
+-----------
+The project is in a Maven build structure. Make sure you have installed the samples-utils artifact, as this will be
+needed for this project to work.
+
+'mvn jetty:run' will start up a web server demonstrating the capabilities. The web application will be available
+under http://localhost:8080/spring-ldap-person-article-spring20/
+
+'mvn eclipse:eclipse' will construct an Eclipse project for you to use. Import that project into Eclipse using
+File/Import/Existing Project, and select this directory.
+
+'mvn test' will run some integration tests that require the LDAP server to be running. It's recommended to run
+'mvn jetty:run' from another terminal window before 'mvn test'.
diff --git a/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java
new file mode 100644
index 00000000..5bbd799d
--- /dev/null
+++ b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDao.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2005-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 Hellborg 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/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java
new file mode 100644
index 00000000..26ff3d0e
--- /dev/null
+++ b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/PersonDaoImpl.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2005-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 Hellborg 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.
+ *
+ *Mattias Hellborg Arthurssonellborg 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/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java
new file mode 100644
index 00000000..9e47adb9
--- /dev/null
+++ b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImpl.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2005-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 Hellborg 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 (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 (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 RuntimeException(
+ "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/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/domain/Person.java b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/domain/Person.java
new file mode 100644
index 00000000..d56d4afd
--- /dev/null
+++ b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/domain/Person.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2005-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 Hellborg 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/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/web/DefaultController.java b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/web/DefaultController.java
new file mode 100644
index 00000000..71accf2c
--- /dev/null
+++ b/samples/article-spring20/src/main/java/org/springframework/ldap/samples/article/web/DefaultController.java
@@ -0,0 +1,132 @@
+package org.springframework.ldap.samples.article.web;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.ldap.core.DirContextOperations;
+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.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
+
+/**
+ * Default controller.
+ *
+ * @author Mattias Hellborg Arthursson
+ * @author Ulrik Sandberg
+ */
+public class DefaultController extends MultiActionController {
+
+ private LdapTreeBuilder ldapTreeBuilder;
+
+ public void setLdapTreeBuilder(LdapTreeBuilder ldapTreeBuilder) {
+ this.ldapTreeBuilder = ldapTreeBuilder;
+ }
+
+ public void setPersonDao(PersonDao personDao) {
+ this.personDao = personDao;
+ }
+
+ private PersonDao personDao;
+
+ public ModelAndView welcomeHandler(HttpServletRequest request, HttpServletResponse response) {
+ return new ModelAndView("welcome");
+ }
+
+ public ModelAndView showTree(HttpServletRequest request, HttpServletResponse response) {
+ LdapTree ldapTree = ldapTreeBuilder.getLdapTree(DistinguishedName.EMPTY_PATH);
+ HtmlRowLdapTreeVisitor visitor = new PersonLinkHtmlRowLdapTreeVisitor();
+ ldapTree.traverse(visitor);
+ return new ModelAndView("showTree", "rows", visitor.getRows());
+ }
+
+ public ModelAndView addPerson(HttpServletRequest request, HttpServletResponse response) {
+ Person person = getPerson();
+
+ personDao.create(person);
+ return showTree(request, response);
+ }
+
+ public ModelAndView updatePhoneNumber(HttpServletRequest request, HttpServletResponse response) {
+ Person person = personDao.findByPrimaryKey("Sweden", "company1", "John Doe");
+ person.setPhone(StringUtils.join(new String[] { person.getPhone(), "0" }));
+
+ personDao.update(person);
+ return showTree(request, response);
+ }
+
+ public ModelAndView removePerson(HttpServletRequest request, HttpServletResponse response) {
+ Person person = getPerson();
+
+ personDao.delete(person);
+ return showTree(request, response);
+ }
+
+ public ModelAndView showPerson(HttpServletRequest request, HttpServletResponse response, Person query) {
+ String country = query.getCountry();
+ String company = query.getCompany();
+ String fullName = query.getFullName();
+ Person person = personDao.findByPrimaryKey(country, company, fullName);
+ return new ModelAndView("showPerson", "person", person);
+ }
+
+ 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;
+ }
+
+ /**
+ * Generates appropriate links for person leaves in the tree.
+ *
+ * @author Mattias Hellborg Arthursson
+ */
+ private static final class PersonLinkHtmlRowLdapTreeVisitor extends HtmlRowLdapTreeVisitor {
+ @Override
+ protected String getLinkForNode(DirContextOperations node) {
+ String[] objectClassValues = node.getStringAttributes("objectClass");
+ if (containsValue(objectClassValues, "person")) {
+ DistinguishedName distinguishedName = (DistinguishedName) node.getDn();
+ String country = encodeValue(distinguishedName.getValue("c"));
+ String company = encodeValue(distinguishedName.getValue("ou"));
+ String fullName = encodeValue(distinguishedName.getValue("cn"));
+
+ return "showPerson.do?country=" + country + "&company=" + company + "&fullName=" + fullName;
+ }
+ else {
+ return super.getLinkForNode(node);
+ }
+ }
+
+ private String encodeValue(String value) {
+ try {
+ return URLEncoder.encode(value, "UTF8");
+ }
+ catch (UnsupportedEncodingException e) {
+ // Not supposed to happen
+ throw new RuntimeException("Unexpected encoding exception", e);
+ }
+ }
+
+ private boolean containsValue(String[] values, String value) {
+ for (String oneValue : values) {
+ if (StringUtils.equals(oneValue, value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/samples/article-spring20/src/main/java/overview.html b/samples/article-spring20/src/main/java/overview.html
new file mode 100644
index 00000000..309641cf
--- /dev/null
+++ b/samples/article-spring20/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/samples/article-spring20/src/main/resources/log4j.properties b/samples/article-spring20/src/main/resources/log4j.properties
new file mode 100644
index 00000000..5ce7a3ee
--- /dev/null
+++ b/samples/article-spring20/src/main/resources/log4j.properties
@@ -0,0 +1,7 @@
+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.logger.org.apache.directory=ERROR
\ No newline at end of file
diff --git a/samples/article-spring20/src/main/webapp/WEB-INF/applicationContext.xml b/samples/article-spring20/src/main/webapp/WEB-INF/applicationContext.xml
new file mode 100644
index 00000000..5331fea0
--- /dev/null
+++ b/samples/article-spring20/src/main/webapp/WEB-INF/applicationContext.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/article-spring20/src/main/webapp/WEB-INF/basic-servlet.xml b/samples/article-spring20/src/main/webapp/WEB-INF/basic-servlet.xml
new file mode 100644
index 00000000..8917ac4b
--- /dev/null
+++ b/samples/article-spring20/src/main/webapp/WEB-INF/basic-servlet.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+/welcome.do=welcome
+/showTree.do=showTree
+/addPerson.do=addPerson
+/updatePhoneNumber.do=updatePhoneNumber
+/removePerson.do=removePerson
+/showPerson.do=showPerson
+
+
+
+
+
+
+ /*.do=defaultController
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/article-spring20/src/main/webapp/WEB-INF/jsp/showPerson.jsp b/samples/article-spring20/src/main/webapp/WEB-INF/jsp/showPerson.jsp
new file mode 100755
index 00000000..4d25b21b
--- /dev/null
+++ b/samples/article-spring20/src/main/webapp/WEB-INF/jsp/showPerson.jsp
@@ -0,0 +1,20 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
+
+
+Back
+
+
+Full name: ${person.fullName}
+
+LastName: ${person.lastName}
+
+Description: ${person.description}
+
+Country: ${person.country}
+
+Company: ${person.company}
+
+Phone: ${person.phone}
+
+
+
diff --git a/samples/article-spring20/src/main/webapp/WEB-INF/jsp/showTree.jsp b/samples/article-spring20/src/main/webapp/WEB-INF/jsp/showTree.jsp
new file mode 100644
index 00000000..80546b96
--- /dev/null
+++ b/samples/article-spring20/src/main/webapp/WEB-INF/jsp/showTree.jsp
@@ -0,0 +1,17 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
+
+
+Operations
+Clicking a link below performs the described operation which will be reflected in the LDAP tree below
+Add new test person 'John Doe' (only works once)
+Add a '0' to the phone number of test person (only works if the person has been created)
+Remove test person
+
+
Tree contents
+Click a person row to see the attribute values (country and company rows do not have additional info)
+
+ ${row}
+
+
+
+
diff --git a/samples/article-spring20/src/main/webapp/WEB-INF/ldap.properties b/samples/article-spring20/src/main/webapp/WEB-INF/ldap.properties
new file mode 100644
index 00000000..8843f1f1
--- /dev/null
+++ b/samples/article-spring20/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/samples/article-spring20/src/main/webapp/WEB-INF/setup_data.ldif b/samples/article-spring20/src/main/webapp/WEB-INF/setup_data.ldif
new file mode 100644
index 00000000..e6d7dce6
--- /dev/null
+++ b/samples/article-spring20/src/main/webapp/WEB-INF/setup_data.ldif
@@ -0,0 +1,35 @@
+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/samples/article-spring20/src/main/webapp/WEB-INF/web.xml b/samples/article-spring20/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..3482f7a9
--- /dev/null
+++ b/samples/article-spring20/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+
+
+
+ Spring LDAP Basic Example
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+
+ basic
+
+ org.springframework.web.servlet.DispatcherServlet
+
+
+ contextConfigLocation
+ /WEB-INF/basic-servlet.xml
+
+ 1
+
+
+
+ basic
+ *.do
+
+
+
+ index.htm
+
+
diff --git a/samples/article-spring20/src/main/webapp/index.htm b/samples/article-spring20/src/main/webapp/index.htm
new file mode 100644
index 00000000..c4a3f6f3
--- /dev/null
+++ b/samples/article-spring20/src/main/webapp/index.htm
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java b/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java
new file mode 100644
index 00000000..a682c53a
--- /dev/null
+++ b/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/AbstractPersonDaoIntegrationTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2005-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.NameNotFoundException;
+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 Hellborg 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("NameNotFoundException (when using Spring LDAP) or RuntimeException (when using traditional) expected");
+ } catch (NameNotFoundException expected) {
+ // expected
+ } catch (RuntimeException expected) {
+ // expected
+ }
+ }
+ }
+
+ public void testGetAllPersonNames() {
+ List result = personDao.getAllPersonNames();
+ assertEquals(2, result.size());
+ String first = (String) result.get(0);
+ assertEquals("Some Person", first);
+ }
+
+ public void testFindAll() {
+ List result = personDao.findAll();
+ assertEquals(2, 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/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java b/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java
new file mode 100644
index 00000000..928207fd
--- /dev/null
+++ b/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/PersonDaoImplIntegrationTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2005-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 Hellborg Arthursson
+ * @author Ulrik Sandberg
+ */
+public class PersonDaoImplIntegrationTest extends
+ AbstractPersonDaoIntegrationTest {
+
+ public void setPersonDao(
+ PersonDaoImpl personDao) {
+ this.personDao = personDao;
+ }
+}
diff --git a/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java b/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java
new file mode 100644
index 00000000..4cc677f9
--- /dev/null
+++ b/samples/article-spring20/src/test/java/org/springframework/ldap/samples/article/dao/TraditionalPersonDaoImplIntegrationTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2005-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * Integration tests for the TraditionalPersonDaoImpl class.
+ *
+ * @author Mattias Hellborg Arthursson
+ * @author Ulrik Sandberg
+ */
+public class TraditionalPersonDaoImplIntegrationTest
+ extends AbstractPersonDaoIntegrationTest {
+
+ public void setPersonDao(
+ TraditionalPersonDaoImpl personDao) {
+ this.personDao = personDao;
+ }
+}
diff --git a/samples/article-spring20/src/test/resources/config/ldap.properties b/samples/article-spring20/src/test/resources/config/ldap.properties
new file mode 100644
index 00000000..8843f1f1
--- /dev/null
+++ b/samples/article-spring20/src/test/resources/config/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/samples/article-spring20/src/test/resources/config/testContext.xml b/samples/article-spring20/src/test/resources/config/testContext.xml
new file mode 100644
index 00000000..4a762506
--- /dev/null
+++ b/samples/article-spring20/src/test/resources/config/testContext.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/pom.xml b/samples/pom.xml
index 86bdd9cf..d51f71d9 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -16,6 +16,7 @@
samples-utils
article
+ article-spring20
demos