UnboundID LDAP SDK for Java

Ping Identity
Product Information
Advantages of the LDAP SDK

Ease of Use

The UnboundID LDAP SDK for Java was specifically designed to be very user-friendly. We have a great deal of experience working with real-world directory environments and writing directory-enabled applications, and as a result we are well aware of the challenges and frustrations that may be encountered when working with other LDAP SDKs.

The primary reason that JNDI is commonly-used for directory-enabled applications is that it is part of the core Java platform. In fact, that is perhaps the only reason for its popularity, since the API itself is absolutely horrible. It is very abstract (in fact, it is intended for use in communicating with a number of different types of systems, so it really isn't an LDAP API as much as an API that happens to provide LDAP support), which means that performing simple tasks often require significantly more code than should be required, and that it uses terminology that can be extremely confusing (e.g., in JNDI, the "bind" method is used to add an entry to the directory rather than performing an LDAP bind operation). Further, the abstract nature of the API may make some tasks either difficult (e.g., performing an LDAP compare operation) or impossible (e.g., retrieving the result code from an operation). Finally, JNDI provides virtually no support for anything but the most basic functionality. It provides no facilities for interacting with data in LDIF form, or for performing ASN.1 encoding/decoding needed to support development of custom controls and extensions.

The Netscape Directory SDK for Java is LDAP-centric and therefore much more user-friendly than JNDI. It doesn't suffer from the terminology conflicts that plagues JNDI, and it does offer support for additional functionality like parsing the server schema, LDIF processing, ASN.1 processing, and base64 processing. The primary concerns related to ease of use of the Netscape Directory SDK for Java aren't really issues of tasks being difficult to perform but rather that many common tasks are not as convenient as they could be.

The UnboundID LDAP SDK for Java is rather similar to the Netscape Directory SDK for Java, and developers familiar with the Netscape SDK should find it easy to switch to the UnboundID offering. However, many kinds of tasks can be performed in the UnboundID LDAP SDK for Java more conveniently and with less effort.

Perhaps the best way to illustrate the relative ease of use of these APIs is by providing a few examples. The rest of this page will demonstrate the code required to perform common tasks in JNDI, the Netscape Directory SDK for Java, and the UnboundID LDAP SDK for Java. For the sake of simplicity, exception handling has been omitted from all examples.

Establishing a Connection

This section will demonstrate the process for establishing a simple LDAPv3 connection (with no SSL or other security) and performing a simple bind operation.

JNDI

In JNDI, when you wish to establish a connection to an LDAP directory server, you should create an InitialLdapContext object. This can be quite a daunting task, because the only way that you have to specify the target server, whether or not to use SSL or some other security mechanism, whether or not to authenticate, etc. is by providing a set of properties. There aren't any convenience methods to help you figure out what those properties might be (although there are constants in some cases), so you have to look through the documentation to try to find them, and in fact some of these properties and/or values use "com.sun.*" prefixes, so it isn't even clear whether they are safe to use in code that might run on Java implementations provided by vendors other than Sun.

Here is an example of the work required to do this in JNDI:

Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://server.example.com:389/");
env.put(Context.SECURITY_PRINCIPAL,
        "uid=test.user,ou=People,dc=example,dc=com");
env.put(Context.SECURITY_CREDENTIALS, "password");
env.put("java.naming.ldap.version", "3");

LdapContext connection = new InitialLdapContext(env, null);

Netscape Directory SDK for Java

The Netscape Directory SDK for Java is significantly simpler to use than JNDI because there is no need to worry about figuring out what properties need to be set. The following example demonstrates the work required to create an authenticated connection using the Netscape Directory SDK for Java:

LDAPConnection connection = new LDAPConnection();
connection.connect(3, "server.example.com", 389,
                   "uid=test.user,ou=People,dc=example,dc=com", "password");

Note the inclusion of the "3" as the first argument to the connect method. This is very important (although unfortunately very easy to overlook, since there is also a version of the connect method without the version but with all of the other arguments) because by default the Netscape Directory SDK for Java uses LDAPv2 and you must override that if you want to use LDAPv3. Otherwise, you may find strange behavior in your application (e.g., not getting response controls when you expect them, or not being able to use extended operations, since LDAPv2 doesn't support them).

UnboundID LDAP SDK for Java

The process for establishing a connection using simple authentication in the UnboundID LDAP SDK for Java is similar to the process for performing the same operation in the Netscape Directory SDK for Java, but it also provides the ability to establish the connection and perform simple authentication in the constructor so it is not necessary to perform as two separate steps (although you can separate them if you wish to do so, e.g., to be able to get response controls from the bind operation). Further, there is no need to explicitly request LDAPv3, since the UnboundID LDAP SDK for Java uses LDAPv3 by default.

LDAPConnection connection = new LDAPConnection("server.example.com", 389,
     "uid=test.user,ou=People,dc=example,dc=com", "password");

Performing a Search Operation

This section will demonstrate the process for performing a search to find a user entry and then retrieve that user's e-mail address from the mail attribute. In each of these cases, it will be assumed that only one entry will be returned, so we don't need to worry about the possibility of multiple entries matching the search criteria.

JNDI

The JNDI "search" methods may be used to perform searches in the directory, and the simplest of these in most cases is the variant that takes string representations of the base DN and filter and a SearchControls object. This is another case where JNDI suffers from a poor choice of terminology because "controls" in this case does not have anything at all to do with LDAP controls but rather with a set of miscellaneous options used to perform the search (e.g., the search scope, attributes to return, size and time limits, etc.).

Something else that directory developers may find confusing is that the methods used to get attribute values have a generic return type of Object. It is not clear from the JNDI specification whether these objects will always be strings or whether they may be other types of objects. Further, it is not necessarily clear if it is possible to obtain the values in any other form (e.g., the raw bytes that make up the value).

The following code illustrates the process for performing the desired search processing in JNDI:

SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchControls.setReturningAttributes(new String[] { "mail" });

String mail = null;
NamingEnumeration<SearchResult> results =
     connection.search("dc=example,dc=com", "(uid=john.doe)", searchControls);
while (results.hasMore())
{
  SearchResult result = results.next();
  Attributes attributes = result.getAttributes();
  Attribute attribute = attributes.get("mail");
  if (attribute != null)
  {
    Object o = attribute.get();
    if (o != null)
    {
      mail = String.valueOf(o);
    }
  }
}

Netscape Directory SDK for Java

Performing this search using the Netscape Directory SDK requires slightly less code than the JNDI version, and again is significantly less confusing. The Netscape Directory SDK provides the ability to get attribute values as either strings or byte arrays. The process for achieving the desired result, however, is still not as convenient as it could be, and a significant problem that may arise is that the LDAPSearchResults.next() method may throw an exception even if the search is not yet complete (e.g., if the server returns a search result reference while there are still more entries to be returned, and the SDK has not been configured to follow referrals, which it is not by default), so just because an exception is thrown doesn't mean that search processing is over.

The following code illustrates the process for performing the desired search processing using the Netscape Directory SDK for Java. Note that for simplicity, this code does not attempt to address the above concern over throwing an exception when there are still more results to return.

LDAPSearchResults searchResults =
     connection.search("dc=example,dc=com", LDAPConnection.SCOPE_SUB,
                       "(uid=john.doe)", new String[] { "mail" }, false);

String mail = null;
while (searchResults.hasMoreElements())
{
  LDAPEntry entry = searchResults.next();
  LDAPAttribute attribute = entry.getAttribute("mail");
  if (attribute != null)
  {
    String[] values = attribute.getStringValueArray();
    if ((values != null) && (values.length > 0))
    {
      mail = values[0];
    }
  }
}

UnboundID LDAP SDK for Java

The following code may be used to perform the same task in the UnboundID LDAP SDK for Java:

SearchResult searchResults = connection.search("dc=example,dc=com",
                                  SearchScope.SUB, "(uid=john.doe)", "mail");

String mail = null;
if (searchResults.getEntryCount() > 0)
{
  SearchResultEntry entry = searchResults.getSearchEntries().get(0);
  mail = entry.getAttributeValue("mail");
}

In this case, you can see that the code is much more streamlined and significantly fewer steps are required to achieve the desired result. You can also see a number of other benefits that the UnboundID LDAP SDK for Java provides:

Performing an Add Operation

This section will demonstrate the process for adding a simple entry to the server. In this case, the entry added will have the following LDIF representation:

dn: dc=example,dc=com
objectClass: top
objectClass: domain
dc: example

JNDI

As noted above, JNDI uses the poorly-named "bind" method to add an entry to the server (and "unbind" to delete an entry). The basic reasoning for this is that it is trying to associate the provided set of data (the attributes) with a name (the DN), but this can be very confusing and frustrating for developers familiar with LDAP directories but not very familiar with JNDI because obviously the terms "bind" and "unbind" have very different meanings in LDAP. Another nit with JNDI is that Attributes and Attribute are interfaces rather than classes, so it is necessary to create BasicAttributes and BasicAttribute objects instead, and it appears that there is no way to create an attribute with multiple values so it is first necessary to create a BasicAttribute with a single value and then add additional values to it.

The code to use to add an entry in JNDI is as follows:

Attributes attributes = new BasicAttributes();

Attribute objectClassAttribute = new BasicAttribute("objectClass", "top");
objectClassAttribute.add("domain");
attributes.put(objectClassAttribute);

attributes.put("dc", "example");

connection.bind("dc=example,dc=com", null, attributes);

Netscape Directory SDK for Java

The Netscape Directory SDK for Java uses the more appropriately-named add method to add entries to a directory server. LDAPAttribute objects to include in the entries can be directly instantiated, and they can be created with multiple values. However, it is still a little frustrating that it is necessary to create an LDAPEntry object in order to perform an add, and that when creating an entry it is necessary to use an LDAPAttributeSet instead of being able to use a more generic array or collection of LDAPAttribute objects.

The code used to perform the add operation in the Netscape Directory SDK for Java is as follows:

LDAPAttributeSet attributeSet = new LDAPAttributeSet();
attributeSet.add(
     new LDAPAttribute("objectClass", new String[] { "top", "domain" }));
attributeSet.add(new LDAPAttribute("dc", "example"));

LDAPEntry entry = new LDAPEntry("dc=example,dc=com", attributeSet);
connection.add(entry);

UnboundID LDAP SDK for Java

The UnboundID LDAP SDK for Java also uses the "add" method to add entries. However, as you can see this is another case in which the process is significantly more streamlined than either JNDI or the Netscape SDK:

LDAPResult addResult = connection.add("dc=example,dc=com",
     new Attribute("objectClass", "top", "domain"),
     new Attribute("dc", "example"));

It isn't necessary to create an entry in this case, since one of the available add methods supports providing a DN and a variable-length set of attributes using varargs. Further, the fact that the set of attribute values can be specified using varargs means that you don't have to explicitly create an array in order to create a multivalued attribute.

Note, however, that the UnboundID LDAP SDK for Java has another trick up its sleeve in that it supports adding an entry using its LDIF representation. If you know exactly what the entry you want to create looks like, then you can use the following code to add it:

LDAPResult addResult = connection.add(
     "dn: dc=example,dc=com",
     "objectClass: top",
     "objectClass: domain",
     "dc: example");

The modify operation also provides the ability to modify existing entries using the LDIF representation, like:

LDAPResult modifyResult = connection.modify(
     "dn: dc=example,dc=com",
     "changetype: modify",
     "replace: description",
     "description: Example");