Lately I have been evaluating a couple of LDAP SDK's. The mean reason for this evaluation is the fact that the standard API provided with Java is not very coder-friendly. A lot of boilerplate code needs to be written even to do simple things.
The frameworks i've been looking at are: Unboundid, Ldaptive and Spring LDAP. In this article I will show an example of LDAP access using Unboundid LDAP SDK. Unboundid contains a sub-framework they call 'Unboundid LDAP SDK Persistence framework'. It contains annotation-based persistence support to/from LDAP.
In this example I will add a user to LDAP and then read it again. The user can be annotated with Unboundid annotations and this makes it very easy to do a basic implementation.
First of all we create a new Maven project and add the following dependencies:
<dependencies>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>2.3.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
Next, we define a User POJO which represents the User domain type. The POJO will be annotated by a couple of Unboundid annotations which indicate how the User is stored within LDAP.
package example;
import com.unboundid.ldap.sdk.persist.FilterUsage;
import com.unboundid.ldap.sdk.persist.LDAPField;
import com.unboundid.ldap.sdk.persist.LDAPObject;
@LDAPObject(structuralClass = "inetOrgPerson", defaultParentDN = "ou=people,dc=example,dc=com")
public class User {
@LDAPField(attribute = "uid", inRDN = true, filterUsage = FilterUsage.ALWAYS_ALLOWED)
private String id;
@LDAPField(attribute = "cn")
private String name;
@LDAPField(attribute = "sn")
private String surName;
@LDAPField(attribute = "mail")
private String emailAddress;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurName() {
return surName;
}
public void setSurName(String surName) {
this.surName = surName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}
The @LDAPObject annotation indicates that the User object is stored in LDAP and is based on the structural LDAP class 'inetOrgPerson'. Furthermore, a parent DN is specified so Unboundid knows where to store/retrieve LDAP user records. The @LDAPField annotation specifies the way each User property is stored in LDAP. More info about these annotation can be found here:
https://www.unboundid.com/products/ldap-sdk/docs/persist/index.php
Next we will define a Dao class which can be used to store/retrieve User's:
package example;
import com.unboundid.ldap.sdk.LDAPInterface;
import com.unboundid.ldap.sdk.persist.LDAPPersistException;
import com.unboundid.ldap.sdk.persist.LDAPPersister;
public class UserDao {
private LDAPInterface ldapServer;
private LDAPPersister<User> persister;
public UserDao(LDAPInterface ldapServer) throws LDAPPersistException {
this.ldapServer = ldapServer;
this.persister = LDAPPersister.getInstance(User.class);
}
public User findUser(String id) throws LDAPPersistException {
User userToFind = new User();
userToFind.setId(id);
User foundUser = persister.searchForObject(userToFind, ldapServer);
return foundUser;
}
public void addUser(User user) throws LDAPPersistException {
persister.add(user, ldapServer, null);
}
}
This Dao class receives an ldap server obect through its constructor. This ldap server object provides a lot of low-level LDAP manipulation operations. It is used by a higher-level persister to persist annotated objects. The findUser and addUser methods show how easy this is. Just create a User instance and pass it to the persister.
To show how this works, we create a basic unit test. In this unit test, an in-memory directory server is created and an LDAP schema is loaded. After that, we add a user and then retrieve it again. It's as easy as this:
package example;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import org.junit.Assert;
import org.junit.Test;
public class AppTest {
@Test
public void testUnboundid() throws Exception {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example, dc=com");
config.addAdditionalBindCredentials("cn=Directory Manager", "password");
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.importFromLDIF(true, "src/test/resources/ldap.ldif");
ds.startListening();
UserDao userDao = new UserDao(ds);
User newUser = new User();
newUser.setId("jdoe");
newUser.setName("John Doe");
newUser.setSurName("doe");
userDao.addUser(newUser);
User foundUser = userDao.findUser("jdoe");
Assert.assertEquals("John Doe", foundUser.getName());
}
}
My conclusion is that Unboundid is a very useful framework for LDAP access. It takes away much of the low-level coding that would otherwise be necessary when using the basic JNDI classes.