From ed08a89377a333c10202ead88d355e16bcb3a0fd Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 27 Feb 2014 23:31:10 +0000
Subject: [PATCH] Backport fix for OPENDJ-1197: API is lacking functionality to specify TCP connect timeout

---
 opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java |   61 +++++++++++++++++++-----------
 1 files changed, 39 insertions(+), 22 deletions(-)

diff --git a/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java b/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java
index 49d6fa1..2f397d2 100644
--- a/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java
+++ b/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java
@@ -28,7 +28,9 @@
 package com.forgerock.opendj.ldap;
 
 import static com.forgerock.opendj.ldap.DefaultTCPNIOTransport.DEFAULT_TRANSPORT;
+import static com.forgerock.opendj.ldap.GrizzlyUtils.configureConnection;
 import static com.forgerock.opendj.ldap.TimeoutChecker.TIMEOUT_CHECKER;
+import static org.forgerock.opendj.ldap.CoreMessages.LDAP_CONNECTION_CONNECT_TIMEOUT;
 import static org.forgerock.opendj.ldap.ErrorResultException.*;
 
 import java.io.IOException;
@@ -72,13 +74,16 @@
      */
     @SuppressWarnings("rawtypes")
     private final class CompletionHandlerAdapter implements
-            CompletionHandler<org.glassfish.grizzly.Connection> {
-
+            CompletionHandler<org.glassfish.grizzly.Connection>, TimeoutEventListener {
         private final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future;
+        private final long timeoutEndTime;
 
         private CompletionHandlerAdapter(
                 final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future) {
             this.future = future;
+            final long timeoutMS = getTimeout();
+            this.timeoutEndTime = timeoutMS > 0 ? System.currentTimeMillis() + timeoutMS : 0;
+            timeoutChecker.get().addListener(this);
         }
 
         @Override
@@ -99,10 +104,10 @@
 
             // Start TLS or install SSL layer asynchronously.
 
-            // Give up immediately if the future has been cancelled.
-            if (future.isCancelled()) {
+            // Give up immediately if the future has been cancelled or timed out.
+            if (future.isDone()) {
+                timeoutChecker.get().removeListener(this);
                 connection.close();
-                releaseTransportAndTimeoutChecker();
                 return;
             }
 
@@ -151,6 +156,7 @@
         @Override
         public void failed(final Throwable throwable) {
             // Adapt and forward.
+            timeoutChecker.get().removeListener(this);
             future.handleErrorResult(adaptConnectionException(throwable));
             releaseTransportAndTimeoutChecker();
         }
@@ -161,16 +167,11 @@
         }
 
         private LDAPConnection adaptConnection(final org.glassfish.grizzly.Connection<?> connection) {
-            /*
-             * Test shows that its much faster with non block writes but risk
-             * running out of memory if the server is slow.
-             */
-            connection.configureBlocking(true);
+            configureConnection(connection, options.isTCPNoDelay(), options.isKeepAlive(), options
+                    .isReuseAddress(), options.getLinger());
             final LDAPConnection ldapConnection =
                     new LDAPConnection(connection, LDAPConnectionFactoryImpl.this);
-            if (options.getTimeout(TimeUnit.MILLISECONDS) > 0) {
-                timeoutChecker.get().addConnection(ldapConnection);
-            }
+            timeoutChecker.get().addListener(ldapConnection);
             clientFilter.registerConnection(connection, ldapConnection);
             return ldapConnection;
         }
@@ -189,20 +190,37 @@
 
         private void onFailure(final LDAPConnection connection, final Throwable t) {
             // Abort connection attempt due to error.
-            connection.close();
+            timeoutChecker.get().removeListener(this);
             future.handleErrorResult(adaptConnectionException(t));
-            releaseTransportAndTimeoutChecker();
+            connection.close();
         }
 
         private void onSuccess(final LDAPConnection connection) {
-            future.handleResult(connection);
-
-            // Close the connection if the future was cancelled.
-            if (future.isCancelled()) {
+            timeoutChecker.get().removeListener(this);
+            if (!future.tryHandleResult(connection)) {
+                // The connection has been either cancelled or it has timed out.
                 connection.close();
-                releaseTransportAndTimeoutChecker();
             }
         }
+
+        @Override
+        public long handleTimeout(final long currentTime) {
+            if (timeoutEndTime == 0) {
+                return 0;
+            } else if (timeoutEndTime > currentTime) {
+                return timeoutEndTime - currentTime;
+            } else {
+                future.handleErrorResult(newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+                        LDAP_CONNECTION_CONNECT_TIMEOUT.get(socketAddress.toString(), getTimeout())
+                                .toString()));
+                return 0;
+            }
+        }
+
+        @Override
+        public long getTimeout() {
+            return options.getConnectTimeout(TimeUnit.MILLISECONDS);
+        }
     }
 
     private final LDAPClientFilter clientFilter;
@@ -271,8 +289,7 @@
                         .build();
         final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future =
                 new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(handler);
-        final CompletionHandlerAdapter cha = new CompletionHandlerAdapter(future);
-        connectorHandler.connect(socketAddress, cha);
+        connectorHandler.connect(socketAddress, new CompletionHandlerAdapter(future));
         return future;
     }
 

--
Gitblit v1.10.0