directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From elecha...@apache.org
Subject svn commit: r902161 [2/3] - in /directory/clients/ldap/trunk: ./ ldap-client-api/ ldap-client-api/src/ ldap-client-api/src/main/ ldap-client-api/src/main/java/ ldap-client-api/src/main/java/org/ ldap-client-api/src/main/java/org/apache/ ldap-client-api...
Date Fri, 22 Jan 2010 16:41:55 GMT
Added: directory/clients/ldap/trunk/ldap-client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java
URL: http://svn.apache.org/viewvc/directory/clients/ldap/trunk/ldap-client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java?rev=902161&view=auto
==============================================================================
--- directory/clients/ldap/trunk/ldap-client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java (added)
+++ directory/clients/ldap/trunk/ldap-client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java Fri Jan 22 16:41:54 2010
@@ -0,0 +1,2831 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.client.api;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.BasicControl;
+import javax.naming.ldap.Control;
+import javax.net.ssl.SSLContext;
+
+import org.apache.directory.shared.asn1.ber.IAsn1Container;
+import org.apache.directory.shared.asn1.codec.DecoderException;
+import org.apache.directory.shared.asn1.primitives.OID;
+import org.apache.directory.shared.ldap.client.api.exception.InvalidConnectionException;
+import org.apache.directory.shared.ldap.client.api.exception.LdapException;
+import org.apache.directory.shared.ldap.client.api.listeners.AddListener;
+import org.apache.directory.shared.ldap.client.api.listeners.BindListener;
+import org.apache.directory.shared.ldap.client.api.listeners.CompareListener;
+import org.apache.directory.shared.ldap.client.api.listeners.DeleteListener;
+import org.apache.directory.shared.ldap.client.api.listeners.ExtendedListener;
+import org.apache.directory.shared.ldap.client.api.listeners.IntermediateResponseListener;
+import org.apache.directory.shared.ldap.client.api.listeners.ModifyDnListener;
+import org.apache.directory.shared.ldap.client.api.listeners.ModifyListener;
+import org.apache.directory.shared.ldap.client.api.listeners.OperationResponseListener;
+import org.apache.directory.shared.ldap.client.api.listeners.SearchListener;
+import org.apache.directory.shared.ldap.client.api.messages.AbandonRequest;
+import org.apache.directory.shared.ldap.client.api.messages.AbstractMessage;
+import org.apache.directory.shared.ldap.client.api.messages.AddRequest;
+import org.apache.directory.shared.ldap.client.api.messages.AddResponse;
+import org.apache.directory.shared.ldap.client.api.messages.BindRequest;
+import org.apache.directory.shared.ldap.client.api.messages.BindResponse;
+import org.apache.directory.shared.ldap.client.api.messages.CompareRequest;
+import org.apache.directory.shared.ldap.client.api.messages.CompareResponse;
+import org.apache.directory.shared.ldap.client.api.messages.DeleteRequest;
+import org.apache.directory.shared.ldap.client.api.messages.DeleteResponse;
+import org.apache.directory.shared.ldap.client.api.messages.ExtendedRequest;
+import org.apache.directory.shared.ldap.client.api.messages.ExtendedResponse;
+import org.apache.directory.shared.ldap.client.api.messages.IntermediateResponse;
+import org.apache.directory.shared.ldap.client.api.messages.LdapResult;
+import org.apache.directory.shared.ldap.client.api.messages.ModifyDnRequest;
+import org.apache.directory.shared.ldap.client.api.messages.ModifyDnResponse;
+import org.apache.directory.shared.ldap.client.api.messages.ModifyRequest;
+import org.apache.directory.shared.ldap.client.api.messages.ModifyResponse;
+import org.apache.directory.shared.ldap.client.api.messages.Referral;
+import org.apache.directory.shared.ldap.client.api.messages.SearchRequest;
+import org.apache.directory.shared.ldap.client.api.messages.SearchResponse;
+import org.apache.directory.shared.ldap.client.api.messages.SearchResultDone;
+import org.apache.directory.shared.ldap.client.api.messages.SearchResultEntry;
+import org.apache.directory.shared.ldap.client.api.messages.SearchResultReference;
+import org.apache.directory.shared.ldap.client.api.messages.future.ResponseFuture;
+import org.apache.directory.shared.ldap.client.api.protocol.LdapProtocolCodecFactory;
+import org.apache.directory.shared.ldap.codec.ControlCodec;
+import org.apache.directory.shared.ldap.codec.LdapConstants;
+import org.apache.directory.shared.ldap.codec.LdapMessageCodec;
+import org.apache.directory.shared.ldap.codec.LdapMessageContainer;
+import org.apache.directory.shared.ldap.codec.LdapResultCodec;
+import org.apache.directory.shared.ldap.codec.TwixTransformer;
+import org.apache.directory.shared.ldap.codec.abandon.AbandonRequestCodec;
+import org.apache.directory.shared.ldap.codec.add.AddRequestCodec;
+import org.apache.directory.shared.ldap.codec.add.AddResponseCodec;
+import org.apache.directory.shared.ldap.codec.bind.BindRequestCodec;
+import org.apache.directory.shared.ldap.codec.bind.BindResponseCodec;
+import org.apache.directory.shared.ldap.codec.bind.LdapAuthentication;
+import org.apache.directory.shared.ldap.codec.bind.SaslCredentials;
+import org.apache.directory.shared.ldap.codec.bind.SimpleAuthentication;
+import org.apache.directory.shared.ldap.codec.compare.CompareRequestCodec;
+import org.apache.directory.shared.ldap.codec.compare.CompareResponseCodec;
+import org.apache.directory.shared.ldap.codec.del.DelRequestCodec;
+import org.apache.directory.shared.ldap.codec.del.DelResponseCodec;
+import org.apache.directory.shared.ldap.codec.extended.ExtendedRequestCodec;
+import org.apache.directory.shared.ldap.codec.extended.ExtendedResponseCodec;
+import org.apache.directory.shared.ldap.codec.intermediate.IntermediateResponseCodec;
+import org.apache.directory.shared.ldap.codec.modify.ModifyRequestCodec;
+import org.apache.directory.shared.ldap.codec.modify.ModifyResponseCodec;
+import org.apache.directory.shared.ldap.codec.modifyDn.ModifyDNRequestCodec;
+import org.apache.directory.shared.ldap.codec.modifyDn.ModifyDNResponseCodec;
+import org.apache.directory.shared.ldap.codec.search.Filter;
+import org.apache.directory.shared.ldap.codec.search.SearchRequestCodec;
+import org.apache.directory.shared.ldap.codec.search.SearchResultDoneCodec;
+import org.apache.directory.shared.ldap.codec.search.SearchResultEntryCodec;
+import org.apache.directory.shared.ldap.codec.search.SearchResultReferenceCodec;
+import org.apache.directory.shared.ldap.codec.unbind.UnBindRequestCodec;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.cursor.Cursor;
+import org.apache.directory.shared.ldap.cursor.ListCursor;
+import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.FilterParser;
+import org.apache.directory.shared.ldap.filter.SearchScope;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.RDN;
+import org.apache.directory.shared.ldap.util.LdapURL;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.mina.core.filterchain.IoFilter;
+import org.apache.mina.core.future.ConnectFuture;
+import org.apache.mina.core.future.WriteFuture;
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoEventType;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.executor.ExecutorFilter;
+import org.apache.mina.filter.executor.UnorderedThreadPoolExecutor;
+import org.apache.mina.filter.ssl.SslFilter;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is the base for every operations sent or received to and
+ * from a LDAP server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LdapConnection  extends IoHandlerAdapter
+{
+
+    /** logger for reporting errors that might not be handled properly upstream */
+    private static final Logger LOG = LoggerFactory.getLogger( LdapConnection.class );
+
+    private static final String LDAP_RESPONSE = "LdapReponse";
+    
+    /** The timeout used for response we are waiting for */ 
+    private long timeOut = LdapConnectionConfig.DEFAULT_TIMEOUT;
+    
+    /** configuration object for the connection */
+    private LdapConnectionConfig config = new LdapConnectionConfig();
+    
+    /** The connector open with the remote server */
+    private IoConnector connector;
+    
+    /** A flag set to true when we used a local connector */ 
+    private boolean localConnector;
+    
+    /** The Ldap codec */
+    private IoFilter ldapProtocolFilter = new ProtocolCodecFilter(
+            new LdapProtocolCodecFactory() );
+
+    /**  
+     * The created session, created when we open a connection with
+     * the Ldap server.
+     */
+    private IoSession ldapSession;
+    
+    /** A Message ID which is incremented for each operation */
+    private AtomicInteger messageId;
+    
+    /** A queue used to store the incoming add responses */
+    private BlockingQueue<AddResponse> addResponseQueue;
+    
+    /** A queue used to store the incoming bind responses */
+    private BlockingQueue<BindResponse> bindResponseQueue;
+    
+    /** A queue used to store the incoming compare responses */
+    private BlockingQueue<CompareResponse> compareResponseQueue;
+    
+    /** A queue used to store the incoming delete responses */
+    private BlockingQueue<DeleteResponse> deleteResponseQueue;
+    
+    /** A queue used to store the incoming extended responses */
+    private BlockingQueue<ExtendedResponse> extendedResponseQueue;
+    
+    /** A queue used to store the incoming modify responses */
+    private BlockingQueue<ModifyResponse> modifyResponseQueue;
+    
+    /** A queue used to store the incoming modifyDN responses */
+    private BlockingQueue<ModifyDnResponse> modifyDNResponseQueue;
+    
+    /** A queue used to store the incoming search responses */
+    private BlockingQueue<SearchResponse> searchResponseQueue;
+    
+    /** A queue used to store the incoming intermediate responses */
+    private BlockingQueue<IntermediateResponse> intermediateResponseQueue;
+
+    /** a map to hold the response listeners based on the operation id */
+    private Map<Integer, OperationResponseListener> listenerMap = new ConcurrentHashMap<Integer, OperationResponseListener>();
+    
+    /** a map to hold the ResponseFutures for all operations */
+    private Map<Integer, ResponseFuture> futureMap = new ConcurrentHashMap<Integer, ResponseFuture>();
+    
+    /** list of controls supported by the server */
+    private List<String> supportedControls;
+
+    private Entry rootDSE;
+
+
+    // ~~~~~~~~~~~~~~~~~ common error messages ~~~~~~~~~~~~~~~~~~~~~~~~~~
+    
+    private static final String OPERATION_CANCELLED = "Operation would have been cancelled";
+
+    private static final String TIME_OUT_ERROR = "TimeOut occured";
+
+    private static final String NO_RESPONSE_ERROR = "The response queue has been emptied, no response was found.";
+
+    private static final String COMPARE_FAILED = "Failed to perform compare operation";
+    
+    
+    //--------------------------- Helper methods ---------------------------//
+    /**
+     * Check if the connection is valid : created and connected
+     *
+     * @return <code>true</code> if the session is valid.
+     */
+    public boolean isSessionValid()
+    {
+        return ( ldapSession != null ) && ldapSession.isConnected();
+    }
+
+    
+    /**
+     * Check that a session is valid, ie we can send requests to the
+     * server
+     *
+     * @throws Exception If the session is not valid
+     */
+    private void checkSession() throws InvalidConnectionException
+    {
+        if ( ldapSession == null )
+        {
+            throw new InvalidConnectionException( "Cannot connect on the server, the connection is null" );
+        }
+        
+        if ( !isSessionValid() )
+        {
+            throw new InvalidConnectionException( "Cannot connect on the server, the connection is invalid" );
+        }
+    }
+    
+    /**
+     * Return the response stored into the current session.
+     *
+     * @return The last request response
+     */
+    public LdapMessageCodec getResponse()
+    {
+        return (LdapMessageCodec)ldapSession.getAttribute( LDAP_RESPONSE );
+    }
+
+    
+    /**
+     * Inject the client Controls into the message
+     */
+    private void setControls( Map<String, Control> controls, LdapMessageCodec message )
+    {
+        // Add the controls
+        if ( controls != null )
+        {
+            for ( Control control:controls.values() )
+            {
+                ControlCodec ctrl = new ControlCodec();
+                
+                ctrl.setControlType( control.getID() );
+                ctrl.setControlValue( control.getEncodedValue() );
+                
+                message.addControl( ctrl );
+            }
+        }
+    }
+    
+    
+    /**
+     * Get the smallest timeout from the client timeout and the connection
+     * timeout.
+     */
+    private long getTimeout( long clientTimeOut )
+    {
+        if ( clientTimeOut <= 0 )
+        {
+            return ( timeOut <= 0 ) ? Long.MAX_VALUE : timeOut;
+        }
+        else if ( timeOut <= 0 )
+        {
+            return clientTimeOut;
+        }
+        else
+        {
+            return timeOut < clientTimeOut ? timeOut : clientTimeOut; 
+        }
+    }
+    
+    
+    /**
+     * Convert a BindResponseCodec to a BindResponse message
+     */
+    private BindResponse convert( BindResponseCodec bindResponseCodec )
+    {
+        BindResponse bindResponse = new BindResponse();
+        
+        bindResponse.setMessageId( bindResponseCodec.getMessageId() );
+        bindResponse.setServerSaslCreds( bindResponseCodec.getServerSaslCreds() );
+        bindResponse.setLdapResult( convert( bindResponseCodec.getLdapResult() ) );
+
+        return bindResponse;
+    }
+
+
+    /**
+     * Convert a IntermediateResponseCodec to a IntermediateResponse message
+     */
+    private IntermediateResponse convert( IntermediateResponseCodec intermediateResponseCodec )
+    {
+        IntermediateResponse intermediateResponse = new IntermediateResponse();
+        
+        intermediateResponse.setMessageId( intermediateResponseCodec.getMessageId() );
+        intermediateResponse.setResponseName( intermediateResponseCodec.getResponseName() );
+        intermediateResponse.setResponseValue( intermediateResponseCodec.getResponseValue() );
+
+        return intermediateResponse;
+    }
+
+
+    /**
+     * Convert a LdapResultCodec to a LdapResult message
+     */
+    private LdapResult convert( LdapResultCodec ldapResultCodec )
+    {
+        LdapResult ldapResult = new LdapResult();
+        
+        ldapResult.setErrorMessage( ldapResultCodec.getErrorMessage() );
+        ldapResult.setMatchedDn( ldapResultCodec.getMatchedDN() );
+        
+        // Loop on the referrals
+        Referral referral = new Referral();
+        
+        if (ldapResultCodec.getReferrals() != null )
+        {
+            for ( LdapURL url:ldapResultCodec.getReferrals() )
+            {
+                referral.addLdapUrls( url );
+            }
+        }
+        
+        ldapResult.setReferral( referral );
+        ldapResult.setResultCode( ldapResultCodec.getResultCode() );
+
+        return ldapResult;
+    }
+
+
+    /**
+     * Convert a SearchResultEntryCodec to a SearchResultEntry message
+     */
+    private SearchResultEntry convert( SearchResultEntryCodec searchEntryResultCodec )
+    {
+        SearchResultEntry searchResultEntry = new SearchResultEntry();
+        
+        searchResultEntry.setMessageId( searchEntryResultCodec.getMessageId() );
+        searchResultEntry.setEntry( searchEntryResultCodec.getEntry() );
+        addControls( searchEntryResultCodec, searchResultEntry );
+
+        return searchResultEntry;
+    }
+
+
+    /**
+     * Convert a SearchResultDoneCodec to a SearchResultDone message
+     */
+    private SearchResultDone convert( SearchResultDoneCodec searchResultDoneCodec )
+    {
+        SearchResultDone searchResultDone = new SearchResultDone();
+        
+        searchResultDone.setMessageId( searchResultDoneCodec.getMessageId() );
+        searchResultDone.setLdapResult( convert( searchResultDoneCodec.getLdapResult() ) );
+        addControls( searchResultDoneCodec, searchResultDone );
+
+        return searchResultDone;
+    }
+
+
+    /**
+     * Convert a SearchResultReferenceCodec to a SearchResultReference message
+     */
+    private SearchResultReference convert( SearchResultReferenceCodec searchEntryReferenceCodec )
+    {
+        SearchResultReference searchResultReference = new SearchResultReference();
+        
+        searchResultReference.setMessageId( searchEntryReferenceCodec.getMessageId() );
+
+        // Loop on the referrals
+        Referral referral = new Referral();
+        
+        if (searchEntryReferenceCodec.getSearchResultReferences() != null )
+        {
+            for ( LdapURL url:searchEntryReferenceCodec.getSearchResultReferences() )
+            {
+                referral.addLdapUrls( url );
+            }
+        }
+        
+        searchResultReference.setReferral( referral );
+        addControls( searchEntryReferenceCodec, searchResultReference );
+        
+        return searchResultReference;
+    }
+
+
+    //------------------------- The constructors --------------------------//
+    /**
+     * Create a new instance of a LdapConnection on localhost,
+     * port 389.
+     */
+    public LdapConnection()
+    {
+        config.setUseSsl( false );
+        config.setLdapPort( config.getDefaultLdapPort() );
+        config.setLdapHost( config.getDefaultLdapHost() );
+        messageId = new AtomicInteger();
+    }
+    
+    
+    /**
+     * 
+     * Creates a new instance of LdapConnection with the given connection configuration.
+     *
+     * @param config the configuration of the LdapConnection
+     */
+    public LdapConnection( LdapConnectionConfig config )
+    {
+        this.config = config;
+        messageId = new AtomicInteger();
+    }
+    
+    
+    /**
+     * Create a new instance of a LdapConnection on localhost,
+     * port 389 if the SSL flag is off, or 636 otherwise.
+     * 
+     * @param useSsl A flag to tell if it's a SSL connection or not.
+     */
+    public LdapConnection( boolean useSsl )
+    {
+        config.setUseSsl( useSsl );
+        config.setLdapPort( useSsl ? config.getDefaultLdapsPort() : config.getDefaultLdapPort() );
+        config.setLdapHost( config.getDefaultLdapHost() );
+        messageId = new AtomicInteger();
+    }
+    
+    
+    /**
+     * Create a new instance of a LdapConnection on a given
+     * server, using the default port (389).
+     *
+     * @param server The server we want to be connected to
+     */
+    public LdapConnection( String server )
+    {
+        config.setUseSsl( false );
+        config.setLdapPort( config.getDefaultLdapPort() );
+        config.setLdapHost( server );
+        messageId = new AtomicInteger();
+    }
+    
+    
+    /**
+     * Create a new instance of a LdapConnection on a given
+     * server, using the default port (389) if the SSL flag 
+     * is off, or 636 otherwise.
+     *
+     * @param server The server we want to be connected to
+     * @param useSsl A flag to tell if it's a SSL connection or not.
+     */
+    public LdapConnection( String server, boolean useSsl )
+    {
+        config.setUseSsl( useSsl );
+        config.setLdapPort( useSsl ? config.getDefaultLdapsPort() : config.getDefaultLdapPort() );
+        config.setLdapHost( server );
+        messageId = new AtomicInteger();
+    }
+    
+    
+    /**
+     * Create a new instance of a LdapConnection on a 
+     * given server and a given port. We don't use ssl.
+     *
+     * @param server The server we want to be connected to
+     * @param port The port the server is listening to
+     */
+    public LdapConnection( String server, int port )
+    {
+        this( server, port, false );
+    }
+    
+    
+    /**
+     * Create a new instance of a LdapConnection on a given
+     * server, and a give port. We set the SSL flag accordingly
+     * to the last parameter.
+     *
+     * @param server The server we want to be connected to
+     * @param port The port the server is listening to
+     * @param useSsl A flag to tell if it's a SSL connection or not.
+     */
+    public LdapConnection( String server, int port, boolean useSsl )
+    {
+        config.setUseSsl( useSsl );
+        config.setLdapPort( port );
+        config.setLdapHost( server );
+        messageId = new AtomicInteger();
+    }
+
+    
+    //-------------------------- The methods ---------------------------//
+    /**
+     * Connect to the remote LDAP server.
+     *
+     * @return <code>true</code> if the connection is established, false otherwise
+     * @throws LdapException if some error has occured
+     */
+    public boolean connect() throws LdapException, IOException
+    {
+        if ( ( ldapSession != null ) && ldapSession.isConnected() ) 
+        {
+            // No need to connect if we already have a connected session
+            return true;
+        }
+
+        // Create the connector if needed
+        if ( connector == null ) 
+        {
+            connector = new NioSocketConnector();
+            localConnector = true;
+            
+            // Add the codec to the chain
+            connector.getFilterChain().addLast( "ldapCodec", ldapProtocolFilter );
+    
+            // If we use SSL, we have to add the SslFilter to the chain
+            if ( config.isUseSsl() ) 
+            {
+                try
+                {
+                    SSLContext sslContext = SSLContext.getInstance( config.getSslProtocol() );
+                    sslContext.init( config.getKeyManagers(), config.getTrustManagers(), config.getSecureRandom() );
+
+                    SslFilter sslFilter = new SslFilter( sslContext );
+                    sslFilter.setUseClientMode(true);
+                    connector.getFilterChain().addFirst( "sslFilter", sslFilter );
+                }
+                catch( Exception e )
+                {
+                    String msg = "Failed to initialize the SSL context";
+                    LOG.error( msg, e );
+                    throw new LdapException( msg, e );
+                }
+            }
+            
+            // Add an executor so that this connection can be used
+            // for handling more than one request (mainly because
+            // we may have to handle some abandon request)
+            connector.getFilterChain().addLast( "executor", 
+                new ExecutorFilter( 
+                    new UnorderedThreadPoolExecutor( 10 ),
+                    IoEventType.MESSAGE_RECEIVED ) );
+    
+            // Inject the protocolHandler
+            connector.setHandler( this );
+        }
+        
+        // Build the connection address
+        SocketAddress address = new InetSocketAddress( config.getLdapHost() , config.getLdapPort() );
+        
+        // And create the connection future
+        ConnectFuture connectionFuture = connector.connect( address );
+        
+        // Wait until it's established
+        connectionFuture.awaitUninterruptibly();
+        
+        if ( !connectionFuture.isConnected() ) 
+        {
+            // disposing connector if not connected
+            try
+            {
+                close();
+            }
+            catch ( IOException ioe )
+            {
+                // Nothing to do
+            }
+            
+            return false;
+        }
+        
+        // Get back the session
+        ldapSession = connectionFuture.getSession();
+        
+        // And inject the current Ldap container into the session
+        IAsn1Container ldapMessageContainer = new LdapMessageContainer();
+        
+        // Store the container into the session 
+        ldapSession.setAttribute( "LDAP-Container", ldapMessageContainer );
+        
+        // Create the responses queues
+        addResponseQueue = new LinkedBlockingQueue<AddResponse>();
+        bindResponseQueue = new LinkedBlockingQueue<BindResponse>();
+        compareResponseQueue = new LinkedBlockingQueue<CompareResponse>();
+        deleteResponseQueue = new LinkedBlockingQueue<DeleteResponse>();
+        extendedResponseQueue = new LinkedBlockingQueue<ExtendedResponse>();
+        modifyResponseQueue = new LinkedBlockingQueue<ModifyResponse>();
+        modifyDNResponseQueue = new LinkedBlockingQueue<ModifyDnResponse>();
+        searchResponseQueue = new LinkedBlockingQueue<SearchResponse>();
+        intermediateResponseQueue = new LinkedBlockingQueue<IntermediateResponse>();
+        
+        // And return
+        return true;
+    }
+    
+    
+    /**
+     * Disconnect from the remote LDAP server
+     *
+     * @return <code>true</code> if the connection is closed, false otherwise
+     * @throws IOException if some I/O error occurs
+     */
+    public boolean close() throws IOException 
+    {
+        // Close the session
+        if ( ( ldapSession != null ) && ldapSession.isConnected() )
+        {
+            ldapSession.close( true );
+        }
+        
+        // clean the queues
+        addResponseQueue.clear();
+        bindResponseQueue.clear();
+        compareResponseQueue.clear();
+        deleteResponseQueue.clear();
+        extendedResponseQueue.clear();
+        modifyResponseQueue.clear();
+        modifyDNResponseQueue.clear();
+        searchResponseQueue.clear();
+        intermediateResponseQueue.clear();
+        
+        // And close the connector if it has been created locally
+        if ( localConnector ) 
+        {
+            // Release the connector
+            connector.dispose();
+            connector = null;
+        }
+        
+        return true;
+    }
+    
+    
+    //------------------------ The LDAP operations ------------------------//
+    // Add operations                                                      //
+    //---------------------------------------------------------------------//
+    /**
+     * Add an entry to the server. This is a blocking add : the user has 
+     * to wait for the response until the AddResponse is returned.
+     * 
+     * @param entry The entry to add
+     * @result the add operation's response 
+     */
+    public AddResponse add( Entry entry ) throws LdapException
+    {
+        if ( entry == null ) 
+        {
+            String msg = "Cannot add empty entry";
+            LOG.debug( msg );
+            throw new NullPointerException( msg );
+        }
+        
+        return add( new AddRequest( entry ), null );
+    }
+    
+    
+    /**
+     * Add an entry present in the AddRequest to the server.
+     * @param addRequest the request object containing an entry and controls(if any)
+     * @return the add operation's response
+     * @throws LdapException
+     */
+    public AddResponse add( AddRequest addRequest ) throws LdapException
+    {
+        return add( addRequest, null );
+    }
+    
+    /**
+     * Add an entry present in the AddRequest to the server.
+     * @param addRequest the request object containing an entry and controls(if any)
+     * @param listener A listener used for an asynchronous add operation
+     * @return the add operation's response, null if non-null listener is provided
+     * @throws LdapException
+     */
+    public AddResponse add( AddRequest addRequest, AddListener listener ) throws LdapException
+    {
+        checkSession();
+
+        AddRequestCodec addReqCodec = new AddRequestCodec();
+        
+        int newId = messageId.incrementAndGet();
+        LdapMessageCodec message = new LdapMessageCodec();
+        message.setMessageId( newId );
+        addReqCodec.setMessageId( newId );
+
+        addReqCodec.setEntry( addRequest.getEntry() );
+        addReqCodec.setEntryDn( addRequest.getEntry().getDn() );
+        setControls( addRequest.getControls(), addReqCodec );
+        
+        message.setProtocolOP( addReqCodec );
+        
+        ResponseFuture addFuture = new ResponseFuture( addResponseQueue );
+        futureMap.put( newId, addFuture );
+
+        // Send the request to the server
+        ldapSession.write( message );
+        
+        AddResponse response = null;
+        if( listener == null )
+        {
+            try
+            {
+                long timeout = getTimeout( addRequest.getTimeout() );
+                response = ( AddResponse ) addFuture.get( timeout, TimeUnit.MILLISECONDS );
+                
+                if ( response == null )
+                {
+                    LOG.error( "Add failed : timeout occured" );
+                    throw new LdapException( TIME_OUT_ERROR );
+                }
+            }
+            catch( InterruptedException ie )
+            {
+                LOG.error( OPERATION_CANCELLED, ie );
+                throw new LdapException( OPERATION_CANCELLED, ie );
+            }
+            catch( Exception e )
+            {
+                LOG.error( NO_RESPONSE_ERROR );
+                futureMap.remove( newId );
+                throw new LdapException( NO_RESPONSE_ERROR, e );
+            }
+        }
+        else
+        {
+            listenerMap.put( newId, listener );            
+        }
+
+        return response;
+    }
+
+    
+    /**
+     * converts the AddResponseCodec to AddResponse.
+     */
+    private AddResponse convert( AddResponseCodec addRespCodec )
+    {
+        AddResponse addResponse = new AddResponse();
+        
+        addResponse.setMessageId( addRespCodec.getMessageId() );
+        addResponse.setLdapResult( convert( addRespCodec.getLdapResult() ) );
+        
+        return addResponse;
+    }
+
+    
+    //------------------------ The LDAP operations ------------------------//
+
+    /**
+     * Abandons a request submitted to the server for performing a particular operation
+     * 
+     * The abandonRequest is always non-blocking, because no response is expected
+     * 
+     * @param messageId the ID of the request message sent to the server 
+     */
+    public void abandon( int messageId )
+    {
+        AbandonRequest abandonRequest = new AbandonRequest();
+        abandonRequest.setAbandonedMessageId( messageId );
+        
+        abandonInternal( abandonRequest );
+    }
+
+    
+    /**
+     * An abandon request essentially with the request message ID of the operation to be cancelled
+     * and/or potentially some controls and timeout (the controls and timeout are not mandatory).
+     * 
+     * The abandonRequest is always non-blocking, because no response is expected
+     *  
+     * @param abandonRequest the abandon operation's request
+     */
+    public void abandon( AbandonRequest abandonRequest )
+    {
+        abandonInternal( abandonRequest );
+    }
+    
+    
+    /**
+     * Internal AbandonRequest handling
+     */
+    private void abandonInternal( AbandonRequest abandonRequest )
+    {
+        // Create the new message and update the messageId
+        LdapMessageCodec message = new LdapMessageCodec();
+
+        // Creates the messageID and stores it into the 
+        // initial message and the transmitted message.
+        int newId = messageId.incrementAndGet();
+        abandonRequest.setMessageId( newId );
+        message.setMessageId( newId );
+
+        // Create the inner abandonRequest
+        AbandonRequestCodec request = new AbandonRequestCodec();
+        
+        // Inject the data into the request
+        request.setAbandonedMessageId( abandonRequest.getAbandonedMessageId() );
+        
+        // Inject the request into the message
+        message.setProtocolOP( request );
+        
+        // Inject the controls
+        setControls( abandonRequest.getControls(), message );
+        
+        LOG.debug( "-----------------------------------------------------------------" );
+        LOG.debug( "Sending request \n{}", message );
+
+        // Send the request to the server
+        ldapSession.write( message );
+
+        // remove the associated listener if any 
+        int abandonId = abandonRequest.getAbandonedMessageId();
+
+        ResponseFuture rf = futureMap.remove( abandonId );
+        OperationResponseListener listener = listenerMap.remove( abandonId );
+
+        // if the listener is not null, this is a async operation and no need to
+        // send cancel signal on future, sending so will leave a dangling poision object in the corresponding queue
+        if( listener != null )
+        {
+            LOG.debug( "removed the listener associated with the abandoned operation with id {}", abandonId );
+        }
+        else // this is a sync operation send cancel signal to the corresponding ResponseFuture
+        {
+            if( rf != null )
+            {
+                LOG.debug( "sending cancel signal to future" );
+                rf.cancel( true );
+            }
+            else
+            {
+                // this shouldn't happen
+                LOG.error( "There is no future asscoiated with operation message ID {}, perhaps the operation would have been completed", abandonId );
+            }
+        }
+    }
+    
+    
+    /**
+     * Anonymous Bind on a server. 
+     *
+     * @return The BindResponse LdapResponse 
+     */
+    public BindResponse bind() throws LdapException, IOException
+    {
+        LOG.debug( "Anonymous Bind request" );
+
+        return bind( StringTools.EMPTY, StringTools.EMPTY_BYTES );
+    }
+    
+    
+    /**
+     * An Unauthenticated Authentication Bind on a server. (cf RFC 4513,
+     * par 5.1.2)
+     *
+     * @param name The name we use to authenticate the user. It must be a 
+     * valid DN
+     * @return The BindResponse LdapResponse 
+     */
+    public BindResponse bind( String name ) throws Exception
+    {
+        LOG.debug( "Bind request : {}", name );
+
+        return bind( name, StringTools.EMPTY_BYTES );
+    }
+
+    
+    /**
+     * An Unauthenticated Authentication Bind on a server. (cf RFC 4513,
+     * par 5.1.2)
+     *
+     * @param name The name we use to authenticate the user.
+     * @return The BindResponse LdapResponse 
+     */
+    public BindResponse bind( LdapDN name ) throws Exception
+    {
+        if ( name == null )
+        {
+            LOG.debug( "Anonymous Bind request : {}", name );
+
+            return bind( StringTools.EMPTY, StringTools.EMPTY_BYTES );
+        }
+        else
+        {
+            LOG.debug( "Unauthenticated Bind request : {}", name );
+
+            return bind( name.getName(), StringTools.EMPTY_BYTES );
+        }
+    }
+
+    
+    /**
+     * Simple Bind on a server.
+     *
+     * @param name The name we use to authenticate the user. It must be a 
+     * valid DN
+     * @param credentials The password. It can't be null 
+     * @return The BindResponse LdapResponse 
+     */
+    public BindResponse bind( String name, String credentials ) throws LdapException, IOException
+    {
+        LOG.debug( "Bind request : {}", name );
+
+        return bind( name, StringTools.getBytesUtf8( credentials ) );
+    }
+
+
+    /**
+     * Simple Bind on a server.
+     *
+     * @param name The name we use to authenticate the user. It must be a 
+     * valid DN
+     * @param credentials The password. It can't be null 
+     * @return The BindResponse LdapResponse 
+     */
+    public BindResponse bind( LdapDN name, String credentials ) throws LdapException, IOException
+    {
+        LOG.debug( "Bind request : {}", name );
+
+        if ( name == null )
+        {
+            return bind( StringTools.EMPTY, StringTools.getBytesUtf8( credentials ) );
+        }
+        else
+        {
+            return bind( name.getName(), StringTools.getBytesUtf8( credentials ) );
+        }
+    }
+
+    
+    /**
+     * Simple Bind on a server.
+     *
+     * @param name The name we use to authenticate the user.
+     * @param credentials The password.
+     * @return The BindResponse LdapResponse 
+     */
+    public BindResponse bind( LdapDN name, byte[] credentials )  throws LdapException, IOException
+    {
+        LOG.debug( "Bind request : {}", name );
+
+        return bind( name.getName(), credentials );
+    }
+
+    
+    /**
+     * Simple Bind on a server.
+     *
+     * @param name The name we use to authenticate the user. It must be a 
+     * valid DN
+     * @param credentials The password.
+     * @return The BindResponse LdapResponse 
+     */
+    public BindResponse bind( String name, byte[] credentials )  throws LdapException, IOException
+    {
+        LOG.debug( "Bind request : {}", name );
+
+        // Create the BindRequest
+        BindRequest bindRequest = new BindRequest();
+        bindRequest.setName( name );
+        bindRequest.setCredentials( credentials );
+        
+        BindResponse response = bind( bindRequest );
+
+        if ( LOG.isDebugEnabled() )
+        {
+            if ( response.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
+            {
+                LOG.debug( " Bind successfull" );
+            }
+            else
+            {
+                LOG.debug( " Bind failure {}", response );
+            }
+        }
+        
+        return response;
+    }
+    
+    
+    /**
+     * Bind to the server using a BindRequest object.
+     *
+     * @param bindRequest The BindRequest POJO containing all the needed
+     * parameters 
+     * @return A LdapResponse containing the result
+     */
+    public BindResponse bind( BindRequest bindRequest ) throws LdapException, IOException
+    {
+        ResponseFuture responseFuture = bind( bindRequest, null );
+        
+        // Get the result from the future
+        try
+        {
+            // Read the response, waiting for it if not available immediately
+            long timeout = getTimeout( bindRequest.getTimeout() );
+            
+            // Get the response, blocking
+            BindResponse bindResponse = (BindResponse)responseFuture.get( timeout, TimeUnit.MILLISECONDS );
+
+            // Everything is fine, return the response
+            LOG.debug( "Bind successful : {}", bindResponse );
+            
+            return bindResponse;
+        }
+        catch ( TimeoutException te )
+        {
+            // Send an abandon request
+            if( !responseFuture.isCancelled() )
+            {
+                abandon( bindRequest.getMessageId() );
+            }
+            
+            // We didn't received anything : this is an error
+            LOG.error( "Bind failed : timeout occured" );
+            throw new LdapException( TIME_OUT_ERROR );
+        }
+        catch ( Exception ie )
+        {
+            // Catch all other exceptions
+            LOG.error( NO_RESPONSE_ERROR, ie );
+            LdapException ldapException = new LdapException( NO_RESPONSE_ERROR );
+            ldapException.initCause( ie );
+            
+            // Send an abandon request
+            if( !responseFuture.isCancelled() )
+            {
+                abandon( bindRequest.getMessageId() );
+            }
+            
+            throw ldapException;
+        }
+    }
+        
+
+    /**
+     * Do a non-blocking bind non-blocking
+     *
+     * @param bindRequest The BindRequest to send
+     * @param listener The listener 
+     * @return ResponseFuture A future
+     */
+    public ResponseFuture bind( BindRequest bindRequest, BindListener bindListener ) throws LdapException, IOException 
+    {
+        // First try to connect, if we aren't already connected.
+        connect();
+        
+        // If the session has not been establish, or is closed, we get out immediately
+        checkSession();
+
+        // Create the new message and update the messageId
+        LdapMessageCodec bindMessage = new LdapMessageCodec();
+
+        // Creates the messageID and stores it into the 
+        // initial message and the transmitted message.
+        // As it's a Bind request, qe reset the MessageId 
+        // value to zero.
+        messageId.set( 0 );
+        int newId = messageId.incrementAndGet();
+        bindRequest.setMessageId( newId );
+        bindMessage.setMessageId( newId );
+        
+        if( bindListener != null )
+        {
+            // This is an asynchronous bind
+            listenerMap.put( newId, bindListener );
+        }
+        
+        // Create a new codec BindRequest object
+        BindRequestCodec request =  new BindRequestCodec();
+        
+        // Set the version
+        request.setVersion( LdapConnectionConfig.LDAP_V3 );
+        
+        // Set the name
+        try
+        {
+            LdapDN dn = new LdapDN( bindRequest.getName() );
+            request.setName( dn );
+        }
+        catch ( InvalidNameException ine )
+        {
+            String msg = "The given dn '" + bindRequest.getName() + "' is not valid";
+            LOG.error( msg );
+            LdapException ldapException = new LdapException( msg );
+            ldapException.initCause( ine );
+
+            throw ldapException;
+        }
+        
+        // Set the credentials
+        LdapAuthentication authentication = null;
+        
+        if ( bindRequest.isSimple() )
+        {
+            // Simple bind
+            authentication = new SimpleAuthentication();
+            ((SimpleAuthentication)authentication).setSimple( bindRequest.getCredentials() );
+        }
+        else
+        {
+            // SASL bind
+            authentication = new SaslCredentials();
+            ((SaslCredentials)authentication).setCredentials( bindRequest.getCredentials() );
+            ((SaslCredentials)authentication).setMechanism( bindRequest.getSaslMechanism() );
+        }
+        
+        // The authentication
+        request.setAuthentication( authentication );
+        
+        // Stores the BindRequest into the message
+        bindMessage.setProtocolOP( request );
+        
+        // Add the controls
+        setControls( bindRequest.getControls(), bindMessage );
+
+        LOG.debug( "-----------------------------------------------------------------" );
+        LOG.debug( "Sending request \n{}", bindMessage );
+
+        // Create a future for this Bind opeation
+        ResponseFuture responseFuture = new ResponseFuture( bindResponseQueue );
+        futureMap.put( newId, responseFuture );
+
+        if ( bindListener != null )
+        {
+            // If this is an asynchronous operation, associate the ID
+            // with the operation listener
+            listenerMap.put( newId, bindListener );
+        }
+
+        // Send the request to the server
+        ldapSession.write( bindMessage );
+        
+        return responseFuture;
+    }
+    
+    
+    /**
+     * Do a search, on the base object, using the given filter. The
+     * SearchRequest parameters default to :
+     * Scope : ONE
+     * DerefAlias : ALWAYS
+     * SizeLimit : none
+     * TimeLimit : none
+     * TypesOnly : false
+     * Attributes : all the user's attributes.
+     * This method is blocking.
+     * 
+     * @param baseDn The base for the search. It must be a valid
+     * DN, and can't be emtpy
+     * @param filterString The filter to use for this search. It can't be empty
+     * @param scope The sarch scope : OBJECT, ONELEVEL or SUBTREE 
+     * @return A cursor on the result. 
+     */
+    public Cursor<SearchResponse> search( String baseDn, String filter, SearchScope scope, 
+        String... attributes ) throws LdapException
+    {
+        // Create a new SearchRequest object
+        SearchRequest searchRequest = new SearchRequest();
+        
+        searchRequest.setBaseDn( baseDn );
+        searchRequest.setFilter( filter );
+        searchRequest.setScope( scope );
+        searchRequest.addAttributes( attributes );
+        searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS );
+
+        // Process the request in blocking mode
+        return searchInternal( searchRequest, null );
+    }
+    
+    
+    /**
+     * Do a search, on the base object, using the given filter. The
+     * SearchRequest parameters default to :
+     * Scope : ONE
+     * DerefAlias : ALWAYS
+     * SizeLimit : none
+     * TimeLimit : none
+     * TypesOnly : false
+     * Attributes : all the user's attributes.
+     * This method is blocking.
+     * 
+     * @param listener a SearchListener used to be informed when a result 
+     * has been found, or when the search is done
+     * @param baseObject The base for the search. It must be a valid
+     * DN, and can't be emtpy
+     * @param filter The filter to use for this search. It can't be empty
+     * @return A cursor on the result. 
+     */
+    public void search( SearchRequest searchRequest, SearchListener listener ) throws LdapException
+    {
+        searchInternal( searchRequest, listener );
+    }
+    
+    
+    /**
+     * performs search in a synchronous mode (as if a null search listener is passed) 
+     * @see #search(SearchRequest, SearchListener)
+     */
+    public Cursor<SearchResponse> search( SearchRequest searchRequest ) throws LdapException
+    {
+        return searchInternal( searchRequest, null );
+    }
+
+    
+    private Cursor<SearchResponse> searchInternal( SearchRequest searchRequest, SearchListener searchListener )
+        throws LdapException
+    {
+        // If the session has not been establish, or is closed, we get out immediately
+        checkSession();
+    
+        // Create the new message and update the messageId
+        LdapMessageCodec searchMessage = new LdapMessageCodec();
+        
+        // Creates the messageID and stores it into the 
+        // initial message and the transmitted message.
+        int newId = messageId.incrementAndGet();
+        searchRequest.setMessageId( newId );
+        searchMessage.setMessageId( newId );
+        
+        // Create a new codec SearchRequest object
+        SearchRequestCodec request =  new SearchRequestCodec();
+        
+        // Set the name
+        try
+        {
+            LdapDN dn = new LdapDN( searchRequest.getBaseDn() );
+            request.setBaseObject( dn );
+        }
+        catch ( InvalidNameException ine )
+        {
+            String msg = "The given dn '" + searchRequest.getBaseDn() + "' is not valid";
+            LOG.error( msg );
+            LdapException ldapException = new LdapException( msg );
+            ldapException.initCause( ine );
+
+            throw ldapException;
+        }
+        
+        // Set the scope
+        request.setScope( searchRequest.getScope() );
+        
+        // Set the typesOnly flag
+        request.setDerefAliases( searchRequest.getDerefAliases().getValue() );
+        
+        // Set the timeLimit
+        request.setTimeLimit( searchRequest.getTimeLimit() );
+        
+        // Set the sizeLimit
+        request.setSizeLimit( searchRequest.getSizeLimit() );
+        
+        // Set the typesOnly flag
+        request.setTypesOnly( searchRequest.getTypesOnly() );
+    
+        // Set the filter
+        Filter filter = null;
+        
+        try
+        {
+            ExprNode filterNode = FilterParser.parse( searchRequest.getFilter() );
+            
+            filter = TwixTransformer.transformFilter( filterNode );
+        }
+        catch ( ParseException pe )
+        {
+            String msg = "The given filter '" + searchRequest.getFilter() + "' is not valid";
+            LOG.error( msg );
+            LdapException ldapException = new LdapException( msg );
+            ldapException.initCause( pe );
+
+            throw ldapException;
+        }
+        
+        request.setFilter( filter );
+        
+        // Set the attributes
+        Set<String> attributes = searchRequest.getAttributes();
+        
+        if ( attributes != null )
+        {
+            for ( String attribute:attributes )
+            {
+                request.addAttribute( attribute );
+            }
+        }
+        
+        // Stores the SearchRequest into the message
+        searchMessage.setProtocolOP( request );
+        
+        // Add the controls
+        setControls( searchRequest.getControls(), searchMessage );
+    
+        LOG.debug( "-----------------------------------------------------------------" );
+        LOG.debug( "Sending request \n{}", searchMessage );
+    
+        ResponseFuture searchFuture = new ResponseFuture( searchResponseQueue );
+        futureMap.put( newId, searchFuture );
+
+        // Send the request to the server
+        ldapSession.write( searchMessage );
+    
+        
+        if ( searchListener == null )
+        {
+            // Read the response, waiting for it if not available immediately
+            try
+            {
+                long timeout = getTimeout( searchRequest.getTimeout() );
+                SearchResponse response = null;
+                List<SearchResponse> searchResponses = new ArrayList<SearchResponse>();
+
+                // We may have more than one response, so loop on the queue
+                do 
+                {
+                    response = ( SearchResponse ) searchFuture.get( timeout, TimeUnit.MILLISECONDS );
+
+                    // Check that we didn't get out because of a timeout
+                    if ( response == null )
+                    {
+                        // Send an abandon request
+                        abandon( searchMessage.getSearchRequest().getMessageId() );
+                        
+                        // We didn't received anything : this is an error
+                        LOG.error( "Search failed : timeout occured" );
+
+                        throw new LdapException( TIME_OUT_ERROR );
+                    }
+                    else
+                    {
+                        if ( response instanceof SearchResultEntry )
+                        {
+                            searchResponses.add( response );
+                        }
+                        else if ( response instanceof SearchResultReference )
+                        {
+                            searchResponses.add( response );
+                        }
+                    }
+                }
+                while ( !( response instanceof SearchResultDone ) );
+
+                LOG.debug( "Search successful, {} elements found", searchResponses.size() );
+                
+                return new ListCursor<SearchResponse>( searchResponses );
+            }
+            catch( InterruptedException ie )
+            {
+                LOG.error( OPERATION_CANCELLED, ie );
+                throw new LdapException( OPERATION_CANCELLED, ie );
+            }
+            catch ( Exception e )
+            {
+                LOG.error( NO_RESPONSE_ERROR, e );
+                LdapException ldapException = new LdapException( NO_RESPONSE_ERROR );
+                ldapException.initCause( e );
+                
+                // Send an abandon request
+                if( !searchFuture.isCancelled() )
+                {
+                    abandon( searchMessage.getBindRequest().getMessageId() );
+                }
+                
+                throw ldapException;
+            }
+        }
+        else
+        {
+            // The listener will be called on a MessageReceived event,
+            // no need to create a cursor
+            listenerMap.put( newId, searchListener );
+            return null;
+        }
+    }
+
+    
+    //------------------------ The LDAP operations ------------------------//
+    // Unbind operations                                                   //
+    //---------------------------------------------------------------------//
+    /**
+     * UnBind from a server. this is a request which expect no response.
+     */
+    public void unBind() throws Exception
+    {
+        // If the session has not been establish, or is closed, we get out immediately
+        checkSession();
+        
+        // Create the UnbindRequest
+        UnBindRequestCodec unbindRequest = new UnBindRequestCodec();
+        
+        // Encode the request
+        LdapMessageCodec unbindMessage = new LdapMessageCodec();
+
+        // Creates the messageID and stores it into the 
+        // initial message and the transmitted message.
+        int newId = messageId.incrementAndGet();
+        unbindRequest.setMessageId( newId );
+        unbindMessage.setMessageId( newId );
+
+        unbindMessage.setProtocolOP( unbindRequest );
+        
+        LOG.debug( "-----------------------------------------------------------------" );
+        LOG.debug( "Sending Unbind request \n{}", unbindMessage );
+        
+        // Send the request to the server
+        WriteFuture unbindFuture = ldapSession.write( unbindMessage );
+        
+        unbindFuture.awaitUninterruptibly();
+        
+        //  We now have to close the connection
+        //close();
+
+        // And get out
+        LOG.debug( "Unbind successful" );
+    }
+    
+    
+    /**
+     * Set the connector to use.
+     *
+     * @param connector The connector to use
+     */
+    public void setConnector( IoConnector connector )
+    {
+        this.connector = connector;
+    }
+
+
+    /**
+     * Set the timeOut for the responses. We wont wait longer than this 
+     * value.
+     *
+     * @param timeOut The timeout, in milliseconds
+     */
+    public void setTimeOut( long timeOut )
+    {
+        this.timeOut = timeOut;
+    }
+    
+    
+    /**
+     * Handle the incoming LDAP messages. This is where we feed the cursor for search 
+     * requests, or call the listener. 
+     */
+    public void messageReceived( IoSession session, Object message) throws Exception 
+    {
+        // Feed the response and store it into the session
+        LdapMessageCodec response = (LdapMessageCodec)message;
+
+        LOG.debug( "-------> {} Message received <-------", response.getMessageTypeName() );
+        
+        // this check is necessary to prevent adding an abandoned operation's
+        // result(s) to corresponding queue
+        ResponseFuture rf = futureMap.get( response.getMessageId() );
+        
+        if( rf == null )
+        {
+            LOG.error( "There is no future associated with the messageId {}, ignoring the message", response.getMessageId()  );
+            return;
+        }
+        
+        SearchListener searchListener = null;
+
+        switch ( response.getMessageType() )
+        {
+            case LdapConstants.ADD_RESPONSE :
+                // Store the response into the responseQueue
+                AddResponseCodec addRespCodec = response.getAddResponse();
+                addRespCodec.addControl( response.getCurrentControl() );
+                addRespCodec.setMessageId( response.getMessageId() );
+                
+                futureMap.remove( addRespCodec.getMessageId() );
+                AddListener addListener = ( AddListener ) listenerMap.remove( addRespCodec.getMessageId() );
+                AddResponse addResp = convert( addRespCodec );
+                if( addListener != null )
+                {
+                    addListener.entryAdded( this, addResp );
+                }
+                else
+                {
+                    addResponseQueue.add( addResp ); 
+                }
+                break;
+                
+            case LdapConstants.BIND_RESPONSE: 
+                // Store the response into the responseQueue
+                BindResponseCodec bindResponseCodec = response.getBindResponse();
+                bindResponseCodec.setMessageId( response.getMessageId() );
+                bindResponseCodec.addControl( response.getCurrentControl() );
+                BindResponse bindResponse = convert( bindResponseCodec );
+
+                futureMap.remove( bindResponseCodec.getMessageId() );
+                // remove the listener from the listener map
+                BindListener bindListener = ( BindListener ) listenerMap.remove( bindResponseCodec.getMessageId() );
+                
+                if ( bindListener != null )
+                {
+                    bindListener.bindCompleted( this, bindResponse );
+                }
+                else
+                {
+                    // Store the response into the responseQueue
+                    bindResponseQueue.add( bindResponse );
+                }
+                
+                break;
+                
+            case LdapConstants.COMPARE_RESPONSE :
+                // Store the response into the responseQueue
+                CompareResponseCodec compResCodec = response.getCompareResponse();
+                compResCodec.setMessageId( response.getMessageId() );
+                compResCodec.addControl( response.getCurrentControl() );
+                CompareResponse compRes = convert( compResCodec );
+                
+                futureMap.remove( compRes.getMessageId() );
+                
+                CompareListener listener = ( CompareListener ) listenerMap.remove( compRes.getMessageId() );
+                if( listener != null )
+                {
+                    listener.attributeCompared( this, compRes );
+                }
+                else
+                {
+                    compareResponseQueue.add( compRes ); 
+                }
+                
+                break;
+                
+            case LdapConstants.DEL_RESPONSE :
+                // Store the response into the responseQueue
+                DelResponseCodec delRespCodec = response.getDelResponse();
+                delRespCodec.setMessageId( response.getMessageId() );
+                delRespCodec.addControl( response.getCurrentControl() );
+                DeleteResponse delResp = convert( delRespCodec );
+                
+                futureMap.remove( delResp.getMessageId() );
+                DeleteListener delListener = ( DeleteListener ) listenerMap.remove( delResp.getMessageId() );
+                if( delListener != null )
+                {
+                    delListener.entryDeleted( this, delResp );
+                }
+                else
+                {
+                    deleteResponseQueue.add( delResp ); 
+                }
+                break;
+                
+            case LdapConstants.EXTENDED_RESPONSE :
+                ExtendedResponseCodec extResCodec = response.getExtendedResponse();
+                extResCodec.setMessageId( response.getMessageId() );
+                extResCodec.addControl( response.getCurrentControl() );
+                
+                ExtendedResponse extResponse = convert( extResCodec );
+                ExtendedListener extListener = ( ExtendedListener ) listenerMap.remove( extResCodec.getMessageId() );
+                if( extListener != null )
+                {
+                    extListener.extendedOperationCompleted( this, extResponse );
+                }
+                else
+                {
+                    // Store the response into the responseQueue
+                    extendedResponseQueue.add( extResponse ); 
+                }
+                
+                break;
+                
+            case LdapConstants.INTERMEDIATE_RESPONSE:
+                // Store the response into the responseQueue
+                IntermediateResponseCodec intermediateResponseCodec = 
+                    response.getIntermediateResponse();
+                intermediateResponseCodec.setMessageId( response.getMessageId() );
+                intermediateResponseCodec.addControl( response.getCurrentControl() );
+                
+                IntermediateResponse intrmResp = convert( intermediateResponseCodec );                
+                IntermediateResponseListener intrmListener = ( IntermediateResponseListener ) listenerMap.get( intermediateResponseCodec.getMessageId() ); 
+                if ( intrmListener != null )
+                {
+                    intrmListener.responseReceived( this, intrmResp );
+                }
+                else
+                {
+                    // Store the response into the responseQueue
+                    intermediateResponseQueue.add( intrmResp );
+                }
+                
+                break;
+     
+            case LdapConstants.MODIFY_RESPONSE :
+                ModifyResponseCodec modRespCodec = response.getModifyResponse();
+                modRespCodec.setMessageId( response.getMessageId() );
+                modRespCodec.addControl( response.getCurrentControl() );
+                ModifyResponse modResp = convert( modRespCodec );
+
+                futureMap.remove( modResp.getMessageId() );
+                ModifyListener modListener = ( ModifyListener ) listenerMap.remove( modResp.getMessageId() );
+                
+                if( modListener != null )
+                {
+                    modListener.modifyCompleted( this, modResp );
+                }
+                else
+                {
+                    modifyResponseQueue.add( modResp ); 
+                }
+                break;
+                
+            case LdapConstants.MODIFYDN_RESPONSE :
+                
+                ModifyDNResponseCodec modDnCodec = response.getModifyDNResponse();
+                modDnCodec.addControl( response.getCurrentControl() );
+                modDnCodec.setMessageId( response.getMessageId() );
+                ModifyDnResponse modDnResp = convert( modDnCodec );
+                
+                futureMap.remove( modDnCodec.getMessageId() );
+                ModifyDnListener modDnListener = ( ModifyDnListener ) listenerMap.remove( modDnCodec.getMessageId() );
+                if( modDnListener != null )
+                {
+                    modDnListener.modifyDnCompleted( this, modDnResp );
+                }
+                else
+                {
+                    // Store the response into the responseQueue
+                    modifyDNResponseQueue.add( modDnResp );
+                }
+                break;
+                
+            case LdapConstants.SEARCH_RESULT_DONE:
+                // Store the response into the responseQueue
+                SearchResultDoneCodec searchResultDoneCodec = 
+                    response.getSearchResultDone();
+                searchResultDoneCodec.setMessageId( response.getMessageId() );
+                searchResultDoneCodec.addControl( response.getCurrentControl() );
+                SearchResultDone srchDone = convert( searchResultDoneCodec );
+                
+                futureMap.remove( searchResultDoneCodec.getMessageId() );
+                // search listener has to be removed from listener map only here
+                searchListener = ( SearchListener ) listenerMap.remove( searchResultDoneCodec.getMessageId() );
+                if ( searchListener != null )
+                {
+                    searchListener.searchDone( this, srchDone );
+                }
+                else
+                {
+                    searchResponseQueue.add( srchDone );
+                }
+                
+                break;
+            
+            case LdapConstants.SEARCH_RESULT_ENTRY:
+                // Store the response into the responseQueue
+                SearchResultEntryCodec searchResultEntryCodec = 
+                    response.getSearchResultEntry();
+                searchResultEntryCodec.setMessageId( response.getMessageId() );
+                searchResultEntryCodec.addControl( response.getCurrentControl() );
+                
+                SearchResultEntry srchEntry = convert( searchResultEntryCodec );
+                searchListener = ( SearchListener ) listenerMap.get( searchResultEntryCodec.getMessageId() );
+                
+                if ( searchListener != null )
+                {
+                    searchListener.entryFound( this, srchEntry );
+                }
+                else
+                {
+                    searchResponseQueue.add( srchEntry );
+                }
+                
+                break;
+                       
+            case LdapConstants.SEARCH_RESULT_REFERENCE:
+                // Store the response into the responseQueue
+                SearchResultReferenceCodec searchResultReferenceCodec = 
+                    response.getSearchResultReference();
+                searchResultReferenceCodec.setMessageId( response.getMessageId() );
+                searchResultReferenceCodec.addControl( response.getCurrentControl() );
+
+                SearchResultReference srchRef = convert( searchResultReferenceCodec );
+                searchListener = ( SearchListener ) listenerMap.get( searchResultReferenceCodec.getMessageId() );
+                if ( searchListener != null )
+                {
+                    searchListener.referralFound( this, srchRef );
+                }
+                else
+                {
+                    searchResponseQueue.add( srchRef );
+                }
+
+                break;
+                       
+             default: LOG.error( "~~~~~~~~~~~~~~~~~~~~~ Unknown message type {} ~~~~~~~~~~~~~~~~~~~~~", response.getMessageTypeName() );
+        }
+    }
+    
+    
+    /**
+     * 
+     * modifies all the attributes present in the entry by applying the same operation.
+     *
+     * @param entry the entry whise attributes to be modified
+     * @param modOp the operation to be applied on all the attributes of the above entry
+     * @return the modify operation's response
+     * @throws LdapException in case of modify operation failure or timeout happens
+     */
+    public ModifyResponse modify( Entry entry, ModificationOperation modOp ) throws LdapException
+    {
+        if( entry == null )
+        {
+            LOG.debug( "received a null entry for modification" );
+            throw new NullPointerException( "Entry to be modified cannot be null" );
+        }
+        
+        ModifyRequest modReq = new ModifyRequest( entry.getDn() );
+        
+        Iterator<EntryAttribute> itr = entry.iterator();
+        while( itr.hasNext() )
+        {
+            modReq.addModification( itr.next(), modOp );
+        }
+        
+        return modify( modReq, null );
+    }
+    
+    
+    /**
+     * 
+     * performs modify operation based on the modifications present in the ModifyRequest.
+     *
+     * @param modRequest the request for modify operation
+     * @param listener callback listener which will be called after the operation is completed
+     * @return the modify operation's response, null if non-null listener is provided
+     * @throws LdapException in case of modify operation failure or timeout happens
+     */
+    public ModifyResponse modify( ModifyRequest modRequest, ModifyListener listener )  throws LdapException
+    {
+        checkSession();
+    
+        LdapMessageCodec modifyMessage = new LdapMessageCodec();
+        
+        int newId = messageId.incrementAndGet();
+        modRequest.setMessageId( newId );
+        modifyMessage.setMessageId( newId );
+        
+        ModifyRequestCodec modReqCodec = new ModifyRequestCodec();
+        modReqCodec.setModifications( modRequest.getMods() );
+        modReqCodec.setObject( modRequest.getDn() );
+
+        modifyMessage.setProtocolOP( modReqCodec );
+        setControls( modRequest.getControls(), modifyMessage );
+
+        ResponseFuture modifyFuture = new ResponseFuture( modifyResponseQueue );
+        futureMap.put( newId, modifyFuture );
+        
+        ldapSession.write( modifyMessage );
+        
+        ModifyResponse response = null;
+        if( listener == null )
+        {
+            try
+            {
+                long timeout = getTimeout( modRequest.getTimeout() );
+                response = ( ModifyResponse ) modifyFuture.get( timeout, TimeUnit.MILLISECONDS );
+                
+                if ( response == null )
+                {
+                    LOG.error( "Modify failed : timeout occured" );
+
+                    throw new LdapException( TIME_OUT_ERROR );
+                }
+            }
+            catch( InterruptedException ie )
+            {
+                LOG.error( OPERATION_CANCELLED, ie );
+                throw new LdapException( OPERATION_CANCELLED, ie );
+            }
+            catch( Exception e )
+            {
+                LOG.error( NO_RESPONSE_ERROR );
+                futureMap.remove( newId );
+
+                throw new LdapException( NO_RESPONSE_ERROR, e );
+            }
+        }
+        else
+        {
+            listenerMap.put( newId, listener );
+        }
+        
+        return response;
+    }
+    
+    
+    /**
+     * converts the ModifyResponseCodec to ModifyResponse.
+     */
+    private ModifyResponse convert( ModifyResponseCodec modRespCodec )
+    {
+        ModifyResponse modResponse = new ModifyResponse();
+        
+        modResponse.setMessageId( modRespCodec.getMessageId() );
+        modResponse.setLdapResult( convert( modRespCodec.getLdapResult() ) );
+
+        return modResponse;
+    }
+
+
+    /**
+     * renames the given entryDn with new Rdn and deletes the old RDN.
+     * @see #rename(String, String, boolean)
+     */
+    public ModifyDnResponse rename( String entryDn, String newRdn ) throws LdapException
+    {
+        return rename( entryDn, newRdn, true );
+    }
+
+    
+    /**
+     * renames the given entryDn with new RDN and deletes the old RDN.
+     * @see #rename(LdapDN, RDN, boolean)
+     */
+    public ModifyDnResponse rename( LdapDN entryDn, RDN newRdn ) throws LdapException
+    {
+        return rename( entryDn, newRdn, true );
+    }
+    
+    
+    /**
+     * @see #rename(LdapDN, RDN, boolean)
+     */
+    public ModifyDnResponse rename( String entryDn, String newRdn, boolean deleteOldRdn ) throws LdapException
+    {
+        try
+        {
+            return rename( new LdapDN( entryDn ), new RDN( newRdn ), deleteOldRdn );
+        }
+        catch( InvalidNameException e )
+        {
+            LOG.error( e.getMessage(), e );
+            throw new LdapException( e.getMessage(), e );
+        }
+    }
+    
+    
+    /**
+     * 
+     * renames the given entryDn with new RDN and deletes the old Rdn if 
+     * deleteOldRdn is set to true.
+     *
+     * @param entryDn the target DN
+     * @param newRdn new Rdn for the target DN
+     * @param deleteOldRdn flag to indicate whether to delete the old Rdn
+     * @return modifyDn operations response
+     * @throws LdapException
+     */
+    public ModifyDnResponse rename( LdapDN entryDn, RDN newRdn, boolean deleteOldRdn ) throws LdapException
+    {
+        ModifyDnRequest modDnRequest = new ModifyDnRequest();
+        modDnRequest.setEntryDn( entryDn );
+        modDnRequest.setNewRdn( newRdn );
+        modDnRequest.setDeleteOldRdn( deleteOldRdn );
+        
+        return modifyDn( modDnRequest, null );
+    }
+    
+
+    /**
+     * @see #move(LdapDN, LdapDN) 
+     */
+    public ModifyDnResponse move( String entryDn, String newSuperiorDn ) throws LdapException
+    {
+        try
+        {
+            return move( new LdapDN( entryDn ), new LdapDN( newSuperiorDn ) );
+        }
+        catch( InvalidNameException e )
+        {
+            LOG.error( e.getMessage(), e );
+            throw new LdapException( e.getMessage(), e );
+        }
+    }
+    
+
+    /**
+     * moves the given entry DN under the new superior DN
+     *
+     * @param entryDn the DN of the target entry
+     * @param newSuperiorDn DN of the new parent/superior
+     * @return modifyDn operations response
+     * @throws LdapException
+     */
+    public ModifyDnResponse move( LdapDN entryDn, LdapDN newSuperiorDn ) throws LdapException
+    {
+        ModifyDnRequest modDnRequest = new ModifyDnRequest();
+        modDnRequest.setEntryDn( entryDn );
+        modDnRequest.setNewSuperior( newSuperiorDn );
+        
+        //TODO not setting the below value is resulting in error
+        modDnRequest.setNewRdn( entryDn.getRdn() );
+        
+        return modifyDn( modDnRequest, null );
+    }
+
+    
+    /**
+     * 
+     * performs the modifyDn operation based on the given ModifyDnRequest.
+     *
+     * @param modDnRequest the request
+     * @param listener callback listener which will be called after the operation is completed
+     * @return modifyDn operations response, null if non-null listener is provided
+     * @throws LdapException
+     */
+    public ModifyDnResponse modifyDn( ModifyDnRequest modDnRequest, ModifyDnListener listener ) throws LdapException
+    {
+        checkSession();
+    
+        LdapMessageCodec modifyDnMessage = new LdapMessageCodec();
+        
+        int newId = messageId.incrementAndGet();
+        modDnRequest.setMessageId( newId );
+        modifyDnMessage.setMessageId( newId );
+
+        ModifyDNRequestCodec modDnCodec = new ModifyDNRequestCodec();
+        modDnCodec.setEntry( modDnRequest.getEntryDn() );
+        modDnCodec.setNewRDN( modDnRequest.getNewRdn() );
+        modDnCodec.setDeleteOldRDN( modDnRequest.isDeleteOldRdn() );
+        modDnCodec.setNewSuperior( modDnRequest.getNewSuperior() );
+        
+        modifyDnMessage.setProtocolOP( modDnCodec );
+        setControls( modDnRequest.getControls(), modifyDnMessage );
+        
+        ResponseFuture modifyDNFuture = new ResponseFuture( modifyDNResponseQueue );
+        futureMap.put( newId, modifyDNFuture );
+        
+        ldapSession.write( modifyDnMessage );
+        
+        if( listener == null )
+        {
+            ModifyDnResponse response = null;
+            try
+            {
+                long timeout = getTimeout( modDnRequest.getTimeout() );
+                response = ( ModifyDnResponse ) modifyDNFuture.get( timeout, TimeUnit.MILLISECONDS );
+                
+                if ( response == null )
+                {
+                    LOG.error( "Modifying DN failed : timeout occured" );
+
+                    throw new LdapException( TIME_OUT_ERROR );
+                }
+            }
+            catch( InterruptedException ie )
+            {
+                LOG.error( OPERATION_CANCELLED, ie );
+                throw new LdapException( OPERATION_CANCELLED, ie );
+            }
+            catch( Exception e )
+            {
+                LOG.error( NO_RESPONSE_ERROR );
+                futureMap.remove( newId );
+
+                LdapException ldapException = new LdapException( NO_RESPONSE_ERROR );
+                ldapException.initCause( e );
+                throw ldapException;
+            }
+            
+            return response;
+        }
+        else
+        {
+            listenerMap.put( newId, listener );
+            return null;
+        }
+    }
+    
+    
+    /**
+     * converts the ModifyDnResponseCodec to ModifyResponse.
+     */
+    private ModifyDnResponse convert( ModifyDNResponseCodec modDnRespCodec )
+    {
+        ModifyDnResponse modDnResponse = new ModifyDnResponse();
+        
+        modDnResponse.setMessageId( modDnRespCodec.getMessageId() );
+        modDnResponse.setLdapResult( convert( modDnRespCodec.getLdapResult() ) );
+        
+        return modDnResponse;
+    }
+
+
+    /**
+     * deletes the entry with the given DN
+     *  
+     * @param dn the target entry's DN as a String
+     * @throws LdapException If the DN is not valid or if the deletion failed
+     */
+    public DeleteResponse delete( String dn ) throws LdapException
+    {
+        try
+        {
+            DeleteRequest deleteRequest = new DeleteRequest( new LdapDN( dn ) );
+
+            return delete( deleteRequest, null );
+        }
+        catch( InvalidNameException e )
+        {
+            LOG.error( e.getMessage(), e );
+            throw new LdapException( e.getMessage(), e );
+        }
+    }
+
+    
+    /**
+     * deletes the entry with the given DN
+     *  
+     * @param dn the target entry's DN
+     * @throws LdapException If the DN is not valid or if the deletion failed
+     */
+    public DeleteResponse delete( LdapDN dn ) throws LdapException
+    {
+        DeleteRequest deleteRequest = new DeleteRequest( dn );
+        
+        return delete( deleteRequest, null ); 
+    }
+
+
+    /**
+     * deletes the entry with the given DN, and all its children
+     *  
+     * @param dn the target entry's DN
+     * @return operation's response
+     * @throws LdapException If the DN is not valid or if the deletion failed
+     */
+    public DeleteResponse deleteTree( LdapDN dn ) throws LdapException
+    {
+        String treeDeleteOid = "1.2.840.113556.1.4.805";
+        
+        if ( isControlSupported( treeDeleteOid ) ) 
+        {
+            DeleteRequest delRequest = new DeleteRequest( dn );
+            delRequest.add( new BasicControl( treeDeleteOid ) );
+            return delete( delRequest, null ); 
+        }
+        else
+        {
+            return deleteRecursive( dn, null, null );
+        }
+    }
+
+    
+    /**
+     * deletes the entry with the given DN, and all its children
+     *  
+     * @param dn the target entry's DN as a String
+     * @return operation's response
+     * @throws LdapException If the DN is not valid or if the deletion failed
+     */
+    public DeleteResponse deleteTree( String dn ) throws LdapException
+    {
+        try
+        {
+            String treeDeleteOid = "1.2.840.113556.1.4.805";
+            LdapDN ldapDn = new LdapDN( dn );
+            
+            if ( isControlSupported( treeDeleteOid ) ) 
+            {
+                DeleteRequest delRequest = new DeleteRequest( ldapDn );
+                delRequest.add( new BasicControl( treeDeleteOid ) );
+                return delete( delRequest, null ); 
+            }
+            else
+            {
+                return deleteRecursive( ldapDn, null, null );
+            }
+        }
+        catch( InvalidNameException e )
+        {
+            LOG.error( e.getMessage(), e );
+            throw new LdapException( e.getMessage(), e );
+        }
+    }
+    
+
+    /**
+     * removes all child entries present under the given DN and finally the DN itself
+     * 
+     * Working:
+     *          This is a recursive function which maintains a Map<LdapDN,Cursor>.
+     *          The way the cascade delete works is by checking for children for a 
+     *          given DN(i.e opening a search cursor) and if the cursor is empty
+     *          then delete the DN else for each entry's DN present in cursor call
+     *          deleteChildren() with the DN and the reference to the map.
+     *          
+     *          The reason for opening a search cursor is based on an assumption
+     *          that an entry *might* contain children, consider the below DIT fragment
+     *          
+     *          parent
+     *          /     \
+     *        child1   child2
+     *                 /     \
+     *               grand21  grand22
+     *               
+     *           The below method works better in the case where the tree depth is >1 
+     *          
+     *   In the case of passing a non-null DeleteListener, the return value will always be null, cause the
+     *   operation is treated as asynchronous and response result will be sent using the listener callback
+     *   
+     *  //FIXME provide another method for optimizing delete operation for a tree with depth <=1
+     *          
+     * @param dn the DN which will be removed after removing its children
+     * @param map a map to hold the Cursor related to a DN
+     * @param listener  the delete operation response listener 
+     * @throws LdapException If the DN is not valid or if the deletion failed
+     */
+    private DeleteResponse deleteRecursive( LdapDN dn, Map<LdapDN, Cursor<SearchResponse>> cursorMap, DeleteListener listener ) throws LdapException
+    {
+        LOG.debug( "searching for {}", dn.getName() );
+        DeleteResponse delResponse = null;
+        Cursor<SearchResponse> cursor = null;
+        
+        try
+        {
+            if ( cursorMap == null )
+            {
+                cursorMap = new HashMap<LdapDN, Cursor<SearchResponse>>();
+            }
+
+            cursor = cursorMap.get( dn ); 
+            
+            if( cursor == null )
+            {
+                cursor = search( dn.getName(), "(objectClass=*)", SearchScope.ONELEVEL, (String[])null ); 
+                LOG.debug( "putting curosr for {}", dn.getName() );
+                cursorMap.put( dn, cursor );
+            }
+            
+            if( ! cursor.next() ) // if this is a leaf entry's DN
+            {
+                LOG.debug( "deleting {}", dn.getName() );
+                cursorMap.remove( dn );
+                cursor.close();
+                delResponse = delete( new DeleteRequest( dn ), listener );
+            }
+            else
+            {
+                do
+                {
+                    SearchResponse searchResp = cursor.get();
+                    
+                    if( searchResp instanceof SearchResultEntry )
+                    {
+                        SearchResultEntry searchResult = ( SearchResultEntry ) searchResp;
+                        deleteRecursive( searchResult.getEntry().getDn(), cursorMap, listener );
+                    }
+                }
+                while( cursor.next() );
+                
+                cursorMap.remove( dn );
+                cursor.close();
+                LOG.debug( "deleting {}", dn.getName() );
+                delResponse = delete( new DeleteRequest( dn ), listener );
+            }
+        }
+        catch( Exception e )
+        {
+            String msg = "Failed to delete child entries under the DN " + dn.getName();
+            LOG.error( msg, e );
+            throw new LdapException( msg, e );
+        }
+        
+        return delResponse;
+    }
+    
+    
+    /**
+     * Performs a synchronous delete operation based on the delete request object.
+     *  
+     * @param delRequest the delete operation's request
+     * @return delete operation's response, null if a non-null listener value is provided
+     * @throws LdapException If the DN is not valid or if the deletion failed
+     */
+    public DeleteResponse delete( DeleteRequest delRequest )  throws LdapException
+    {
+        // Just call the delete method with a null listener
+        return delete( delRequest, null );
+    }
+
+
+    /**
+     * Performs a delete operation based on the delete request object.
+     *  
+     * @param delRequest the delete operation's request
+     * @param listener the delete operation response listener
+     * @return delete operation's response, null if a non-null listener value is provided
+     * @throws LdapException If the DN is not valid or if the deletion failed
+     */
+    public DeleteResponse delete( DeleteRequest delRequest, DeleteListener listener )  throws LdapException
+    {
+        checkSession();
+    
+        LdapMessageCodec deleteMessage = new LdapMessageCodec();
+        
+        int newId = messageId.incrementAndGet();
+        delRequest.setMessageId( newId );
+        deleteMessage.setMessageId( newId );
+
+        DelRequestCodec delCodec = new DelRequestCodec();
+        delCodec.setEntry( delRequest.getTargetDn() );
+
+        deleteMessage.setProtocolOP( delCodec );
+        setControls( delRequest.getControls(), deleteMessage );
+        
+        ResponseFuture deleteFuture = new ResponseFuture( deleteResponseQueue );
+        futureMap.put( newId, deleteFuture );
+
+        ldapSession.write( deleteMessage );
+        
+        DeleteResponse response = null;
+        
+        if( listener == null )
+        {
+            try
+            {
+                long timeout = getTimeout( delRequest.getTimeout() );
+                response = ( DeleteResponse ) deleteFuture.get( timeout, TimeUnit.MILLISECONDS );
+                
+                if ( response == null )
+                {
+                    LOG.error( "Delete DN failed : timeout occured" );
+
+                    throw new LdapException( TIME_OUT_ERROR );
+                }
+            }
+            catch( InterruptedException ie )
+            {
+                LOG.error( OPERATION_CANCELLED, ie );
+                throw new LdapException( OPERATION_CANCELLED, ie );
+            }
+            catch( Exception e )
+            {
+                LOG.error( NO_RESPONSE_ERROR );
+                futureMap.remove( newId );
+
+                LdapException ldapException = new LdapException( NO_RESPONSE_ERROR );
+                ldapException.initCause( e );
+                throw ldapException;
+            }
+        }
+        else
+        {
+            listenerMap.put( newId, listener );
+        }
+
+        return response;
+    }
+
+
+    /**
+     * Compares a whether a given attribute's value matches that of the 
+     * existing value of the attribute present in the entry with the given DN
+     *
+     * @param dn the target entry's String DN
+     * @param attributeName the attribute's name
+     * @param value a String value with which the target entry's attribute value to be compared with
+     * @return compare operation's response
+     * @throws LdapException
+     */
+    public CompareResponse compare( String dn, String attributeName, String value ) throws LdapException
+    {
+        try
+        {
+            CompareRequest compareRequest = new CompareRequest();
+            compareRequest.setEntryDn( new LdapDN( dn ) );
+            compareRequest.setAttrName( attributeName );
+            compareRequest.setValue( value );
+            
+            return compare( compareRequest, null );
+        }
+        catch( Exception e )
+        {
+            LOG.error( COMPARE_FAILED, e );
+            throw new LdapException( COMPARE_FAILED, e );
+        }
+    }
+
+
+    /**
+     * Compares a whether a given attribute's value matches that of the 
+     * existing value of the attribute present in the entry with the given DN
+     *
+     * @param dn the target entry's String DN
+     * @param attributeName the attribute's name
+     * @param value a byte[] value with which the target entry's attribute value to be compared with
+     * @return compare operation's response
+     * @throws LdapException
+     */
+    public CompareResponse compare( String dn, String attributeName, byte[] value ) throws LdapException
+    {
+        try
+        {
+            CompareRequest compareRequest = new CompareRequest();
+            compareRequest.setEntryDn( new LdapDN( dn ) );
+            compareRequest.setAttrName( attributeName );
+            compareRequest.setValue( value );
+            
+            return compare( compareRequest, null );
+        }
+        catch( Exception e )
+        {
+            LOG.error( COMPARE_FAILED, e );
+            throw new LdapException( COMPARE_FAILED, e );
+        }
+    }
+
+
+    /**
+     * Compares a whether a given attribute's value matches that of the 
+     * existing value of the attribute present in the entry with the given DN
+     *
+     * @param dn the target entry's String DN
+     * @param attributeName the attribute's name
+     * @param value a Value<?> value with which the target entry's attribute value to be compared with
+     * @return compare operation's response
+     * @throws LdapException
+     */

[... 460 lines stripped ...]


Mime
View raw message