Back-port fix for OPENDJ-1112: LoadBalancing connection factories need better diagnostic messages
3 files added
3 files modified
| | |
| | | } |
| | | |
| | | private void notifyOffline(final ErrorResultException error) { |
| | | // Save the error in case the load-balancer is exhausted. |
| | | lastFailure = error; |
| | | |
| | | if (isOperational.getAndSet(false)) { |
| | | // Transition from online to offline. |
| | | if (DEBUG_LOG.isLoggable(Level.WARNING)) { |
| | | DEBUG_LOG.warning(String.format( |
| | | "Connection factory '%s' is no longer operational: %s", factory, error |
| | | .getMessage())); |
| | | synchronized (listenerLock) { |
| | | try { |
| | | listener.handleConnectionFactoryOffline(factory, error); |
| | | } catch (RuntimeException e) { |
| | | handleListenerException(e); |
| | | } |
| | | } |
| | | |
| | | synchronized (stateLock) { |
| | |
| | | private void notifyOnline() { |
| | | if (!isOperational.getAndSet(true)) { |
| | | // Transition from offline to online. |
| | | if (DEBUG_LOG.isLoggable(Level.INFO)) { |
| | | DEBUG_LOG.info(String.format("Connection factory'%s' is now operational", |
| | | factory)); |
| | | synchronized (listenerLock) { |
| | | try { |
| | | listener.handleConnectionFactoryOnline(factory); |
| | | } catch (RuntimeException e) { |
| | | handleListenerException(e); |
| | | } |
| | | } |
| | | |
| | | synchronized (stateLock) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void handleListenerException(RuntimeException e) { |
| | | if (DEBUG_LOG.isLoggable(Level.SEVERE)) { |
| | | DEBUG_LOG.log(Level.SEVERE, |
| | | "A run-time error occurred while processing a load-balancer event", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private final class MonitorRunnable implements Runnable { |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 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. |
| | | if (DEBUG_LOG.isLoggable(Level.INFO)) { |
| | | DEBUG_LOG.info(String.format("Connection factory'%s' is now operational", |
| | | factory)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleConnectionFactoryOffline(ConnectionFactory factory, |
| | | ErrorResultException error) { |
| | | if (DEBUG_LOG.isLoggable(Level.WARNING)) { |
| | | DEBUG_LOG.warning(String.format( |
| | | "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(); |
| | | |
| | | /** |
| | | * The last connection failure which caused a connection factory to be |
| | | * marked offline. This is used in order to help diagnose problems when the |
| | | * load-balancer has exhausted all of its factories. |
| | | */ |
| | | private volatile ErrorResultException lastFailure = null; |
| | | |
| | | /** |
| | | * The event listener which should be notified when connection factories go |
| | | * on or off-line. |
| | | */ |
| | | private final LoadBalancerEventListener listener; |
| | | |
| | | /** |
| | | * Ensures that events are notified one at a time. |
| | | */ |
| | | private final Object listenerLock = new Object(); |
| | | |
| | | /** |
| | | * Guarded by stateLock. |
| | | */ |
| | |
| | | private ScheduledFuture<?> monitoringFuture; |
| | | private AtomicBoolean isClosed = new AtomicBoolean(); |
| | | |
| | | AbstractLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories) { |
| | | this(factories, 1, TimeUnit.SECONDS, null); |
| | | } |
| | | |
| | | AbstractLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories, |
| | | final long interval, final TimeUnit unit) { |
| | | this(factories, interval, unit, null); |
| | | } |
| | | |
| | | AbstractLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories, |
| | | final long interval, final TimeUnit unit, final ScheduledExecutorService scheduler) { |
| | | final LoadBalancerEventListener listener, final long interval, final TimeUnit unit, |
| | | final ScheduledExecutorService scheduler) { |
| | | Validator.ensureNotNull(factories, unit); |
| | | |
| | | this.monitoredFactories = new ArrayList<MonitoredConnectionFactory>(factories.size()); |
| | |
| | | this.scheduler = DEFAULT_SCHEDULER.acquireIfNull(scheduler); |
| | | this.monitoringInterval = interval; |
| | | this.monitoringIntervalTimeUnit = unit; |
| | | this.listener = listener != null ? listener : DEFAULT_LISTENER; |
| | | } |
| | | |
| | | @Override |
| | |
| | | * timeout period. |
| | | */ |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | "No operational connection factories available"); |
| | | "No operational connection factories available", lastFailure); |
| | | } |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | |
| | | * 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. |
| | | * 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 |
| | |
| | | * The ordered collection of connection factories. |
| | | */ |
| | | public FailoverLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories) { |
| | | super(factories); |
| | | this(factories, null, 1, TimeUnit.SECONDS, null); |
| | | } |
| | | |
| | | /** |
| | | * 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<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<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<ConnectionFactory> factories, |
| | | final LoadBalancerEventListener listener, final long interval, final TimeUnit unit, |
| | | final ScheduledExecutorService scheduler) { |
| | | super(factories, listener, interval, unit, scheduler); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public FailoverLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories, |
| | | final long interval, final TimeUnit unit) { |
| | | super(factories, interval, unit); |
| | | this(factories, null, interval, unit, null); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public FailoverLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories, |
| | | final long interval, final TimeUnit unit, final ScheduledExecutorService scheduler) { |
| | | super(factories, interval, unit, scheduler); |
| | | this(factories, null, interval, unit, scheduler); |
| | | } |
| | | |
| | | /** |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * Copyright 2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import java.util.EventListener; |
| | | |
| | | /** |
| | | * 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. |
| | | * <p> |
| | | * <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. |
| | | */ |
| | | public interface LoadBalancerEventListener extends EventListener { |
| | | |
| | | /** |
| | | * Invoked when the load-balancer is unable to obtain a connection from the |
| | | * specified connection factory. The connection factory will be removed from |
| | | * the load-balancer and monitored periodically in order to determine when |
| | | * it is available again, at which point an online notification event will |
| | | * occur. |
| | | * |
| | | * @param factory |
| | | * The connection factory which has failed. |
| | | * @param error |
| | | * The last error that occurred. |
| | | */ |
| | | void handleConnectionFactoryOffline(ConnectionFactory factory, ErrorResultException error); |
| | | |
| | | /** |
| | | * Invoked when the load-balancer detects that a previously offline |
| | | * connection factory is available for use again. |
| | | * |
| | | * @param factory |
| | | * The connection factory which is now available for use. |
| | | */ |
| | | void handleConnectionFactoryOnline(ConnectionFactory factory); |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | |
| | | */ |
| | | public final class RoundRobinLoadBalancingAlgorithm extends AbstractLoadBalancingAlgorithm { |
| | | private final int maxIndex; |
| | | |
| | | private final AtomicInteger nextIndex = new AtomicInteger(-1); |
| | | |
| | | /** |
| | |
| | | * The ordered collection of connection factories. |
| | | */ |
| | | public RoundRobinLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories) { |
| | | super(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<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<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<ConnectionFactory> factories, |
| | | final LoadBalancerEventListener listener, final long interval, final TimeUnit unit, |
| | | final ScheduledExecutorService scheduler) { |
| | | super(factories, listener, interval, unit, scheduler); |
| | | this.maxIndex = factories.size(); |
| | | } |
| | | |
| | |
| | | */ |
| | | public RoundRobinLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories, |
| | | final long interval, final TimeUnit unit) { |
| | | super(factories, interval, unit); |
| | | this.maxIndex = factories.size(); |
| | | this(factories, null, interval, unit, null); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public RoundRobinLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories, |
| | | final long interval, final TimeUnit unit, final ScheduledExecutorService scheduler) { |
| | | super(factories, interval, unit, scheduler); |
| | | this.maxIndex = factories.size(); |
| | | this(factories, null, interval, unit, scheduler); |
| | | } |
| | | |
| | | /** |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | 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.newLoadBalancer; |
| | | import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult; |
| | | 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.concurrent.TimeUnit; |
| | | import java.util.logging.Level; |
| | | |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import com.forgerock.opendj.util.CompletedFutureResult; |
| | | import com.forgerock.opendj.util.StaticUtils; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | public class AbstractLoadBalancingAlgorithmTestCase extends SdkTestCase { |
| | | private static ConnectionFactory mockAsync(final ConnectionFactory mock) { |
| | | return new ConnectionFactory() { |
| | | @Override |
| | | public void close() { |
| | | mock.close(); |
| | | } |
| | | |
| | | @Override |
| | | public Connection getConnection() throws ErrorResultException { |
| | | return mock.getConnection(); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Connection> getConnectionAsync( |
| | | final ResultHandler<? super Connection> handler) { |
| | | try { |
| | | final Connection connection = mock.getConnection(); |
| | | if (handler != null) { |
| | | handler.handleResult(connection); |
| | | } |
| | | return new CompletedFutureResult<Connection>(connection); |
| | | } catch (final ErrorResultException e) { |
| | | if (handler != null) { |
| | | handler.handleErrorResult(e); |
| | | } |
| | | return new CompletedFutureResult<Connection>(e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return mock.toString(); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * Disables logging before the tests. |
| | | */ |
| | | @BeforeClass() |
| | | public void disableLogging() { |
| | | StaticUtils.DEBUG_LOG.setLevel(Level.SEVERE); |
| | | } |
| | | |
| | | /** |
| | | * Re-enable logging after the tests. |
| | | */ |
| | | @AfterClass() |
| | | public void enableLogging() { |
| | | StaticUtils.DEBUG_LOG.setLevel(Level.INFO); |
| | | } |
| | | |
| | | /** |
| | | * Tests fix for OPENDJ-1112: when a load balancer fails completely the |
| | | * connection exception should include the last error that occurred. |
| | | */ |
| | | @Test |
| | | public void testFinalFailureExposedAsCause() throws Exception { |
| | | /* |
| | | * Create load-balancer with two failed connection factories. |
| | | */ |
| | | final ConnectionFactory first = mock(ConnectionFactory.class, "first"); |
| | | final ErrorResultException firstError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(first.getConnection()).thenThrow(firstError); |
| | | |
| | | final ConnectionFactory second = mock(ConnectionFactory.class, "second"); |
| | | final ErrorResultException secondError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(second.getConnection()).thenThrow(secondError); |
| | | |
| | | final ConnectionFactory loadBalancer = |
| | | newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(asList(first, second))); |
| | | |
| | | /* |
| | | * Belt and braces check to ensure that factory methods don't return |
| | | * same instance and fool this test. |
| | | */ |
| | | assertThat(firstError).isNotSameAs(secondError); |
| | | |
| | | try { |
| | | loadBalancer.getConnection().close(); |
| | | fail("Unexpectedly obtained a connection"); |
| | | } catch (final ErrorResultException e) { |
| | | assertThat(e.getCause()).isSameAs(secondError); |
| | | } finally { |
| | | loadBalancer.close(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Tests fix for OPENDJ-1112: event listener should be notified when a |
| | | * factory changes state from online to offline or vice versa. |
| | | */ |
| | | @Test |
| | | public void testLoadBalancerEventListenerNotification() throws Exception { |
| | | /* |
| | | * Create load-balancer with two failed connection factories and a mock |
| | | * listener and scheduler. The first factory will succeed on the second |
| | | * attempt. |
| | | */ |
| | | final ConnectionFactory first = mock(ConnectionFactory.class, "first"); |
| | | final ConnectionFactory firstAsync = mockAsync(first); |
| | | final ErrorResultException firstError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(first.getConnection()).thenThrow(firstError).thenReturn(mock(Connection.class)); |
| | | |
| | | final ConnectionFactory second = mock(ConnectionFactory.class, "second"); |
| | | final ConnectionFactory secondAsync = mockAsync(second); |
| | | final ErrorResultException secondError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(second.getConnection()).thenThrow(secondError); |
| | | |
| | | 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)); |
| | | |
| | | /* |
| | | * Belt and braces check to ensure that factory methods don't return |
| | | * same instance and fool this test. |
| | | */ |
| | | assertThat(firstError).isNotSameAs(secondError); |
| | | |
| | | try { |
| | | loadBalancer.getConnection().close(); |
| | | fail("Unexpectedly obtained a connection"); |
| | | } catch (final ErrorResultException e) { |
| | | // Check that the event listener has been fired for both factories. |
| | | verify(listener).handleConnectionFactoryOffline(firstAsync, firstError); |
| | | verify(listener).handleConnectionFactoryOffline(secondAsync, secondError); |
| | | verifyNoMoreInteractions(listener); |
| | | |
| | | // Check that the factories are being monitored. |
| | | assertThat(scheduler.isScheduled()).isTrue(); |
| | | |
| | | // Forcefully run the monitor task and check that the first factory is online. |
| | | scheduler.getCommand().run(); |
| | | verify(listener).handleConnectionFactoryOnline(firstAsync); |
| | | verifyNoMoreInteractions(listener); |
| | | } finally { |
| | | loadBalancer.close(); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.concurrent.Callable; |
| | | import java.util.concurrent.Delayed; |
| | | import java.util.concurrent.ExecutionException; |
| | | import java.util.concurrent.Future; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.ScheduledFuture; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.TimeoutException; |
| | | |
| | | /** |
| | | * A mock scheduled executor which allows unit tests to directly invoke |
| | | * scheduled tasks without needing to wait. |
| | | */ |
| | | final class MockScheduler implements ScheduledExecutorService { |
| | | |
| | | // Saved scheduled task. |
| | | private Runnable command; |
| | | private long delay; |
| | | private boolean isScheduled = false; |
| | | private TimeUnit unit; |
| | | |
| | | MockScheduler() { |
| | | // Nothing to do. |
| | | } |
| | | |
| | | @Override |
| | | public boolean awaitTermination(final long timeout, final TimeUnit unit) |
| | | throws InterruptedException { |
| | | // Unused. |
| | | return false; |
| | | } |
| | | |
| | | @Override |
| | | public void execute(final Runnable command) { |
| | | // Unused. |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks) |
| | | throws InterruptedException { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks, |
| | | final long timeout, final TimeUnit unit) throws InterruptedException { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public <T> T invokeAny(final Collection<? extends Callable<T>> tasks) |
| | | throws InterruptedException, ExecutionException { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public <T> T invokeAny(final Collection<? extends Callable<T>> tasks, final long timeout, |
| | | final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isShutdown() { |
| | | // Unused. |
| | | return false; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isTerminated() { |
| | | // Unused. |
| | | return false; |
| | | } |
| | | |
| | | @Override |
| | | public <V> ScheduledFuture<V> schedule(final Callable<V> callable, final long delay, |
| | | final TimeUnit unit) { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public ScheduledFuture<?> schedule(final Runnable command, final long delay, final TimeUnit unit) { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public ScheduledFuture<?> scheduleAtFixedRate(final Runnable command, final long initialDelay, |
| | | final long period, final TimeUnit unit) { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public ScheduledFuture<?> scheduleWithFixedDelay(final Runnable command, |
| | | final long initialDelay, final long delay, final TimeUnit unit) { |
| | | this.command = command; |
| | | this.delay = delay; |
| | | this.unit = unit; |
| | | this.isScheduled = true; |
| | | return new ScheduledFuture<Object>() { |
| | | @Override |
| | | public boolean cancel(final boolean mayInterruptIfRunning) { |
| | | isScheduled = false; |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public int compareTo(final Delayed o) { |
| | | // Unused. |
| | | return 0; |
| | | } |
| | | |
| | | @Override |
| | | public Object get() throws InterruptedException, ExecutionException { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Object get(final long timeout, final TimeUnit unit) throws InterruptedException, |
| | | ExecutionException, TimeoutException { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public long getDelay(final TimeUnit unit) { |
| | | // Unused. |
| | | return 0; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isCancelled() { |
| | | return !isScheduled; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isDone() { |
| | | // Unused. |
| | | return false; |
| | | } |
| | | |
| | | }; |
| | | } |
| | | |
| | | @Override |
| | | public void shutdown() { |
| | | // Unused. |
| | | } |
| | | |
| | | @Override |
| | | public List<Runnable> shutdownNow() { |
| | | // Unused. |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | @Override |
| | | public <T> Future<T> submit(final Callable<T> task) { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Future<?> submit(final Runnable task) { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public <T> Future<T> submit(final Runnable task, final T result) { |
| | | // Unused. |
| | | return null; |
| | | } |
| | | |
| | | Runnable getCommand() { |
| | | return command; |
| | | } |
| | | |
| | | long getDelay() { |
| | | return delay; |
| | | } |
| | | |
| | | TimeUnit getUnit() { |
| | | return unit; |
| | | } |
| | | |
| | | boolean isScheduled() { |
| | | return isScheduled; |
| | | } |
| | | } |