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.
| | |
| | | <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> |
| | |
| | | |
| | | 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; |
| | |
| | | logger.debug(LocalizableMessage.raw("Starting monitoring thread")); |
| | | monitoringFuture = |
| | | scheduler.get().scheduleWithFixedDelay(new MonitorRunnable(), 0, |
| | | monitoringInterval, monitoringIntervalTimeUnit); |
| | | monitoringIntervalMS, TimeUnit.MILLISECONDS); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | 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(); |
| | |
| | | * 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 |
| | |
| | | |
| | | import java.net.InetAddress; |
| | | import java.net.InetSocketAddress; |
| | | import java.util.Collection; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 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); |
| | |
| | | * |
| | | * |
| | | * 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; |
| | | } |
| | | |
| | | } |
| | |
| | | * |
| | | * 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. |
| | |
| | | * <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 |
| | |
| | | * |
| | | * |
| | | * 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; |
| | | } |
| | |
| | | * |
| | | * |
| | | * 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 |
| | |
| | | |
| | | 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 |
| | |
| | | * 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) { |
| | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | |
| | | 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); |
| | | |
| | |
| | | + "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(); |
| | |
| | | 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; |
| | |
| | | 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.*; |
| | |
| | | |
| | | // 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 = |
| | |
| | | |
| | | // 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() { |
| | | |
| | |
| | | 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; |
| | |
| | | // --- 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 --- |
| | |
| | | */ |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | .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); |
| | |
| | | |
| | | // 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; |
| | | } |
| | |
| | | } |
| | | } |
| | | if (servers.size() > 1) { |
| | | return Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(servers, |
| | | heartBeatIntervalSeconds, TimeUnit.SECONDS)); |
| | | return newRoundRobinLoadBalancer(servers, options); |
| | | } else { |
| | | return servers.get(0); |
| | | } |