Q:

How do I combine a TransactionAwareContextSourceProxy with a PoolingContextSource?

I am trying to combining a TransactionAwareContextSourceProxy with a PoolingContextSource. My tests work fine when using only a TransactionAwareContextSourceProxy or a PoolingContextSource. However, when combining the two, I get the following exception:
14:38:21.961|  WARN DirContext 'javax.naming.ldap.InitialLdapContext@3a47c130' failed validation with an exception.[DefaultDirContextValidator.validateDirContext(176)]
javax.naming.OperationNotSupportedException: [LDAP: error code 53 - Unwilling To Perform]; remaining name ''
    at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3114)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2987)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2794)
    at com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1826)
    at com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1749)
    at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(ComponentDirContext.java:368)
    at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:338)
    at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:321)
    at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:248)
    at org.springframework.ldap.pool.validation.DefaultDirContextValidator.validateDirContext(DefaultDirContextValidator.java:165)
    at org.springframework.ldap.pool.factory.DirContextPoolableObjectFactory.validateObject(DirContextPoolableObjectFactory.java:169)
    at org.apache.commons.pool.impl.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:837)
    at org.springframework.ldap.pool.factory.PoolingContextSource.getContext(PoolingContextSource.java:422)
    at org.springframework.ldap.pool.factory.PoolingContextSource.getReadWriteContext(PoolingContextSource.java:408)

and
org.springframework.transaction.CannotCreateTransactionException: Could not create DirContext instance for transaction; nested exception is org.springframework.dao.DataAccessResourceFailureException: Failed to borrow DirContext from pool.; nested exception is java.util.NoSuchElementException: Could not create a validated object
    at org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate.doBegin(AbstractCompensatingTransactionManagerDelegate.java:91)
    at org.springframework.ldap.transaction.compensating.manager.ContextSourceAndHibernateTransactionManager.doBegin(ContextSourceAndHibernateTransactionManager.java:80)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.startTransaction(TransactionalTestExecutionListener.java:507)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.startNewTransaction(TransactionalTestExecutionListener.java:269)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.beforeTestMethod(TransactionalTestExecutionListener.java:162)
    at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:374)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.springframework.dao.DataAccessResourceFailureException: Failed to borrow DirContext from pool.; nested exception is java.util.NoSuchElementException: Could not create a validated object
    at org.springframework.ldap.pool.factory.PoolingContextSource.getContext(PoolingContextSource.java:425)
    at org.springframework.ldap.pool.factory.PoolingContextSource.getReadWriteContext(PoolingContextSource.java:408)
    at org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManagerDelegate.getNewHolder(ContextSourceTransactionManagerDelegate.java:86)
    at org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate.doBegin(AbstractCompensatingTransactionManagerDelegate.java:84)
    ... 26 more
Caused by: java.util.NoSuchElementException: Could not create a validated object
    at org.apache.commons.pool.impl.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:840)
    at org.springframework.ldap.pool.factory.PoolingContextSource.getContext(PoolingContextSource.java:422)
    ... 29 more

This is my Spring configuration:
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
    <property name="url" value="${ldap.url}" />
    <property name="base" value="${ldap.base.main}" />
    <property name="userDn" value="${ldap.userDn}" />
    <property name="password" value="${ldap.password}" />
    <!-- Do not use standard pooling, but pooling support from Spring. -->
    <property name="pooled" value="false" />
</bean>
<bean id="transactionAwareContextSource" class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy">
    <constructor-arg ref="poolingContextSource" />
</bean>
<bean id="poolingContextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
    <property name="contextSource" ref="contextSourceTarget" />
    <property name="dirContextValidator" ref="dirContextValidator" />
    <property name="testOnBorrow" value="${ldap.pooling.testOnBorrow}" />
    <property name="testWhileIdle" value="${ldap.pooling.testWhileIdle}" />
    <property name="maxActive" value="${ldap.pooling.maxActive}" />
    <property name="maxTotal" value="${ldap.pooling.maxTotal}" />
    <property name="maxIdle" value="${ldap.pooling.maxIdle}" />
    <property name="minIdle" value="${ldap.pooling.minIdle}" />
    <property name="maxWait" value="${ldap.pooling.maxWaiting}" />
</bean>

<bean id="dirContextValidator" class="org.springframework.ldap.pool.validation.DefaultDirContextValidator">
    <property name="base" value="${ldap.validationBase.main}" />
</bean>

<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
    <constructor-arg ref="transactionAwareContextSource" />
</bean>
<bean id="transactionManager" 
    class="org.springframework.ldap.transaction.compensating.manager.ContextSourceAndHibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
    <property name="contextSource" ref="transactionAwareContextSource" />
</bean>

Has anybody gotten this to work successfully?
If not, do I really need ldap pooling or are ldap connections should cheap that it is not really important?

A:

This works with Spring LDAP 1.3.1 with the following configuration:
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
    <property name="contextSource" ref="contextSource"/>
</bean>

<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
    <property name="url" value="${ldap.url}"/>
    <property name="userDn" value="${ldap.userDn}"/>
    <property name="password" value="${ldap.password}"/>
    <property name="pooled" value="false"/>
</bean>

<bean id="pooledContextSource" 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"/>
    <property name="minIdle" value="${ldap.minIdle}"/>
    <property name="maxIdle" value="${ldap.maxIdle}"/>
    <property name="maxActive" value="${ldap.maxActive}"/>
    <property name="maxTotal" value="${ldap.maxTotal}"/>
    <property name="maxWait" value="${ldap.maxWait}"/>
</bean>

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

<bean id="contextSource"
      class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy">
    <constructor-arg ref="pooledContextSource"/>
</bean>

