/* * 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 getConnectionAsync( final ResultHandler handler) { try { final Connection connection = mock.getConnection(); if (handler != null) { handler.handleResult(connection); } return new CompletedFutureResult(connection); } catch (final ErrorResultException e) { if (handler != null) { handler.handleErrorResult(e); } return new CompletedFutureResult(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(); } } }