# Pooling Support

## Introduction

Pooling LDAP connections helps mitigate the overhead of creating a new LDAP connection for each LDAP interaction. While Java LDAP pooling support exists it is limited in its configuration options and features, such as connection validation and pool maintenance. Spring LDAP provides support for detailed pool configuration on a per- `ContextSource` basis.

Pooling support is provided by `PoolingContextSource` which can wrap any `ContextSource` and pool both read-only and read-write `DirContext` objects. Jakarta Commons-Pool is used to provide the underlying pool implementation.

## DirContext Validation

Validation of pooled connections is the primary motivation for using a custom pooling library versus the JDK provided LDAP pooling functionality. Validation allows pooled `DirContext` connections to be checked to ensure they are still properly connected and configured when checking them out of the pool, in to the pool or while idle in the pool

The `DirContextValidator` interface is used by the `PoolingContextSource` for validation and `DefaultDirContextValidator` is provided as the default validation implementation. `DefaultDirContextValidator` does a `DirContext.search(String, String, SearchControls)` , with an empty name, a filter of `"objectclass=*"` and `SearchControls` set to limit a single result with the only the objectclass attribute and a 500ms timeout. If the returned `NamingEnumeration` has results the `DirContext` passes validation, if no results are returned or an exception is thrown the `DirContext` fails validation. The `DefaultDirContextValidator` should work with no configuration changes on most LDAP servers and provide the fastest way to validate the `DirContext` .

## Pool Properties

The following properties are available on the `PoolingContextSource` for configuration of the DirContext pool. The `contextSource` property must be set and the `dirContextValidator` property must be set if validation is enabled, all other properties are optional.

Pooling Configuration Properties

Parameter | Default | Description
--- | --- | ---
`contextSource` | `null` | The `ContextSource` implementation to get `DirContext` s from to populate the pool.
`dirContextValidator` | `null` | The `DirContextValidator` implementation to use when validating connections. This is required if `testOnBorrow` , `testOnReturn` , or `testWhileIdle` options are set to `true` .
`maxActive` | `8` | The maximum number of active connections of each type (read-only|read-write) that can be allocated from this pool at the same time, or non-positive for no limit.
`maxTotal` | `-1` | The overall maximum number of active connections (for all types) that can be allocated from this pool at the same time, or non-positive for no limit.
`maxIdle` | `8` | The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool, without extra ones being released, or non-positive for no limit.
`minIdle` | `0` | The minimum number of active connections of each type (read-only|read-write) that can remain idle in the pool, without extra ones being created, or zero to create none.
`maxWait` | `-1` | The maximum number of milliseconds that the pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.
`whenExhaustedAction` | `1` (BLOCK) | Specifies the behaviour when the pool is exhausted. The FAIL (`0`) option will throw a `NoSuchElementException` when the pool is exhausted. The BLOCK (`1`) option will wait until a new object is available. If `maxWait` is positive a `NoSuchElementException` is thrown if no new object is available after the `maxWait` time expires. The GROW (`2`) option will create and return a new object (essentially making `maxActive` meaningless).
`testOnBorrow` | `false` | The indication of whether objects will be validated before being borrowed from the pool. If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.
`testOnReturn` | `false` | The indication of whether objects will be validated before being returned to the pool.
`testWhileIdle` | `false` | The indication of whether objects will be validated by the idle object evictor (if any). If an object fails to validate, it will be dropped from the pool.
`timeBetweenEvictionRunsMillis` | `-1` | The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle object evictor thread will be run.
`numTestsPerEvictionRun` | `3` | The number of objects to examine during each run of the idle object evictor thread (if any).
`minEvictableIdleTimeMillis` | `1000 * 60 * 30` | The minimum amount of time an object may sit idle in the pool before it is eligible for eviction by the idle object evictor (if any).

## Configuration

Configuring pooling should look very familiar if you're used to Jakarta Commons-Pool or Commons-DBCP. You will first create a normal `ContextSource` then wrap it in a `PoolingContextSource` .

    <beans>
       ...
       <bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
          <property name="contextSource" ref="contextSourceTarget" />
       </bean>

       <bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
          <property name="url" value="ldap://localhost:389" />
          <property name="base" value="dc=example,dc=com" />
          <property name="userDn" value="cn=Manager" />
          <property name="password" value="secret" />
          <property name="pooled" value="false"/>
       </bean>
       ...
    </beans>

In a real world example you would probably configure the pool options and enable connection validation; the above serves as an example to demonstrate the general idea.

Ensure that the `pooled` property is set to `false` on any `ContextSource` that will be wrapped in a `PoolingContextSource` . The `PoolingContextSource` must be able to create new connections when needed and if `pooled` is set to `true` that may not be possible.

You'll notice that the actual `ContextSource` gets an id with a "Target" suffix. The bean you will actually refer to is the `PoolingContextSource` that wraps the target `contextSource`

### Validation Configuration

Adding validation and a few pool configuration tweaks to the above example is straight forward. Inject a `DirContextValidator` and set when validation should occur and the pool is ready to go.

    <beans>
       ...
       <bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
          <property name="contextSource" ref="contextSourceTarget" />
          <property name="dirContextValidator" ref="dirContextValidator" />
          <property name="testOnBorrow" value="true" />
          <property name="testWhileIdle" value="true" />
       </bean>

       <bean id="dirContextValidator"
             class="org.springframework.ldap.pool.validation.DefaultDirContextValidator" />

       <bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
          <property name="url" value="ldap://localhost:389" />
          <property name="base" value="dc=example,dc=com" />
          <property name="userDn" value="cn=Manager" />
          <property name="password" value="secret" />
          <property name="pooled" value="false"/>
       </bean>
       ...
    </beans>

The above example will test each `DirContext` before it is passed to the client application and test `DirContext` s that have been sitting idle in the pool.

## Known Issues

### Custom Authentication

The `PoolingContextSource` assumes that all `DirContext` objects retrieved from `ContextSource.getReadOnlyContext()` will have the same environment and likewise that all `DirContext` objects retrieved from `ContextSource.getReadWriteContext()` will have the same environment. This means that wrapping a `LdapContextSource` configured with an `AuthenticationSource` in a `PoolingContextSource` will not function as expected. The pool would be populated using the credentials of the first user and unless new connections were needed subsequent context requests would not be filled for the user specified by the `AuthenticationSource` for the requesting thread.