| /* |
| * 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.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.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; |
| |
| /** |
| * |
| * Describe the methods to be implemented by the LdapConnection class. |
| * |
| * @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() ); |
| |
| 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() ) ); |
| |
| 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 ); |
| |
| 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 |
| */ |
| private boolean connect() throws LdapException |
| { |
| if ( ( ldapSession != null ) && ldapSession.isConnected() ) |
| { |
| 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 ); |
| } |
| } |
| |
| 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 |
| connector.dispose(); |
| 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(); |
| } |
| |
| 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 |
| { |
| 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 |
| { |
| 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 |
| { |
| 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 |
| { |
| 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 |
| { |
| LOG.debug( "Bind request : {}", name ); |
| |
| // Create the BindRequest |
| BindRequest bindRequest = new BindRequest(); |
| bindRequest.setName( name ); |
| bindRequest.setCredentials( credentials ); |
| |
| BindResponse response = bind( bindRequest, null ); |
| |
| 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 |
| { |
| return bind( bindRequest, null ); |
| } |
| |
| |
| /** |
| * Do a non-blocking bind non-blocking |
| * |
| * @param bindRequest The BindRequest to send |
| * @param listener The listener |
| */ |
| public BindResponse bind( BindRequest bindRequest, BindListener bindListener ) throws LdapException |
| { |
| // 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. |
| int newId = messageId.incrementAndGet(); |
| bindRequest.setMessageId( newId ); |
| bindMessage.setMessageId( newId ); |
| |
| if( bindListener != null ) |
| { |
| 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 ); |
| |
| ResponseFuture responseFuture = new ResponseFuture( bindResponseQueue ); |
| futureMap.put( newId, responseFuture ); |
| |
| // Send the request to the server |
| ldapSession.write( bindMessage ); |
| |
| if ( bindListener == null ) |
| { |
| // And get the result |
| 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; |
| } |
| } |
| else |
| { |
| // Return null. |
| listenerMap.put( newId, bindListener ); |
| return 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 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 |
| { |
| // 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 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 |
| ldapSession.write( unbindMessage ); |
| |
| // 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 |
| */ |
| public CompareResponse compare( String dn, String attributeName, Value<?> 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 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( LdapDN dn, String attributeName, String value ) throws LdapException |
| { |
| CompareRequest compareRequest = new CompareRequest(); |
| compareRequest.setEntryDn( dn ); |
| compareRequest.setAttrName( attributeName ); |
| compareRequest.setValue( value ); |
| |
| return compare( compareRequest, null ); |
| } |
| |
| |
| /** |
| * 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 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( LdapDN dn, String attributeName, byte[] value ) throws LdapException |
| { |
| CompareRequest compareRequest = new CompareRequest(); |
| compareRequest.setEntryDn( dn ); |
| compareRequest.setAttrName( attributeName ); |
| compareRequest.setValue( value ); |
| |
| return compare( compareRequest, null ); |
| } |
| |
| |
| /** |
| * 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 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 |
| */ |
| public CompareResponse compare( LdapDN dn, String attributeName, Value<?> value ) throws LdapException |
| { |
| CompareRequest compareRequest = new CompareRequest(); |
| compareRequest.setEntryDn( dn ); |
| compareRequest.setAttrName( attributeName ); |
| compareRequest.setValue( value.get() ); |
| |
| return compare( compareRequest, null ); |
| } |
| |
| |
| /** |
| * compares an entry's attribute's value with that of the given value |
| * |
| * @param compareRequest the CompareRequest which contains the target DN, attribute name and value |
| * @return compare operation's response |
| * @throws LdapException |
| */ |
| public CompareResponse compare( CompareRequest compareRequest ) throws LdapException |
| { |
| return compare( compareRequest, null ); |
| } |
| |
| |
| /** |
| * compares an entry's attribute's value with that of the given value |
| * |
| * @param compareRequest the CompareRequest which contains the target DN, attribute name and value |
| * @param listener a non-null listener if provided will be called to receive this operation's response asynchronously |
| * @return compare operation's response |
| * @throws LdapException |
| */ |
| public CompareResponse compare( CompareRequest compareRequest, CompareListener listener ) throws LdapException |
| { |
| checkSession(); |
| |
| CompareRequestCodec compareReqCodec = new CompareRequestCodec(); |
| |
| int newId = messageId.incrementAndGet(); |
| LdapMessageCodec message = new LdapMessageCodec(); |
| message.setMessageId( newId ); |
| compareReqCodec.setMessageId( newId ); |
| |
| compareReqCodec.setEntry( compareRequest.getEntryDn() ); |
| compareReqCodec.setAttributeDesc( compareRequest.getAttrName() ); |
| compareReqCodec.setAssertionValue( compareRequest.getValue() ); |
| setControls( compareRequest.getControls(), compareReqCodec ); |
| |
| message.setProtocolOP( compareReqCodec ); |
| |
| ResponseFuture compareFuture = new ResponseFuture( compareResponseQueue ); |
| futureMap.put( newId, compareFuture ); |
| |
| // Send the request to the server |
| ldapSession.write( message ); |
| |
| CompareResponse response = null; |
| if( listener == null ) |
| { |
| try |
| { |
| long timeout = getTimeout( compareRequest.getTimeout() ); |
| response = ( CompareResponse ) compareFuture.get( timeout, TimeUnit.MILLISECONDS ); |
| |
| if ( response == null ) |
| { |
| LOG.error( "Compare 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 CompareResponseCodec to CompareResponse. |
| */ |
| private CompareResponse convert( CompareResponseCodec compareRespCodec ) |
| { |
| CompareResponse compareResponse = new CompareResponse(); |
| |
| compareResponse.setMessageId( compareRespCodec.getMessageId() ); |
| compareResponse.setLdapResult( convert( compareRespCodec.getLdapResult() ) ); |
| |
| return compareResponse; |
| } |
| |
| |
| /** |
| * converts the DeleteResponseCodec to DeleteResponse object. |
| */ |
| private DeleteResponse convert( DelResponseCodec delRespCodec ) |
| { |
| DeleteResponse response = new DeleteResponse(); |
| |
| response.setMessageId( delRespCodec.getMessageId() ); |
| response.setLdapResult( convert( delRespCodec.getLdapResult() ) ); |
| |
| return response; |
| } |
| |
| |
| /** |
| * @see #extended(OID, byte[]) |
| */ |
| public ExtendedResponse extended( String oid ) throws LdapException |
| { |
| return extended( oid, null ); |
| } |
| |
| |
| /** |
| * @see #extended(OID, byte[]) |
| */ |
| public ExtendedResponse extended( String oid, byte[] value ) throws LdapException |
| { |
| try |
| { |
| return extended( new OID( oid ), value ); |
| } |
| catch( DecoderException e ) |
| { |
| String msg = "Failed to decode the OID " + oid; |
| LOG.error( msg ); |
| throw new LdapException( msg, e ); |
| } |
| } |
| |
| |
| /** |
| * @see #extended(OID, byte[]) |
| */ |
| public ExtendedResponse extended( OID oid ) throws LdapException |
| { |
| return extended( oid, null ); |
| } |
| |
| |
| /** |
| * sends a extended operation request to the server with the given OID and value |
| * |
| * @param oid the object identifier of the extended operation |
| * @param value value to be used by the extended operation, can be a null value |
| * @return extended operation's response |
| * @throws LdapException |
| */ |
| public ExtendedResponse extended( OID oid, byte[] value ) throws LdapException |
| { |
| ExtendedRequest extRequest = new ExtendedRequest( oid ); |
| extRequest.setValue( value ); |
| |
| return extended( extRequest ); |
| } |
| |
| |
| /** |
| * @see #extended(ExtendedRequest, ExtendedListener) |
| */ |
| public ExtendedResponse extended( ExtendedRequest extendedRequest ) throws LdapException |
| { |
| return extended( extendedRequest, null ); |
| } |
| |
| |
| /** |
| * requests the server to perform an extended operation based on the given request. |
| * |
| * @param extendedRequest the object containing the details of the extended operation to be performed |
| * @param listener an ExtendedListener instance, if a non-null instance is provided the result will be sent to this listener |
| * @return extended operation's reponse, or null if a non-null listener instance is provided |
| * @throws LdapException |
| */ |
| public ExtendedResponse extended( ExtendedRequest extendedRequest, ExtendedListener listener ) throws LdapException |
| { |
| checkSession(); |
| |
| ExtendedRequestCodec extReqCodec = new ExtendedRequestCodec(); |
| |
| int newId = messageId.incrementAndGet(); |
| LdapMessageCodec message = new LdapMessageCodec(); |
| message.setMessageId( newId ); |
| extReqCodec.setMessageId( newId ); |
| |
| extReqCodec.setRequestName( extendedRequest.getOid() ); |
| extReqCodec.setRequestValue( extendedRequest.getValue() ); |
| setControls( extendedRequest.getControls(), extReqCodec ); |
| |
| message.setProtocolOP( extReqCodec ); |
| |
| ResponseFuture extendedFuture = new ResponseFuture( extendedResponseQueue ); |
| futureMap.put( newId, extendedFuture ); |
| |
| // Send the request to the server |
| ldapSession.write( message ); |
| |
| ExtendedResponse response = null; |
| if( listener == null ) |
| { |
| try |
| { |
| long timeout = getTimeout( extendedRequest.getTimeout() ); |
| response = ( ExtendedResponse ) extendedFuture.get( timeout, TimeUnit.MILLISECONDS ); |
| |
| if ( response == null ) |
| { |
| LOG.error( "Extended operation 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 ExtendedResponseCodec to ExtendedResponse. |
| */ |
| private ExtendedResponse convert( ExtendedResponseCodec extRespCodec ) |
| { |
| ExtendedResponse extResponse = new ExtendedResponse(); |
| |
| OID oid = null; |
| try |
| { |
| if( extRespCodec.getResponseName() != null ) |
| { |
| oid = new OID( extRespCodec.getResponseName() ); |
| } |
| } |
| catch( DecoderException e ) |
| { |
| // can happen in case of a PROTOCOL_ERROR result, ignore |
| //LOG.error( "invalid response name {}", extRespCodec.getResponseName() ); |
| } |
| |
| extResponse.setOid( oid ); |
| extResponse.setValue( extRespCodec.getResponse() ); |
| extResponse.setMessageId( extRespCodec.getMessageId() ); |
| extResponse.setLdapResult( convert( extRespCodec.getLdapResult() ) ); |
| |
| return extResponse; |
| } |
| |
| |
| /** |
| * checks if a control with the given OID is supported |
| * |
| * @param controlOID the OID of the control |
| * @return true if the control is supported, false otherwise |
| */ |
| public boolean isControlSupported( String controlOID ) throws LdapException |
| { |
| return getSupportedConrols().contains( controlOID ); |
| } |
| |
| |
| /** |
| * get the Conrols supported by server. |
| * |
| * @return a list of control OIDs supported by server |
| * @throws LdapException |
| */ |
| public List<String> getSupportedConrols() throws LdapException |
| { |
| if( supportedControls != null ) |
| { |
| return supportedControls; |
| } |
| |
| if( rootDSE == null ) |
| { |
| fetchRootDSE(); |
| } |
| |
| supportedControls = new ArrayList<String>(); |
| |
| EntryAttribute attr = rootDSE.get( SchemaConstants.SUPPORTED_CONTROL_AT ); |
| Iterator<Value<?>> itr = attr.getAll(); |
| |
| while( itr.hasNext() ) |
| { |
| supportedControls.add( ( String ) itr.next().get() ); |
| } |
| |
| return supportedControls; |
| } |
| |
| |
| /** |
| * fetches the rootDSE from the server |
| * @throws LdapException |
| */ |
| private void fetchRootDSE() throws LdapException |
| { |
| Cursor<SearchResponse> cursor = null; |
| try |
| { |
| cursor = search( "", "(objectClass=*)", SearchScope.OBJECT, "*", "+" ); |
| cursor.next(); |
| SearchResultEntry searchRes = ( SearchResultEntry ) cursor.get(); |
| |
| rootDSE = searchRes.getEntry(); |
| } |
| catch( Exception e ) |
| { |
| String msg = "Failed to fetch the RootDSE"; |
| LOG.error( msg ); |
| throw new LdapException( msg, e ); |
| } |
| finally |
| { |
| if( cursor != null ) |
| { |
| try |
| { |
| cursor.close(); |
| } |
| catch( Exception e ) |
| { |
| LOG.error( "Failed to close open cursor", e ); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * gives the configuration information of the connection |
| * |
| * @return the configuration of the connection |
| */ |
| public LdapConnectionConfig getConfig() |
| { |
| return config; |
| } |
| |
| } |