From 72650d4cc41c64136d064967d7fec3726d850fee Mon Sep 17 00:00:00 2001
From: Ludovic Poitou <ludovic.poitou@forgerock.com>
Date: Thu, 14 Oct 2010 11:52:28 +0000
Subject: [PATCH] Multiple enhancements and bug fixes to the SDK (update from OpenDS by matthew_swift):

---
 sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java |  272 ++++++++++++++++-------------------------------------
 1 files changed, 84 insertions(+), 188 deletions(-)

diff --git a/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java b/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
index 4910e44..6ac3148 100644
--- a/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
+++ b/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
@@ -29,15 +29,9 @@
 
 
 
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
-import java.util.logging.Level;
-
-import org.opends.sdk.responses.Responses;
-
-import com.sun.opends.sdk.util.StaticUtils;
-import com.sun.opends.sdk.util.Validator;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 
 
@@ -45,213 +39,115 @@
  * 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 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 "fails over" to
- * another operational connection factory in the list. If none of the connection
- * factories are operational then a {@code ConnectionException} is returned to
- * the client.
+ * 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)
  */
-class FailoverLoadBalancingAlgorithm implements LoadBalancingAlgorithm
+public final class FailoverLoadBalancingAlgorithm extends
+    AbstractLoadBalancingAlgorithm
 {
-  private static final class MonitoredConnectionFactory extends
-      AbstractConnectionFactory implements
-      ResultHandler<AsynchronousConnection>
-  {
-    private final ConnectionFactory factory;
-
-    private volatile boolean isOperational;
-
-    private volatile FutureResult<?> pendingConnectFuture;
-
-
-
-    private MonitoredConnectionFactory(final ConnectionFactory factory)
-    {
-      this.factory = factory;
-      this.isOperational = true;
-    }
-
-
-
-    @Override
-    public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-        final ResultHandler<? super AsynchronousConnection> resultHandler)
-    {
-      final ResultHandler<AsynchronousConnection> handler =
-        new ResultHandler<AsynchronousConnection>()
-      {
-        public void handleErrorResult(final ErrorResultException error)
-        {
-          isOperational = false;
-          if (resultHandler != null)
-          {
-            resultHandler.handleErrorResult(error);
-          }
-          if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
-          {
-            StaticUtils.DEBUG_LOG
-                .warning(String.format("Connection factory " + factory
-                    + " is no longer operational: " + error.getMessage()));
-          }
-        }
-
-
-
-        public void handleResult(final AsynchronousConnection result)
-        {
-          isOperational = true;
-          if (resultHandler != null)
-          {
-            resultHandler.handleResult(result);
-          }
-          if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
-          {
-            StaticUtils.DEBUG_LOG.warning(String.format("Connection factory "
-                + factory + " is now operational"));
-          }
-        }
-      };
-      return factory.getAsynchronousConnection(handler);
-    }
-
-
-
-    public void handleErrorResult(final ErrorResultException error)
-    {
-      isOperational = false;
-    }
-
-
-
-    public void handleResult(final AsynchronousConnection result)
-    {
-      isOperational = true;
-      // TODO: Notify the server is back up
-      result.close();
-    }
-
-
-
-    private boolean isOperational()
-    {
-      return isOperational;
-    }
-  }
-
-
-
-  private final class MonitorThread extends Thread
-  {
-    private MonitorThread()
-    {
-      super("Connection Factory Health Monitor");
-      this.setDaemon(true);
-    }
-
-
-
-    @Override
-    public void run()
-    {
-      while (true)
-      {
-        for (final MonitoredConnectionFactory f : monitoredFactories)
-        {
-          if (!f.isOperational
-              && (f.pendingConnectFuture == null || f.pendingConnectFuture
-                  .isDone()))
-          {
-            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-            {
-              StaticUtils.DEBUG_LOG.finest(String
-                  .format("Attempting connect on factory " + f));
-            }
-            f.pendingConnectFuture = f.factory.getAsynchronousConnection(f);
-          }
-        }
-
-        try
-        {
-          sleep(10000);
-        }
-        catch (final InterruptedException e)
-        {
-          // Termination requested - exit.
-          break;
-        }
-      }
-    }
-  }
-
-
-
-  private final List<MonitoredConnectionFactory> monitoredFactories;
-
-
 
   /**
-   * Creates a new fail-over load balancing algorithm which will fail-over
-   * across the provided collection of connection factories.
+   * Creates a new fail-over load balancing algorithm which will use a default
+   * scheduler for monitoring offline connection factories every 10 seconds.
    *
    * @param factories
-   *          The connection factories which will be used for fail-over.
+   *          The ordered collection of connection factories.
    */
   public FailoverLoadBalancingAlgorithm(
       final Collection<ConnectionFactory> factories)
   {
-    Validator.ensureNotNull(factories);
-
-    monitoredFactories = new ArrayList<MonitoredConnectionFactory>(factories
-        .size());
-    for (final ConnectionFactory f : factories)
-    {
-      monitoredFactories.add(new MonitoredConnectionFactory(f));
-    }
-
-    new MonitorThread().start();
+    super(factories);
   }
 
 
 
   /**
-   * Creates a new fail-over load balancing algorithm which will fail-over
-   * across the provided list of connection factories.
+   * Creates a new fail-over load balancing algorithm which will use the
+   * provided scheduler for monitoring offline connection factories every 10
+   * seconds.
    *
    * @param factories
-   *          The connection factories which will be used for fail-over.
+   *          The ordered collection of connection factories.
+   * @param scheduler
+   *          The scheduler which should for periodically monitoring offline
+   *          connection factories to see if they are usable again.
    */
-  public FailoverLoadBalancingAlgorithm(final ConnectionFactory... factories)
+  public FailoverLoadBalancingAlgorithm(
+      final Collection<ConnectionFactory> factories,
+      final ScheduledExecutorService scheduler)
   {
-    Validator.ensureNotNull((Object[]) factories);
-
-    monitoredFactories = new ArrayList<MonitoredConnectionFactory>(
-        factories.length);
-    for (final ConnectionFactory f : factories)
-    {
-      monitoredFactories.add(new MonitoredConnectionFactory(f));
-    }
-
-    new MonitorThread().start();
+    super(factories, scheduler);
   }
 
 
 
-  public ConnectionFactory getNextConnectionFactory()
-      throws ErrorResultException
+  /**
+   * Creates a new fail-over load balancing algorithm which will use the
+   * provided scheduler for monitoring offline connection factories.
+   *
+   * @param factories
+   *          The ordered collection of connection factories.
+   * @param scheduler
+   *          The scheduler which should for periodically monitoring offline
+   *          connection factories to see if they are usable again.
+   * @param interval
+   *          The interval between attempts to poll offline connection
+   *          factories.
+   * @param unit
+   *          The time unit for the interval between attempts to poll offline
+   *          connection factories.
+   */
+  public FailoverLoadBalancingAlgorithm(
+      final Collection<ConnectionFactory> factories,
+      final ScheduledExecutorService scheduler, final long interval,
+      final TimeUnit unit)
   {
-    for (final MonitoredConnectionFactory f : monitoredFactories)
-    {
-      if (f.isOperational())
-      {
-        return f;
-      }
-    }
-
-    throw ErrorResultException.wrap(Responses.newResult(
-        ResultCode.CLIENT_SIDE_CONNECT_ERROR).setDiagnosticMessage(
-        "No operational connection factories available"));
+    super(factories, scheduler, interval, unit);
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  String getAlgorithmName()
+  {
+    return "Failover";
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  int getInitialConnectionFactoryIndex()
+  {
+    // Always start with the first connection factory.
+    return 0;
+  }
+
 }

--
Gitblit v1.10.0