mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
23.37.2015 e0a30f365c33e3c77cf9e7405e924b9e680d217d
OPENDJ-1607 use Options for configuring load-balancers

* made round-robin and fail-over load balancing algs package private and
added factory methods in Connections class

* defined common load-balancing options in LoadBalancingAlgorithm

* move default event listener implementations to
LoadBalancerEventListener.
13 files modified
645 ■■■■■ changed files
opendj-sdk/opendj-core/clirr-ignored-api-changes.xml 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java 40 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connections.java 75 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/FailoverLoadBalancingAlgorithm.java 149 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancerEventListener.java 40 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancingAlgorithm.java 41 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/RoundRobinLoadBalancingAlgorithm.java 150 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties 2 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java 41 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java 52 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java 21 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Proxy.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java 15 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/clirr-ignored-api-changes.xml
@@ -577,4 +577,14 @@
    <method>java.util.Set asSetOf(org.forgerock.opendj.ldap.Function, java.lang.Object[])</method>
    <justification>Method needs to be final in order to use SafeVarArgs annotation</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/FailoverLoadBalancingAlgorithm</className>
    <differenceType>1001</differenceType>
    <justification>Class instances are now created using Connections.newFailoverLoadBalancer</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/RoundRobinLoadBalancingAlgorithm</className>
    <differenceType>1001</differenceType>
    <justification>Class instances are now created using Connections.newRoundRobinLoadBalancer</justification>
  </difference>
</differences>
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
@@ -36,6 +36,7 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.util.Options;
import org.forgerock.util.Reject;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.promise.Promise;
@@ -169,7 +170,7 @@
                        logger.debug(LocalizableMessage.raw("Starting monitoring thread"));
                        monitoringFuture =
                                scheduler.get().scheduleWithFixedDelay(new MonitorRunnable(), 0,
                                        monitoringInterval, monitoringIntervalTimeUnit);
                                        monitoringIntervalMS, TimeUnit.MILLISECONDS);
                    }
                }
            }
@@ -219,26 +220,6 @@
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    /**
     * A default event listener which just logs the event.
     */
    private static final LoadBalancerEventListener DEFAULT_LISTENER = new LoadBalancerEventListener() {
        @Override
        public void handleConnectionFactoryOnline(ConnectionFactory factory) {
            // Transition from offline to online.
            // TODO: I18N
            logger.info(LocalizableMessage.raw("Connection factory '%s' is now operational", factory));
        }
        @Override
        public void handleConnectionFactoryOffline(ConnectionFactory factory, LdapException error) {
            // TODO: I18N
            logger.warn(LocalizableMessage.raw("Connection factory '%s' is no longer operational: %s",
                    factory, error.getMessage()));
        }
    };
    private final List<MonitoredConnectionFactory> monitoredFactories;
    private final ReferenceCountedObject<ScheduledExecutorService>.Reference scheduler;
    private final Object stateLock = new Object();
@@ -265,28 +246,25 @@
     * Guarded by stateLock.
     */
    private int offlineFactoriesCount;
    private final long monitoringInterval;
    private final TimeUnit monitoringIntervalTimeUnit;
    private final long monitoringIntervalMS;
    /**
     * Guarded by stateLock.
     */
    private ScheduledFuture<?> monitoringFuture;
    private final AtomicBoolean isClosed = new AtomicBoolean();
    AbstractLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories,
            final LoadBalancerEventListener listener, final long interval, final TimeUnit unit,
            final ScheduledExecutorService scheduler) {
        Reject.ifNull(factories, unit);
    AbstractLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories, final Options options) {
        Reject.ifNull(factories, options);
        this.monitoredFactories = new ArrayList<>(factories.size());
        int i = 0;
        for (final ConnectionFactory f : factories) {
            this.monitoredFactories.add(new MonitoredConnectionFactory(f, i++));
        }
        this.scheduler = DEFAULT_SCHEDULER.acquireIfNull(scheduler);
        this.monitoringInterval = interval;
        this.monitoringIntervalTimeUnit = unit;
        this.listener = listener != null ? listener : DEFAULT_LISTENER;
        this.scheduler = DEFAULT_SCHEDULER.acquireIfNull(options.get(LOAD_BALANCER_SCHEDULER));
        this.monitoringIntervalMS = options.get(LOAD_BALANCER_MONITORING_INTERVAL).to(TimeUnit.MILLISECONDS);
        this.listener = options.get(LOAD_BALANCER_EVENT_LISTENER);
    }
    @Override
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connections.java
@@ -31,6 +31,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -364,15 +365,79 @@
    }
    /**
     * Creates a new load balancer which will obtain connections using the
     * provided load balancing algorithm.
     * Creates a new "round-robin" load-balance which will load-balance connections across the provided set of
     * connection factories. A round robin load balancing algorithm distributes connection requests across a list of
     * connection factories one at a time. When the end of the list is reached, the algorithm starts again from the
     * beginning.
     * <p/>
     * This algorithm is typically used for load-balancing <i>within</i> data centers, where load must be distributed
     * equally across multiple data sources. This algorithm contrasts with the {@link FailoverLoadBalancingAlgorithm}
     * which is used for load-balancing <i>between</i> data centers.
     * <p/>
     * If a problem occurs that temporarily prevents connections from being obtained for one of the connection
     * factories, then this algorithm automatically "fails over" to the next operational connection factory in the list.
     * If none of the connection factories are operational then a {@code ConnectionException} is returned to the
     * client.
     * <p/>
     * The implementation periodically attempts to connect to failed connection factories in order to determine if they
     * have become available again.
     *
     * @param factories
     *         The connection factories.
     * @param options
     *         This configuration options for the load-balancer. See {@link LoadBalancingAlgorithm} for common options.
     * @return The new round-robin load balancer.
     * @see #newFailoverLoadBalancer(Collection, Options)
     * @see LoadBalancingAlgorithm
     */
    public static ConnectionFactory newRoundRobinLoadBalancer(
            final Collection<? extends ConnectionFactory> factories, final Options options) {
        return new LoadBalancer(new RoundRobinLoadBalancingAlgorithm(factories, options));
    }
    /**
     * Creates a new "fail-over" load-balance which will load-balance connections across the provided set of connection
     * factories. A fail-over load balancing algorithm provides fault tolerance across multiple underlying connection
     * factories.
     * <p/>
     * This algorithm is typically used for load-balancing <i>between</i> data centers, where there is preference to
     * always forward connection requests to the <i>closest available</i> data center. This algorithm contrasts with the
     * {@link RoundRobinLoadBalancingAlgorithm} which is used for load-balancing <i>within</i> a data center.
     * <p/>
     * This algorithm selects connection factories based on the order in which they were provided during construction.
     * More specifically, an attempt to obtain a connection factory will always return the <i>first operational</i>
     * connection factory in the list. Applications should, therefore, organize the connection factories such that the
     * <i>preferred</i> (usually the closest) connection factories appear before those which are less preferred.
     * <p/>
     * If a problem occurs that temporarily prevents connections from being obtained for one of the connection
     * factories, then this algorithm automatically "fails over" to the next operational connection factory in the list.
     * If none of the connection factories are operational then a {@code ConnectionException} is returned to the
     * client.
     * <p/>
     * The implementation periodically attempts to connect to failed connection factories in order to determine if they
     * have become available again.
     *
     * @param factories
     *         The connection factories.
     * @param options
     *         This configuration options for the load-balancer. See {@link LoadBalancingAlgorithm} for common options.
     * @return The new fail-over load balancer.
     * @see #newRoundRobinLoadBalancer(Collection, Options)
     * @see LoadBalancingAlgorithm
     */
    public static ConnectionFactory newFailoverLoadBalancer(
            final Collection<? extends ConnectionFactory> factories, final Options options) {
        return new LoadBalancer(new FailoverLoadBalancingAlgorithm(factories, options));
    }
    /**
     * Creates a new load balancer which will obtain connections using the provided load balancing algorithm.
     *
     * @param algorithm
     *            The load balancing algorithm which will be used to obtain the
     *            next
     *         The load balancing algorithm which will be used to obtain the next
     * @return The new load balancer.
     * @throws NullPointerException
     *             If {@code algorithm} was {@code null}.
     *         If {@code algorithm} was {@code null}.
     */
    public static ConnectionFactory newLoadBalancer(final LoadBalancingAlgorithm algorithm) {
        return new LoadBalancer(algorithm);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/FailoverLoadBalancingAlgorithm.java
@@ -22,169 +22,32 @@
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 *      Portions copyright 2013-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.forgerock.util.Options;
/**
 * A fail-over load balancing algorithm provides fault tolerance across multiple
 * underlying connection factories.
 * <p>
 * This algorithm is typically used for load-balancing <i>between</i> data
 * centers, where there is preference to always forward connection requests to
 * the <i>closest available</i> data center. This algorithm contrasts with the
 * {@link RoundRobinLoadBalancingAlgorithm} which is used for load-balancing
 * <i>within</i> a data center.
 * <p>
 * This algorithm selects connection factories based on the order in which they
 * were provided during construction. More specifically, an attempt to obtain a
 * connection factory will always return the <i>first operational</i> connection
 * factory in the list. Applications should, therefore, organize the connection
 * factories such that the <i>preferred</i> (usually the closest) connection
 * factories appear before those which are less preferred.
 * <p>
 * If a problem occurs that temporarily prevents connections from being obtained
 * for one of the connection factories, then this algorithm automatically
 * "fails over" to the next operational connection factory in the list. If none
 * of the connection factories are operational then a
 * {@code ConnectionException} is returned to the client.
 * <p>
 * The implementation periodically attempts to connect to failed connection
 * factories in order to determine if they have become available again.
 *
 * @see RoundRobinLoadBalancingAlgorithm
 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm)
 */
public final class FailoverLoadBalancingAlgorithm extends AbstractLoadBalancingAlgorithm {
    /**
     * Creates a new fail-over load balancing algorithm which will monitor
     * offline connection factories every 1 second using the default scheduler.
     *
     * @param factories
     *            The ordered collection of connection factories.
     */
    public FailoverLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories) {
        this(factories, null, 1, TimeUnit.SECONDS, null);
final class FailoverLoadBalancingAlgorithm extends AbstractLoadBalancingAlgorithm {
    FailoverLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories, final Options options) {
        super(factories, options);
    }
    /**
     * Creates a new fail-over load balancing algorithm which will monitor
     * offline connection factories every 1 second using the default scheduler.
     *
     * @param factories
     *            The ordered collection of connection factories.
     * @param listener
     *            The event listener which should be notified whenever a
     *            connection factory changes state from online to offline or
     *            vice-versa.
     */
    public FailoverLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories,
            final LoadBalancerEventListener listener) {
        this(factories, listener, 1, TimeUnit.SECONDS, null);
    }
    /**
     * Creates a new fail-over load balancing algorithm which will monitor
     * offline connection factories using the specified frequency using the
     * default scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param listener
     *            The event listener which should be notified whenever a
     *            connection factory changes state from online to offline or
     *            vice-versa.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     */
    public FailoverLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories,
            final LoadBalancerEventListener listener, final long interval, final TimeUnit unit) {
        this(factories, listener, interval, unit, null);
    }
    /**
     * Creates a new fail-over load balancing algorithm which will monitor
     * offline connection factories using the specified frequency and scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param listener
     *            The event listener which should be notified whenever a
     *            connection factory changes state from online to offline or
     *            vice-versa.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     * @param scheduler
     *            The scheduler which should for periodically monitoring dead
     *            connection factories to see if they are usable again.
     */
    public FailoverLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories,
            final LoadBalancerEventListener listener, final long interval, final TimeUnit unit,
            final ScheduledExecutorService scheduler) {
        super(factories, listener, interval, unit, scheduler);
    }
    /**
     * Creates a new fail-over load balancing algorithm which will monitor
     * offline connection factories using the specified frequency using the
     * default scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     */
    public FailoverLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories,
            final long interval, final TimeUnit unit) {
        this(factories, null, interval, unit, null);
    }
    /**
     * Creates a new fail-over load balancing algorithm which will monitor
     * offline connection factories using the specified frequency and scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     * @param scheduler
     *            The scheduler which should for periodically monitoring dead
     *            connection factories to see if they are usable again.
     */
    public FailoverLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories,
            final long interval, final TimeUnit unit, final ScheduledExecutorService scheduler) {
        this(factories, null, interval, unit, scheduler);
    }
    /** {@inheritDoc} */
    @Override
    String getAlgorithmName() {
        return "Failover";
    }
    /** {@inheritDoc} */
    @Override
    int getInitialConnectionFactoryIndex() {
        // Always start with the first connection factory.
        return 0;
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancerEventListener.java
@@ -20,13 +20,18 @@
 *
 * CDDL HEADER END
 *
 *      Copyright 2013-2014 ForgeRock AS.
 *      Copyright 2013-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
import static com.forgerock.opendj.ldap.CoreMessages.LOAD_BALANCER_EVENT_LISTENER_LOG_OFFLINE;
import static com.forgerock.opendj.ldap.CoreMessages.LOAD_BALANCER_EVENT_LISTENER_LOG_ONLINE;
import java.util.EventListener;
import org.forgerock.i18n.slf4j.LocalizedLogger;
/**
 * An object that registers to be notified when a connection factory associated
 * with a load-balancer changes state from offline to online or vice-versa.
@@ -34,8 +39,41 @@
 * <b>NOTE:</b> load-balancer implementations must ensure that only one event is
 * sent at a time. Event listener implementations should not need to be thread
 * safe.
 *
 * @see LoadBalancingAlgorithm#LOAD_BALANCER_EVENT_LISTENER
 */
public interface LoadBalancerEventListener extends EventListener {
    /**
     * An event listener implementation which logs events to the LoadBalancingAlgorithm logger. This event listener is
     * the default implementation configured using the {@link LoadBalancingAlgorithm#LOAD_BALANCER_EVENT_LISTENER}
     * option.
     */
    LoadBalancerEventListener LOG_EVENTS = new LoadBalancerEventListener() {
        private final LocalizedLogger logger = LocalizedLogger.getLocalizedLogger(LoadBalancingAlgorithm.class);
        @Override
        public void handleConnectionFactoryOnline(final ConnectionFactory factory) {
            logger.info(LOAD_BALANCER_EVENT_LISTENER_LOG_ONLINE.get(factory));
        }
        @Override
        public void handleConnectionFactoryOffline(final ConnectionFactory factory, final LdapException error) {
            logger.warn(LOAD_BALANCER_EVENT_LISTENER_LOG_OFFLINE.get(factory, error.getMessage()));
        }
    };
    /** An event listener implementation which ignores all events. */
    LoadBalancerEventListener NO_OP = new LoadBalancerEventListener() {
        @Override
        public void handleConnectionFactoryOnline(final ConnectionFactory factory) {
            // Do nothing.
        }
        @Override
        public void handleConnectionFactoryOffline(final ConnectionFactory factory, final LdapException error) {
            // Do nothing.
        }
    };
    /**
     * Invoked when the load-balancer is unable to obtain a connection from the
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancingAlgorithm.java
@@ -22,35 +22,58 @@
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 ForgeRock AS.
 *      Portions Copyright 2013-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
import static org.forgerock.util.time.Duration.duration;
import java.io.Closeable;
import java.util.concurrent.ScheduledExecutorService;
import org.forgerock.util.Option;
import org.forgerock.util.time.Duration;
/**
 * A load balancing algorithm distributes connection requests across one or more
 * underlying connection factories in an implementation defined manner.
 * A load balancing algorithm distributes connection requests across one or more underlying connection factories in an
 * implementation defined manner.
 *
 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm) newLoadBalancer
 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm)
 */
public interface LoadBalancingAlgorithm extends Closeable {
    /**
     * Specifies the interval between successive attempts to reconnect to offline load-balanced connection factories.
     * The default configuration is to attempt to reconnect every second.
     */
    Option<Duration> LOAD_BALANCER_MONITORING_INTERVAL = Option.withDefault(duration("1 seconds"));
    /**
     * Releases any resources associated with this algorithm, including any
     * associated connection factories.
     * Specifies the event listener which should be notified whenever a load-balanced connection factory changes state
     * from online to offline or vice-versa. By default events will be logged to the {@code LoadBalancingAlgorithm}
     * logger using the {@link LoadBalancerEventListener#LOG_EVENTS} listener.
     */
    Option<LoadBalancerEventListener> LOAD_BALANCER_EVENT_LISTENER =
            Option.of(LoadBalancerEventListener.class, LoadBalancerEventListener.LOG_EVENTS);
    /**
     * Specifies the scheduler which will be used for periodically reconnecting to offline connection factories. A
     * system-wide scheduler will be used by default.
     */
    Option<ScheduledExecutorService> LOAD_BALANCER_SCHEDULER = Option.of(ScheduledExecutorService.class, null);
    /**
     * Releases any resources associated with this algorithm, including any associated connection factories.
     */
    @Override
    void close();
    /**
     * Returns a connection factory which should be used in order to satisfy the
     * next connection request.
     * Returns a connection factory which should be used in order to satisfy the next connection request.
     *
     * @return The connection factory.
     * @throws LdapException
     *             If no connection factories are available for use.
     *         If no connection factories are available for use.
     */
    ConnectionFactory getConnectionFactory() throws LdapException;
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/RoundRobinLoadBalancingAlgorithm.java
@@ -22,167 +22,35 @@
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 *      Portions copyright 2013-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.forgerock.util.Options;
/**
 * A round robin load balancing algorithm distributes connection requests across
 * a list of connection factories one at a time. When the end of the list is
 * reached, the algorithm starts again from the beginning.
 * <p>
 * This algorithm is typically used for load-balancing <i>within</i> data
 * centers, where load must be distributed equally across multiple data sources.
 * This algorithm contrasts with the {@link FailoverLoadBalancingAlgorithm}
 * which is used for load-balancing <i>between</i> data centers.
 * <p>
 * If a problem occurs that temporarily prevents connections from being obtained
 * for one of the connection factories, then this algorithm automatically
 * "fails over" to the next operational connection factory in the list. If none
 * of the connection factories are operational then a
 * {@code ConnectionException} is returned to the client.
 * <p>
 * The implementation periodically attempts to connect to failed connection
 * factories in order to determine if they have become available again.
 *
 * @see FailoverLoadBalancingAlgorithm
 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm)
 * A round robin load balancing algorithm distributes connection requests across a list of
 * connection factories one at a time. When the end of the list is reached, the algorithm starts again from the
 * beginning.
 */
public final class RoundRobinLoadBalancingAlgorithm extends AbstractLoadBalancingAlgorithm {
final class RoundRobinLoadBalancingAlgorithm extends AbstractLoadBalancingAlgorithm {
    private final int maxIndex;
    private final AtomicInteger nextIndex = new AtomicInteger(-1);
    /**
     * Creates a new round robin load balancing algorithm which will monitor
     * offline connection factories every 1 second using the default scheduler.
     *
     * @param factories
     *            The ordered collection of connection factories.
     */
    public RoundRobinLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories) {
        this(factories, null, 1, TimeUnit.SECONDS, null);
    }
    /**
     * Creates a new round robin load balancing algorithm which will monitor
     * offline connection factories every 1 second using the default scheduler.
     *
     * @param factories
     *            The ordered collection of connection factories.
     * @param listener
     *            The event listener which should be notified whenever a
     *            connection factory changes state from online to offline or
     *            vice-versa.
     */
    public RoundRobinLoadBalancingAlgorithm(
            final Collection<? extends ConnectionFactory> factories,
            final LoadBalancerEventListener listener) {
        this(factories, listener, 1, TimeUnit.SECONDS, null);
    }
    /**
     * Creates a new round robin load balancing algorithm which will monitor
     * offline connection factories using the specified frequency using the
     * default scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param listener
     *            The event listener which should be notified whenever a
     *            connection factory changes state from online to offline or
     *            vice-versa.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     */
    public RoundRobinLoadBalancingAlgorithm(
            final Collection<? extends ConnectionFactory> factories,
            final LoadBalancerEventListener listener, final long interval, final TimeUnit unit) {
        this(factories, null, interval, unit, null);
    }
    /**
     * Creates a new round robin load balancing algorithm which will monitor
     * offline connection factories using the specified frequency and scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param listener
     *            The event listener which should be notified whenever a
     *            connection factory changes state from online to offline or
     *            vice-versa.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     * @param scheduler
     *            The scheduler which should for periodically monitoring dead
     *            connection factories to see if they are usable again.
     */
    public RoundRobinLoadBalancingAlgorithm(
            final Collection<? extends ConnectionFactory> factories,
            final LoadBalancerEventListener listener, final long interval, final TimeUnit unit,
            final ScheduledExecutorService scheduler) {
        super(factories, listener, interval, unit, scheduler);
    RoundRobinLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories, final Options options) {
        super(factories, options);
        this.maxIndex = factories.size();
    }
    /**
     * Creates a new round robin load balancing algorithm which will monitor
     * offline connection factories using the specified frequency using the
     * default scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     */
    public RoundRobinLoadBalancingAlgorithm(
            final Collection<? extends ConnectionFactory> factories, final long interval,
            final TimeUnit unit) {
        this(factories, null, interval, unit, null);
    }
    /**
     * Creates a new round robin load balancing algorithm which will monitor
     * offline connection factories using the specified frequency and scheduler.
     *
     * @param factories
     *            The connection factories.
     * @param interval
     *            The interval between attempts to poll offline factories.
     * @param unit
     *            The time unit for the interval between attempts to poll
     *            offline factories.
     * @param scheduler
     *            The scheduler which should for periodically monitoring dead
     *            connection factories to see if they are usable again.
     */
    public RoundRobinLoadBalancingAlgorithm(
            final Collection<? extends ConnectionFactory> factories, final long interval,
            final TimeUnit unit, final ScheduledExecutorService scheduler) {
        this(factories, null, interval, unit, scheduler);
    }
    /** {@inheritDoc} */
    @Override
    String getAlgorithmName() {
        return "RoundRobin";
    }
    /** {@inheritDoc} */
    @Override
    int getInitialConnectionFactoryIndex() {
        // A round robin pool of one connection factories is unlikely in
opendj-sdk/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
@@ -1669,3 +1669,5 @@
LDAP_CONNECTION_CONNECT_TIMEOUT=The connection attempt to server %s has failed \
 because the connection timeout period of %d ms was exceeded
LOAD_BALANCER_EVENT_LISTENER_LOG_ONLINE=Connection factory '%s' is now operational
LOAD_BALANCER_EVENT_LISTENER_LOG_OFFLINE=Connection factory '%s' is no longer operational: %s
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java
@@ -21,27 +21,35 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2014 ForgeRock AS
 *      Copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap;
import java.util.concurrent.TimeUnit;
import static java.util.Arrays.asList;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.forgerock.opendj.ldap.Connections.newRoundRobinLoadBalancer;
import static org.forgerock.opendj.ldap.LdapException.newLdapException;
import static org.forgerock.opendj.ldap.LoadBalancingAlgorithm.LOAD_BALANCER_EVENT_LISTENER;
import static org.forgerock.opendj.ldap.LoadBalancingAlgorithm.LOAD_BALANCER_MONITORING_INTERVAL;
import static org.forgerock.opendj.ldap.LoadBalancingAlgorithm.LOAD_BALANCER_SCHEDULER;
import static org.forgerock.util.Options.defaultOptions;
import static org.forgerock.util.promise.Promises.newExceptionPromise;
import static org.forgerock.util.promise.Promises.newResultPromise;
import static org.forgerock.util.time.Duration.duration;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.util.logging.Level;
import org.forgerock.util.Options;
import org.forgerock.util.promise.Promise;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static java.util.Arrays.*;
import static org.fest.assertions.Assertions.*;
import static org.fest.assertions.Fail.*;
import static org.forgerock.opendj.ldap.Connections.*;
import static org.forgerock.opendj.ldap.LdapException.*;
import static org.forgerock.util.promise.Promises.*;
import static org.mockito.Mockito.*;
@SuppressWarnings("javadoc")
public class AbstractLoadBalancingAlgorithmTestCase extends SdkTestCase {
    private static ConnectionFactory mockAsync(final ConnectionFactory mock) {
@@ -105,8 +113,7 @@
        final LdapException secondError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN);
        when(second.getConnection()).thenThrow(secondError);
        final ConnectionFactory loadBalancer =
                newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(asList(first, second)));
        final ConnectionFactory loadBalancer = newRoundRobinLoadBalancer(asList(first, second), defaultOptions());
        /*
         * Belt and braces check to ensure that factory methods don't return
@@ -147,9 +154,11 @@
        final LoadBalancerEventListener listener = mock(LoadBalancerEventListener.class);
        final MockScheduler scheduler = new MockScheduler();
        final ConnectionFactory loadBalancer =
                newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(
                        asList(firstAsync, secondAsync), listener, 1, TimeUnit.SECONDS, scheduler));
        final Options options = defaultOptions()
                                   .set(LOAD_BALANCER_EVENT_LISTENER, listener)
                                   .set(LOAD_BALANCER_MONITORING_INTERVAL, duration("1 second"))
                                   .set(LOAD_BALANCER_SCHEDULER, scheduler);
        final ConnectionFactory loadBalancer = newRoundRobinLoadBalancer(asList(firstAsync, secondAsync), options);
        /*
         * Belt and braces check to ensure that factory methods don't return
opendj-sdk/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java
@@ -234,40 +234,26 @@
        factories[7][0] = newFixedConnectionPool(onlineServer, 10);
        // Round robin.
        factories[8][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
                        onlineServer, offlineServer1)));
        factories[8][0] = newRoundRobinLoadBalancer(asList(onlineServer, offlineServer1), defaultOptions());
        factories[9][0] = factories[8][0];
        factories[10][0] = factories[8][0];
        factories[11][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, onlineServer)));
        factories[12][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, offlineServer2, onlineServer)));
        factories[13][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays
                        .<ConnectionFactory> asList(Connections.newFixedConnectionPool(
                                offlineServer1, 10), Connections.newFixedConnectionPool(
                                onlineServer, 10))));
        factories[11][0] = newRoundRobinLoadBalancer(asList(offlineServer1, onlineServer), defaultOptions());
        factories[12][0] = newRoundRobinLoadBalancer(asList(offlineServer1, offlineServer2, onlineServer),
                                                     defaultOptions());
        factories[13][0] = newRoundRobinLoadBalancer(asList(newFixedConnectionPool(offlineServer1, 10),
                                                            newFixedConnectionPool(onlineServer, 10)),
                                                     defaultOptions());
        // Fail-over.
        factories[14][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
                        onlineServer, offlineServer1)));
        factories[14][0] = newFailoverLoadBalancer(asList(onlineServer, offlineServer1), defaultOptions());
        factories[15][0] = factories[14][0];
        factories[16][0] = factories[14][0];
        factories[17][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, onlineServer)));
        factories[18][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, offlineServer2, onlineServer)));
        factories[19][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays
                        .<ConnectionFactory> asList(Connections.newFixedConnectionPool(
                                offlineServer1, 10), Connections.newFixedConnectionPool(
                                onlineServer, 10))));
        factories[17][0] = newFailoverLoadBalancer(asList(offlineServer1, onlineServer), defaultOptions());
        factories[18][0] = newFailoverLoadBalancer(asList(offlineServer1, offlineServer2, onlineServer),
                                                   defaultOptions());
        factories[19][0] = newFailoverLoadBalancer(asList(newFixedConnectionPool(offlineServer1, 10),
                                                          newFixedConnectionPool(onlineServer, 10)),
                                                   defaultOptions());
        factories[20][0] = newFixedConnectionPool(onlineServer, 10);
@@ -698,10 +684,12 @@
            + "closing the connection factory causes NPE")
    public void testFactoryCloseBeforeConnectionClose() throws Exception {
        InetSocketAddress socketAddress = getServerSocketAddress();
        final ConnectionFactory factory =
                newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(newFixedConnectionPool(
                        newHeartBeatConnectionFactory(new LDAPConnectionFactory(
                                socketAddress.getHostName(), socketAddress.getPort())), 2))));
        final LDAPConnectionFactory ldap = new LDAPConnectionFactory(socketAddress.getHostName(),
                                                                     socketAddress.getPort(),
                                                                     defaultOptions()
                                                                            .set(HEARTBEAT_ENABLED, true));
        final ConnectionPool pool = newFixedConnectionPool(ldap, 2);
        final ConnectionFactory factory = newFailoverLoadBalancer(singletonList(pool), defaultOptions());
        Connection conn = null;
        try {
            conn = factory.getConnection();
opendj-sdk/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
@@ -45,7 +45,6 @@
import org.forgerock.opendj.ldap.ProviderNotFoundException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.LdapResultHandler;
import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm;
import org.forgerock.opendj.ldap.SdkTestCase;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.ServerConnection;
@@ -72,9 +71,11 @@
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static java.util.Arrays.asList;
import static org.fest.assertions.Assertions.*;
import static org.fest.assertions.Fail.*;
import static org.forgerock.opendj.ldap.Connections.newFixedConnectionPool;
import static org.forgerock.opendj.ldap.Connections.newRoundRobinLoadBalancer;
import static org.forgerock.opendj.ldap.LdapException.*;
import static org.forgerock.opendj.ldap.LDAPListener.*;
import static org.forgerock.opendj.ldap.TestCaseUtils.*;
@@ -326,11 +327,10 @@
            // Round robin.
            final ConnectionFactory loadBalancer =
                    Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays
                            .<ConnectionFactory> asList(Connections.newFixedConnectionPool(
                                    offlineServer1, 10), Connections.newFixedConnectionPool(
                                    offlineServer2, 10), Connections.newFixedConnectionPool(
                                    onlineServer, 10))));
                    newRoundRobinLoadBalancer(asList(newFixedConnectionPool(offlineServer1, 10),
                                                     newFixedConnectionPool(offlineServer2, 10),
                                                     newFixedConnectionPool(onlineServer, 10)),
                                              defaultOptions());
            final MockServerConnection proxyServerConnection = new MockServerConnection();
            final MockServerConnectionFactory proxyServerConnectionFactory =
@@ -406,11 +406,10 @@
            // Round robin.
            final ConnectionFactory loadBalancer =
                    Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays
                            .<ConnectionFactory> asList(Connections.newFixedConnectionPool(
                                    offlineServer1, 10), Connections.newFixedConnectionPool(
                                    offlineServer2, 10), Connections.newFixedConnectionPool(
                                    onlineServer, 10))));
                    newRoundRobinLoadBalancer(asList(newFixedConnectionPool(offlineServer1, 10),
                                                     newFixedConnectionPool(offlineServer2, 10),
                                                     newFixedConnectionPool(onlineServer, 10)),
                                              defaultOptions());
            final MockServerConnection proxyServerConnection = new MockServerConnection() {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Proxy.java
@@ -44,7 +44,6 @@
import org.forgerock.opendj.ldap.LDAPListener;
import org.forgerock.opendj.ldap.RequestContext;
import org.forgerock.opendj.ldap.RequestHandlerFactory;
import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm;
import org.forgerock.opendj.ldap.ServerConnectionFactory;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.util.Options;
@@ -116,12 +115,8 @@
        // --- JCite pools ---
        // --- JCite load balancer ---
        final RoundRobinLoadBalancingAlgorithm algorithm =
                new RoundRobinLoadBalancingAlgorithm(factories);
        final RoundRobinLoadBalancingAlgorithm bindAlgorithm =
                new RoundRobinLoadBalancingAlgorithm(bindFactories);
        final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
        final ConnectionFactory bindFactory = Connections.newLoadBalancer(bindAlgorithm);
        final ConnectionFactory factory = Connections.newRoundRobinLoadBalancer(factories, factoryOptions);
        final ConnectionFactory bindFactory = Connections.newRoundRobinLoadBalancer(bindFactories, bindFactoryOptions);
        // --- JCite load balancer ---
        // --- JCite backend ---
opendj-sdk/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
@@ -15,9 +15,13 @@
 */
package org.forgerock.opendj.rest2ldap;
import static java.util.Arrays.asList;
import static org.forgerock.json.resource.ResourceException.newResourceException;
import static org.forgerock.opendj.ldap.Connections.newCachedConnectionPool;
import static org.forgerock.opendj.ldap.Connections.newFailoverLoadBalancer;
import static org.forgerock.opendj.ldap.Connections.newRoundRobinLoadBalancer;
import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*;
import static org.forgerock.opendj.ldap.LoadBalancingAlgorithm.LOAD_BALANCER_MONITORING_INTERVAL;
import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
import static org.forgerock.opendj.ldap.schema.CoreSchema.getEntryUUIDAttributeType;
import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.CONTROLS;
@@ -26,7 +30,6 @@
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -51,7 +54,6 @@
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.EntryNotFoundException;
import org.forgerock.opendj.ldap.FailoverLoadBalancingAlgorithm;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LdapException;
@@ -59,7 +61,6 @@
import org.forgerock.opendj.ldap.MultipleEntriesFoundException;
import org.forgerock.opendj.ldap.RDN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm;
import org.forgerock.opendj.ldap.SSLContextBuilder;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.TimeoutResultException;
@@ -989,6 +990,8 @@
                                       .set(HEARTBEAT_ENABLED, true)
                                       .set(HEARTBEAT_INTERVAL, heartBeatInterval)
                                       .set(HEARTBEAT_TIMEOUT, heartBeatTimeout)
                                       .set(LOAD_BALANCER_MONITORING_INTERVAL, heartBeatInterval);
        // Parse pool parameters,
        final int connectionPoolSize =
                Math.max(configuration.get("connectionPoolSize").defaultTo(10).asInteger(), 1);
@@ -1066,8 +1069,7 @@
        // Create fail-over.
        if (secondary != null) {
            return Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
                    primary, secondary), heartBeatIntervalSeconds, TimeUnit.SECONDS));
            return newFailoverLoadBalancer(asList(primary, secondary), options);
        } else {
            return primary;
        }
@@ -1112,8 +1114,7 @@
            }
        }
        if (servers.size() > 1) {
            return Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(servers,
                    heartBeatIntervalSeconds, TimeUnit.SECONDS));
            return newRoundRobinLoadBalancer(servers, options);
        } else {
            return servers.get(0);
        }