From 583404868c92e881eb3470b148132dc035d86a3d Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 29 Aug 2013 21:01:42 +0000
Subject: [PATCH] Partial fix for OPENDJ-1112: LoadBalancing connection factories need better diagnostic messages

---
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java         |   13 ++++++
 opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java |   98 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+), 1 deletions(-)

diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
index a868df4..2a4aaed 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
@@ -162,6 +162,9 @@
         }
 
         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)) {
@@ -225,6 +228,14 @@
     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;
+
     /**
      * Guarded by stateLock.
      */
@@ -335,6 +346,6 @@
          * timeout period.
          */
         throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
-                "No operational connection factories available");
+                "No operational connection factories available", lastFailure);
     }
 }
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java
new file mode 100644
index 0000000..82aa5a2
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java
@@ -0,0 +1,98 @@
+/*
+ * 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.when;
+
+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.StaticUtils;
+
+@SuppressWarnings("javadoc")
+public class AbstractLoadBalancingAlgorithmTestCase extends SdkTestCase {
+
+    /**
+     * 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);
+        final ErrorResultException firstError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN);
+        when(first.getConnection()).thenThrow(firstError);
+
+        final ConnectionFactory second = mock(ConnectionFactory.class);
+        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 (ErrorResultException e) {
+            assertThat(e.getCause()).isSameAs(secondError);
+        } finally {
+            loadBalancer.close();
+        }
+    }
+}

--
Gitblit v1.10.0