You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ka...@apache.org on 2009/06/10 19:03:09 UTC
svn commit: r783424 -
/directory/shared/trunk/client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java
Author: kayyagari
Date: Wed Jun 10 17:03:09 2009
New Revision: 783424
URL: http://svn.apache.org/viewvc?rev=783424&view=rev
Log:
o implemented delete operation(with support for deleting the children)
o fetching of rootDSE and supported controls
o fixed an issue by setting the alias dereferencing option to 'never' in the simple search method
o fixed a classcast exception and timeout issue in the searchmethod
o removed stacktrace printing
Modified:
directory/shared/trunk/client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java
Modified: directory/shared/trunk/client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java?rev=783424&r1=783423&r2=783424&view=diff
==============================================================================
--- directory/shared/trunk/client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java (original)
+++ directory/shared/trunk/client-api/src/main/java/org/apache/directory/shared/ldap/client/api/LdapConnection.java Wed Jun 10 17:03:09 2009
@@ -24,6 +24,7 @@
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;
@@ -41,10 +42,10 @@
import javax.net.ssl.SSLContext;
import org.apache.directory.shared.asn1.ber.IAsn1Container;
-import org.apache.directory.shared.ldap.NotImplementedException;
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.BindListener;
+import org.apache.directory.shared.ldap.client.api.listeners.DeleteListener;
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.OperationResponseListener;
@@ -55,6 +56,8 @@
import org.apache.directory.shared.ldap.client.api.messages.BindRequestImpl;
import org.apache.directory.shared.ldap.client.api.messages.BindResponse;
import org.apache.directory.shared.ldap.client.api.messages.BindResponseImpl;
+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.IntermediateResponse;
import org.apache.directory.shared.ldap.client.api.messages.IntermediateResponseImpl;
import org.apache.directory.shared.ldap.client.api.messages.LdapResult;
@@ -88,6 +91,8 @@
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.del.DelRequestCodec;
+import org.apache.directory.shared.ldap.codec.del.DelResponseCodec;
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;
@@ -99,15 +104,19 @@
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.message.control.CascadeControl;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.name.Rdn;
import org.apache.directory.shared.ldap.util.LdapURL;
@@ -222,6 +231,11 @@
/** a map to hold the response listeners based on the operation id */
private Map<Integer, OperationResponseListener> listenerMap = new ConcurrentHashMap<Integer, OperationResponseListener>();
+ /** list of controls supported by the server */
+ private List<String> supportedControls;
+
+ private Entry rootDSE;
+
//--------------------------- Helper methods ---------------------------//
/**
* Check if the connection is valid : created and connected
@@ -1072,7 +1086,8 @@
searchRequest.setFilter( filter );
searchRequest.setScope( scope );
searchRequest.addAttributes( attributes );
-
+ searchRequest.setDerefAliases( AliasDerefMode.NEVER_DEREF_ALIASES );
+
// Process the request in blocking mode
return searchInternal( searchRequest, null );
}
@@ -1224,7 +1239,7 @@
if ( response == null )
{
// Send an abandon request
- abandon( searchMessage.getBindRequest().getMessageId() );
+ abandon( searchMessage.getSearchRequest().getMessageId() );
// We didn't received anything : this is an error
LOG.error( "Bind failed : timeout occured" );
@@ -1243,7 +1258,7 @@
}
}
}
- while ( !( response instanceof SearchResultDone ) );
+ while ( !( response instanceof SearchResultDoneCodec ) );
// Release the session lock
unlockSession();
@@ -1746,7 +1761,6 @@
unlockSession();
LdapException ldapException = new LdapException();
ldapException.initCause( e );
- e.printStackTrace();
throw ldapException;
}
@@ -1775,4 +1789,304 @@
return modDnResponse;
}
+
+ /**
+ * @see #delete(LdapDN)
+ */
+ public DeleteResponse delete( String dn ) throws LdapException
+ {
+ try
+ {
+ return delete( new LdapDN( dn ) );
+ }
+ 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
+ */
+ public DeleteResponse delete( LdapDN dn ) throws LdapException
+ {
+ return delete( dn, false );
+ }
+
+
+ /**
+ * deletes the entry with the given DN and all its children
+ *
+ * @param dn the target entry DN
+ * @param deleteAllChildren flag to indicate whether to delete the children
+ * @return delete operation's response
+ * @throws LdapException
+ */
+ public DeleteResponse delete( LdapDN dn, boolean deleteAllChildren ) throws LdapException
+ {
+ DeleteRequest delRequest = new DeleteRequest( dn );
+
+ if( deleteAllChildren )
+ {
+ if( isControlSupported( CascadeControl.CONTROL_OID ) )
+ {
+ delRequest.add( new CascadeControl() );
+ }
+ else
+ {
+ return deleteChildren( dn, new HashMap() );
+ }
+ }
+
+ return delete( delRequest, null );
+ }
+
+
+ /**
+ * 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
+ *
+ * //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
+ * @throws LdapException
+ */
+ private DeleteResponse deleteChildren( LdapDN dn, Map<LdapDN, Cursor<SearchResponse>> cursorMap ) throws LdapException
+ {
+ LOG.debug( "searching for {}", dn.getUpName() );
+ 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.getUpName(), "(objectClass=*)", SearchScope.ONELEVEL, SchemaConstants.ENTRY_UUID_AT );
+ LOG.debug( "putting curosr for {}", dn.getUpName() );
+ cursorMap.put( dn, cursor );
+ }
+
+ if( ! cursor.next() ) // if this is a leaf entry's DN
+ {
+ LOG.debug( "deleting {}", dn.getUpName() );
+ cursorMap.remove( dn );
+ cursor.close();
+ delResponse = delete( new DeleteRequest( dn ), null );
+ }
+ else
+ {
+ do
+ {
+ SearchResponse searchResp = ( SearchResponse ) cursor.get();
+ if( searchResp instanceof SearchResultEntry )
+ {
+ SearchResultEntry searchResult = ( SearchResultEntry ) searchResp;
+ deleteChildren( searchResult.getEntry().getDn(), cursorMap );
+ }
+ }
+ while( cursor.next() );
+
+ cursorMap.remove( dn );
+ cursor.close();
+ LOG.debug( "deleting {}", dn.getUpName() );
+ delResponse = delete( new DeleteRequest( dn ), null );
+ }
+ }
+ catch( Exception e )
+ {
+ String msg = "Failed to delete child entries under the DN " + dn.getUpName();
+ LOG.error( msg, e );
+ throw new LdapException( msg, e );
+ }
+
+ return delResponse;
+ }
+
+
+ /**
+ * 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
+ */
+ public DeleteResponse delete( DeleteRequest delRequest, DeleteListener listener ) throws LdapException
+ {
+ checkSession();
+
+ lockSession();
+
+ 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 );
+
+ ldapSession.write( deleteMessage );
+
+ if( listener == null )
+ {
+ LdapMessageCodec response = null;
+ try
+ {
+ long timeout = getTimeout( delRequest.getTimeout() );
+ response = deleteResponseQueue.poll( timeout, TimeUnit.MILLISECONDS );
+
+ if ( response == null )
+ {
+ LOG.error( "Delete DN failed : timeout occured" );
+ unlockSession();
+ throw new LdapException( TIME_OUT_ERROR );
+ }
+ }
+ catch( Exception e )
+ {
+ LOG.error( NO_RESPONSE_ERROR );
+ unlockSession();
+ LdapException ldapException = new LdapException();
+ ldapException.initCause( e );
+ throw ldapException;
+ }
+
+ unlockSession();
+
+ return convert( response.getDelResponse() );
+ }
+ else
+ {
+ listenerMap.put( newId, listener );
+ return null;
+ }
+ }
+
+
+ /**
+ * 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;
+ }
+
+
+ /**
+ * 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();
+ SearchResultEntryImpl searchRes = ( SearchResultEntryImpl ) 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 );
+ }
+ }
+ }
+ }
}