Connections

LDAP connections are stateful and persistent, which means they must be opened before operations are performed and then closed when no longer needed. Connections are created using a ConnectionFactory. Ldaptive provides two implementations of ConnectionFactory: DefaultConnectionFactory and PooledConnectionFactory.

A DefaultConnectionFactory can be used statically or as an instance variable:

Connection conn = DefaultConnectionFactory.getConnection("ldap://directory.ldaptive.org");
ConnectionFactory connFactory = new DefaultConnectionFactory("ldap://directory.ldaptive.org");
Connection conn = connFactory.getConnection();
try {
  // open the connection to the ldap
  conn.open();

  // perform an operation on the connection
} finally {
  // close the connection to the ldap
  conn.close();
}

See the pooling guide for details on how to use a PooledConnectionFactory.

startTLS / SSL

When transmitting sensitive data to or from an LDAP it’s important to use a secure connection. To use SSL:

ConnectionConfig connConfig = new ConnectionConfig("ldap://directory.ldaptive.org:636");
connConfig.setUseSSL(true);
Connection conn = DefaultConnectionFactory.getConnection(connConfig);
// open an SSL connection
conn.open();

startTLS allows the client to upgrade and downgrade the security of the connection as needed. To use startTLS:

ConnectionConfig connConfig = new ConnectionConfig("ldap://directory.ldaptive.org:389");
connConfig.setUseStartTLS(true);
Connection conn = DefaultConnectionFactory.getConnection(connConfig);
// open a connection and startTLS
conn.open();

In practice it is not advisable to downgrade a TLS connection, after all, you’ve already done the hard work to establish a TLS connection. In fact, many LDAP servers don’t even support the operation. The server will simply close the connection if a stopTLS operation is received. Consequently ldaptive doesn’t have functions for starting and stopping TLS on an open connection. You must decide whether you wish to use startTLS before the connection is opened.

Trust Issues

When using SSL or startTLS trust errors are very common. The client must be configured to trust the server and when performing client authentication, the server must be configured to trust the client. This sections deals with how to configure your LDAP client with the proper trust and authentication material.

Java cacerts file

You can add either the server certificate or the server certificate’s CA to the cacerts file included with your Java installation. This is the simplest solution, but be aware that it impacts the trust of all secure connections made by the JVM.

keytool -import -file $PATH_TO_CERT -keystore $JAVA_HOME/jre/lib/security/cacerts -alias my_server_cert

Command line options

Java supports command line options for designating both the truststore and keystore to be used for secure connections. Note that this impacts the trust of all secure connections made by the JVM.

java -Djavax.net.ssl.keyStore=$PATH_TO/my.keystore -Djavax.net.ssl.trustStore=$PATH_TO/my.truststore

When performing client authentication the JVM will select the first certificate in my.keystore that matches the allowed CAs supplied by the server.

Trust Managers

If you have a implementation of javax.net.ssl.TrustManager it can be added directory to SslConfig. Ldaptive provides several implementations which may be helpful:

Note that if you provide both trust managers and a credential config to the SslConfig, all trust managers will be required.

Credential Configuration

Ldaptive includes several classes to make the use of keystores and X509 credentials easier. CredentialConfig implementations support loading key and trust material from both the classpath and the file system.

Use a custom truststore for startTLS connections that is located on the classpath:

ConnectionConfig connConfig = new ConnectionConfig("ldap://directory.ldaptive.org");
connConfig.setUseStartTLS(true);

KeyStoreCredentialConfig credConfig = new KeyStoreCredentialConfig();
credConfig.setTrustStore("classpath:/my.truststore");

connConfig.setSslConfig(new SslConfig(credConfig));
Connection conn = DefaultConnectionFactory.getConnection(connConfig);

Use X509 certificates for both authentication and trust that are located on the file system:

ConnectionConfig connConfig = new ConnectionConfig("ldap://directory.ldaptive.org");
connConfig.setUseStartTLS(true);

X509CredentialConfig credConfig = new X509CredentialConfig();
credConfig.setTrustCertificates("file:/tmp/certs.pem");
credConfig.setAuthenticationCertificate("file:/tmp/mycert.pem");
credConfig.setAuthenticationKey("file:/tmp/mykey.pkcs8");

connConfig.setSslConfig(new SslConfig(credConfig));
Connection conn = DefaultConnectionFactory.getConnection(connConfig);

Supported certificate formats include:

Supported private key formats include:

Hostname Validation

RFC 2830 section 3.6 specifies how hostnames should be validated for startTLS. No such RFC exists for LDAPS and neither JNDI nor any of the other Ldaptive providers perform any hostname checks after the SSL handshake. Ldaptive remedies this issue by injecting hostname validation as a trust manager when an LDAPS connection is detected. The validation rules are as follows:

As of version 1.2.3, hostname validation can be controlled via the CertificateHostnameVerifier interface which is a property of SslConfig. One important caveat is that the JNDI provider always performs its own hostname check when using startTLS. Any custom hostname checks will only be performed if their check fails.

Operation Retry

Since LDAP connections are persistent they can be disrupted in a variety of ways: server restarts, miss-behaving load balancers, network blips, etc. Because of the myriad number of ways in which an LDAP connection may suddenly stop working ldaptive provides operation retry functionality. This means that for certain LDAP error codes the library will simply attempt to close, reopen the connection, and then try the operation again. This behavior is controlled by the following properties on the ReopenOperationExceptionHandler:

Name Default Value Description
retry 0 number of times to retry; set to -1 to retry indefinitely
retryWait 0 time in milliseconds to wait between retries
retryBackoff 0 factor by which to delay successive retries

Note that retry controls the number of retries after the first reopen. A single reopen is always attempted.

For example:

Connection conn = DefaultConnectionFactory.getConnection("ldap://directory.ldaptive.org");
SearchOperation search = new SearchOperation(conn);
SearchOperation.ReopenOperationExceptionHandler handler = search.new ReopenOperationExceptionHandler();
handler.setRetry(5); // retry operations 5 times
handler.setRetryWait(3000); // wait 3 seconds between retries
handler.setRetryBackoff(2); // set a backoff factor of 2
search.setOperationExceptionHandler(handler);

Operation Exception Result Codes

So what causes an operation to be retried? Each provider has a default list of result codes that when encountered by an operation will cause it to be retried.

JNDI Provider

See How LDAP Error Codes Map to JNDI Exceptions for details on which naming exceptions map to which result codes.

JLDAP Provider

Apache LDAP Provider

UnboundID Provider

OpenDJ Provider

If you need to modify the default setting you can do so by changing the provider configuration:

DefaultConnectionFactory connFactory = new DefaultConnectionFactory("ldap://directory.ldaptive.org");
connFactory.getProvider().getProviderConfig().setOperationExceptionResultCodes(
  new ResultCode[] {ResultCode.BUSY, ResultCode.UNAVAILABLE, });
Connection conn = connFactory.getConnection();

URLs & Connection Strategies

Ldaptive does not require URLs to be of any certain form, but it is recommended to use the form:

scheme://host:port -> ldap://directory.ldaptive.org:389

All the providers in ldaptive will attempt to parse this form correctly. If multiple URLs are provided you can specify a client side connection strategy to control the behavior if a connection cannot be established.

Connection Strategies

Name Behavior
DEFAULT no action, behavior dictated by the provider
ACTIVE_PASSIVE attempt each URL in the order provided for each connection; the first URL is always used unless it fails
ROUND_ROBIN attempt the next URL in the order provided for each connection; URLs are rotated regardless of connection success or failure
RANDOM attempt a random URL; useful for stateless implementations
ConnectionConfig connConfig = new ConnectionConfig("ldap://directory-1.ldaptive.org ldap://directory-2.ldaptive.org");
connConfig.setConnectionStrategy(new RoundRobinConnectionStrategy());
DefaultConnectionFactory connFactory = new DefaultConnectionFactory(connConfig);
Connection conn = connFactory.getConnection();

Note that if multiple URLs are provided with a DEFAULT strategy to the JNDI provider then you will get the JNDI active/passive behavior. No other ldaptive provider currently supports multiple URLS in the DEFAULT strategy. If multiple URLs are supplied, those providers typically select the last URL in the list.

Provider Properties

Some providers support arbitrary string based properties to control certain connection behavior. To set those properties:

Map<String, Object> props = new HashMap<String, Object>();
props.put("custom.property.name", "custom.property.value");
DefaultConnectionFactory connFactory = new DefaultConnectionFactory("ldap://directory.ldaptive.org");
connFactory.getProvider().getProviderConfig().setProperties(props);
Connection conn = connFactory.getConnection();

See the providers guide for more information.