first design draft for supporting HA
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/HAConnectionStrategy.java b/src/main/java/org/apache/directory/fortress/core/ldap/HAConnectionStrategy.java
new file mode 100644
index 0000000..d8a380d
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/HAConnectionStrategy.java
@@ -0,0 +1,6 @@
+package org.apache.directory.fortress.core.ldap;
+
+public enum HAConnectionStrategy
+{
+ ACTIVE_PASSIVE, ROUND_ROBIN;
+}
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/LdapHAConnectionPool.java b/src/main/java/org/apache/directory/fortress/core/ldap/LdapHAConnectionPool.java
new file mode 100644
index 0000000..238c399
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/LdapHAConnectionPool.java
@@ -0,0 +1,158 @@
+package org.apache.directory.fortress.core.ldap;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.pool.impl.GenericObjectPool;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+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.ValidatingPoolableLdapConnectionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LdapHAConnectionPool
+{
+ private static final Logger LOG = LoggerFactory.getLogger( LdapHAConnectionPool.class );
+ private static final int DEFAULT_RETRIES_ALLOWED = 2;
+ private static final Map<String, LdapConnectionPool> CONNECTION_POOL_LOOKUP_TABLE = new HashMap<>();
+ private final LdapConnectionPool[] connectionPools;
+ private final HAConnectionStrategy connectionStrategy;
+ private final AtomicInteger requestCounter;
+ private final int availableNodes;
+ private final AtomicInteger masterNodeIndex;
+ private final AtomicInteger allowedRetriesCounter;
+ private final int allowedRetries;
+
+ public LdapHAConnectionPool( LdapConnectionConfig[] configs, HAConnectionStrategy connectionStrategy,
+ boolean testOnBorrow, byte exhaustedAction, int maxActive, int minIdle, int maxIdle, int allowedRetries )
+ {
+ this.availableNodes = configs.length;
+ this.requestCounter = new AtomicInteger( 0 );
+ this.masterNodeIndex = new AtomicInteger( 0 );
+ if ( allowedRetries == 0 )
+ {
+ this.allowedRetries = DEFAULT_RETRIES_ALLOWED;
+ } else
+ {
+ this.allowedRetries = allowedRetries;
+ }
+
+ this.allowedRetriesCounter = new AtomicInteger( allowedRetries );
+ connectionPools = new LdapConnectionPool[configs.length];
+ for ( int i = 0; i < configs.length; i++ )
+ {
+ connectionPools[i] = new LdapConnectionPool( new ValidatingPoolableLdapConnectionFactory( configs[i] ) );
+ connectionPools[i].setTestOnBorrow( true );
+ connectionPools[i].setWhenExhaustedAction( GenericObjectPool.WHEN_EXHAUSTED_GROW );
+ connectionPools[i].setMaxActive( maxActive );
+ connectionPools[i].setMinIdle( minIdle );
+ connectionPools[i].setMaxIdle( maxIdle );
+ }
+ this.connectionStrategy = connectionStrategy;
+ }
+
+ public LdapConnection getConnection() throws LdapException
+ {
+ LdapConnection connection;
+ try
+ {
+ connection = getConnectionByStrategy();
+ LOG.trace( "borrowed connection {}", connection );
+ } catch ( LdapException | RuntimeException e )
+ {
+ throw e;
+ } catch ( Exception e )
+ {
+ LOG.error( "An unexpected exception was thrown: ", e );
+ throw new RuntimeException( e );
+ }
+
+ return connection;
+ }
+
+ private LdapConnection getConnectionByStrategy() throws LdapException
+ {
+ LdapConnection borrowedConnection = null;
+ switch ( this.connectionStrategy )
+ {
+ case ACTIVE_PASSIVE:
+ try
+ {
+ int poolIndex = masterNodeIndex.get();
+ borrowedConnection = connectionPools[poolIndex].borrowObject();
+ allowedRetriesCounter.set( allowedRetries );
+ CONNECTION_POOL_LOOKUP_TABLE.put( borrowedConnection.toString(), connectionPools[poolIndex] );
+ } catch ( LdapException | RuntimeException e )
+ {
+ if ( masterNodeIndex.get() < ( availableNodes - 1 ) )
+ {
+ masterNodeIndex.incrementAndGet();
+ return getConnectionByStrategy();
+ } else
+ {
+ if ( allowedRetriesCounter.decrementAndGet() > 0 )
+ {
+ masterNodeIndex.set( 0 );
+ return getConnectionByStrategy();
+ } else
+ {
+ throw new RuntimeException( "could not allowed more retries for active passive strategy" );
+ }
+ }
+
+ } catch ( Exception e )
+ {
+ LOG.error( "An unexpected exception with active passive strategy was thrown: ", e );
+ throw new RuntimeException( e );
+ }
+ break;
+
+ default:
+ try
+ {
+ int poolIndex = getCurrentRequestNode();
+ borrowedConnection = connectionPools[poolIndex].borrowObject();
+ allowedRetriesCounter.set( allowedRetries );
+ CONNECTION_POOL_LOOKUP_TABLE.put( borrowedConnection.toString(), connectionPools[poolIndex] );
+ } catch ( LdapException | RuntimeException e )
+ {
+ if ( allowedRetriesCounter.decrementAndGet() > 0 )
+ {
+ return getConnectionByStrategy();
+ } else
+ {
+ throw new RuntimeException( "could not allowed more retries for round robin strategy" );
+ }
+
+ } catch ( Exception e )
+ {
+ LOG.error( "An unexpected exception was thrown: ", e );
+ throw new RuntimeException( e );
+ }
+ break;
+ }
+
+ return borrowedConnection;
+ }
+
+ private int getCurrentRequestNode()
+ {
+ return requestCounter.getAndIncrement() % availableNodes;
+ }
+
+ public void releaseConnection( LdapConnection connection )
+ {
+ try
+ {
+ CONNECTION_POOL_LOOKUP_TABLE.get( connection.toString() ).releaseConnection( connection );
+ CONNECTION_POOL_LOOKUP_TABLE.remove( connection.toString() );
+ } catch ( Exception e )
+ {
+ throw new RuntimeException( e.getMessage(), e );
+ }
+ }
+
+}