Chapter 2. Basic Operations

2.1. Search and Lookup Using AttributesMapper

In this example we will use an AttributesMapper to easily build a List of all common names of all person objects.

Example 2.1. AttributesMapper that returns a single attribute

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;

   public void setLdapTemplate(LdapTemplate ldapTemplate) {
      this.ldapTemplate = ldapTemplate;
   }

   public List getAllPersonNames() {
      return ldapTemplate.search(
         "", "(objectclass=person)",
         new AttributesMapper() {
            public Object mapFromAttributes(Attributes attrs)
               throws NamingException {
               return attrs.get("cn").get();
            }
         });
   }
}

The inline implementation of AttributesMapper just gets the desired attribute value from the Attributes and returns it. Internally, LdapTemplate iterates over all entries found, calling the given AttributesMapper for each entry, and collects the results in a list. The list is then returned by the search method.

Note that the AttributesMapper implementation could easily be modified to return a full Person object:

Example 2.2. AttributesMapper that returns a Person object

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   private class PersonAttributesMapper implements AttributesMapper {
      public Object mapFromAttributes(Attributes attrs) throws NamingException {
         Person person = new Person();
         person.setFullName((String)attrs.get("cn").get());
         person.setLastName((String)attrs.get("sn").get());
         person.setDescription((String)attrs.get("description").get());
         return person;
      }
   }

   public List getAllPersons() {
      return ldapTemplate.search("", "(objectclass=person)", new PersonAttributesMapper());
   }
}

If you have the distinguished name (dn) that identifies an entry, you can retrieve the entry directly, without searching for it. This is called a lookup in Java LDAP. The following example shows how a lookup results in a Person object:

Example 2.3. A lookup resulting in a Person object

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public Person findPerson(String dn) {
      return (Person) ldapTemplate.lookup(dn, new PersonAttributesMapper());
   }
}

This will look up the specified dn and pass the found attributes to the supplied AttributesMapper, in this case resulting in a Person object.

2.2. Building Dynamic Filters

We can build dynamic filters to use in searches, using the classes from the org.springframework.ldap.filter package. Let's say that we want the following filter: (&(objectclass=person)(sn=?)), where we want the ? to be replaced with the value of the parameter lastName. This is how we do it using the filter support classes:

Example 2.4. Building a search filter dynamically

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public List getPersonNamesByLastName(String lastName) {
      AndFilter filter = new AndFilter();
      filter.and(new EqualsFilter("objectclass", "person"));
      filter.and(new EqualsFilter("sn", lastName));
      return ldapTemplate.search(
         "", filter.encode(),
         new AttributesMapper() {
            public Object mapFromAttributes(Attributes attrs)
               throws NamingException {
               return attrs.get("cn").get();
            }
         });
   }
}

To perform a wildcard search, it's possible to use the WhitespaceWildcardsFilter:

Example 2.5. Building a wildcard search filter

AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new WhitespaceWildcardsFilter("cn", cn));

Note

In addition to simplifying building of complex search filters, the Filter classes also provide proper escaping of any unsafe characters. This prevents "ldap injection", where a user might use such characters to inject unwanted operations into your LDAP operations.

2.3. Building Dynamic Distinguished Names

The standard Name interface represents a generic name, which is basically an ordered sequence of components. The Name interface also provides operations on that sequence; e.g., add or remove. LdapTemplate provides an implementation of the Name interface: DistinguishedName. Using this class will greatly simplify building distinguished names, especially considering the sometimes complex rules regarding escapings and encodings. As with the Filter classes this helps preventing potentially malicious data being injected into your LDAP operations.

The following example illustrates how DistinguishedName can be used to dynamically construct a distinguished name:

Example 2.6. Building a distinguished name dynamically

package com.example.dao;

import org.springframework.ldap.core.support.DistinguishedName;
import javax.naming.Name;

public class PersonDaoImpl implements PersonDao {
   public static final String BASE_DN = "dc=example,dc=com";
   ...
   protected Name buildDn(Person p) {
      DistinguishedName dn = new DistinguishedName(BASE_DN);
      dn.add("c", p.getCountry());
      dn.add("ou", p.getCompany());
      dn.add("cn", p.getFullname());
      return dn;
   }
}

Assuming that a Person has the following attributes:

countrySweden
companySome Company
fullnameSome Person

The code above would then result in the following distinguished name:

cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com

In Java 5, there is an implementation of the Name interface: LdapName. If you are in the Java 5 world, you might as well use LdapName. However, you may still use DistinguishedName if you so wish.

2.4. Binding and Unbinding

2.4.1. Binding Data

Inserting data in Java LDAP is called binding. In order to do that, a distinguished name that uniquely identifies the new entry is required. The following example shows how data is bound using LdapTemplate:

Example 2.7. Binding data using Attributes

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void create(Person p) {
      Name dn = buildDn(p);
      ldapTemplate.bind(dn, null, buildAttributes(p));
   }

   private Attributes buildAttributes(Person p) {
      Attributes attrs = new BasicAttributes();
      BasicAttribute ocattr = new BasicAttribute("objectclass");
      ocattr.add("top");
      ocattr.add("person");
      attrs.put(ocattr);
      attrs.put("cn", "Some Person");
      attrs.put("sn", "Person");
      return attrs;
   }
}

The Attributes building is--while dull and verbose--sufficient for many purposes. It is, however, possible to simplify the binding operation further, which will be described in Chapter 3, Simpler Attribute Access and Manipulation with DirContextAdapter.

2.4.2. Unbinding Data

Removing data in Java LDAP is called unbinding. A distinguished name (dn) is required to identify the entry, just as in the binding operation. The following example shows how data is unbound using LdapTemplate:

Example 2.8. Unbinding data

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void delete(Person p) {
      Name dn = buildDn(p);
      ldapTemplate.unbind(dn);
   }
}

2.5. Modifying

In Java LDAP, data can be modified in two ways: either using rebind or modifyAttributes.

2.5.1. Modifying using rebind

A rebind is a very crude way to modify data. It's basically an unbind followed by a bind. It looks like this:

Example 2.9. Modifying using rebind

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void update(Person p) {
      Name dn = buildDn(p);
      ldapTemplate.rebind(dn, null, buildAttributes(p));
   }
}

2.5.2. Modifying using modifyAttributes

If only the modified attributes should be replaced, there is a method called modifyAttributes that takes an array of modifications:

Example 2.10. Modifying using modifyAttributes

package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void updateDescription(Person p) {
      Name dn = buildDn(p);
      Attribute attr = new BasicAttribute("description", p.getDescription())
      ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
      ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
   }
}

Building Attributes and ModificationItem arrays is a lot of work, but as you will see in Chapter 3, Simpler Attribute Access and Manipulation with DirContextAdapter, the update operations can be simplified.

2.6. Sample applications

It is recommended that you review the Spring LDAP sample applications included in the release distribution for best-practice illustrations of the features of this library. A description of each sample is provided below:

  1. spring-ldap-person - the sample demonstrating most features.

  2. spring-ldap-article - the sample application that was written to accompany a java.net article about Spring LDAP.