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 2010/05/14 15:11:56 UTC

svn commit: r944244 - /directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConsumer.java

Author: kayyagari
Date: Fri May 14 13:11:56 2010
New Revision: 944244

URL: http://svn.apache.org/viewvc?rev=944244&view=rev
Log:
o fixed an issue with handling present entries sent with syncinfo message

Modified:
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConsumer.java

Modified: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConsumer.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConsumer.java?rev=944244&r1=944243&r2=944244&view=diff
==============================================================================
--- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConsumer.java (original)
+++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConsumer.java Fri May 14 13:11:56 2010
@@ -25,9 +25,12 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.apache.directory.ldap.client.api.LdapAsyncConnection;
 import org.apache.directory.ldap.client.api.LdapNetworkConnection;
@@ -61,9 +64,12 @@ import org.apache.directory.shared.ldap.
 import org.apache.directory.shared.ldap.entry.EntryAttribute;
 import org.apache.directory.shared.ldap.entry.Modification;
 import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.filter.AndNode;
 import org.apache.directory.shared.ldap.filter.EqualityNode;
 import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
 import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
 import org.apache.directory.shared.ldap.filter.SearchScope;
 import org.apache.directory.shared.ldap.message.AliasDerefMode;
 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
@@ -73,6 +79,8 @@ import org.apache.directory.shared.ldap.
 import org.apache.directory.shared.ldap.message.control.replication.SynchronizationModeEnum;
 import org.apache.directory.shared.ldap.name.DN;
 import org.apache.directory.shared.ldap.name.RDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.AttributeTypeOptions;
 import org.apache.directory.shared.ldap.schema.SchemaManager;
 import org.apache.directory.shared.ldap.util.StringTools;
 import org.slf4j.Logger;
@@ -98,7 +106,7 @@ public class SyncReplConsumer
     private byte[] syncCookie;
 
     /** the logger */
-    private static final Logger LOG = LoggerFactory.getLogger( SyncReplConsumer.class.getSimpleName() );
+    private static final Logger LOG = LoggerFactory.getLogger( SyncReplConsumer.class );
 
     /** conection to the syncrepl provider */
     private LdapAsyncConnection connection;
@@ -145,6 +153,13 @@ public class SyncReplConsumer
     /** the cookie that was saved last time */
     private byte[] lastSavedCookie;
 
+    private static AttributeType ENTRY_UUID_AT;
+    
+    private static final PresenceNode ENTRY_UUID_PRESENCE_FILTER = new PresenceNode( SchemaConstants.ENTRY_UUID_AT );
+
+    private static final Set<AttributeTypeOptions> ENTRY_UUID_ATOP_SET = new HashSet<AttributeTypeOptions>();
+
+    
     /**
      * @return the config
      */
@@ -168,6 +183,10 @@ public class SyncReplConsumer
 
         schemaManager = directoryservice.getSchemaManager();
         
+        ENTRY_UUID_AT = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ENTRY_UUID_AT );
+        
+        ENTRY_UUID_ATOP_SET.add( new AttributeTypeOptions( ENTRY_UUID_AT ) );
+        
         prepareSyncSearchRequest();
     }
 
@@ -251,8 +270,11 @@ public class SyncReplConsumer
         SyncDoneValueControl syncDoneCtrl = new SyncDoneValueControl();
         try
         {
-            syncDoneCtrl = ( SyncDoneValueControl ) syncDoneControlDecoder.decode( ctrl.getValue(), syncDoneCtrl );
-            refreshDeletes = syncDoneCtrl.isRefreshDeletes();
+            if( ctrl != null )
+            {
+                syncDoneCtrl = ( SyncDoneValueControl ) syncDoneControlDecoder.decode( ctrl.getValue(), syncDoneCtrl );
+                refreshDeletes = syncDoneCtrl.isRefreshDeletes();
+            }
         }
         catch ( Exception e )
         {
@@ -286,6 +308,11 @@ public class SyncReplConsumer
         {
             // log the error and handle it appropriately
             LOG.warn( "sync operation was not successful, received result code {}", resultCode );
+            if( resultCode == ResultCodeEnum.NO_SUCH_OBJECT )
+            {
+                LOG.warn( "given replication base DN {} is not found on provider, disconnecting the consumer from the provider", config.getBaseDn() );
+                disconnet();
+            }
         }
 
         LOG.debug( "//////////////// END handleSearchDone//////////////////////" );
@@ -426,9 +453,14 @@ public class SyncReplConsumer
             List<byte[]> uuidList = syncInfoValue.getSyncUUIDs();
             // if refreshDeletes set to true then delete all the entries with entryUUID
             // present in the syncIdSet 
-            if ( syncInfoValue.isRefreshDeletes() && ( uuidList != null ) )
+            if ( syncInfoValue.isRefreshDeletes() )
             {
-                deleteEntries( uuidList );
+                deleteEntries( uuidList, false );
+            }
+            else
+            {
+                LOG.debug( "refresh present syncinfo list has {} UUIDs", uuidList.size() );
+                deleteEntries( uuidList, true );
             }
 
             LOG.info( "refreshDone: " + syncInfoValue.isRefreshDone() );
@@ -762,7 +794,7 @@ public class SyncReplConsumer
      * @param uuidList the list of UUIDs 
      * @throws Exception in case of any problems while deleting the entries
      */
-    public void deleteEntries( List<byte[]> uuidList ) throws Exception
+    public void deleteEntries( List<byte[]> uuidList, boolean isRefreshPresent ) throws Exception
     {
         if ( uuidList == null || uuidList.isEmpty() )
         {
@@ -783,7 +815,7 @@ public class SyncReplConsumer
         for ( ; i < count; i++ )
         {
             startIndex = i * NODE_LIMIT;
-            _deleteEntries_( uuidList.subList( startIndex, startIndex + NODE_LIMIT ) );
+            _deleteEntries_( uuidList.subList( startIndex, startIndex + NODE_LIMIT ), isRefreshPresent );
         }
 
         if ( ( uuidList.size() % NODE_LIMIT ) != 0 )
@@ -793,7 +825,7 @@ public class SyncReplConsumer
             {
                 startIndex = i * NODE_LIMIT;
             }
-            _deleteEntries_( uuidList.subList( startIndex, uuidList.size() ) );
+            _deleteEntries_( uuidList.subList( startIndex, uuidList.size() ), isRefreshPresent );
         }
     }
 
@@ -803,7 +835,7 @@ public class SyncReplConsumer
      *
      * @param limitedUuidList a list of UUIDs whose size is less than or equal to #NODE_LIMIT
      */
-    private void _deleteEntries_( List<byte[]> limitedUuidList ) throws Exception
+    private void _deleteEntries_( List<byte[]> limitedUuidList, boolean isRefreshPresent ) throws Exception
     {
         ExprNode filter = null;
         int size = limitedUuidList.size();
@@ -812,35 +844,57 @@ public class SyncReplConsumer
             String uuid = StringTools.uuidToString( limitedUuidList.get( 0 ) );
             filter = new EqualityNode<String>( SchemaConstants.ENTRY_UUID_AT,
                 new org.apache.directory.shared.ldap.entry.StringValue( uuid ) );
+            if( isRefreshPresent )
+            {
+                filter = new NotNode( filter );   
+            }
         }
         else
         {
-            filter = new OrNode();
+            if( isRefreshPresent )
+            {
+                filter = new AndNode();
+            }
+            else
+            {
+                filter = new OrNode();
+            }
+            
             for ( int i = 0; i < size; i++ )
             {
                 String uuid = StringTools.uuidToString( limitedUuidList.get( i ) );
-                EqualityNode<String> uuidEqNode = new EqualityNode<String>( SchemaConstants.ENTRY_UUID_AT,
+                ExprNode uuidEqNode = new EqualityNode<String>( SchemaConstants.ENTRY_UUID_AT,
                     new org.apache.directory.shared.ldap.entry.StringValue( uuid ) );
-                ( ( OrNode ) filter ).addNode( uuidEqNode );
+
+                if( isRefreshPresent )
+                {
+                    uuidEqNode = new NotNode( uuidEqNode );
+                    ( ( AndNode ) filter ).addNode( uuidEqNode );
+                }
+                else
+                {
+                    ( ( OrNode ) filter ).addNode( uuidEqNode );
+                }
             }
         }
 
         DN dn = new DN( config.getBaseDn() );
         dn.normalize( schemaManager.getNormalizerMapping() );
 
-        EntryFilteringCursor cursor = session.search( dn, SearchScope.SUBTREE, filter,
-            AliasDerefMode.NEVER_DEREF_ALIASES, new HashSet() );
+        LOG.debug( "selecting entries to be deleted using filter {}", filter.toString() );
+        EntryFilteringCursor cursor = session.search( dn, SearchScope.SUBTREE, filter, AliasDerefMode.NEVER_DEREF_ALIASES, ENTRY_UUID_ATOP_SET );
         cursor.beforeFirst();
 
         while ( cursor.next() )
         {
             ClonedServerEntry entry = cursor.get();
-            session.delete( new DN( entry.getDn().getName() ), true );
+            deleteRecursive( entry.getDn(), null );
         }
 
         cursor.close();
     }
 
+    
     /**
      * A Thread implementation for synchronizing the DIT in refreshOnly mode
      */
@@ -918,4 +972,87 @@ public class SyncReplConsumer
         LOG.debug( "starting the cookie saver thread" );
         cookieSaverThread.start();
     }
+    
+    
+    /**
+     * removes all child entries present under the given DN and finally the DN itself
+     * 
+     * Working:
+     *          This is a recursive function which maintains a Map<DN,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
+     *   
+     * @param rootDn the DN which will be removed after removing its children
+     * @param map a map to hold the Cursor related to a DN
+     * @throws Exception If the DN is not valid or if the deletion failed
+     */
+    private void deleteRecursive( DN rootDn, Map<DN, EntryFilteringCursor> cursorMap ) throws Exception
+    {
+        LOG.debug( "searching for {}", rootDn.getName() );
+        EntryFilteringCursor cursor = null;
+
+        try
+        {
+            if ( cursorMap == null )
+            {
+                cursorMap = new HashMap<DN, EntryFilteringCursor>();
+            }
+
+            cursor = cursorMap.get( rootDn );
+
+            if ( cursor == null )
+            {
+                cursor = session.search( rootDn,SearchScope.ONELEVEL, ENTRY_UUID_PRESENCE_FILTER, AliasDerefMode.NEVER_DEREF_ALIASES, ENTRY_UUID_ATOP_SET );
+                cursor.beforeFirst();
+                LOG.debug( "putting cursor for {}", rootDn.getName() );
+                cursorMap.put( rootDn, cursor );
+            }
+
+            if ( !cursor.next() ) // if this is a leaf entry's DN
+            {
+                LOG.debug( "deleting {}", rootDn.getName() );
+                cursorMap.remove( rootDn );
+                cursor.close();
+                session.delete( rootDn );
+            }
+            else
+            {
+                do
+                {
+                    ClonedServerEntry entry = cursor.get();
+
+                    deleteRecursive( entry.getDn(), cursorMap );
+                }
+                while ( cursor.next() );
+
+                cursorMap.remove( rootDn );
+                cursor.close();
+                LOG.debug( "deleting {}", rootDn.getName() );
+                session.delete( rootDn );
+            }
+        }
+        catch ( Exception e )
+        {
+            String msg = "Failed to delete child entries under the DN " + rootDn.getName();
+            LOG.error( msg, e );
+            throw e;
+        }
+    }
+
 }