Comparing Java LDAP SDK Performance

At UnboundID, we take performance seriously and are always trying to improve. This applies just as much for our client-side SDK as for our server products, since a server isn’t very useful without client applications to take advantage of it. There are a number of tools that can be used to measure various aspects of directory server performance, but it’s not as simple to measure the performance of client-side libraries.

To help address this problem, I’ve written a simple tool that can be used to perform a number of different kinds of LDAP operations using various Java-based LDAP SDKs. It’s not particularly elaborate and there’s only a command-line interface, but it provides a range of options, including which SDK to use, the type of operation to test, the number of concurrent threads to use, the length of time to run the test (and to warm-up before beginning to collect data), the type of entries to use, the number of entries to return per search, the result code that should be returned for each operation, and how frequently to close and re-establish connections.

It’s obviously the case that, as the primary developer for the UnboundID LDAP SDK for Java, I am more than a little biased about which SDK I prefer. However, to the best of my knowledge the way that the tool performs the tests is as fair as possible and uses the most efficient mechanism offered by each of the libraries. If anyone believes that there is a more efficient way to use any of the SDKs, then I’d be happy to hear about it and update the results accordingly.

At present, the tool provides at least some level of support for the following SDKs:

  • Apache LDAP API, version 1.0.0-M3. Although I have written code in the hope of testing this SDK, it does not appear to be fully functional at the present time. For example, when trying to perform searches with multiple threads using a separate connection per thread (which is the only way I have used it to this point), it looks like only a single thread is actually able to perform searches and all the others throw timeout exceptions. If anyone knows how to work around that, I’d be happy to hear about it. Until this problem is resolved, this tool isn’t very useful for testing its performance.

  • JNDI, as is included in Java SE. For my testing, I used Java SE 1.6.0_25. JNDI is a very abstract API that has the ability to communicate using a number of protocols, and as such was not designed specifically for LDAP. Unfortunately, this means that it’s not ideal for LDAP in a lot of ways. For example, it doesn’t appear that JNDI provides any way to get the actual numeric LDAP result code returned by the server in response to various operations, and it also looks like it does not support bind (for the purpose of authenticating clients) as a distinct type of operation but only in the course of establishing a connection or re-authenticating before performing some other kind of operation. As such, the performance testing tool does not support bind operations, and it does not support testing with operations that return non-successful responses because the result code cannot be verified.

  • Netscape Directory SDK for Java, version 4.17 (compiled from source, as there does not appear to be a download for a pre-built version of the library). This SDK is fully supported by the performance testing tool.

  • Novell LDAP Classes for Java, also known as JLDAP, version 2009.10.07-1. This SDK is fully supported by the performance testing tool.

  • OpenDJ LDAP SDK, version 3.0.0 (snapshot build from May 28, 2011). This appears to be a fork of the OpenDS LDAP SDK that has had the package structure changed, and may have some number of additional changes as well. However, I was not able to successfully use this SDK to run any tests because the code that I used (despite identical code working for the OpenDS SDK, with the exception of changing the package names in import statements) threw an exception when trying to run, indicating that it was attempting to subclass a class that had previously been declared final. It also appeared to be missing an org.forgerock.i18n.LocalizedIllegalArgumentException class, although I worked around that problem by writing my own version of that class.

  • OpenDS LDAP SDK for Java, 0.9.0 build from May 26, 2011. This SDK is fully supported by the performance testing tool. In addition, because the API provides options for both synchronous and asynchronous connections, the “–useSynchronousMode” option is supported to request using the synchronous version of the API that does not support the use of abandon or multiple concurrent operations on the same connection, while omitting this argument will use a version that does support this capability.

  • UnboundID LDAP SDK for Java, version 2.2.0. This SDK is fully supported, including the use of the “–useSynchronousMode” option.

These tests obviously require communication with an LDAP directory server. Because the intention is not to measure the performance of the directory server but rather the SDK being used to communicate with that server, it is ideal to use something that is as fast as possible (so that the server is not a bottleneck) and that can be manipulated to give an arbitrary response for any operation. For this purpose, a custom server was created using the LDAP Listener API provided as part of the UnboundID LDAP SDK for Java. It is important to note, however, that even though this API is part of the UnboundID LDAP SDK, it can be used with any kind of client and all interaction with it was over the LDAP protocol using a socket connected over the test system’s loopback interface. The UnboundID LDAP SDK did not have any advantage over any other SDK when interacting with this server.

All of the tests that I ran used Java SE 1.6.0_25 (64-bit version) on a system with a 2.67GHz 8-core Intel Core i7 CPU with 12GB of memory, running a fully-patched version of Ubuntu Linux version 11.04. A detailed description of each of the tests that I ran is provided below, along with the results that I obtained. Each test was run with the JNDI, Netscape, Novell, OpenDS, and UnboundID SDKs, using 1, 2, 4, 8, 16, 32, 64, and 128 client threads. For the OpenDS and UnboundID SDKs, tests were run using both the asynchronous and synchronous modes of operation.

Add Operation Performance

When processing add operations, performance may vary based on the size of the entry being added to the server. As such, I ran two different add performance tests: a “normal-sized” entry (an inetOrgPerson entry with 15 attributes) and a “large” entry (a groupOfUniqueNames entry with 1000 uniqueMember values).

The results I measured when running these tests were:

Add Throughput for Normal-Sized Entries

API Highest Normal-Sized Entry Add Throughput
JNDI 89,027.954 adds/sec
Netscape 98,916.582 adds/sec
Novell 86,766.964 adds/sec
OpenDS (asynchronous mode) 88,525.069 adds/sec
OpenDS (synchronous mode) 88,586.290 adds/sec
UnboundID (asynchronous mode) 142,105.659 adds/sec
UnboundID (synchronous mode) 174,665.853 adds/sec

 

Add Throughput for Large Entries

SDK Highest Large Entry Add Throughput
JNDI 6,472.209 adds/sec
Netscape 8,723.301 adds/sec
Novell 7,437.703 adds/sec
OpenDS (asynchronous mode) 9,454.340 adds/sec
OpenDS (synchronous mode) 9,747.643 adds/sec
UnboundID (asynchronous mode) 17,602.504 adds/sec
UnboundID (synchronous mode) 18,545.810 adds/sec

 

From these tests, it appears that the UnboundID LDAP SDK for Java is significantly faster than any of the other SDKs when processing add operations, and using the UnboundID LDAP SDK in synchronous mode provides a notable performance improvement over the default asynchronous mode. In contrast, the OpenDS LDAP SDK does not appear to exhibit a significant difference in add performance based on whether the asynchronous or synchronous version of the API is selected.

Search Operation Performance

As for adds, search operation performance can vary significantly based on the size of the entry being returned. As such, I ran tests search tests using the same “normal-sized” and “large” entries as for the add operation testing, and I also tested the case of returning only a single attribute in each entry. Further, because the server can return multiple entries for a single search operation, I ran tests with both operations returning a single entry and 100 identical entries. Results from those tests are provided below:

Search Throughput for 1 Tiny Entry

SDK Highest Search Throughput for 1 Tiny Entry
JNDI 54,272.423 searches/sec
Netscape 74,601.755 searches/sec
Novell 69,686.323 searches/sec
OpenDS (asynchronous mode) 73,964.204 searches/sec
OpenDS (synchronous mode) 74,572.779 searches/sec
UnboundID (asynchronous mode) 109,159.192 searches/sec
UnboundID (synchronous mode) 168,315.209 searches/sec

 

Search Throughput for 1 Normal-Sized Entry

SDK Highest Search Throughput for 1 Normal-Sized Entry
JNDI 46,355.770 searches/sec
Netscape 49,668.681 searches/sec
Novell 55,988.055 searches/sec
OpenDS (asynchronous mode) 55,408.763 searches/sec
OpenDS (synchronous mode) 54,923.308 searches/sec
UnboundID (asynchronous mode) 83,846.853 searches/sec
UnboundID (synchronous mode) 115,738.348 searches/sec

 

Search Throughput for 1 Large Entry

SDK Highest Search Throughput for 1 Large Entry
JNDI 11,045.600 searches/sec
Netscape 3,849.413 searches/sec
Novell 748.249 searches/sec
OpenDS (asynchronous mode) 10,449.903 searches/sec
OpenDS (synchronous mode) 10,374.687 searches/sec
UnboundID (asynchronous mode) 20,645.026 searches/sec
UnboundID (synchronous mode) 21,341.607 searches/sec

 

Search Throughput for 100 Tiny Entries

SDK Highest Search Throughput for 100 Tiny Entries
JNDI 5,749.687 searches/sec
Netscape 2,768.797 searches/sec
Novell 2,739.363 searches/sec
OpenDS (asynchronous mode) 8,295.155 searches/sec
OpenDS (synchronous mode) 8,315.379 searches/sec
UnboundID (asynchronous mode) 5,566.711 searches/sec
UnboundID (synchronous mode) 7,265.108 searches/sec

 

Search Throughput for 100 Normal-Sized Entries

SDK Highest Search Throughput for 100 Normal-Sized Entries
JNDI 1,983.319 searches/sec
Netscape 875.249 searches/sec
Novell 1,681.767 searches/sec
OpenDS (asynchronous mode) 1,959.581 searches/sec
OpenDS (synchronous mode) 1,917.131 searches/sec
UnboundID (asynchronous mode) 2,308.414 searches/sec
UnboundID (synchronous mode) 3,278.463 searches/sec

 

Search Throughput for 100 Large Entries

SDK Highest Search Throughput for 100 Large Entries
JNDI 127.716 searches/sec
Netscape 39.667 searches/sec
Novell 6.731 searches/sec
OpenDS (asynchronous mode) 117.633 searches/sec
OpenDS (synchronous mode) 117.233 searches/sec
UnboundID (asynchronous mode) 225.800 searches/sec
UnboundID (synchronous mode) 237.400 searches/sec

 

In this case, there is a significant variation in many of the SDKs based on the size and number of entries being returned. The UnboundID LDAP SDK is significantly faster than the other SDKs in most cases (with a notable improvement on top of that when using synchronous mode), but the OpenDS SDK is quite a bit faster than the UnboundID LDAP SDK in the case of a search returning 100 entries with only a single attribute per entry, but that is not the case for normal-sized or large entries. On the other hand, both the Netscape and Novell SDKs appear to be extremely slow when dealing with large search result entries, and the Netscape SDK is also much slower than the others for large entry sets. It is also important to note the significant drop in search performance when using JNDI for larger numbers of threads when returning a single tiny or normal-sized entry.

Modify Operation Performance

For a client SDK, modify performance has fewer variables than for either add or search operations. For a directory server, there are a number of factors, including the size of the target entry, the size of the modified attributes, and whether any of the target attributes is indexed, but none of these has an impact on the client. It is certainly the case that a modify request could update a large number of attributes and/or attribute values, but generally clients modify only one or two values at a time. As such, the only modify test run was for a modify operation replacing a single attribute value. Results for this test are:

Modify Throughput

SDK Highest Modify Throughput
JNDI 139,593.362 searches/sec
Netscape 139,362.802 searches/sec
Novell 122,199.697 searches/sec
OpenDS (asynchronous mode) 109,556.189 searches/sec
OpenDS (synchronous mode) 109,832.363 searches/sec
UnboundID (asynchronous mode) 196,411.917 searches/sec
UnboundID (synchronous mode) 242,248.797 searches/sec

 

Again, the UnboundID LDAP SDK is significantly faster than the other SDKs, and again, there is a significant advantage to using synchronous mode. The Novell SDK’s modify performance seems to drop off significantly for higher numbers of threads.

Summary

Download a complete set of results for all tests run as an OpenDocument spreadsheet.

In most cases, the UnboundID LDAP SDK for Java is faster than all other SDKs by a wide margin, and in all cases using the UnboundID LDAP SDK for Java in synchronous mode was faster than using the SDK in the default asynchronous mode. As such, if you are using the UnboundID LDAP SDK for Java and don’t need to perform asynchronous operations, then it is highly recommended that you enable synchronous mode for connections used by that application.

The only case in which the UnboundID LDAP SDK for Java was not the fastest is for a search operation in which a large number of entries were returned with only a single attribute per entry. It was the fastest for both other tests involving a large number of entries, and it was also the fastest for returning only one entry with a single attribute. I will investigate the UnboundID LDAP SDK’s performance in this area to determine whether it can be improved.

The OpenDS LDAP SDK (which was started after I left OpenDS, and for which I have not participated in its development in any way) appears to be the second fastest. It was the only SDK to perform faster than the UnboundID LDAP SDK in any of the tests, and it was never the slowest of any of the tests. There does not appear to be any measurable difference in performance when using the synchronous mode versus asynchronous mode. Across all of the tests, the OpenDS LDAP SDK achieved about 56.0% of the overall performance of the UnboundID LDAP SDK in synchronous mode, and 67.8% of the performance of the UnboundID LDAP SDK in asynchronous mode.

JNDI offers middle-of-the-pack performance in most cases, but its very poor showing for searches returning a single entry with high numbers of threads may be a significant cause for concern, since this is a very common kind of operation.

The Novell SDK performance when dealing with large search result entries is very troublesome, and it is also significantly slower than all other SDKs for modify operations with a high degree of concurrency. The Netscape SDK also appears to have problems with large search result entries, and its search performance for searches returning multiple entries is a problem as well.

UnboundID LDAP SDK for Java 2.2.0

UnboundID LDAP SDK for Java 2.2.0 has just been released and is available for download from the UnboundID website or the SourceForge project page, and is also available in the Maven central repository.

The release notes provide a full overview of the changes in this release over the previous 2.1.0 version. There are several bug fixes, but some of the most notable new features include:

  • A new Minimal Edition has been introduced. The Minimal Edition is available under the same licenses as the Standard Edition and provides support for all LDAP operations, but a number of capabilities have been removed (e.g., support for SASL authentication, a number of controls and extended operations, the persistence framework, the listener framework and in-memory directory server, JNDI and Netscape SDK migration support, etc.). The primary goal of the Minimal Edition is to provide a version of the LDAP SDK with a small jar file size which is desirable for resource-constrained environments like Android applications or other embedded use. The Minimal Edition is available as a separate download, from either the UnboundID website or SourceForge project.

  • Connection pooling support has been updated to provide the ability to automatically retry operations if the first attempt fails in a way that indicates the connection may no longer be valid. In such cases, a new connection will be established (potentially to a different server, based on the ServerSet in use for the pool) and the operation will be re-attempted on that connection. This can help isolate applications from failures if one of the target directory servers is shut down, crashes, hangs, or begins behaving erratically.

  • The in-memory directory server has been updated to add support for maintaining referential integrity (e.g., so that if an entry is deleted then that user can be automatically removed from any static groups in which the user was a member), to support LDAP transactions as described in RFC 5805, and to add support for inserting an arbitrary delay before processing operations (which can be useful in simulating environments with slower response times or higher network latencies). There have also been a couple of fixes for bugs that could cause the in-memory directory server to behave incorrectly.

  • The LDAP SDK persistence framework has been updated to provide better support for searches. Previously, it was difficult to search for entries using anything but equality searches. The generate-source-from-schema tool has been updated so that it will now generate additional methods that can make it easier to perform other kinds of searches, including presence, substring (starts with, ends with, and contains), greater-or-equal, less-or-equal, and approximately-equal-to.

  • New methods have been added which make it significantly easier to interact with response controls. Each response control class now has one or more static get methods that can be used to extract and decode a response control of that type from a given response object.

  • Support for GSSAPI authentication has been significantly improved to add support for a number of new options, including the ability to indicate whether to use (or even require) a ticket cache, to specify an alternate location for the ticket cache file, and to request that the TGT be renewed. Changes have also been introduced to make it easier to access GSSAPI debugging information.

  • A new option has been added that makes it possible to automatically send an abandon request to the directory server if a client-side timeout is encountered while waiting for a response to an operation. Previously, the LDAP SDK would throw an exception but did not have any option to attempt to abandon the operation in the directory server.

  • The LDAP SDK can now use schema information (if available) in the process of normalizing and comparing DNs and RDNs. This can provide more accurate matching for DNs that use attributes in which something other than case-inexact string matching should be used.

  • The LDIF reader has been updated to provide the ability to read data from multiple files. This can be useful for cases in which the complete set of data you want to process is broken up into multiple files (e.g., representing different portions of the DIT).

UnboundID LDAP SDK for Java 2.1.0

We have just released the latest version of the UnboundID LDAP SDK for Java, version 2.1.0. You can get it from either the UnboundID website, or from the SourceForge project page.

This is a pretty significant update that has a number of changes in several areas. You can see the release notes for a fairly comprehensive overview of the changes that have been made in this version, but the major focus of the 2.1.0 release was in improving testability, and there are two new features that can significantly help with that:

  • The LDAP SDK now comes with the ability to create one or more very lightweight yet surprisingly feature-rich LDAP server instances that you can use for testing the directory-enabled applications that you are developing. Although it’s not suitable for production use, it does support quite a few controls and extended operations, SASL authentication, an LDAP-accessible changelog and other features that your application might rely on. It’s also got a lot of nice testing-friendly features like the ability to make assertions about the state of data in the server, the ability to create and restore point-in-time snapshots so that you can revert the server to a known state, and the ability to have multiple listeners that you can control independently for things like testing failover between servers.

  • It also provides a new LDAPTestUtils class that has lots of helpful methods for use in writing test cases for directory-enabled applications. This includes methods for generating simple entries of various types (e.g., users, groups, organizations, organizationalUnits, etc.), for verifying the contents of a directory server over LDAP, and for verifying results received from processing operations.

These new features have already made it significantly easier for my own testing in developing the LDAP SDK itself, when writing LDAP-based tools, and even in the development of the UnboundID server products like the Directory Server, Directory Proxy Server, and Synchronization Server. Hopefully, it will make it easier for you to test your own applications as well. I’ll be writing additional posts in the near future that go into more detail about how you can use these new features to make it easier to test your directory-enabled applications.

UnboundID LDAP SDK for Java 2.0.1

We have just released UnboundID LDAP SDK for Java version 2.0.1. It is available for download from the UnboundID website or from the SourceForge project page, and should show up in the Maven central repository in the near future. Some of the changes in this release include:

  • The persistence framework has been updated to support object inheritance. If an object whose class is marked with the @LDAPObject annotation also has a superclass that is marked with @LDAPObject, then the annotated fields and methods of the superclass will be used in the course of converting between objects and entries.

  • The EntryValidator has been updated to fix a potential bug that could cause an exception to be thrown when processing an entry that did not include all superior classes for all of the object classes in the entry.

  • Updated the ValuePattern to provide support for back-references, which make it possible to include the same value twice in a generated string (e.g., in the pattern “uid=user.[1-100],ou=org.[ref:1],dc=example,dc=com”, a random number between 1 and 100 will be chosen for the user number, and then that same value will also be used for the organization number). In addition, a bug has been fixed that could interfere with the ability to use the ValuePattern to obtain content from files on Windows systems when the provided path included a drive letter.

  • The argument parsing framework has been updated to include a new ArgumentListArgument, in which the values of the argument list argument are themselves argument strings (e.g., “–arg1Name arg1value –arg2Name –arg3Name arg3value”), and the argument will have its own parser to perform the validation. Also, the StringArgument has been updated to make it possible to specify a regular expression that can be used to constrain the set of allowed values.

  • When creating an LDAPConnectionPool using a ServerSet, it is now possible to request that zero connections be initially created. If this is done, then the pool will not have any connections established until the first time an attempt is made to use it.

  • The Filter class has been updated to expose static encodeValue methods, which may be used to properly escape a value for use in the string representation of a filter. This should only be used if you are manually creating a filter string representation, and is not necessary if you are creating a filter by providing attribute type and value objects.

  • Debug messages generated by the LDAP SDK have been improved so that they can be more useful, especially for those messages related to LDAP communication in an environment in which many connections may be in use at the same time. Each debug message about LDAP communication will include information about the connection used for that communication and the LDAP message ID for the request or response. Further, result messages will now include the protocol op type.

UnboundID LDAP SDK for Java 2.0.0

We have just released the 2.0.0 version of the UnboundID LDAP SDK for Java. It is available for download on the UnboundID LDAP SDK product page or on the SourceForge project page.

This is a major new release that includes significant new functionality, including:

  • A persistence API that makes it easy to interact with entries in an LDAP directory as Java objects. Annotations are used to establish mappings between LDAP attributes and content in Java objects, and the data is stored in a format that makes it easily accessible by other APIs as well. Tools are provided to help you generate source code from existing schema, or generate LDAP schema from annotated source.

  • A listener API that makes it easy to develop applications that can listen for communication from LDAP clients (i.e., applications which act as an LDAP server). A new tool is provided which makes use of this API to act as a simple LDAP proxy that can intercept and decode LDAP communication as it passes between an LDAP client and server.

  • New types of command-line arguments, including Boolean value arguments, duration arguments, and search scope arguments. The file argument type has also been updated to allow you to specify a base directory for relative paths so that non-absolute paths will be relative to a specified location.

There are also a few other minor enhancements and bug fixes.

UnboundID LDAP SDK for Java 1.1.6

I have just made a new 1.1.6 release of the UnboundID LDAP SDK for Java available for download. You can get it on either the UnboundID LDAP SDK product page or on the SourceForge project page. It should also be available in the Maven Central Repository in the near future (probably within the next day or two).

There are only a couple of minor changes in this release over the previous 1.1.5 version, including:

  • I corrected an error in the documentation around the use of the content synchronization request control that incorrectly recommended using a response timeout of -1 rather than 0. A value of 0 indicates that no timeout should be used, while a value of -1 means that the connection-level timeout (which is 5 minutes by default) should be used.

  • I fixed a problem in the way that the string representation of RDNs was generated when a DN was parsed from a string. Previously, some extra spaces could have been removed. The resulting string representation was technically equivalent to the version that was originally provided, but the original formatting should have been preserved.

  • I updated the source for the Android LDAP client to match what was published as version 1.1 in the Android market.

Minor Updates to the Android LDAP Client

I have just uploaded a new version of the UnboundID LDAP client to the Android market. It’s kind of embarrassing that it took this long to put out such a minor update, but I had visions of a much more significant update that I never got around to coding and I kept putting off some simple stuff. Hopefully the minor improvements are worth the update, and maybe I’ll get around to the bigger version at some point in the future.

The changes included in this update are:

  • I’ve updated the application to use the latest version of the LDAP SDK. This includes several bug fixes over the version used in the previous release of the LDAP client, including some fixes around SSL and StartTLS.

  • It is now more liberal with what it considers a user entry (and will therefore allow special treatment for things like telephone numbers, e-mail addresses, postal addresses, etc.). Previously, it would only work for entries with an object class of “person”. It will now work for any entry that has a “cn” attribute and at least one of the following additional attributes: mail, mailAlternateAddress, telephoneNumber, homePhone, mobile, pager, and facsimileTelephoneNumber.

  • It should now be usable on devices with any screen size. Previously, it was only available for devices with what was considered a “normal” screen size, and it might not have been available on some devices with a very low resolution, or on devices with much higher resolutions. I don’t know how it looks on any of those other devices, but hopefully it’s at least usable.

  • It should now be possible to move the application to the SD card on devices running Android 2.2.

I’ve tested the application in the emulator for Android versions 1.5 and 2.2, and on actual phones running Android 1.6, 2.1, and 2.2, and I haven’t encountered any problems on any of them. If you run into a problem, please let me know.

LDAP Password Changes in Active Directory

I’ve never really been a big fan of Active Directory. Microsoft tends to treat standards more like suggestions than rules, and Active Directory has some good examples of that. I was recently asked a question about how you can change a password in Active Directory over LDAP. In most directory servers, you would either use the LDAP password modify extended operation (as described in RFC 3062), or you would perform a simple modify operation to replace the userPassword attribute with the clear-text password (and the server would automatically perform any necessary encoding to obscure the value). However, Active Directory has a number of very unusual requirements, so it’s probably worth making a note of them. They include:

  • Active Directory doesn’t appear to support the password modify extended operation, so you must change passwords using a normal LDAP modify operation.
  • Active Directory stores passwords in the unicodePwd attribute, rather than userPassword.
  • Active Directory will only accept password changes over secure connections. I have only ever used SSL. It may be that you can also use StartTLS, or perhaps SASL with confidentiality, but I’m not sure about that.
  • The new password must be enclosed in quotation marks, and it must use a UTF-16 little-endian encoding.
  • Active Directory may impose some strength requirements on the password, although exactly what those requirements are may vary from one instance to another.

Knowing these requirements, you should be able to write code using any LDAP API that will allow you to perform password changes in Active Directory. The following code demonstrates how to do it using the UnboundID LDAP SDK for Java:

import java.io.UnsupportedEncodingException;
import javax.net.ssl.SSLSocketFactory;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;
/**
* This class provides a simple utility method that may be used to change the
* password of a user stored in an Active Directory server instance.
*/
public class ADPasswordChange
{
/**
* Perform the complete set of processing required to change a user's
* password in an Active Directory server.
*
* @param  adHost        The address of the Active Directory server.
* @param  adSSLPort     The SSL-based port of the Active Directory server
*                       (typically 636).
* @param  bindDN        The DN to use when binding to the Active Directory
*                       server instance.  It must have sufficient permission
*                       to change user passwords.
* @param  bindPassword  The clear-text password to use when binding to the
*                       Active Directory server instance.
* @param  userDN        The DN of the user whose password should be changed.
* @param  newPassword   The clear-text new password to assign to the user.
*
* @throws  LDAPException  If a problem is encountered while performing any
*                         of the required processing.
*/
public static void changePasswordInAD(final String adHost,
final int adSSLPort,
final String bindDN,
final String bindPassword,
final String userDN,
final String newPassword)
throws LDAPException
{
// Properly encode the password.  It must be enclosed in quotation marks,
// and it must use a UTF-16LE encoding.
System.out.println("Going to encode the password.");
final byte[] quotedPasswordBytes;
try
{
final String quotedPassword = '"' + newPassword + '"';
quotedPasswordBytes = quotedPassword.getBytes("UTF-16LE");
}
catch (final UnsupportedEncodingException uee)
{
throw new LDAPException(ResultCode.LOCAL_ERROR,
"Unable to encode the quoted password in UTF-16LE:  " +
StaticUtils.getExceptionMessage(uee),
uee);
}
// Create an SSL socket factory to use during the course of establishing
// an SSL-based connection to the server.  For simplicity, we'll cheat and
// use a trust manager that will trust any certificate that the server
// presents, but in production environments you should validate the
// certificate more carefully.
System.out.println("Going to create the SSL socket factory.");
final SSLSocketFactory socketFactory;
final SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
try
{
socketFactory = sslUtil.createSSLSocketFactory();
}
catch (final Exception e)
{
throw new LDAPException(ResultCode.LOCAL_ERROR,
"Unable to create an SSL socket factory to use for establishing " +
"a secure connection:  " + StaticUtils.getExceptionMessage(e),
e);
}
// Create a secure connection to the Active Directory server.
System.out.println("Going to establish the secure connection.");
final LDAPConnection connection = new LDAPConnection(socketFactory, adHost,
adSSLPort, bindDN, bindPassword);
try
{
// Attempt to modify the user password.
System.out.println("Going to replace the user's password.");
final Modification mod = new Modification(ModificationType.REPLACE,
"unicodePwd", quotedPasswordBytes);
connection.modify(userDN, mod);
}
finally
{
System.out.println("Closing the connection.");
connection.close();
}
}
}

UnboundID LDAP SDK for Java 1.1.5 and 2.0.0-rc1

I have just released a new 1.1.5 release of the UnboundID LDAP SDK for Java. It is available for download from the UnboundID LDAP SDK product page, as well as from the SourceForge project page. It should be available in the Maven Central Repository in the near future.

This release has only a few changes over the previous 1.1.4 version, including:

  • I have fixed a bug which could cause problems when using SSL or StartTLS over high-latency networks.

  • I have fixed a bug which could cause LDAPConnection.close to close the connection without an unbind request if the connection had previously been closed and re-established.

  • I have added support for the content synchronization operation as defined in RFC 4533.

I have also released a new 2.0.0-rc1 build of the LDAP SDK, which is available on the SourceForge project page. This is hopefully the last build prior to the official release. It includes the above changes, as well as the following additional changes:

  • The default object encoder in the persistence framework now provides support for URL, URI, and enum objects.

  • Java source code generated by the persistence framework now includes static getPersister and decode convenience methods which make it easier to interact with objects using the persistence framework.

  • LDAP command-line tools now provide an option that allows you to specify the format for the key store and/or trust store when using SSL or StartTLS. It still uses the JKS format by default, but you can request an alternate format like JCEKS or PKCS12 if desired.

  • The LDAP assertion control implementation now includes a generate convenience method that makes it easy to generate an assertion request control based on a provided entry. It can be used to make it easier to ensure that some or all of the attributes in the entry have not been updated since the entry was last retrieved.

  • I have added a new BooleanValueArgument class, which is similar to the BooleanArgument class except that it takes an explicit value (e.g., “true”, or “false”, although it will accept other strings as well, including “yes”, “on”, and “1” for true, and “no”, “off”, and “0” for false) rather than inferring the value based only on whether the argument is present.

  • I have added a new DurationArgument class, which combines an integer with a time unit (e.g., “10ms”). It can be used to specify lengths of time.

  • I have updated the FileArgument class so that it is possible to specify a base DN for relative paths. By default, relative paths will be relative to the current working directory, but you can indicate that they should instead be relative to a specified parent directory.

UnboundID LDAP SDK for Java 1.1.4 and 2.0.0-beta1

I have published two new builds of the UnboundID LDAP SDK for Java. The 1.1.4 build is an official release that contains a number of minor enhancements and bug fixes over the previous 1.1.3 release. The 2.0.0-beta1 build is a preview of our next major release and includes significant new functionality, including frameworks for persisting Java objects in an LDAP directory and for more easily creating applications which accept communication from LDAP clients. The 1.1.4 version is available for immediate download on the UnboundID website, and both the 1.1.4 and 2.0.0-beta1 builds are available for download from the SourceForge project page. The 1.1.4 release is fully supported for production use and has a stable API that will not change in incompatible ways in the future. Although we also believe that the 2.0.0-beta1 build is also production ready, we do reserve the right to change any of the new APIs prior to the official release if there is a compelling reason to do so.

Some of the most notable changes between the 1.1.3 and 1.1.4 releases include:

  • I have fixed a bug that could cause problems when trying to use SSL or StartTLS in conjunction with synchronous communication mode. This problem primarily occurred in deployments in which the client and server were not on the same network and there could be a significant delay between phases of the negotiation.

  • A new search-and-mod-rate command line tool has been added that can be used to test LDAP server performance under a combined load of search and modify operations. The searches will be performed in much the same way as with the searchrate tool, but each entry returned will also be modified.

  • The ldapsearch tool has been updated to provide an option to repeat the search multiple times at regular intervals. This can be useful, for example, if you want to periodically retrieve monitor information from the server.

  • The traditional and thread-local connection pool implementations have been updated to provide an option to avoid closing and re-establishing a large number of connections in a short period of time in the event that a maximum connection time limit has been configured. It is now possible to specify the minimum length of time that should pass between closures of expired connections.

  • If a problem is encountered while trying to decode a response read from the directory server, the exception that is thrown now includes better information about the problem that actually occurred to make it easier to diagnose the problem.

  • The searchrate, modrate, and authrate tools have been updated to better handle the case in which the target directory server becomes unavailable during processing. The tools are now better able to re-establish connections to that server when it becomes available again.

The 2.0.0-beta1 build includes everything in the 1.1.4 release, as well as additional new functionality. In previous blog posts, I have discussed some of the features added to the LDAP SDK persistence framework, and that functionality has been further improved in this new version. Some of those changes include better support for operational attributes, the ability to use lazy initialization for a specified set of attributes, and adding support for simple bind operations. It also includes a new listener API, which makes it possible to easily create applications capable of accepting communication from LDAP clients. A new ldap-debugger example tool makes use of the listener API and can serve as a simple proxy which decodes all LDAP communication that passes through it.

We are very interested in getting feedback on the new APIs included in the upcoming 2.0.0 release because we are committed to maintaining compatibility once they have been officially released. If you are interested in either the persistence or the listener APIs, please at least look over the javadoc and let us know what you think.