This version is still in development and is not considered stable yet. For the latest stable version, please use Spring LDAP 4.0.2!

Introduction

This section offers a relatively quick introduction to Spring LDAP. It includes the following content:

Overview

Spring LDAP is designed to simplify LDAP programming in Java. Some of the features provided by the library are:

  • JdbcTemplate-style template simplifications to LDAP programming.

  • JPA- or Hibernate-style annotation-based object and directory mapping.

  • Spring Data repository support, including support for QueryDSL.

  • Utilities to simplify building LDAP queries and distinguished names.

  • Proper LDAP connection pooling.

  • Client-side LDAP compensating transaction support.

Traditional Java LDAP versus LdapClient

Consider a method that should search some storage for all persons and return their names in a list. By using JDBC, we would create a connection and run a query by using a statement. We would then loop over the result set and retrieve the column we want, adding it to a list.

Working against an LDAP database with JNDI, we would create a context and perform a search by using a search filter. We would then loop over the resulting naming enumeration, retrieve the attribute we want, and add it to a list.

The traditional way of implementing this person-name search method in Java LDAP looks like the next example. Note the code marked bold - this is the code that actually performs tasks related to the business purpose of the method. The rest is plumbing.

public class TraditionalPersonRepoImpl implements PersonRepo {
   public List<String> getAllPersonNames() {
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      env.put(Context.PROVIDER_URL, "ldap://localhost:389/dc=example,dc=com");

      DirContext ctx;
      try {
         ctx = new InitialDirContext(env);
      } catch (NamingException e) {
         throw new RuntimeException(e);
      }

      List<String> list = new LinkedList<String>();
      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 = attr.get().toString();
            list.add(cn);
         }
      } catch (NameNotFoundException e) {
         // The base context was not found.
         // Just clean up and exit.
      } catch (NamingException e) {
         throw new RuntimeException(e);
      } finally {
         if (results != null) {
            try {
               results.close();
            } catch (Exception e) {
               // Never mind this.
            }
         }
         if (ctx != null) {
            try {
               ctx.close();
            } catch (Exception e) {
               // Never mind this.
            }
         }
      }
      return list;
   }
}

By using the Spring LDAP AttributesMapper and LdapClient classes, we get the exact same functionality with the following code:

import static org.springframework.ldap.query.LdapQueryBuilder.query;

public class PersonRepoImpl implements PersonRepo {
   private LdapClient ldapClient;

   public void setLdapClient(LdapClient ldapClient) {
      this.ldapClient = ldapClient;
   }

   public List<String> getAllPersonNames() {
      return ldapClient.search().query(
            query().where("objectclass").is("person")
         ).toObject((Attributes attrs) ->
            attrs.get("cn").get().toString();
         );
   }
}

The amount of boilerplate code is significantly less than in the traditional example. The LdapClient search method makes sure a DirContext instance is created, performs the search, maps the attributes to a string by using the given AttributesMapper, collects the strings in an internal list, and, finally, returns the list. It also makes sure that the NamingEnumeration and DirContext are properly closed and takes care of any exceptions that might happen.

Naturally, this being a Spring Framework sub-project, we use Spring to configure our application, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:ldap="http://www.springframework.org/schema/ldap"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/ldap https://www.springframework.org/schema/ldap/spring-ldap.xsd">

   <ldap:context-source
          url="ldap://localhost:389"
          base="dc=example,dc=com"
          username="cn=Manager"
          password="secret" />

   <bean id="ldapClient" class="org.springframework.ldap.core.LdapClient" factory-method="create">
        <constructor-arg ref="contextSource" />
    </bean>

   <bean id="personRepo" class="com.example.repo.PersonRepoImpl">
      <property name="ldapClient" ref="ldapClient" />
   </bean>
</beans>
To use the custom XML namespace to configure the Spring LDAP components, you need to include references to this namespace in your XML declaration, as in the preceding example.

Packaging Overview

At a minimum, to use Spring LDAP you need the following:

  • spring-ldap-core: The Spring LDAP library

  • spring-core: Miscellaneous utility classes used internally by the framework

  • spring-beans: Interfaces and classes for manipulating Java beans

  • slf4j: A simple logging facade, used internally

In addition to the required dependencies, the following optional dependencies are required for certain functionality:

  • spring-data-ldap: Base infrastructure for repository support and so on

  • spring-context: Needed if your application is wired up by using the Spring Application Context. spring-context adds the ability for application objects to obtain resources by using a consistent API. It is definitely needed if you plan to use the BaseLdapPathBeanPostProcessor.

  • spring-tx: Needed if you plan to use the client-side compensating transaction support.

  • spring-jdbc: Needed if you plan to use the client-side compensating transaction support.

  • commons-pool: Needed if you plan to use the pooling functionality.

  • spring-batch: Needed if you plan to use the LDIF parsing functionality together with Spring Batch.

spring-data-ldap transitively adds spring-repository.xsd, which spring-ldap.xsd uses. Because of this, Spring LDAP’s XML config support requires the dependency even when Spring Data’s feature set is not in use.

Getting Started

The samples provide some useful examples of how to use Spring LDAP for common use cases.

Support

If you have questions, ask them on Stack Overflow with the spring-ldap tag. The project web page is spring.io/spring-ldap/.

Acknowledgements

The initial effort when starting the Spring LDAP project was sponsored by Jayway. Current maintenance of the project is funded by Pivotal, which has since been acquired by VMware.

Thanks to Structure101 for providing an open source license that has come in handy for keeping the project structure in check.