Overview#

These are some Tips and How Tos on using the UnboundID LDAP SDK For Java.

Many of these are from News Groups[1], emails or our actual usage.

Check if user is Member of Group with Compare Request#

A Compare Request is the most efficient method to determine Group Membership.

Using memberOf:

CompareRequest compareRequest = new CompareRequest(userDN, "memberOf", groupDN);
CompareResult compareResult = connection.compare(compareRequest);
if (compareResult.compareMatched())
{
  // The user is a member of the group.
}
else
{
  // The user is not a member of the group.
}

Using member:

CompareRequest compareRequest = new CompareRequest(groupDN, "member", userDN);
CompareResult compareResult = connection.compare(compareRequest);
if (compareResult.compareMatched())
{
  // The user is a member of the group.
}
else
{
  // The user is not a member of the group.
}

Figuring Out if Connection is Valid#

you can just do:
 SearchResult searchResult;
 try
 {
   searchResult = connection.search(searchRequest);
 }
 catch (LDAPSearchException e)
 {
   searchResult = e.getSearchResult();
 }

 // Do something with the result here.
This will take care of all of the error handling associated with figuring out whether the connection is still valid or not, and it can even automatically retry the operation on a newly-created connection (and if you're using a ServerSet as a means of load-balancing and/or failover, then the new connection might go to a different server) as a way to hide these kinds of failures from the client.

LDAP Connection Pools and Synchronous Operations #

First, synchronous mode is a mode of operation that changes the way that the LDAP SDK reads data from the server. It has nothing to do with locking or synchronized blocks.

By default, whenever you create an LDAPConnection, the UnboundID LDAP SDK For Java opens a background thread that is dedicated to reading data from the server on that connection. This is needed to support asynchronous use of a connection, in which you can have multiple requests in progress simultaneously on the same connection.

However, if you don't need this capability (and it can actually be dangerous to use if the connection is part of a connection pool) then you can use LDAPConnectionOptions.setUseSynchronousMode(true) before creating the connection and it will create the connection in a way that doesn't use a background reader thread but instead will read responses on the same thread that sent the associated request. This offers better performance and eliminates the need for a separate reader thread.

The downsides are that you can't use asynchronous Operations and that the case in which a connection is closed by the server won't be detected until you try to process an operation on that connection (unless you have a health check configured that allows the connection pool to periodically check the validity of that connection).

Second, when I said "the client is getting an immediate failure on trying to use a connection that is no longer valid", I meant getting an LDAPException with a result code that indicates that the underlying connection may no longer be valid. You can use the ResultCode.isConnectionUsable method as a guideline to help determine whether the failure might be because the connection is no longer valid and should be re-established, although it is overly cautious and some of the result codes for which it indicates that a connection is no longer usable may also be used for other kinds of problems in which the connection is fine but there was just a problem encountered while processing the request. Still, it's better to be safe than sorry, and getting a new connection and trying again won't hurt anything except costing a little bit of processing time.

Third, connection pool health checks aren't meant to be used outside of the connection pool. Most of the time, you shouldn't call the LDAPConnectionPoolHealthCheck methods yourself. Instead, you should let the connection pool do that for you. You do that by calling LDAPConnectionPool.setHealthCheck, and the pool will take care of invoking the health check at all of the appropriate times.

So to create a LDAPConnectionPool in Synchronous Mode, you could do something like:

// Create the connection options
 LDAPConnectionOptions ldapOptions = new LDAPConnectionOptions();
 ldapOptions.setAbandonOnTimeout(true);
 ldapOptions.setBindWithDNRequiresPassword(false);
 ldapOptions.setResponseTimeoutMillis(60000);
 ldapOptions.setUsePooledSchema(true);
 ldapOptions.setPooledSchemaTimeoutMillis(60000);
 ldapOptions.setUseSynchronousMode(true);

 // Create the failover server set with the desired addresses and
 // ports, as well as an SSLSocketFactory if you want secure 
 // connections and the connection options created above.
 FailoverServerSet failoverSet = new FailoverServerSet(addresses,
      ports, sslSocketFactory, connectionOptions);

 // Create a bind request that will be used to authenticate
 // newly-established connections.
 SimpleBindRequest bindRequest = 
      new SimpleBindRequest(this.adminUserId, this.password);

 // Create and configure the connection pool.
 LDAPConnectionPool pool = new LDAPConnectionPool(failoverSet,
      bindRequest, 50, 500);
 pool.setMaxConnectionAgeMillis(1800);
 pool.setMaxDefunctReplacementConnectionAgeMillis(1800L);
 pool.setMaxWaitTimeMillis(3000L);
 pool.setRetryFailedOperationsDueToInvalidConnections(
      typesOfOperationOnFailure);
 pool.setHealthCheckIntervalMillis(1900L);

LDAP Connection Pools and getConnection()#

The getConnection(String,int) method will never create a new connection, nor will it wait for a connection to become available. If you call that method and the pool does not currently have an idle connection to the specified server, that method will return null.

However, the getConnection() method that doesn't take any arguments will be subject to the policy that you set via the:

  • setCreateIfNecessary
  • setMaxWaitTimeMillis
methods (and if you don't call those methods, then it will use the default values of
  • createIfNecessary=true
  • maxWaitTimeMillis=0).
So if you call getConnection() and there are currently no connections available, then the pool will wait for up to maxWaitTimeMillis for a connection to become available, and if no connections get released during that time, then it will either create a new connection (if createIfNecessary is true) or throw an LDAPException. The getConnection() method will never return null.

The search, modify, etc. methods that the pool provides for performing operations use getConnection() in order to get the connection that they use for those operations, so the behavior you specify using setCreateIfNecessary and setMaxWaitTimeMillis will apply to them just as it would if you directly called getConnection() yourself.

If createIfNecessary is true, then it is possible that you could temporarily have more connections established than the maximum pool size. The maximum pool size really indicates the maximum number of connections that the pool will allow to be idle at any time. If you have a burst of activity that uses up all of the available connections and requires new connections to be created, and then that burst subsides and all of the connections are released back to the pool, then the pool will only keep as many connections as the configured maximum. Once the pool has the maximum number of connections that aren't in use, any other connections that get released back to the pool will be closed.

Authenticate LDAP users in asynchronous mode with UnboundID LDAP SDK[2]#

LDAP is an inherently asynchronous protocol, meaning that, in most cases, you can issue multiple concurrent requests over the same connection and the client will be able to correlate the responses with their appropriate requests.

However, the LDAP protocol specification (RFC 4511 section 4.2.1) states that bind operations cannot be processed on a connection that has any other outstanding operations. In particular "Before processing a Bind Request, all uncompleted operations MUST either complete or be abandoned" and "After sending a Bind Request, clients MUST NOT send further LDAP PDUs until receiving the Bind Response." This is because a bind operation is used to change the authentication state of a connection (and in some cases may also include negotiating a communication security layer). It is dangerous to have other types of operations in progress on the connection while a bind is being processed because the bind processing may change the nature of the response to the client.

The UnboundID LDAP SDK For Java is inherently asynchronous by default, meaning that it operates in a mode that allows multiple operations to be processed concurrently on a single connection. There are two ways to do this:

Simply use the same connection concurrently across multiple threads.

For example, if you have two threads that are sharing the same connection, you could call LDAPConnection.search on one thread while LDAPConnection.modify is being called on another. In this mode of operation, whenever you send an LDAP request on a thread, that thread will block until it gets the response (or until the response timeout is reached, or until it detects that the connection has been terminated), but other threads can use the same connection for other operations.

Use the asynchronous API to allow a single thread to interact with multiple operations simultaneously.

For example, if you call LDAPConnection.modify, then the LDAP SDK will send the request and wait until it gets the response, but if you call LDAPConnection.asyncModify, then the LDAP SDK will send the request and then immediately return control to the thread so that it can do something else. It will provide you with an AsyncRequestID that you can use to determine whether the operation has completed (and if so to get that response) whenever you want to check on it.

But as per the restriction around bind operations, the LDAP SDK does not provide an LDAPConnection.asyncBind because your application should not ever attempt to process a bind operation on a connection that might have one or more other operations in progress. The LDAP SDK won't explicitly prevent you from calling LDAPConnection.bind simultaneously on the same connection from two separate threads, but you need to be careful to not do that in your own code.

In general, I would recommend that you avoid using asynchronous operations altogether. There is rarely a good reason to use them, and it will likely make your application less reliable. If your application needs to be able to issue multiple LDAP requests simultaneously, then it is far better to use separate connections for those requests, and the best way to do that is to use an LDAPConnectionPool to manage those connections.

The LDAPConnectionPool class is specifically designed to make this as convenient and reliable as possible. While you can check out a connection, use it to perform an operation, and then release that connection to the pool, it is better to just invoke the connection against the pool and have it do the connection management for you. For example, if you want to perform a search operation on a pooled connection, instead of:

LDAPConnection connection = pool.getConnection();
SearchResult searchResult = connection.search(searchRequest);
pool.releaseConnection(connection);
you should simply do the following:
SearchResult searchResult = pool.search(searchRequest);
Not only is this more convenient, but it has several advantages that make your application more reliable, including:
  • Your application doesn't have to worry about making sure that the connection gets returned to the pool once the operation is complete, nor to try to determine whether the connection is still usable or not. Applications that check out and release their own connections have a tendency to encounter circumstances that can cause a connection to get checked out but not released (which means that connections get leaked and over time may consume all available file descriptors on the directory server). It can also lead to cases in which an application mistakenly puts a no-longer-valid connection back into the pool where it can cause other operations to fail.
  • The LDAP SDK connection pooling mechanism provides support for establishing connections to multiple servers in support of high availability and load balancing. You don't need to worry about this in your application, nor do you need to have any kind of hardware load balancer (and the LDAP SDK is able to do it better than a hardware load balancer anyway).
  • The LDAP SDK connection pool can be configured to automatically retry operations that fail in a way that suggests that the connection is no longer valid. If the connection pool detects such a failure, then it will automatically create a new connection (possibly to a different server than the one to which the former connection was established) and send the same request over the new connection. This is completely transparent to your application.
  • The LDAP SDK provides the ability to periodically check the health of the connections it maintains so that your application is less likely to encounter a connection that is no longer available. It can also close and re-establish connections after some amount of time to work around cases in which the directory server (or commonly, some poorly-designed piece of network hardware) will drop a connection after it has been established for too long.

LDAP Connection Pools and Number of Connections[3]#

The LDAP Connection Pool only keeps track of the number of connections that are not currently being used. If you release a connection back to the pool and the number of currently-available connections is already at the maximum, then the connection you are releasing will be closed instead of being put back in the pool.

What is important to note, however, is how the pool behaves when you try to get a connection and there isn't one immediately available. The connection pool offers four possible modes of operation:

  • Immediately throw an exception to indicate that no connections are available.
  • Immediately create a new connection to use.
  • Wait for up to a specified period of time for an existing connection to be released back to the pool. If it's not possible to get an existing connection within that period of time, then throw an exception to indicate that no connections are available.
  • Wait for up to a specified period of time for an existing connection to be released back to the pool. If it's not possible to get an existing connection within that period of time, then create a new connection to use.

Which mode the connection pool will use in this case is controlled by two settings. The setMaxWaitTimeMillis method is used to specify how long to wait for an existing connection to become available (by default, there is no wait at all), and the setCreateIfNecessary method is used to indicate whether the pool can create a new connection if one is needed but none are available after waiting the specified period of time (by default, the pool will create a new connection instead of throwing an exception).

This does mean that under heavy load, you can end up with a case in which there are more connections in use than the specified maximum. However, when that load dies down, then any extra connections that had been created to handle the spike in traffic will be closed, so that you won't end up with more than the maximum number of connections. However, if you want, you can configure the connection pool so that it won't ever have more than the maximum allowed number of connections, and you can either make your clients wait for an existing connection to be released or handle the possibility that no connections are available.

More Information#

There might be more information for this subject on one of the following:

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-13) was last changed on 07-Mar-2016 13:34 by jim