You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by lu...@apache.org on 2015/01/19 23:54:29 UTC

svn commit: r1653146 - in /directory/apacheds/trunk: ldap-client-test/src/main/java/org/apache/directory/shared/client/api/ ldap-client-test/src/test/java/org/apache/directory/shared/client/api/ server-annotations/src/main/java/org/apache/directory/ser...

Author: lucastheisen
Date: Mon Jan 19 22:54:28 2015
New Revision: 1653146

URL: http://svn.apache.org/r1653146
Log:
- Updated CreateLdapConnectionPool annotation and CreateLdapConnectionPoolRule to support different pool implementations
- Removed redundant unit test from PasswordPolicyIT that was flaky due to Thread.sleep() race conditions
- Updated LdapApiIntegrationUtils to use DefaultLdapConnectionPool because it casts the returned connection to LdapNetworkConnection which does not work if the connection is wrapped

Added:
    directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/TrackingLdapConnectionFactory.java   (with props)
    directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/ValidatingLdapConnectionPoolTest.java   (with props)
Modified:
    directory/apacheds/trunk/ldap-client-test/src/main/java/org/apache/directory/shared/client/api/LdapApiIntegrationUtils.java
    directory/apacheds/trunk/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateLdapConnectionPool.java
    directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/ppolicy/PasswordPolicyIT.java
    directory/apacheds/trunk/test-framework/src/main/java/org/apache/directory/server/core/integ/CreateLdapConnectionPoolRule.java

Modified: directory/apacheds/trunk/ldap-client-test/src/main/java/org/apache/directory/shared/client/api/LdapApiIntegrationUtils.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/ldap-client-test/src/main/java/org/apache/directory/shared/client/api/LdapApiIntegrationUtils.java?rev=1653146&r1=1653145&r2=1653146&view=diff
==============================================================================
--- directory/apacheds/trunk/ldap-client-test/src/main/java/org/apache/directory/shared/client/api/LdapApiIntegrationUtils.java (original)
+++ directory/apacheds/trunk/ldap-client-test/src/main/java/org/apache/directory/shared/client/api/LdapApiIntegrationUtils.java Mon Jan 19 22:54:28 2015
@@ -25,11 +25,11 @@ import java.util.Map;
 
 import org.apache.directory.api.ldap.codec.api.SchemaBinaryAttributeDetector;
 import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.ldap.client.api.DefaultPoolableLdapConnectionFactory;
 import org.apache.directory.ldap.client.api.LdapConnection;
 import org.apache.directory.ldap.client.api.LdapConnectionConfig;
 import org.apache.directory.ldap.client.api.LdapConnectionPool;
 import org.apache.directory.ldap.client.api.LdapNetworkConnection;
-import org.apache.directory.ldap.client.api.ValidatingPoolableLdapConnectionFactory;
 import org.apache.directory.server.constants.ServerDNConstants;
 import org.apache.directory.server.ldap.LdapServer;
 
@@ -138,7 +138,7 @@ public class LdapApiIntegrationUtils
             config.setLdapPort( port );
             config.setName( DEFAULT_ADMIN );
             config.setCredentials( DEFAULT_PASSWORD );
-            ValidatingPoolableLdapConnectionFactory factory = new ValidatingPoolableLdapConnectionFactory( config );
+            DefaultPoolableLdapConnectionFactory factory = new DefaultPoolableLdapConnectionFactory( config );
             LdapConnectionPool pool = new LdapConnectionPool( factory );
             pool.setTestOnBorrow( true );
             pools.put( port, pool );

Added: directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/TrackingLdapConnectionFactory.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/TrackingLdapConnectionFactory.java?rev=1653146&view=auto
==============================================================================
--- directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/TrackingLdapConnectionFactory.java (added)
+++ directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/TrackingLdapConnectionFactory.java Mon Jan 19 22:54:28 2015
@@ -0,0 +1,31 @@
+package org.apache.directory.shared.client.api;
+
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.ldap.client.api.DefaultLdapConnectionFactory;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapConnectionConfig;
+
+public class TrackingLdapConnectionFactory extends DefaultLdapConnectionFactory {
+    private int bindCalled = 0;
+    
+    public TrackingLdapConnectionFactory( LdapConnectionConfig config ) {
+        super( config );
+    }
+    
+    @Override
+    public LdapConnection bindConnection( LdapConnection connection ) throws LdapException
+    {
+        bindCalled++;
+        return super.bindConnection( connection );
+    }
+
+    @Override
+    public LdapConnection configureConnection( LdapConnection connection )
+    {
+        return super.configureConnection( connection );
+    }
+    
+    public int getBindCalled() {
+        return bindCalled;
+    }
+}
\ No newline at end of file

Propchange: directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/TrackingLdapConnectionFactory.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/ValidatingLdapConnectionPoolTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/ValidatingLdapConnectionPoolTest.java?rev=1653146&view=auto
==============================================================================
--- directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/ValidatingLdapConnectionPoolTest.java (added)
+++ directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/ValidatingLdapConnectionPoolTest.java Mon Jan 19 22:54:28 2015
@@ -0,0 +1,156 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.shared.client.api;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapConnectionPool;
+import org.apache.directory.ldap.client.api.LookupLdapConnectionValidator;
+import org.apache.directory.ldap.client.api.ValidatingPoolableLdapConnectionFactory;
+import org.apache.directory.server.annotations.CreateLdapConnectionPool;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.annotations.ApplyLdifs;
+import org.apache.directory.server.core.annotations.ContextEntry;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.annotations.CreateIndex;
+import org.apache.directory.server.core.annotations.CreatePartition;
+import org.apache.directory.server.core.integ.CreateLdapConnectionPoolRule;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A test class for the connection pool.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+@CreateLdapServer(
+    transports = {
+            @CreateTransport(protocol = "LDAP")
+    })
+@CreateDS(name = "classDS",
+    enableChangeLog = true,
+    partitions = {
+            @CreatePartition(
+                    name = "example",
+                    suffix = "dc=example,dc=com",
+                    contextEntry = @ContextEntry(
+                            entryLdif =
+                            "dn: dc=example,dc=com\n" +
+                            "objectClass: domain\n" +
+                            "objectClass: top\n" +
+                            "dc: example\n\n"
+                    ),
+                    indexes = {
+                            @CreateIndex(attribute = "objectClass"),
+                            @CreateIndex(attribute = "dc"),
+                            @CreateIndex(attribute = "ou")
+            })
+    })
+@ApplyLdifs({
+            "dn: cn=class,ou=system",
+            "objectClass: person",
+            "cn: class",
+            "sn: sn_class"
+    })
+@CreateLdapConnectionPool(
+    factoryClass = ValidatingPoolableLdapConnectionFactory.class,
+    connectionFactoryClass = TrackingLdapConnectionFactory.class,
+    validatorClass = LookupLdapConnectionValidator.class,
+    maxActive = 1,
+    testOnBorrow = true )
+public class ValidatingLdapConnectionPoolTest
+{
+    private static Logger LOG = LoggerFactory.getLogger( ValidatingLdapConnectionPoolTest.class );
+
+    @ClassRule
+    public static CreateLdapConnectionPoolRule createLdapConnectionPoolRule = 
+            new CreateLdapConnectionPoolRule();
+
+    @Test
+    public void testClassLdapConnectionPool()
+    {
+        LOG.debug( "testClassLdapConnectionPool" );
+
+        LdapConnectionPool pool = createLdapConnectionPoolRule.getLdapConnectionPool();
+        TrackingLdapConnectionFactory factory = 
+            (TrackingLdapConnectionFactory)createLdapConnectionPoolRule.getLdapConnectionFactory();
+        LdapConnection ldapConnection = null;
+        try
+        {
+            ldapConnection = pool.getConnection();
+            assertEquals( 1, factory.getBindCalled() );
+        }
+        catch ( LdapException e )
+        {
+            fail( e.getMessage() );
+        }
+        finally
+        {
+            if ( ldapConnection != null )
+            {
+                try
+                {
+                    pool.releaseConnection( ldapConnection );
+                    assertEquals( 1, factory.getBindCalled() );
+                }
+                catch ( LdapException e )
+                {
+                    fail( "failed to release connection: " + e.getMessage() );
+                }
+            }
+        }
+        try
+        {
+            ldapConnection = pool.getConnection();
+            assertEquals( 1, factory.getBindCalled() );
+    
+            ldapConnection.bind( ServerDNConstants.ADMIN_SYSTEM_DN, "secret" );
+            assertEquals( 1, factory.getBindCalled() );
+        }
+        catch ( LdapException e )
+        {
+            fail( e.getMessage() );
+        }
+        finally
+        {
+            if ( ldapConnection != null )
+            {
+                try
+                {
+                    pool.releaseConnection( ldapConnection );
+                    assertEquals( 2, factory.getBindCalled() );
+                }
+                catch ( LdapException e )
+                {
+                    fail( "failed to release connection: " + e.getMessage() );
+                }
+            }
+        }
+    }
+}

Propchange: directory/apacheds/trunk/ldap-client-test/src/test/java/org/apache/directory/shared/client/api/ValidatingLdapConnectionPoolTest.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: directory/apacheds/trunk/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateLdapConnectionPool.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateLdapConnectionPool.java?rev=1653146&r1=1653145&r2=1653146&view=diff
==============================================================================
--- directory/apacheds/trunk/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateLdapConnectionPool.java (original)
+++ directory/apacheds/trunk/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateLdapConnectionPool.java Mon Jan 19 22:54:28 2015
@@ -27,6 +27,14 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.directory.ldap.client.api.DefaultLdapConnectionFactory;
+import org.apache.directory.ldap.client.api.DefaultLdapConnectionValidator;
+import org.apache.directory.ldap.client.api.DefaultPoolableLdapConnectionFactory;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapConnectionFactory;
+import org.apache.directory.ldap.client.api.LdapConnectionValidator;
+
 
 /**
  * A annotation used to define a LdapConnection configuration. 
@@ -46,14 +54,20 @@ import java.lang.annotation.Target;
     { ElementType.METHOD, ElementType.TYPE })
 public @interface CreateLdapConnectionPool
 {
-    /** The connection timeout in millis, default 30000 */
-    long timeout() default 30000L;
-
-
     /** Attributes names to be added to the list of default binary attributes */
     String[] additionalBinaryAttributes() default {};
     
     
+    /** LdapConnection factory implementation class */
+    Class<? extends LdapConnectionFactory> connectionFactoryClass() default 
+            DefaultLdapConnectionFactory.class;
+    
+    
+    /** LdapConnection pool factory implementation class */
+    Class<? extends PoolableObjectFactory<LdapConnection>> factoryClass() default 
+            DefaultPoolableLdapConnectionFactory.class;
+    
+    
     /** Connections borrowed in LIFO order, default true */
     boolean lifo() default true;
     
@@ -102,6 +116,15 @@ public @interface CreateLdapConnectionPo
     long timeBetweenEvictionRunsMillis() default -1L;
     
     
+    /** The connection timeout in millis, default 30000 */
+    long timeout() default 30000L;
+    
+    
+    /** The class to use for validation */
+    Class<? extends LdapConnectionValidator> validatorClass() default 
+        DefaultLdapConnectionValidator.class;
+
+
     /** The default action when connections are exhausted, default 1 (block) */
     byte whenExhaustedAction() default 1;
 }
\ No newline at end of file

Modified: directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/ppolicy/PasswordPolicyIT.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/ppolicy/PasswordPolicyIT.java?rev=1653146&r1=1653145&r2=1653146&view=diff
==============================================================================
--- directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/ppolicy/PasswordPolicyIT.java (original)
+++ directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/ppolicy/PasswordPolicyIT.java Mon Jan 19 22:54:28 2015
@@ -1285,60 +1285,6 @@ public class PasswordPolicyIT extends Ab
     @Test
     public void testPwdExpireWarning() throws Exception
     {
-        // The password will expire in 5 seconds
-        policyConfig.setPwdMaxAge( 5 );
-        policyConfig.setPwdGraceAuthNLimit( 0 );
-        // Send a warning 3 seconds before the expiration
-        policyConfig.setPwdExpireWarning( 3 );
-
-        Dn userDn = new Dn( "cn=userExpireWarning,ou=system" );
-        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
-
-        addUser( adminConnection, "userExpireWarning", "12345" );
-
-        LdapConnection userConnection = new LdapNetworkConnection( "localhost", ldapServer.getPort() );
-        userConnection.setTimeOut( 0L );
-
-        BindRequest bindReq = new BindRequestImpl();
-        bindReq.setDn( userDn );
-        bindReq.setCredentials( "12345" );
-        bindReq.addControl( PP_REQ_CTRL );
-
-        for ( int i = 0; i < 5; i++ )
-        {
-            BindResponse bindResponse = userConnection.bind( bindReq );
-            assertEquals( ResultCodeEnum.SUCCESS, bindResponse.getLdapResult().getResultCode() );
-
-            PasswordPolicy respCtrl = getPwdRespCtrl( bindResponse );
-            assertNotNull( respCtrl );
-
-            if ( i < 2 )
-            {
-                assertNull( respCtrl.getResponse() );
-            }
-            else
-            {
-                assertEquals( 5 - i, respCtrl.getResponse().getTimeBeforeExpiration() );
-            }
-
-            // Added an one second wait
-            Thread.sleep( 1000 );
-        }
-
-        // Added an one second wait
-        Thread.sleep( 1000 );
-
-        // We should not be able to login
-        checkBindFailure( userDn, "12345" );
-
-        userConnection.close();
-        adminConnection.close();
-    }
-    
-
-    @Test
-    public void testPwdExpireWarningToo() throws Exception
-    {
         policyConfig.setPwdGraceAuthNLimit( 0 );
         policyConfig.setPwdMaxAge( 3600 ); // 1 hour
         policyConfig.setPwdExpireWarning( 600 ); // 10 minutes
@@ -1367,7 +1313,7 @@ public class PasswordPolicyIT extends Ab
             assertNotNull( respCtrl );
             assertNull( respCtrl.getResponse() );
 
-            // now modify change time
+            // now modify change time to trigger warning
             ModifyRequest modifyRequest = new ModifyRequestImpl();
             modifyRequest.setName( userDn );
             modifyRequest.replace( "pwdChangedTime", DateUtils.getGeneralizedTime( new Date().getTime() - 3100000 ) );
@@ -1382,6 +1328,23 @@ public class PasswordPolicyIT extends Ab
             assertNotNull( respCtrl );
             assertNotNull( respCtrl.getResponse() );
             assertTrue( respCtrl.getResponse().getTimeBeforeExpiration() > 0 );
+
+            // now modify change time to trigger expired
+            modifyRequest = new ModifyRequestImpl();
+            modifyRequest.setName( userDn );
+            modifyRequest.replace( "pwdChangedTime", DateUtils.getGeneralizedTime( new Date().getTime() - 3700000 ) );
+            adminConnection.modify( modifyRequest );
+
+            BindRequest bindReq3 = new BindRequestImpl();
+            bindReq3.setDn( userDn );
+            bindReq3.setCredentials( "12345" );
+            bindReq3.addControl( new PasswordPolicyImpl() );
+            bindResponse = userConnection.bind( bindReq3 );
+            assertEquals( ResultCodeEnum.INVALID_CREDENTIALS, bindResponse.getLdapResult().getResultCode() );
+            respCtrl = getPwdRespCtrl( bindResponse );
+            assertNotNull( respCtrl );
+            assertNotNull( respCtrl.getResponse() );
+            assertEquals( PasswordPolicyErrorEnum.PASSWORD_EXPIRED, respCtrl.getResponse().getPasswordPolicyError() );
         }
         finally {
             safeCloseConnections( userConnection, userConnection2, adminConnection );

Modified: directory/apacheds/trunk/test-framework/src/main/java/org/apache/directory/server/core/integ/CreateLdapConnectionPoolRule.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/test-framework/src/main/java/org/apache/directory/server/core/integ/CreateLdapConnectionPoolRule.java?rev=1653146&r1=1653145&r2=1653146&view=diff
==============================================================================
--- directory/apacheds/trunk/test-framework/src/main/java/org/apache/directory/server/core/integ/CreateLdapConnectionPoolRule.java (original)
+++ directory/apacheds/trunk/test-framework/src/main/java/org/apache/directory/server/core/integ/CreateLdapConnectionPoolRule.java Mon Jan 19 22:54:28 2015
@@ -19,12 +19,17 @@
 package org.apache.directory.server.core.integ;
 
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.apache.commons.pool.PoolableObjectFactory;
 import org.apache.commons.pool.impl.GenericObjectPool.Config;
 import org.apache.directory.api.ldap.codec.api.DefaultConfigurableBinaryAttributeDetector;
-import org.apache.directory.ldap.client.api.DefaultLdapConnectionFactory;
+import org.apache.directory.ldap.client.api.LdapConnection;
 import org.apache.directory.ldap.client.api.LdapConnectionConfig;
+import org.apache.directory.ldap.client.api.LdapConnectionFactory;
 import org.apache.directory.ldap.client.api.LdapConnectionPool;
-import org.apache.directory.ldap.client.api.ValidatingPoolableLdapConnectionFactory;
+import org.apache.directory.ldap.client.api.LdapConnectionValidator;
 import org.apache.directory.ldap.client.template.LdapConnectionTemplate;
 import org.apache.directory.server.annotations.CreateLdapConnectionPool;
 import org.apache.directory.server.ldap.LdapServer;
@@ -46,7 +51,9 @@ public class CreateLdapConnectionPoolRul
     private CreateLdapConnectionPoolRule classCreateLdapConnectionPoolRule;
     private CreateLdapConnectionPool createLdapConnectionPool;
     private LdapConnectionPool ldapConnectionPool;
+    private LdapConnectionFactory ldapConnectionFactory;
     private LdapConnectionTemplate ldapConnectionTemplate;
+    private PoolableObjectFactory<LdapConnection> poolableLdapConnectionFactory;
 
 
     public CreateLdapConnectionPoolRule()
@@ -89,8 +96,15 @@ public class CreateLdapConnectionPoolRul
                         LdapConnectionPool oldLdapConnectionPool = ldapConnectionPool;
                         LdapConnectionTemplate oldLdapConnectionTemplate = ldapConnectionTemplate;
 
+                        Class<? extends PoolableObjectFactory<LdapConnection>> factoryClass =
+                                classCreateLdapConnectionPoolRule.createLdapConnectionPool.factoryClass();
+                        Class<? extends LdapConnectionFactory> connectionFactoryClass =
+                                classCreateLdapConnectionPoolRule.createLdapConnectionPool.connectionFactoryClass();
+                        Class<? extends LdapConnectionValidator> validatorClass =
+                                classCreateLdapConnectionPoolRule.createLdapConnectionPool.validatorClass();
                         ldapConnectionPool = classCreateLdapConnectionPoolRule
-                            .createLdapConnectionPool( ldapServer );
+                                .createLdapConnectionPool( ldapServer, factoryClass, 
+                                    connectionFactoryClass, validatorClass );
                         ldapConnectionTemplate = new LdapConnectionTemplate( ldapConnectionPool );
 
                         try
@@ -120,7 +134,14 @@ public class CreateLdapConnectionPoolRul
                 public void evaluate() throws Throwable
                 {
                     LOG.trace( "Creating ldap connection pool" );
-                    ldapConnectionPool = createLdapConnectionPool( getLdapServer() );
+                    Class<? extends PoolableObjectFactory<LdapConnection>> factoryClass =
+                            createLdapConnectionPool.factoryClass();
+                    Class<? extends LdapConnectionFactory> connectionFactoryClass =
+                            createLdapConnectionPool.connectionFactoryClass();
+                    Class<? extends LdapConnectionValidator> validatorClass =
+                            createLdapConnectionPool.validatorClass();
+                    ldapConnectionPool = createLdapConnectionPool( getLdapServer(), factoryClass, 
+                            connectionFactoryClass, validatorClass );
                     ldapConnectionTemplate = new LdapConnectionTemplate( ldapConnectionPool );
 
                     try
@@ -139,7 +160,10 @@ public class CreateLdapConnectionPoolRul
     }
 
 
-    private LdapConnectionPool createLdapConnectionPool( LdapServer ldapServer )
+    private LdapConnectionPool createLdapConnectionPool( LdapServer ldapServer, 
+            Class<? extends PoolableObjectFactory<LdapConnection>> factoryClass,
+            Class<? extends LdapConnectionFactory> connectionFactoryClass,
+            Class<? extends LdapConnectionValidator> validatorClass )
     {
         LdapConnectionConfig config = new LdapConnectionConfig();
         config.setLdapHost( "localhost" );
@@ -157,9 +181,6 @@ public class CreateLdapConnectionPoolRul
             config.setBinaryAttributeDetector( binaryAttributeDetector );
         }
 
-        DefaultLdapConnectionFactory factory = new DefaultLdapConnectionFactory( config );
-        factory.setTimeOut( createLdapConnectionPool.timeout() );
-
         Config poolConfig = new Config();
         poolConfig.lifo = createLdapConnectionPool.lifo();
         poolConfig.maxActive = createLdapConnectionPool.maxActive();
@@ -179,9 +200,69 @@ public class CreateLdapConnectionPoolRul
             .timeBetweenEvictionRunsMillis();
         poolConfig.whenExhaustedAction = createLdapConnectionPool
             .whenExhaustedAction();
+        
+        try
+        {
+            Constructor<? extends LdapConnectionFactory> constructor = 
+                    connectionFactoryClass.getConstructor( LdapConnectionConfig.class );
+            ldapConnectionFactory = constructor.newInstance( config );
+        }
+        catch ( Exception e )
+        {
+            throw new IllegalArgumentException( "invalid connectionFactoryClass " 
+                    + connectionFactoryClass.getName() + ": " + e.getMessage(), e );
+        }
+        try
+        {
+            Method timeoutSetter = connectionFactoryClass.getMethod( "setTimeOut", Long.TYPE );
+            if ( timeoutSetter != null ) {
+                timeoutSetter.invoke( ldapConnectionFactory, createLdapConnectionPool.timeout() );
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new IllegalArgumentException( "invalid connectionFactoryClass "
+                    + connectionFactoryClass.getName() + ", missing setTimeOut(long): " 
+                    + e.getMessage(), e );
+        }
+        
+        try
+        {
+            Constructor<? extends PoolableObjectFactory<LdapConnection>> constructor = 
+                    factoryClass.getConstructor( LdapConnectionFactory.class );
+            poolableLdapConnectionFactory = constructor.newInstance( ldapConnectionFactory );
+        }
+        catch ( Exception e )
+        {
+            throw new IllegalArgumentException( "invalid factoryClass " 
+                    + factoryClass.getName() + ": " + e.getMessage(), e );
+        }
+        try
+        {
+            Method setValidator = factoryClass.getMethod( "setValidator", LdapConnectionValidator.class );
+            if ( setValidator != null ) {
+                setValidator.invoke( poolableLdapConnectionFactory, 
+                    validatorClass.newInstance() );
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new IllegalArgumentException( "invalid connectionFactoryClass "
+                    + connectionFactoryClass.getName() + ", missing setTimeOut(long): " 
+                    + e.getMessage(), e );
+        }
+
+        return new LdapConnectionPool( poolableLdapConnectionFactory, poolConfig );
+    }
 
-        return new LdapConnectionPool(
-            new ValidatingPoolableLdapConnectionFactory( factory ), poolConfig );
+
+    public LdapConnectionFactory getLdapConnectionFactory()
+    {
+        return ldapConnectionFactory == null
+            ? ( classCreateLdapConnectionPoolRule == null
+                ? null
+                : classCreateLdapConnectionPoolRule.getLdapConnectionFactory() )
+            : ldapConnectionFactory;
     }