Fixed for DIRAPI-342
diff --git a/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java b/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
index 8b90d44..6ac9dc5 100644
--- a/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
+++ b/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
@@ -1159,6 +1159,7 @@
MSG_04174_CREATING_NEW_CONNECTION_TEMPLATE( "MSG_04174_CREATING_NEW_CONNECTION_TEMPLATE" ),
MSG_04175_TRUST_MANAGER_IO_EXCEPTION( "MSG_04175_TRUST_MANAGER_IO_EXCEPTION" ),
MSG_04176_TRUST_MANAGER_ON_CLASSPATH( "MSG_04176_TRUST_MANAGER_ON_CLASSPATH" ),
+ MSG_04177_CONNECTION_TIMEOUT( "MSG_04177_CONNECTION_TIMEOUT" ),
// api-ldap-codec-core 5000-5999
// <> 5000-5099
diff --git a/i18n/src/main/resources/org/apache/directory/api/i18n/messages.properties b/i18n/src/main/resources/org/apache/directory/api/i18n/messages.properties
index 39de605..b1f93a4 100644
--- a/i18n/src/main/resources/org/apache/directory/api/i18n/messages.properties
+++ b/i18n/src/main/resources/org/apache/directory/api/i18n/messages.properties
@@ -140,6 +140,7 @@
MSG_04174_CREATING_NEW_CONNECTION_TEMPLATE=Creating new connection template from connectionPool
MSG_04175_TRUST_MANAGER_IO_EXCEPTION=LdapClientTrustStoreManager.getTrustManagers on input stream close operation caught IOException={0}
MSG_04176_TRUST_MANAGER_ON_CLASSPATH={0}.getTrustManagers on classpath
+MSG_04177_CONNECTION_TIMEOUT=Connection timeout after {0}ms.
# api-ldap-codec-core 5000-5999
# api-ldap-codec-core <> 5000-5099
diff --git a/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java b/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java
index 7c86a16..5ef677c 100644
--- a/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java
+++ b/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java
@@ -40,6 +40,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
@@ -150,6 +151,7 @@
import org.apache.directory.api.util.Strings;
import org.apache.directory.ldap.client.api.callback.SaslCallbackHandler;
import org.apache.directory.ldap.client.api.exception.InvalidConnectionException;
+import org.apache.directory.ldap.client.api.exception.LdapConnectionTimeOutException;
import org.apache.directory.ldap.client.api.future.AddFuture;
import org.apache.directory.ldap.client.api.future.BindFuture;
import org.apache.directory.ldap.client.api.future.CompareFuture;
@@ -211,7 +213,7 @@
* The created session, created when we open a connection with
* the Ldap server.
*/
- private IoSession ldapSession;
+ private IoSession ioSession;
/** a map to hold the ResponseFutures for all operations */
private Map<Integer, ResponseFuture<? extends Response>> futureMap = new ConcurrentHashMap<>();
@@ -225,9 +227,6 @@
/** A flag indicating that the BindRequest has been issued and successfully authenticated the user */
private AtomicBoolean authenticated = new AtomicBoolean( false );
- /** A flag indicating that the connection is connected or not */
- private AtomicBoolean connected = new AtomicBoolean( false );
-
/** a list of listeners interested in getting notified when the
* connection's session gets closed cause of network issues
*/
@@ -245,9 +244,11 @@
/** The krb5 configuration property */
private static final String KRB5_CONF = "java.security.krb5.conf";
- /** A future used to block any action until the handhake is completed */
+ /** A future used to block any action until the handshake is completed */
private HandshakeFuture handshakeFuture;
+ private CountDownLatch closeLatch = new CountDownLatch( 1 );
+
// ~~~~~~~~~~~~~~~~~ common error messages ~~~~~~~~~~~~~~~~~~~~~~~~~~
static final String TIME_OUT_ERROR = I18n.err( I18n.ERR_04170_TIMEOUT_OCCURED );
@@ -531,7 +532,8 @@
@Override
public boolean isConnected()
{
- return ( ldapSession != null ) && connected.get() && !ldapSession.isClosing();
+ return ( ioSession != null ) && ioSession.isConnected() && !ioSession.isClosing();
+
}
@@ -552,7 +554,7 @@
*/
public boolean isSecured()
{
- return isConnected() && ldapSession.isSecured();
+ return isConnected() && ioSession.isSecured();
}
@@ -564,12 +566,12 @@
*/
private void checkSession() throws InvalidConnectionException
{
- if ( ldapSession == null )
+ if ( ioSession == null )
{
throw new InvalidConnectionException( I18n.err( I18n.ERR_04104_NULL_CONNECTION_CANNOT_CONNECT ) );
}
- if ( !connected.get() )
+ if ( !isConnected() )
{
throw new InvalidConnectionException( I18n.err( I18n.ERR_04108_INVALID_CONNECTION ) );
}
@@ -654,87 +656,69 @@
{
// Build the connection address
SocketAddress address = new InetSocketAddress( config.getLdapHost(), config.getLdapPort() );
- long maxRetry = System.currentTimeMillis() + timeout;
ConnectFuture connectionFuture = connector.connect( address );
boolean result = false;
- while ( maxRetry > System.currentTimeMillis() )
+ // Wait until it's established
+ try
{
- // Wait until it's established
- try
+ result = connectionFuture.await( timeout );
+ }
+ catch ( InterruptedException e )
+ {
+ connector.dispose();
+ connector = null;
+
+ if ( LOG.isDebugEnabled() )
{
- result = connectionFuture.await( timeout );
+ LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION,
+ config.getLdapHost(),
+ config.getLdapPort() ), e );
}
- catch ( InterruptedException e )
+
+ throw new LdapOtherException( e.getMessage(), e );
+ }
+
+ if ( !result )
+ {
+ // It may be an exception, or a timeout
+ Throwable connectionException = connectionFuture.getException();
+
+ connector = null;
+
+ if ( connectionException == null )
{
- connector.dispose();
- connector = null;
-
+ // This was a timeout
+ String message = I18n.msg( I18n.MSG_04177_CONNECTION_TIMEOUT, timeout );
+
if ( LOG.isDebugEnabled() )
{
- LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION,
- config.getLdapHost(),
- config.getLdapPort() ), e );
+ LOG.debug( message );
}
- throw new LdapOtherException( e.getMessage(), e );
+ throw new LdapConnectionTimeOutException( message );
}
- finally
+ else
{
- if ( result )
+ if ( LOG.isDebugEnabled() )
{
- boolean isConnected = connectionFuture.isConnected();
-
- if ( !isConnected )
+ if ( ( connectionException instanceof ConnectException )
+ || ( connectionException instanceof UnresolvedAddressException ) )
{
- Throwable connectionException = connectionFuture.getException();
-
- if ( LOG.isDebugEnabled() )
- {
- if ( ( connectionException instanceof ConnectException )
- || ( connectionException instanceof UnresolvedAddressException ) )
- {
- // No need to wait
- // We know that there was a permanent error such as "connection refused".
- LOG.debug( I18n.msg( I18n.MSG_04144_CONNECTION_ERROR, connectionFuture.getException().getMessage() ) );
- }
-
- LOG.debug( I18n.msg( I18n.MSG_04143_CONNECTION_RETRYING ) );
- }
-
- // Wait 500 ms and retry
- try
- {
- Thread.sleep( 500 );
- }
- catch ( InterruptedException e )
- {
- connector = null;
-
- if ( LOG.isDebugEnabled() )
- {
- LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION,
- config.getLdapHost(),
- config.getLdapPort() ), e );
- }
-
- throw new LdapOtherException( e.getMessage(), e );
- }
+ // No need to wait
+ // We know that there was a permanent error such as "connection refused".
+ LOG.debug( I18n.msg( I18n.MSG_04144_CONNECTION_ERROR, connectionFuture.getException().getMessage() ) );
}
- else
- {
- break;
- }
+
+ LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION,
+ config.getLdapHost(),
+ config.getLdapPort() ), connectionException );
}
+
+ throw new LdapOtherException( connectionException.getMessage(), connectionException );
}
}
- if ( connectionFuture == null )
- {
- connector.dispose();
- throw new InvalidConnectionException( I18n.err( I18n.ERR_04109_CANNOT_CONNECT ) );
- }
-
return connectionFuture;
}
@@ -790,6 +774,9 @@
{
cause = ( Throwable ) connectionFuture.getSession().getAttribute( EXCEPTION_KEY );
}
+
+ // Cancel the latch
+ closeLatch.countDown();
// if there is no cause assume timeout
if ( cause == null )
@@ -834,7 +821,7 @@
{
if ( LOG.isDebugEnabled() )
{
- LOG.debug( I18n.msg( I18n.MSG_04134_CLOSING, responseFuture ) );
+ LOG.debug( I18n.msg( I18n.MSG_04137_NOD_RECEIVED ) );
}
responseFuture.cancel();
@@ -894,7 +881,7 @@
{
@SuppressWarnings("unchecked")
LdapMessageContainer<? extends Message> container =
- ( LdapMessageContainer<? extends Message> ) ldapSession
+ ( LdapMessageContainer<? extends Message> ) ioSession
.getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR );
if ( container != null )
@@ -913,7 +900,7 @@
atDetector = new SchemaBinaryAttributeDetector( schemaManager );
}
- ldapSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR,
+ ioSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR,
new LdapMessageContainer<Message>( codec, atDetector ) );
}
}
@@ -926,12 +913,12 @@
@Override
public boolean connect() throws LdapException
{
- if ( ( ldapSession != null ) && connected.get() )
+ if ( isConnected() )
{
// No need to connect if we already have a connected session
return true;
}
-
+
// Create the connector if needed
if ( connector == null )
{
@@ -944,6 +931,9 @@
// Check if we are good to go
if ( !connectionFuture.isConnected() )
{
+ // Release the latch
+ closeLatch.countDown();
+
close( connectionFuture );
}
@@ -957,13 +947,15 @@
setCloseListener( connectionFuture );
// Get back the session
- ldapSession = connectionFuture.getSession();
+ ioSession = connectionFuture.getSession();
// Store the container into the session if we don't have one
setBinaryDetector();
// Initialize the MessageId
messageId.set( 0 );
+
+ closeLatch = new CountDownLatch( 1 );
// And return
return true;
@@ -977,31 +969,23 @@
public void close()
{
// Close the session
- if ( ( ldapSession != null ) && connected.get() )
+ if ( isConnected() )
{
- ldapSession.closeNow();
- connected.set( false );
+ ioSession.closeNow();
}
- // And close the connector if it has been created locally
- // Release the connector
- connectorMutex.lock();
-
try
{
- if ( connector != null )
- {
- connector.dispose();
- connector = null;
+ if ( ( ioSession != null ) && ioSession.isConnected() )
+ {
+ closeLatch.await();
}
}
- finally
+ catch ( InterruptedException e )
{
- connectorMutex.unlock();
+ // TODO Auto-generated catch block
+ e.printStackTrace();
}
-
- // Reset the messageId
- messageId.set( 0 );
}
@@ -1259,7 +1243,7 @@
abandonRequest.setMessageId( newId );
// Send the request to the server
- ldapSession.write( abandonRequest );
+ ioSession.write( abandonRequest );
// remove the associated listener if any
int abandonId = abandonRequest.getAbandoned();
@@ -1726,7 +1710,6 @@
BindFuture bindFuture = bindAsync( request );
-
// Get the result from the future
try
{
@@ -2182,6 +2165,7 @@
}
catch ( Exception e )
{
+ closeLatch.countDown();
throw new LdapException( e );
}
}
@@ -2382,28 +2366,19 @@
// Send the request to the server
// Use this for logging instead: WriteFuture unbindFuture = ldapSession.write( unbindRequest )
- WriteFuture unbindFuture = ldapSession.write( unbindRequest );
+ WriteFuture unbindFuture = ioSession.write( unbindRequest );
unbindFuture.awaitUninterruptibly( timeout );
- authenticated.set( false );
-
- // Close all the Future for this session
- for ( ResponseFuture<? extends Response> responseFuture : futureMap.values() )
+ try
{
- responseFuture.cancel();
+ closeLatch.await();
}
-
- // clear the mappings
- clearMaps();
-
- // We now have to close the session
- close();
-
- connected.set( false );
-
- // Last, not least, reset the MessageId value
- messageId.set( 0 );
+ catch ( InterruptedException e )
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
// And get out
if ( LOG.isDebugEnabled() )
@@ -4497,7 +4472,7 @@
schemaManager = tmp;
// Change the container's BinaryDetector
- ldapSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR,
+ ioSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR,
new LdapMessageContainer<>( codec,
new SchemaBinaryAttributeDetector( schemaManager ) ) );
@@ -4746,7 +4721,6 @@
codec, config.getBinaryAttributeDetector() );
session.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, ldapMessageContainer );
- connected.set( true );
}
@@ -4756,15 +4730,18 @@
@Override
public void sessionClosed( IoSession session ) throws Exception
{
- // no need to handle if this session was closed by the user
- if ( ldapSession == null || !connected.get() )
+ authenticated.set( false );
+
+ // Close all the Future for this session
+ for ( ResponseFuture<? extends Response> responseFuture : futureMap.values() )
{
- return;
+ responseFuture.cancel();
}
- ldapSession.closeNow();
- connected.set( false );
- // Reset the messageId
+ // clear the mappings
+ clearMaps();
+
+ // Last, not least, reset the MessageId value
messageId.set( 0 );
connectorMutex.lock();
@@ -4782,8 +4759,6 @@
connectorMutex.unlock();
}
- clearMaps();
-
if ( conCloseListeners != null )
{
if ( LOG.isDebugEnabled() )
@@ -4796,6 +4771,8 @@
listener.connectionClosed();
}
}
+
+ closeLatch.countDown();
}
@@ -4820,7 +4797,7 @@
checkSession();
- if ( ldapSession.isSecured() )
+ if ( ioSession.isSecured() )
{
if ( LOG.isDebugEnabled() )
{
@@ -4901,20 +4878,20 @@
// for LDAPS/TLS
handshakeFuture = new HandshakeFuture();
- if ( ( ldapSession == null ) || !connected.get() )
+ if ( ( ioSession == null ) || !isConnected() )
{
connector.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter );
}
else
// for StartTLS
{
- ldapSession.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter );
+ ioSession.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter );
boolean isSecured = handshakeFuture.get( timeout, TimeUnit.MILLISECONDS );
if ( !isSecured )
{
- Throwable cause = ( Throwable ) ldapSession.getAttribute( EXCEPTION_KEY );
+ Throwable cause = ( Throwable ) ioSession.getAttribute( EXCEPTION_KEY );
throw new LdapTlsHandshakeException( I18n.err( I18n.ERR_04120_TLS_HANDSHAKE_ERROR ), cause );
}
}
@@ -5137,7 +5114,7 @@
private void writeRequest( Request request ) throws LdapException
{
// Send the request to the server
- WriteFuture writeFuture = ldapSession.write( request );
+ WriteFuture writeFuture = ioSession.write( request );
long localTimeout = timeout;
@@ -5152,7 +5129,7 @@
}
// Wait for the message to be sent to the server
- if ( !ldapSession.isConnected() )
+ if ( !ioSession.isConnected() )
{
// We didn't received anything : this is an error
if ( LOG.isErrorEnabled() )
@@ -5160,7 +5137,7 @@
LOG.error( I18n.err( I18n.ERR_04118_SOMETHING_WRONG_HAPPENED ) );
}
- Exception exception = ( Exception ) ldapSession.removeAttribute( EXCEPTION_KEY );
+ Exception exception = ( Exception ) ioSession.removeAttribute( EXCEPTION_KEY );
if ( exception instanceof LdapException )
{