You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by sa...@apache.org on 2011/11/17 10:23:58 UTC

svn commit: r1203122 [1/2] - in /directory/apacheds/branches/apacheds-txns: core-api/src/main/java/org/apache/directory/server/core/api/partition/ core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/ core-shared/src/main/java/org/ap...

Author: saya
Date: Thu Nov 17 09:23:57 2011
New Revision: 1203122

URL: http://svn.apache.org/viewvc?rev=1203122&view=rev
Log:
Added apply mehtods to log edits to amke merging updates and applying them easier.Also added EntryModification and IndexModification interfaces.

Addded operations move, rename, moveandrename, lookup to the OperationExecutionManager and fixed a couple of bugs with the previous operations. Also made operations keep track of their dn space dependency.

The next step is to change the search engine to use wrapped master tables and indices. Also will make txn layer take into account the possiility of not having txns enabled. It seems this might be necessary in some cases like bootstrap.

Added:
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/partition/OperationExecutionManager.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/EntryModification.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/IndexModification.java
    directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryReplace.java
Modified:
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/AbstractLogEdit.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/LogEdit.java
    directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultOperationExecutionManager.java
    directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/ReadWriteTxn.java
    directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/DataChangeContainer.java
    directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryAddDelete.java
    directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryChange.java
    directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/IndexChange.java

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/partition/OperationExecutionManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/partition/OperationExecutionManager.java?rev=1203122&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/partition/OperationExecutionManager.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/partition/OperationExecutionManager.java Thu Nov 17 09:23:57 2011
@@ -0,0 +1,236 @@
+package org.apache.directory.server.core.api.partition;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.api.entry.ClonedServerEntry;
+import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.api.partition.index.Index;
+import org.apache.directory.server.core.api.partition.index.IndexCursor;
+import org.apache.directory.server.core.api.partition.index.IndexEntry;
+import org.apache.directory.server.core.api.partition.index.MasterTable;
+import org.apache.directory.server.core.api.partition.index.ParentIdAndRdn;
+import org.apache.directory.server.core.api.txn.logedit.DataChange;
+import org.apache.directory.server.core.api.txn.logedit.EntryModification;
+import org.apache.directory.server.core.api.txn.logedit.IndexModification;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.model.cursor.Cursor;
+import org.apache.directory.shared.ldap.model.entry.Attribute;
+import org.apache.directory.shared.ldap.model.entry.DefaultModification;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.entry.Modification;
+import org.apache.directory.shared.ldap.model.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.model.entry.Value;
+import org.apache.directory.shared.ldap.model.exception.LdapAliasDereferencingException;
+import org.apache.directory.shared.ldap.model.exception.LdapAliasException;
+import org.apache.directory.shared.ldap.model.exception.LdapContextNotEmptyException;
+import org.apache.directory.shared.ldap.model.exception.LdapEntryAlreadyExistsException;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.exception.LdapNoSuchObjectException;
+import org.apache.directory.shared.ldap.model.exception.LdapOperationErrorException;
+import org.apache.directory.shared.ldap.model.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.model.exception.LdapUnwillingToPerformException;
+import org.apache.directory.shared.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.model.message.SearchScope;
+import org.apache.directory.shared.ldap.model.name.Ava;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.ldap.model.name.Rdn;
+import org.apache.directory.shared.ldap.model.schema.AttributeType;
+import org.apache.directory.shared.ldap.model.schema.SchemaManager;
+import org.apache.directory.shared.ldap.model.schema.UsageEnum;
+
+
+public interface OperationExecutionManager
+{
+
+    /**
+     * Adds an entry to the given partition.
+     *
+     * @param partition 
+     * @param addContext the context used  to add and entry to this ContextPartition
+     * @throws LdapException if there are any problems
+     */
+    public void add( Partition partition, AddOperationContext addContext ) throws LdapException;
+
+
+    /**
+     * Deletes a leaf entry from this ContextPartition: non-leaf entries cannot be 
+     * deleted until this operation has been applied to their children.
+     *
+     * @param partition partition entry lives in
+     * @param deleteContext the context of the entry to
+     * delete from this ContextPartition.
+     * @throws Exception if there are any problems
+     */
+    public void delete( Partition partition, DeleteOperationContext deleteContext ) throws LdapException;
+
+
+    /**
+     * Delete the entry associated with a given Id
+     * @param partition partition entry lives in
+     * @param entryDn dn of the entry to be deleted
+     * @param id The id of the entry to delete
+     * @throws Exception If the deletion failed
+     */
+    public void delete( Partition partition, Dn entryDn, UUID id ) throws LdapException;
+
+
+    /**
+     * Modifies an entry by adding, removing or replacing a set of attributes.
+     *
+     * @param partition partition entry lives in
+     * @param modifyContext The context containing the modification operation 
+     * to perform on the entry which is one of constants specified by the 
+     * DirContext interface:
+     * <code>ADD_ATTRIBUTE, REMOVE_ATTRIBUTE, REPLACE_ATTRIBUTE</code>.
+     * 
+     * @throws Exception if there are any problems
+     */
+    public void modify( Partition partition, ModifyOperationContext modifyContext ) throws LdapException;
+
+
+    public Entry modify( Partition partition, Dn dn, Modification... mods ) throws Exception;
+
+
+    /**
+     * Modifies an entry by changing its relative name. Optionally attributes
+     * associated with the old relative name can be removed from the entry.
+     * This makes sense only in certain namespaces like LDAP and will be ignored
+     * if it is irrelevant.
+     *
+     * @param partition partition where the renamed entry lives
+     * @param renameContext the modify Dn context
+     * @throws Exception if there are any problems
+     */
+    public void rename( Partition partition, RenameOperationContext renameContext ) throws LdapException;
+
+
+    /**
+     * Changes the relative distinguished name of an entry specified by a
+     * distinguished name with the optional removal of the old Rdn attribute
+     * value from the entry.  Name changes propagate down as dn changes to the
+     * descendants of the entry where the Rdn changed.
+     *
+     * An Rdn change operation does not change parent child relationships.  It
+     * merely propagates a name change at a point in the DIT where the Rdn is
+     * changed. The change propagates down the subtree rooted at the
+     * distinguished name specified.
+     *
+     * @param partition partition where the renamed entry lives
+     * @param dn the normalized distinguished name of the entry to alter
+     * @param newRdn the new Rdn to set
+     * @param deleteOldRdn whether or not to remove the old Rdn attr/val
+     * @param entry the modified entry
+     * @param originalEntry entry to be renamed as read from the underlying partition
+     * @throws Exception if there are any errors propagating the name changes
+     */
+    public void rename( Partition partition, Dn dn, Rdn newRdn, boolean deleteOldRdn, Entry entry, Entry originalEntry )
+        throws Exception;
+
+
+    /**
+     * Transplants a child entry, to a position in the namespace under a new
+     * parent entry.
+     *
+     * @param partition partition where the moved entry lives in
+     * @param moveContext The context containing the DNs to move
+     * @throws Exception if there are any problems
+     */
+    public void move( Partition partition, MoveOperationContext moveContext ) throws LdapException;
+
+
+    /**
+     * <p>Move an entry from one place to the other. The Rdn remains unchanged,
+     * the parent Dn changes</p>
+     * <p>We have to update some of the index when moving an entry. Assuming
+     * that the target destination does not exist, the following index must
+     * be updated :</p>
+     * <ul>
+     * <li><b>oneLevel</b> index</li>
+     * <li><b>subLevel</b> index</li>
+     * </ul>
+     * <p>If the moved entry is an alias, then we also have to update the
+     * following index :</p>
+     * <ul>
+     * <li><b>oneAlias</b> index</li>
+     * <li><b>subAlias</b> index</li>
+     * </ul>
+     * <p>The <b>Alias</b> index is not updated, as the entry ID won't change.</p> 
+     * <p>We have a few check we must do before moving the entry :
+     * <ul>
+     * <li>The destination must not exist
+     * <li>The moved entry must exist (this has already been checked)
+     * <li>The moved entry must not inherit from a referral (already checked)
+     * </ul>
+     *
+     * @param partition partition of the oldDn
+     * @param oldDn The previous entry Dn
+     * @param newSuperior The new superior Dn
+     * @param newDn The new Dn
+     * @param modifiedEntry Entry to be moved. Modifications might have been performed on the entry
+     * @param originalEntry entry to be moved. Version of the entry as read from the underlying partition.
+     * @throws Exception If the move failed
+     */
+    public void move( Partition partition, Dn oldDn, Dn newSuperiorDn, Dn newDn, Entry modifiedEntry,
+        Entry originalEntry ) throws Exception;
+
+
+    /**
+     * Transplants a child entry, to a position in the namespace under a new
+     * parent entry and changes the RN of the child entry which can optionally
+     * have its old RN attributes removed.  The removal of old RN attributes
+     * may not make sense in all namespaces.  If the concept is undefined in a
+     * namespace this parameters is ignored.  An example of a namespace where
+     * this parameter is significant is the LDAP namespace.
+     *
+     * @param partition partition where the moved and renamed entry lives in
+     * @param moveAndRenameContext The context contain all the information about
+     * the modifyDN operation
+     * @throws Exception if there are any problems
+     */
+    public void moveAndRename( Partition partition, MoveAndRenameOperationContext moveAndRenameContext )
+        throws LdapException;
+
+
+    public void moveAndRename( Partition partition, Dn oldDn, Dn newSuperiorDn, Rdn newRdn, Entry modifiedEntry,
+        Entry originalEntry, boolean deleteOldRdn ) throws Exception;
+
+
+    /**
+     * Looks up an entry by distinguished/absolute name.  This is a simplified
+     * version of the search operation used to point read an entry used for
+     * convenience.
+     * 
+     * Depending on the context parameters, we my look for a simple entry,
+     * or for a restricted set of attributes for this entry
+     *
+     * @param partition partition where from which the lookup will be done
+     * @param lookupContext The context containing the parameters
+     * @return an Attributes object representing the entry
+     * @throws Exception if there are any problems
+     */
+    public Entry lookup( Partition partition, LookupOperationContext lookupContext ) throws LdapException;
+
+
+    /**
+     * Get back an entry knowing its ID
+     *
+     * @param partition partition entry lives in
+     * @param id The Entry ID we want to get back
+     * @return The found Entry, or null if not found
+     * @throws Exception If the lookup failed for any reason (except a not found entry)
+     */
+    public Entry lookup( Partition partition, UUID id ) throws LdapException;
+
+}

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/AbstractLogEdit.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/AbstractLogEdit.java?rev=1203122&r1=1203121&r2=1203122&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/AbstractLogEdit.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/AbstractLogEdit.java Thu Nov 17 09:23:57 2011
@@ -30,6 +30,7 @@ public abstract class AbstractLogEdit im
     /** position in the wal */
     private transient LogAnchor logAnchor = new LogAnchor();
     
+    
     /**
      * {@inheritDoc}
      */
@@ -37,4 +38,13 @@ public abstract class AbstractLogEdit im
     {
         return logAnchor;
     }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void apply( boolean recovery ) throws Exception
+    {
+        // do nothing by default
+    }
 }

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/EntryModification.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/EntryModification.java?rev=1203122&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/EntryModification.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/EntryModification.java Thu Nov 17 09:23:57 2011
@@ -0,0 +1,12 @@
+
+package org.apache.directory.server.core.api.txn.logedit;
+
+import java.util.UUID;
+
+import org.apache.directory.server.core.api.partition.Partition;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+
+public interface EntryModification extends DataChange
+{
+    Entry applyModification( Partition partition, Entry curEntry, UUID entrId, long changeLsn, boolean recovery );
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/IndexModification.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/IndexModification.java?rev=1203122&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/IndexModification.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/IndexModification.java Thu Nov 17 09:23:57 2011
@@ -0,0 +1,9 @@
+
+package org.apache.directory.server.core.api.txn.logedit;
+
+import org.apache.directory.server.core.api.partition.Partition;
+
+public interface IndexModification extends DataChange
+{
+    void applyModification( Partition partition, boolean recovery ) throws Exception;
+}

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/LogEdit.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/LogEdit.java?rev=1203122&r1=1203121&r2=1203122&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/LogEdit.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/api/txn/logedit/LogEdit.java Thu Nov 17 09:23:57 2011
@@ -37,4 +37,11 @@ public interface LogEdit extends Externa
      * @return position of the log edit in the wal
      */
     LogAnchor getLogAnchor();
+    
+    /**
+     * Applies the logedit to the underlying partitions
+     *
+     * @param recovery true if at recovery stage 
+     */
+    void apply( boolean recovery ) throws Exception;
 }

Modified: directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultOperationExecutionManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultOperationExecutionManager.java?rev=1203122&r1=1203121&r2=1203122&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultOperationExecutionManager.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultOperationExecutionManager.java Thu Nov 17 09:23:57 2011
@@ -1,13 +1,20 @@
 package org.apache.directory.server.core.shared.partition;
 
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 import java.util.UUID;
 
 import org.apache.directory.server.constants.ApacheSchemaConstants;
 import org.apache.directory.server.core.api.entry.ClonedServerEntry;
 import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
 import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
 import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.index.Index;
 import org.apache.directory.server.core.api.partition.index.IndexCursor;
@@ -18,9 +25,11 @@ import org.apache.directory.server.core.
 import org.apache.directory.server.core.shared.txn.logedit.DataChangeContainer;
 import org.apache.directory.server.core.shared.txn.logedit.EntryAddDelete;
 import org.apache.directory.server.core.shared.txn.logedit.EntryChange;
+import org.apache.directory.server.core.shared.txn.logedit.EntryReplace;
 import org.apache.directory.server.core.shared.txn.logedit.IndexChange;
 import org.apache.directory.server.i18n.I18n;
 import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.model.cursor.Cursor;
 import org.apache.directory.shared.ldap.model.entry.Attribute;
 import org.apache.directory.shared.ldap.model.entry.DefaultModification;
 import org.apache.directory.shared.ldap.model.entry.Entry;
@@ -35,11 +44,15 @@ import org.apache.directory.shared.ldap.
 import org.apache.directory.shared.ldap.model.exception.LdapNoSuchObjectException;
 import org.apache.directory.shared.ldap.model.exception.LdapOperationErrorException;
 import org.apache.directory.shared.ldap.model.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.model.exception.LdapUnwillingToPerformException;
 import org.apache.directory.shared.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.model.message.SearchScope;
+import org.apache.directory.shared.ldap.model.name.Ava;
 import org.apache.directory.shared.ldap.model.name.Dn;
 import org.apache.directory.shared.ldap.model.name.Rdn;
 import org.apache.directory.shared.ldap.model.schema.AttributeType;
 import org.apache.directory.shared.ldap.model.schema.SchemaManager;
+import org.apache.directory.shared.ldap.model.schema.UsageEnum;
 
 
 public class DefaultOperationExecutionManager
@@ -53,11 +66,7 @@ public class DefaultOperationExecutionMa
     // The Add operation
     //---------------------------------------------------------------------------------------------
     /**
-     * Adds an entry to the given partition.
-     *
-     * @param partition 
-     * @param addContext the context used  to add and entry to this ContextPartition
-     * @throws LdapException if there are any problems
+     * {@inheritDoc}
      */
     public void add( Partition partition, AddOperationContext addContext ) throws LdapException
     {
@@ -66,6 +75,9 @@ public class DefaultOperationExecutionMa
             Entry entry = ( ( ClonedServerEntry ) addContext.getEntry() ).getClonedEntry();
             Dn entryDn = entry.getDn();
 
+            // Add write dependency on the dn
+            txnLogManager.addWrite( entryDn, SearchScope.SUBTREE );
+
             // check if the entry already exists
             if ( getEntryId( partition, entryDn ) != null )
             {
@@ -106,7 +118,7 @@ public class DefaultOperationExecutionMa
             // Get a new ID for the added entry
             MasterTable master = partition.getMasterTable();
             UUID id = master.getNextId( entry );
-            DataChangeContainer changeContainer = new DataChangeContainer( suffixDn );
+            DataChangeContainer changeContainer = new DataChangeContainer( partition );
             changeContainer.setEntryID( id );
             IndexChange indexChange;
 
@@ -231,6 +243,9 @@ public class DefaultOperationExecutionMa
             // And finally prepare the entry change
             EntryAddDelete entryAdd = new EntryAddDelete( entry, EntryAddDelete.Type.ADD );
             changeContainer.addChange( entryAdd );
+
+            // log the change
+            txnLogManager.log( changeContainer, false );
         }
         catch ( LdapException le )
         {
@@ -247,18 +262,15 @@ public class DefaultOperationExecutionMa
     // The Delete operation
     //---------------------------------------------------------------------------------------------
     /**
-     * Deletes a leaf entry from this ContextPartition: non-leaf entries cannot be 
-     * deleted until this operation has been applied to their children.
-     *
-     * @param partition partition entry lives in
-     * @param deleteContext the context of the entry to
-     * delete from this ContextPartition.
-     * @throws Exception if there are any problems
+     * {@inheritDoc}
      */
     public void delete( Partition partition, DeleteOperationContext deleteContext ) throws LdapException
     {
         Dn dn = deleteContext.getDn();
 
+        // Add write dependency on the dn
+        txnLogManager.addWrite( dn, SearchScope.SUBTREE );
+
         UUID id = getEntryId( partition, dn );
 
         // don't continue if id is null
@@ -296,11 +308,7 @@ public class DefaultOperationExecutionMa
 
 
     /**
-     * Delete the entry associated with a given Id
-     * @param partition partition entry lives in
-     * @param entryDn dn of the entry to be deleted
-     * @param id The id of the entry to delete
-     * @throws Exception If the deletion failed
+     * {@inheritDoc}
      */
     public void delete( Partition partition, Dn entryDn, UUID id ) throws LdapException
     {
@@ -319,7 +327,7 @@ public class DefaultOperationExecutionMa
             }
 
             Dn suffixDn = partition.getSuffixDn();
-            DataChangeContainer changeContainer = new DataChangeContainer( suffixDn );
+            DataChangeContainer changeContainer = new DataChangeContainer( partition );
             changeContainer.setEntryID( id );
             IndexChange indexChange;
 
@@ -327,7 +335,7 @@ public class DefaultOperationExecutionMa
 
             if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
             {
-                dropAliasIndices( partition, entryDn, id, changeContainer );
+                dropAliasIndices( partition, entryDn, id, null, changeContainer );
             }
 
             // Update the ObjectClass index
@@ -429,7 +437,12 @@ public class DefaultOperationExecutionMa
                 }
             }
 
-            master.remove( id );
+            // And finally prepare the entry change
+            EntryAddDelete entryAdd = new EntryAddDelete( entry, EntryAddDelete.Type.DELETE );
+            changeContainer.addChange( entryAdd );
+
+            // log the change
+            txnLogManager.log( changeContainer, false );
 
         }
         catch ( Exception e )
@@ -443,15 +456,7 @@ public class DefaultOperationExecutionMa
     // The Modify operation
     //---------------------------------------------------------------------------------------------
     /**
-     * Modifies an entry by adding, removing or replacing a set of attributes.
-     *
-     * @param partition partition entry lives in
-     * @param modifyContext The context containing the modification operation 
-     * to perform on the entry which is one of constants specified by the 
-     * DirContext interface:
-     * <code>ADD_ATTRIBUTE, REMOVE_ATTRIBUTE, REPLACE_ATTRIBUTE</code>.
-     * 
-     * @throws Exception if there are any problems
+     * {@inheritDoc}
      */
     public void modify( Partition partition, ModifyOperationContext modifyContext ) throws LdapException
     {
@@ -479,9 +484,12 @@ public class DefaultOperationExecutionMa
         master = txnLogManager.wrap( partition.getSuffixDn(), master );
         Entry entry = master.get( id );
 
-        DataChangeContainer changeContainer = new DataChangeContainer( partition.getSuffixDn() );
+        DataChangeContainer changeContainer = new DataChangeContainer( partition );
         changeContainer.setEntryID( id );
 
+        // Add write dependency on the dn
+        txnLogManager.addWrite( dn, SearchScope.OBJECT );
+
         for ( Modification mod : mods )
         {
 
@@ -504,6 +512,9 @@ public class DefaultOperationExecutionMa
             }
         }
 
+        // log the changes
+        txnLogManager.log( changeContainer, false );
+
         return entry;
     }
 
@@ -537,6 +548,8 @@ public class DefaultOperationExecutionMa
         AttributeType attributeType = mods.getAttributeType();
         Index<?> index;
         IndexChange indexChange;
+        Attribute changedAttribute = entry.get( attributeType );
+        boolean prevValueExists = ( ( changedAttribute != null ) && ( changedAttribute.size() > 0 ) );
 
         // Special case for the ObjectClass index
         if ( modsOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
@@ -559,12 +572,10 @@ public class DefaultOperationExecutionMa
             }
 
             // If the attr didn't exist for this id then created a log edit for an add for the presence index
-            Index<?> presenceIdx;
-            presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
-            presenceIdx = txnLogManager.wrap( partition.getSuffixDn(), presenceIdx );
-
-            if ( !( ( Index<Object> ) presenceIdx ).forward( modsOid, id ) )
+            if ( prevValueExists == false && mods.size() > 0 )
             {
+                Index<?> presenceIdx;
+                presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
                 indexChange = new IndexChange( presenceIdx, ApacheSchemaConstants.APACHE_PRESENCE_AT_OID, modsOid, id,
                     IndexChange.Type.ADD, true );
                 changeContainer.addChange( indexChange );
@@ -572,6 +583,13 @@ public class DefaultOperationExecutionMa
         }
 
         // create log edit for the entry change
+        Modification undo = null;
+
+        if ( prevValueExists )
+        {
+            undo = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, changedAttribute );
+        }
+
         EntryChange entryChange = new EntryChange( mod, null );
         changeContainer.addChange( entryChange );
 
@@ -611,6 +629,8 @@ public class DefaultOperationExecutionMa
         AttributeType attributeType = mods.getAttributeType();
         Index<?> index;
         IndexChange indexChange;
+        Attribute replacedAttribute = entry.get( attributeType );
+        boolean prevValueExists = ( ( replacedAttribute != null ) && ( replacedAttribute.size() > 0 ) );
 
         // Special case for the ObjectClass index
         if ( modsOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
@@ -619,8 +639,6 @@ public class DefaultOperationExecutionMa
             objectClassIdx = partition.getSystemIndex( modsOid );
             objectClassIdx = txnLogManager.wrap( partition.getSuffixDn(), objectClassIdx );
 
-            // TODO check if 
-
             // if the id exists in the index drop all existing attribute
             // value index entries and add new ones
             IndexCursor<?> indexCursor = objectClassIdx.reverseCursor( id );
@@ -682,7 +700,7 @@ public class DefaultOperationExecutionMa
              * If no attribute values exist for this entryId in the index then
              * we remove the presence index entry for the removed attribute.
              */
-            if ( mods.size() == 0 )
+            if ( ( mods.size() == 0 ) && prevValueExists == true )
             {
                 Index<?> presenceIdx;
                 presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
@@ -690,6 +708,14 @@ public class DefaultOperationExecutionMa
                     IndexChange.Type.DELETE, true );
                 changeContainer.addChange( indexChange );
             }
+            else if ( ( mods.size() > 0 ) && prevValueExists == false )
+            {
+                Index<?> presenceIdx;
+                presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
+                indexChange = new IndexChange( presenceIdx, ApacheSchemaConstants.APACHE_PRESENCE_AT_OID, modsOid, id,
+                    IndexChange.Type.ADD, true );
+                changeContainer.addChange( indexChange );
+            }
         }
         else if ( modsOid.equals( SchemaConstants.ENTRY_CSN_AT_OID ) )
         {
@@ -708,14 +734,13 @@ public class DefaultOperationExecutionMa
 
         if ( modsOid.equals( SchemaConstants.ALIASED_OBJECT_NAME_AT_OID ) )
         {
-            dropAliasIndices( partition, entryDn, id, changeContainer );
+            dropAliasIndices( partition, entryDn, id, null, changeContainer );
         }
 
         // create log edit for the entry change
         Modification undo = null;
-        Attribute replacedAttribute = entry.get( attributeType );
 
-        if ( replacedAttribute != null )
+        if ( prevValueExists )
         {
             undo = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, replacedAttribute );
         }
@@ -760,6 +785,8 @@ public class DefaultOperationExecutionMa
         AttributeType attributeType = mods.getAttributeType();
         Index<?> index;
         IndexChange indexChange;
+        Attribute changedAttribute = entry.get( attributeType );
+        boolean prevValueExists = ( ( changedAttribute != null ) && ( changedAttribute.size() > 0 ) );
 
         // Special case for the ObjectClass index
         if ( modsOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
@@ -806,7 +833,6 @@ public class DefaultOperationExecutionMa
         else if ( partition.hasUserIndexOn( attributeType ) )
         {
             index = partition.getUserIndex( attributeType );
-            index = txnLogManager.wrap( partition.getSuffixDn(), index );
 
             /*
              * If there are no attribute values in the modifications then this
@@ -817,6 +843,7 @@ public class DefaultOperationExecutionMa
             {
                 // if the id exists in the index drop all existing attribute
                 // value index entries and add new ones
+                index = txnLogManager.wrap( partition.getSuffixDn(), index );
                 IndexCursor<?> indexCursor = index.reverseCursor( id );
                 IndexEntry<?> indexEntry;
 
@@ -834,6 +861,16 @@ public class DefaultOperationExecutionMa
                 {
                     indexCursor.close();
                 }
+
+                if ( prevValueExists )
+                {
+                    Index<?> presenceIdx;
+                    presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
+                    indexChange = new IndexChange( presenceIdx, ApacheSchemaConstants.APACHE_PRESENCE_AT_OID, modsOid,
+                        id,
+                        IndexChange.Type.DELETE, true );
+                    changeContainer.addChange( indexChange );
+                }
             }
             else
             {
@@ -842,29 +879,29 @@ public class DefaultOperationExecutionMa
                     indexChange = new IndexChange( index, modsOid, value.getValue(), id, IndexChange.Type.DELETE, false );
                     changeContainer.addChange( indexChange );
                 }
-            }
 
-            /*
-             * If no attribute values exist for this entryId in the index then
-             * we remove the presence index entry for the removed attribute.
-             */
-            if ( null == index.reverseLookup( id ) )
-            {
-                Index<?> presenceIdx;
-                presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
-                indexChange = new IndexChange( presenceIdx, ApacheSchemaConstants.APACHE_PRESENCE_AT_OID, modsOid, id,
-                    IndexChange.Type.DELETE, true );
-                changeContainer.addChange( indexChange );
+                /*
+                 *  If the above modifications are going to remove all the attribute values, then update the presence index as well. Here we rely on the
+                 *  fact that only existing values in an entry can be removed.
+                 */
+                if ( prevValueExists && ( mods.size() == changedAttribute.size() ) )
+                {
+                    Index<?> presenceIdx;
+                    presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
+                    indexChange = new IndexChange( presenceIdx, ApacheSchemaConstants.APACHE_PRESENCE_AT_OID, modsOid,
+                        id,
+                        IndexChange.Type.DELETE, true );
+                    changeContainer.addChange( indexChange );
+                }
             }
         }
 
         // Prepare the entry change
         Modification undo = null;
-        Attribute replacedAttribute = entry.get( attributeType );
 
-        if ( replacedAttribute != null )
+        if ( prevValueExists )
         {
-            undo = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, replacedAttribute );
+            undo = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, changedAttribute );
         }
 
         EntryChange entryChange = new EntryChange( mod, undo );
@@ -873,12 +910,225 @@ public class DefaultOperationExecutionMa
         // Aliases->single valued comp/partial attr removal is not relevant here
         if ( modsOid.equals( SchemaConstants.ALIASED_OBJECT_NAME_AT_OID ) )
         {
-            dropAliasIndices( partition, entryDn, id, changeContainer );
+            dropAliasIndices( partition, entryDn, id, null, changeContainer );
         }
     }
 
 
     //---------------------------------------------------------------------------------------------
+    // The Rename operation
+    //---------------------------------------------------------------------------------------------
+    /**
+     * {@inheritDoc}
+     */
+    public void rename( Partition partition, RenameOperationContext renameContext ) throws LdapException
+    {
+        try
+        {
+            Dn oldDn = renameContext.getDn();
+            Rdn newRdn = renameContext.getNewRdn();
+            boolean deleteOldRdn = renameContext.getDeleteOldRdn();
+            Entry originalEntry = renameContext.getOriginalEntry();
+
+            if ( renameContext.getEntry() != null )
+            {
+                Entry modifiedEntry = renameContext.getModifiedEntry();
+                rename( partition, oldDn, newRdn, deleteOldRdn, modifiedEntry, originalEntry );
+            }
+            else
+            {
+                rename( partition, oldDn, newRdn, deleteOldRdn, null, originalEntry );
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new LdapOperationErrorException( e.getMessage(), e );
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    public void rename( Partition partition, Dn dn, Rdn newRdn, boolean deleteOldRdn, Entry entry, Entry originalEntry )
+        throws Exception
+    {
+        UUID id = getEntryId( partition, dn );
+        SchemaManager schemaManager = partition.getSchemaManager();
+        Dn suffixDn = partition.getSuffixDn();
+
+        DataChangeContainer changeContainer = new DataChangeContainer( partition );
+        changeContainer.setEntryID( id );
+        IndexChange indexChange;
+
+        if ( entry == null )
+        {
+            entry = originalEntry.clone();
+        }
+
+        Dn updn = entry.getDn();
+
+        newRdn.apply( schemaManager );
+
+        Dn parentDn = updn.getParent();
+        Dn newDn = new Dn( newRdn, parentDn );
+        newDn.apply( schemaManager );
+
+        // Add subtree dependency to old and new dn
+        txnLogManager.addWrite( updn, SearchScope.SUBTREE );
+        txnLogManager.addWrite( newDn, SearchScope.SUBTREE );
+
+        /*
+         * H A N D L E   N E W   R D N
+         * ====================================================================
+         * Add the new Rdn attribute to the entry.  If an index exists on the
+         * new Rdn attribute we add the index for this attribute value pair.
+         * Also we make sure that the presence index shows the existence of the
+         * new Rdn attribute within this entry.
+         */
+
+        for ( Ava newAtav : newRdn )
+        {
+            String newNormType = newAtav.getNormType();
+            Object newNormValue = newAtav.getNormValue().getValue();
+
+            AttributeType newRdnAttrType = schemaManager.lookupAttributeTypeRegistry( newNormType );
+
+            if ( partition.hasUserIndexOn( newRdnAttrType ) )
+            {
+                Index<?> index = partition.getUserIndex( newRdnAttrType );
+                index = txnLogManager.wrap( partition.getSuffixDn(), index );
+
+                if ( !( ( Index<Object> ) index ).forward( newNormValue, id ) )
+                {
+                    indexChange = new IndexChange( index, newNormType, newNormValue, id,
+                        IndexChange.Type.ADD, true );
+                    changeContainer.addChange( indexChange );
+                }
+
+                // Check the entry before modifying it below so that we can check if we need to update the presence index.
+                Attribute curAttribute = entry.get( newRdnAttrType );
+
+                if ( ( curAttribute == null ) || ( curAttribute.size() == 0 ) )
+                {
+                    Index<?> presenceIdx;
+                    presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
+                    indexChange = new IndexChange( presenceIdx, ApacheSchemaConstants.APACHE_PRESENCE_AT_OID,
+                        newNormType, id,
+                        IndexChange.Type.ADD, false );
+                    changeContainer.addChange( indexChange );
+                }
+            }
+
+            // Change the entry
+            entry.add( newRdnAttrType, newAtav.getNormValue() );
+
+        }
+
+        /*
+         * H A N D L E   O L D   R D N
+         * ====================================================================
+         * If the old Rdn is to be removed we need to get the attribute and
+         * value for it.  Keep in mind the old Rdn need not be based on the
+         * same attr as the new one.  We remove the Rdn value from the entry
+         * and remove the value/id tuple from the index on the old Rdn attr
+         * if any.  We also test if the delete of the old Rdn index tuple
+         * removed all the attribute values of the old Rdn using a reverse
+         * lookup.  If so that means we blew away the last value of the old
+         * Rdn attribute.  In this case we need to remove the attrName/id
+         * tuple from the presence index.
+         *
+         * We only remove an ATAV of the old Rdn if it is not included in the
+         * new Rdn.
+         */
+
+        if ( deleteOldRdn )
+        {
+            Rdn oldRdn = updn.getRdn();
+
+            for ( Ava oldAtav : oldRdn )
+            {
+                // check if the new ATAV is part of the old Rdn
+                // if that is the case we do not remove the ATAV
+                boolean mustRemove = true;
+
+                for ( Ava newAtav : newRdn )
+                {
+                    if ( oldAtav.equals( newAtav ) )
+                    {
+                        mustRemove = false;
+                        break;
+                    }
+                }
+
+                if ( mustRemove )
+                {
+                    String oldNormType = oldAtav.getNormType();
+                    String oldNormValue = oldAtav.getNormValue().getString();
+                    AttributeType oldRdnAttrType = schemaManager.lookupAttributeTypeRegistry( oldNormType );
+                    entry.remove( oldRdnAttrType, oldNormValue );
+
+                    if ( partition.hasUserIndexOn( oldRdnAttrType ) )
+                    {
+                        Index<?> index = partition.getUserIndex( oldRdnAttrType );
+                        indexChange = new IndexChange( index, oldNormType, oldNormValue, id,
+                            IndexChange.Type.DELETE, false );
+                        changeContainer.addChange( indexChange );
+
+                        /*
+                         * If there is no value for id in this index due to our
+                         * drop above we remove the oldRdnAttr from the presence idx
+                         */
+                        Attribute curAttribute = entry.get( oldRdnAttrType );
+
+                        if ( ( curAttribute == null ) || ( curAttribute.size() == 0 ) )
+                        {
+                            Index<?> presenceIdx;
+                            presenceIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID );
+                            indexChange = new IndexChange( presenceIdx, ApacheSchemaConstants.APACHE_PRESENCE_AT_OID,
+                                oldNormType, id,
+                                IndexChange.Type.DELETE, true );
+                            changeContainer.addChange( indexChange );
+                        }
+                    }
+                }
+            }
+        }
+
+        /*
+         * H A N D L E   D N   C H A N G E
+         * ====================================================================
+         * We only need to update the Rdn index.
+         * No need to calculate the new Dn.
+         */
+
+        Index<?> rdnIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_RDN_AT_OID );
+        UUID parentId = UUID.fromString( entry.get( SchemaConstants.ENTRY_PARENT_ID_AT ).getString() );
+
+        ParentIdAndRdn oldKey = new ParentIdAndRdn( parentId, newRdn );
+        indexChange = new IndexChange( rdnIdx, ApacheSchemaConstants.APACHE_RDN_AT_OID, oldKey, id,
+            IndexChange.Type.DELETE, true );
+        changeContainer.addChange( indexChange );
+
+        ParentIdAndRdn key = new ParentIdAndRdn( parentId, newRdn );
+        indexChange = new IndexChange( rdnIdx, ApacheSchemaConstants.APACHE_RDN_AT_OID, key, id,
+            IndexChange.Type.ADD, true );
+        changeContainer.addChange( indexChange );
+
+        /*
+         * Finall prepare the log edit for the entry change
+         */
+        EntryReplace entryReplace = new EntryReplace( entry, originalEntry );
+        changeContainer.addChange( entryReplace );
+
+        // log the change
+        txnLogManager.log( changeContainer, false );
+
+    }
+
+
+    //---------------------------------------------------------------------------------------------
     // Alias index manipulation
     //---------------------------------------------------------------------------------------------
     /**
@@ -926,6 +1176,9 @@ public class DefaultOperationExecutionMa
             throw e;
         }
 
+        // Add read dependency on the target dn
+        txnLogManager.addRead( normalizedAliasTargetDn, SearchScope.OBJECT );
+
         // L O O K U P   T A R G E T   I D
         targetId = getEntryId( partition, normalizedAliasTargetDn );
 
@@ -1032,7 +1285,8 @@ public class DefaultOperationExecutionMa
      * @throws Exception if we cannot delete index values in the database
      */
     @SuppressWarnings("unchecked")
-    private void dropAliasIndices( Partition partition, Dn aliasDn, UUID aliasId, DataChangeContainer changeContainer )
+    private void dropAliasIndices( Partition partition, Dn aliasDn, UUID aliasId, String targetDn,
+        DataChangeContainer changeContainer )
         throws Exception
     {
         SchemaManager schemaManager = partition.getSchemaManager();
@@ -1040,7 +1294,12 @@ public class DefaultOperationExecutionMa
         Index<?> aliasIdx;
         aliasIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
         aliasIdx = txnLogManager.wrap( partition.getSuffixDn(), aliasIdx );
-        String targetDn = ( ( Index<String> ) aliasIdx ).reverseLookup( aliasId );
+
+        if ( targetDn == null )
+        {
+            targetDn = ( ( Index<String> ) aliasIdx ).reverseLookup( aliasId );
+        }
+
         UUID targetId = getEntryId( partition, new Dn( schemaManager, targetDn ) );
         IndexChange indexChange;
 
@@ -1110,6 +1369,352 @@ public class DefaultOperationExecutionMa
     }
 
 
+    /**
+     * For all aliases including and under the moved base, this method removes
+     * one and subtree alias index tuples for old ancestors above the moved base
+     * that will no longer be ancestors after the move.
+     *
+     * @param movedBase the base at which the move occured - the moved node
+     * @throws Exception if system userIndices fail
+     */
+    @SuppressWarnings("unchecked")
+    private String dropMovedAliasIndices( Partition partition, final Dn movedBase, DataChangeContainer changeContainer )
+        throws Exception
+    {
+        UUID movedBaseId = getEntryId( partition, movedBase );
+        boolean isAlias = false;
+
+        Index<?> aliasIdx;
+        aliasIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
+        aliasIdx = txnLogManager.wrap( partition.getSuffixDn(), aliasIdx );
+        String targetDn = ( ( Index<String> ) aliasIdx ).reverseLookup( movedBaseId );
+
+        if ( targetDn != null )
+        {
+            dropAliasIndices( partition, movedBase, movedBaseId, targetDn, changeContainer );
+            isAlias = true;
+        }
+
+        return targetDn;
+    }
+
+
+    //---------------------------------------------------------------------------------------------
+    // The Move operation
+    //---------------------------------------------------------------------------------------------
+    /**
+     * {@inheritDoc}
+     */
+    public void move( Partition partition, MoveOperationContext moveContext ) throws LdapException
+    {
+        if ( moveContext.getNewSuperior().isDescendantOf( moveContext.getDn() ) )
+        {
+            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
+                "cannot place an entry below itself" );
+        }
+
+        try
+        {
+            Dn oldDn = moveContext.getDn();
+            Dn newSuperior = moveContext.getNewSuperior();
+            Dn newDn = moveContext.getNewDn();
+            Entry modifiedEntry = moveContext.getModifiedEntry();
+            Entry originalEntry = moveContext.getOriginalEntry();
+
+            move( partition, oldDn, newSuperior, newDn, modifiedEntry, originalEntry );
+
+        }
+        catch ( Exception e )
+        {
+            throw new LdapOperationErrorException( e.getMessage(), e );
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void move( Partition partition, Dn oldDn, Dn newSuperiorDn, Dn newDn, Entry modifiedEntry,
+        Entry originalEntry ) throws Exception
+    {
+        // Check that the parent Dn exists
+        UUID newParentId = getEntryId( partition, newSuperiorDn );
+
+        if ( newParentId == null )
+        {
+            // This is not allowed : the parent must exist
+            LdapEntryAlreadyExistsException ne = new LdapEntryAlreadyExistsException(
+                I18n.err( I18n.ERR_256_NO_SUCH_OBJECT, newSuperiorDn.getName() ) );
+            throw ne;
+        }
+
+        // Now check that the new entry does not exist
+        UUID newId = getEntryId( partition, newDn );
+
+        if ( newId != null )
+        {
+            // This is not allowed : we should not be able to move an entry
+            // to an existing position
+            LdapEntryAlreadyExistsException ne = new LdapEntryAlreadyExistsException(
+                I18n.err( I18n.ERR_250_ENTRY_ALREADY_EXISTS, newSuperiorDn.getName() ) );
+            throw ne;
+        }
+
+        // Add subtree dependency to old and new dn
+        txnLogManager.addWrite( oldDn, SearchScope.SUBTREE );
+        txnLogManager.addWrite( newDn, SearchScope.SUBTREE );
+
+        // Get the entry and the old parent IDs
+        UUID entryId = getEntryId( partition, oldDn );
+        UUID oldParentId = getParentId( partition, entryId );
+
+        // the below case arises only when the move( Dn oldDn, Dn newSuperiorDn, Dn newDn  ) is called
+        // directly using the Store API, in this case the value of modified entry will be null
+        // we need to lookup the entry to update the parent ID
+
+        if ( originalEntry == null )
+        {
+            MasterTable master = partition.getMasterTable();
+            master = txnLogManager.wrap( partition.getSuffixDn(), master );
+
+            // First get the entry
+            originalEntry = master.get( entryId );
+        }
+
+        if ( modifiedEntry == null )
+        {
+            modifiedEntry = originalEntry.clone();
+        }
+        moveInternal( partition, oldDn, newSuperiorDn, newDn, modifiedEntry, originalEntry, newParentId );
+
+    }
+
+
+    //---------------------------------------------------------------------------------------------
+    // The MoveAndRename operation
+    //---------------------------------------------------------------------------------------------
+    /**
+     * {@inheritDoc}
+     */
+    public void moveAndRename( Partition partition, MoveAndRenameOperationContext moveAndRenameContext )
+        throws LdapException
+    {
+        if ( moveAndRenameContext.getNewSuperiorDn().isDescendantOf( moveAndRenameContext.getDn() ) )
+        {
+            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
+                "cannot place an entry below itself" );
+        }
+
+        try
+        {
+            Dn oldDn = moveAndRenameContext.getDn();
+            Dn newSuperiorDn = moveAndRenameContext.getNewSuperiorDn();
+            Rdn newRdn = moveAndRenameContext.getNewRdn();
+            boolean deleteOldRdn = moveAndRenameContext.getDeleteOldRdn();
+            Entry modifiedEntry = moveAndRenameContext.getModifiedEntry();
+            Entry originalEntry = moveAndRenameContext.getOriginalEntry();
+
+            moveAndRename( partition, oldDn, newSuperiorDn, newRdn, modifiedEntry, originalEntry, deleteOldRdn );
+        }
+        catch ( LdapException le )
+        {
+            // In case we get an LdapException, just rethrow it as is to 
+            // avoid having it lost
+            throw le;
+        }
+        catch ( Exception e )
+        {
+            throw new LdapOperationErrorException( e.getMessage(), e );
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void moveAndRename( Partition partition, Dn oldDn, Dn newSuperiorDn, Rdn newRdn, Entry modifiedEntry,
+        Entry originalEntry, boolean deleteOldRdn ) throws Exception
+    {
+        // Check that the old entry exists
+        UUID oldId = getEntryId( partition, oldDn );
+
+        if ( oldId == null )
+        {
+            // This is not allowed : the old entry must exist
+            LdapNoSuchObjectException nse = new LdapNoSuchObjectException(
+                I18n.err( I18n.ERR_256_NO_SUCH_OBJECT, oldDn ) );
+            throw nse;
+        }
+
+        // Check that the new superior exist
+        UUID newSuperiorId = getEntryId( partition, newSuperiorDn );
+
+        if ( newSuperiorId == null )
+        {
+            // This is not allowed : the new superior must exist
+            LdapNoSuchObjectException nse = new LdapNoSuchObjectException(
+                I18n.err( I18n.ERR_256_NO_SUCH_OBJECT, newSuperiorDn ) );
+            throw nse;
+        }
+
+        Dn newDn = newSuperiorDn.add( newRdn );
+
+        // Now check that the new entry does not exist
+        UUID newId = getEntryId( partition, newDn );
+
+        if ( newId != null )
+        {
+            // This is not allowed : we should not be able to move an entry
+            // to an existing position
+            LdapEntryAlreadyExistsException ne = new LdapEntryAlreadyExistsException(
+                I18n.err( I18n.ERR_250_ENTRY_ALREADY_EXISTS, newSuperiorDn.getName() ) );
+            throw ne;
+        }
+
+        if ( originalEntry == null )
+        {
+            MasterTable master = partition.getMasterTable();
+            master = txnLogManager.wrap( partition.getSuffixDn(), master );
+
+            // First get the entry
+            originalEntry = master.get( oldId );
+        }
+
+        if ( modifiedEntry == null )
+        {
+            modifiedEntry = originalEntry.clone();
+        }
+
+        rename( partition, oldDn, newRdn, deleteOldRdn, modifiedEntry, originalEntry );
+        moveInternal( partition, oldDn, newSuperiorDn, newDn, modifiedEntry, originalEntry, newSuperiorId );
+    }
+
+
+    //---------------------------------------------------------------------------------------------
+    // The Lookup operation
+    //---------------------------------------------------------------------------------------------
+    /**
+     * {@inheritDoc}
+     */
+    public Entry lookup( Partition partition, LookupOperationContext lookupContext ) throws LdapException
+    {
+        UUID id = getEntryId( partition, lookupContext.getDn() );
+
+        if ( id == null )
+        {
+            return null;
+        }
+
+        Entry entry = lookup( partition, id );
+
+        // Remove all the attributes if the NO_ATTRIBUTE flag is set
+        if ( lookupContext.hasNoAttribute() )
+        {
+            entry.clear();
+
+            return entry;
+        }
+
+        if ( lookupContext.hasAllUser() )
+        {
+            if ( lookupContext.hasAllOperational() )
+            {
+                return entry;
+            }
+            else
+            {
+                for ( Attribute attribute : ( ( ( ClonedServerEntry ) entry ).getOriginalEntry() ).getAttributes() )
+                {
+                    AttributeType attributeType = attribute.getAttributeType();
+                    String oid = attributeType.getOid();
+
+                    if ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS )
+                    {
+                        if ( !lookupContext.getAttrsId().contains( oid ) )
+                        {
+                            entry.removeAttributes( attributeType );
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            if ( lookupContext.hasAllOperational() )
+            {
+                for ( Attribute attribute : ( ( ( ClonedServerEntry ) entry ).getOriginalEntry() ).getAttributes() )
+                {
+                    AttributeType attributeType = attribute.getAttributeType();
+
+                    if ( attributeType.getUsage() == UsageEnum.USER_APPLICATIONS )
+                    {
+                        entry.removeAttributes( attributeType );
+                    }
+                }
+            }
+            else
+            {
+                if ( lookupContext.getAttrsId().size() == 0 )
+                {
+                    for ( Attribute attribute : ( ( ( ClonedServerEntry ) entry ).getOriginalEntry() ).getAttributes() )
+                    {
+                        AttributeType attributeType = attribute.getAttributeType();
+
+                        if ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS )
+                        {
+                            entry.removeAttributes( attributeType );
+                        }
+                    }
+                }
+                else
+                {
+                    for ( Attribute attribute : ( ( ( ClonedServerEntry ) entry ).getOriginalEntry() ).getAttributes() )
+                    {
+                        AttributeType attributeType = attribute.getAttributeType();
+                        String oid = attributeType.getOid();
+
+                        if ( !lookupContext.getAttrsId().contains( oid ) )
+                        {
+                            entry.removeAttributes( attributeType );
+                        }
+                    }
+                }
+            }
+        }
+
+        return entry;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Entry lookup( Partition partition, UUID id ) throws LdapException
+    {
+        try
+        {
+            MasterTable master = partition.getMasterTable();
+            master = txnLogManager.wrap( partition.getSuffixDn(), master );
+            Entry entry = master.get( id );
+
+            if ( entry != null )
+            {
+                // We have to store the DN in this entry
+                Dn dn = buildEntryDn( partition, id );
+                entry.setDn( dn );
+
+                return new ClonedServerEntry( entry );
+            }
+
+            return null;
+        }
+        catch ( Exception e )
+        {
+            throw new LdapOperationErrorException( e.getMessage(), e );
+        }
+    }
+
+
     //---------------------------------------------------------------------------------------------
     // ID and DN operations
     //---------------------------------------------------------------------------------------------
@@ -1163,9 +1768,59 @@ public class DefaultOperationExecutionMa
 
 
     /**
+     * builds the Dn of the entry identified by the given id
+     *
+     * @param partition partition entry lives in
+     * @param id the entry's id
+     * @return the normalized Dn of the entry
+     * @throws Exception
+     */
+    private Dn buildEntryDn( Partition partition, UUID id ) throws Exception
+    {
+        UUID parentId = id;
+        UUID rootId = Partition.rootID;
+        SchemaManager schemaManager = partition.getSchemaManager();
+
+        StringBuilder upName = new StringBuilder();
+        boolean isFirst = true;
+
+        do
+        {
+            Index<?> rdnIdx;
+            rdnIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_RDN_AT_OID );
+            rdnIdx = txnLogManager.wrap( partition.getSuffixDn(), rdnIdx );
+            ParentIdAndRdn cur = ( ( Index<ParentIdAndRdn> ) rdnIdx ).reverseLookup( parentId );
+
+            Rdn[] rdns = cur.getRdns();
+
+            for ( Rdn rdn : rdns )
+            {
+                if ( isFirst )
+                {
+                    isFirst = false;
+                }
+                else
+                {
+                    upName.append( ',' );
+                }
+
+                upName.append( rdn.getName() );
+            }
+
+            parentId = cur.getParentId();
+        }
+        while ( !parentId.equals( rootId ) );
+
+        Dn dn = new Dn( schemaManager, upName.toString() );
+
+        return dn;
+    }
+
+
+    /**
      * Gets the parent id of the given child id.
      *
-     * @param partition partitin childId lives in.
+     * @param partition partition childId lives in.
      * @param childId id of the entry for which we want to get the parent id.
      * @return parent id
      * @throws Exception
@@ -1209,4 +1864,165 @@ public class DefaultOperationExecutionMa
         return suffixId;
     }
 
+
+    /**
+     * Updates the SubLevel Index as part of a move operation.
+     *
+     * @param partition partition of the moved base
+     * @param entryId child id to be moved
+     * @param oldParentId old parent's id
+     * @param newParentId new parent's id
+     * @param changeContainer container for the txn log edits
+     * @throws Exception
+     */
+    private void updateSubLevelIndex( Partition partition, UUID entryId, UUID oldParentId, UUID newParentId,
+        DataChangeContainer changeContainer ) throws Exception
+    {
+        UUID tempId = oldParentId;
+        List<UUID> parentIds = new ArrayList<UUID>();
+        UUID suffixId = getSuffixId( partition );
+        IndexChange indexChange;
+
+        // find all the parents of the oldParentId
+        while ( ( tempId != null ) && !tempId.equals( Partition.rootID ) && !tempId.equals( suffixId ) )
+        {
+            parentIds.add( tempId );
+            tempId = getParentId( partition, tempId );
+        }
+
+        Index<?> subLevelIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_SUB_LEVEL_AT_OID );
+        subLevelIdx = txnLogManager.wrap( partition.getSuffixDn(), subLevelIdx );
+
+        // find all the children of the childId
+        Cursor<IndexEntry<UUID>> cursor = ( ( Index<UUID> ) subLevelIdx ).forwardCursor( entryId );
+
+        List<UUID> childIds = new ArrayList<UUID>();
+        childIds.add( entryId );
+
+        try
+        {
+            while ( cursor.next() )
+            {
+                childIds.add( cursor.get().getId() );
+            }
+        }
+        finally
+        {
+            cursor.close();
+        }
+
+        // detach the childId and all its children from oldParentId and all it parents excluding the root
+        for ( UUID pid : parentIds )
+        {
+            for ( UUID cid : childIds )
+            {
+                indexChange = new IndexChange( subLevelIdx, ApacheSchemaConstants.APACHE_SUB_LEVEL_AT_OID, pid, cid,
+                    IndexChange.Type.DELETE, true );
+                changeContainer.addChange( indexChange );
+            }
+        }
+
+        parentIds.clear();
+        tempId = newParentId;
+
+        // find all the parents of the newParentId
+        while ( ( tempId != null ) && !tempId.equals( Partition.rootID ) && !tempId.equals( suffixId ) )
+        {
+            parentIds.add( tempId );
+            tempId = getParentId( partition, tempId );
+        }
+
+        // attach the childId and all its children to newParentId and all it parents excluding the root
+        for ( UUID id : parentIds )
+        {
+            for ( UUID cid : childIds )
+            {
+                indexChange = new IndexChange( subLevelIdx, ApacheSchemaConstants.APACHE_SUB_LEVEL_AT_OID, id, cid,
+                    IndexChange.Type.ADD, true );
+                changeContainer.addChange( indexChange );
+            }
+        }
+    }
+
+
+    private void moveInternal( Partition partition, Dn oldDn, Dn newSuperiorDn, Dn newDn, Entry modifiedEntry,
+        Entry originalEntry, UUID newParentId ) throws Exception
+    {
+        // Get the entry and the old parent IDs
+        UUID entryId = getEntryId( partition, oldDn );
+        UUID oldParentId = getParentId( partition, entryId );
+
+        DataChangeContainer changeContainer = new DataChangeContainer( partition );
+        changeContainer.setEntryID( entryId );
+        IndexChange indexChange;
+
+        /*
+         * All aliases including and below oldChildDn, will be affected by
+         * the move operation with respect to one and subtree userIndices since
+         * their relationship to ancestors above oldChildDn will be
+         * destroyed.  For each alias below and including oldChildDn we will
+         * drop the index tuples mapping ancestor ids above oldChildDn to the
+         * respective target ids of the aliases.
+         */
+        String aliasTargetDn = dropMovedAliasIndices( partition, oldDn, changeContainer );
+
+        /*
+         * Drop the old parent child relationship and add the new one
+         * Set the new parent id for the child replacing the old parent id
+         */
+        Index<?> oneLevelIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_ONE_LEVEL_AT_OID );
+
+        indexChange = new IndexChange( oneLevelIdx, ApacheSchemaConstants.APACHE_ONE_LEVEL_AT_OID, oldParentId,
+            entryId,
+            IndexChange.Type.DELETE, true );
+        changeContainer.addChange( indexChange );
+
+        indexChange = new IndexChange( oneLevelIdx, ApacheSchemaConstants.APACHE_ONE_LEVEL_AT_OID, newParentId,
+            entryId,
+            IndexChange.Type.ADD, true );
+        changeContainer.addChange( indexChange );
+
+        updateSubLevelIndex( partition, entryId, oldParentId, newParentId, changeContainer );
+
+        // Update the Rdn index
+        Index<?> rdnIdx = partition.getSystemIndex( ApacheSchemaConstants.APACHE_RDN_AT_OID );
+
+        ParentIdAndRdn oldKey = new ParentIdAndRdn( oldParentId, oldDn.getRdn() );
+        indexChange = new IndexChange( rdnIdx, ApacheSchemaConstants.APACHE_RDN_AT_OID, oldKey, entryId,
+            IndexChange.Type.DELETE, true );
+        changeContainer.addChange( indexChange );
+
+        ParentIdAndRdn newKey = new ParentIdAndRdn( newParentId, oldDn.getRdn() );
+        indexChange = new IndexChange( rdnIdx, ApacheSchemaConstants.APACHE_RDN_AT_OID, newKey, entryId,
+            IndexChange.Type.ADD, true );
+        changeContainer.addChange( indexChange );
+
+        /*
+         * Read Alias Index Tuples
+         *
+         * If this is a name change due to a move operation then the one and
+         * subtree userIndices for aliases were purged before the aliases were
+         * moved.  Now we must add them for each alias entry we have moved.
+         *
+         * aliasTarget is used as a marker to tell us if we're moving an
+         * alias.  If it is null then the moved entry is not an alias.
+         */
+        if ( aliasTargetDn != null )
+        {
+            addAliasIndices( partition, entryId, newDn, aliasTargetDn, changeContainer );
+        }
+
+        // Update the master table with the modified entry
+        modifiedEntry.put( SchemaConstants.ENTRY_PARENT_ID_AT, newParentId.toString() );
+
+        /*
+         * Finally prepare the log edit for the entry change
+         */
+        EntryReplace entryReplace = new EntryReplace( modifiedEntry, originalEntry );
+        changeContainer.addChange( entryReplace );
+
+        // log the change
+        txnLogManager.log( changeContainer, false );
+    }
+
 }

Modified: directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/ReadWriteTxn.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/ReadWriteTxn.java?rev=1203122&r1=1203121&r2=1203122&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/ReadWriteTxn.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/ReadWriteTxn.java Thu Nov 17 09:23:57 2011
@@ -30,6 +30,7 @@ import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.Comparator;
 
+import org.apache.directory.server.core.api.txn.logedit.EntryModification;
 import org.apache.directory.server.core.api.txn.logedit.LogEdit;
 import org.apache.directory.server.core.shared.txn.logedit.IndexChange;
 import org.apache.directory.server.core.api.txn.logedit.DataChange;
@@ -270,76 +271,12 @@ import org.apache.directory.shared.ldap.
             {
                 container = ( DataChangeContainer ) edit;
 
-                /**
-                 * Check if the container has changes for the entry
-                 * and the version says we need to apply this change
-                 */
-                //TODO check version and id here. 
-                UUID entryId = container.getEntryID();
-                boolean applyChanges = false;
-
-                if ( entryId != null )
-                {
-                    /*
-                     * Container has changes for entry. Check if the entry change
-                     * affects out entry by comparing id and partitionDn.
-                     */
-
-                    Comparator<UUID> idComp = UUIDComparator.INSTANCE;
-
-                    if ( partitionDn.equals( container.getPartitionDn() )
-                        && ( idComp.compare( entryID, container.getEntryID() ) == 0 ) )
-                    {
-                        applyChanges = true;
-                    }
-
-                }
-
-                if ( applyChanges )
+                Entry nextEntry = container.mergeUpdates( partitionDn, entryID, curEntry, cloneOnChange );
+                
+                if ( nextEntry != curEntry )
                 {
-                    List<DataChange> dataChanges = container.getChanges();
-                    Iterator<DataChange> dit = dataChanges.iterator();
-                    DataChange nextChange;
-
-                    while ( dit.hasNext() )
-                    {
-                        nextChange = dit.next();
-
-                        if ( ( nextChange instanceof EntryChange ) && ( curEntry != null ) )
-                        {
-                            EntryChange entryChange = ( EntryChange ) nextChange;
-
-                            if ( needToCloneOnChange )
-                            {
-                                curEntry = curEntry.clone();
-                                needToCloneOnChange = false;
-                            }
-
-                            try
-                            {
-                                AttributeUtils.applyModification( curEntry, entryChange.getRedoChange() );
-                            }
-                            catch ( LdapException e )
-                            {
-                                //TODO decide whether to throw IOException or an internal exception here
-                            }
-                        }
-                        else if ( nextChange instanceof EntryAddDelete )
-                        {
-                            EntryAddDelete addDelete = ( EntryAddDelete ) nextChange;
-                            needToCloneOnChange = false;
-
-                            if ( addDelete.getType() == EntryAddDelete.Type.ADD )
-                            {
-                                curEntry = addDelete.getChangedEntry();
-                            }
-                            else
-                            {
-                                curEntry = null;
-                            }
-                        }
-                    }
-
+                    cloneOnChange = false;
+                    curEntry = nextEntry;
                 }
             }
         }

Modified: directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/DataChangeContainer.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/DataChangeContainer.java?rev=1203122&r1=1203121&r2=1203122&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/DataChangeContainer.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/DataChangeContainer.java Thu Nov 17 09:23:57 2011
@@ -24,16 +24,23 @@ import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 
+import java.util.Comparator;
 import java.util.List;
 import java.util.LinkedList;
 import java.util.Iterator;
 import java.util.UUID;
 
+import org.apache.directory.shared.ldap.model.entry.Entry;
 import org.apache.directory.shared.ldap.model.name.Dn;
 
+import org.apache.directory.server.core.api.partition.Partition;
+import org.apache.directory.server.core.api.partition.index.MasterTable;
 import org.apache.directory.server.core.api.partition.index.Serializer;
+import org.apache.directory.server.core.api.partition.index.UUIDComparator;
 import org.apache.directory.server.core.api.txn.logedit.AbstractLogEdit;
 import org.apache.directory.server.core.api.txn.logedit.DataChange;
+import org.apache.directory.server.core.api.txn.logedit.EntryModification;
+import org.apache.directory.server.core.api.txn.logedit.IndexModification;
 
 import org.apache.directory.server.core.shared.txn.TxnManagerFactory;
 import org.apache.directory.server.core.api.txn.TxnManager;
@@ -53,6 +60,9 @@ public class DataChangeContainer extends
     /** Transaction under which the change is done */
     private long txnID;
 
+    /** Partition stored for fast access */
+    private transient Partition partition;
+    
     /** partition this change applies to */
     private Dn partitionDn;
 
@@ -67,12 +77,16 @@ public class DataChangeContainer extends
     }
 
 
+    public DataChangeContainer( Partition partition )
+    {
+        this.partitionDn = partition.getSuffixDn();
+    }
+
     public DataChangeContainer( Dn partitionDn )
     {
         this.partitionDn = partitionDn;
     }
 
-
     public long getTxnID()
     {
         return txnID;
@@ -89,6 +103,11 @@ public class DataChangeContainer extends
     {
         return partitionDn;
     }
+    
+    public Partition getPartition()
+    {
+        return partition;
+    }
 
 
     public UUID getEntryID()
@@ -108,11 +127,141 @@ public class DataChangeContainer extends
         return changes;
     }
 
+
     public void addChange( DataChange change )
     {
         changes.add( change );
     }
 
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void apply( boolean recovery ) throws Exception
+    {
+        long changeLsn = getLogAnchor().getLogLSN();
+        Entry curEntry = null;
+        boolean entryExisted = false;
+
+        // TODO find the partition from the dn if changeContainer doesnt have it.
+
+        if ( entryID != null )
+        {
+            MasterTable master = partition.getMasterTable();
+            curEntry = master.get( entryID );
+
+            if ( curEntry != null )
+            {
+                curEntry = curEntry.clone();
+                entryExisted = true;
+            }
+        }
+
+        Iterator<DataChange> dit = changes.iterator();
+        DataChange nextChange;
+
+        while ( dit.hasNext() )
+        {
+            nextChange = dit.next();
+
+            if ( ( nextChange instanceof EntryModification ) )
+            {
+                EntryModification entryModification = ( EntryModification ) nextChange;
+
+                curEntry = entryModification.applyModification( partition, curEntry, entryID, changeLsn, false );
+            }
+            else
+            {
+                IndexModification indexModification = ( IndexModification ) nextChange;
+                indexModification.applyModification( partition, false );
+            }
+        }
+
+        if ( curEntry != null )
+        {
+            MasterTable master = partition.getMasterTable();
+            master.put( entryID, curEntry );
+        }
+        else
+        {
+            if ( entryExisted )
+            {
+                MasterTable master = partition.getMasterTable();
+                master.remove( entryID );
+            }
+        }
+    }
+
+
+    /**
+     * Applies the updates made by this log edit to the entry identified by the entryID and partition dn. 
+     *
+     * @param entryPartitionDn dn of the partition of the entry
+     * @param id id of the entry
+     * @param curEntry entry to be merged
+     * @param needToCloneOnChange true if entry should be cloned while applying a change.
+     * @return entry after it is merged with the updates in the txn.
+     */
+    public Entry mergeUpdates( Dn entryPartitionDn, UUID id, Entry curEntry, boolean needToCloneOnChange )
+    {
+        /**
+        * Check if the container has changes for the entry
+        * and the version says we need to apply this change
+        */
+        boolean applyChanges = false;
+
+        if ( entryID != null )
+        {
+            /*
+             * Container has changes for an entry. Check if the entry change
+             * affects out entry by comparing id and partitionDn.
+             */
+
+            Comparator<UUID> idComp = UUIDComparator.INSTANCE;
+
+            if ( entryPartitionDn.equals( partitionDn )
+                && ( idComp.compare( id, entryID ) == 0 ) )
+            {
+                applyChanges = true;
+            }
+
+        }
+
+        if ( applyChanges )
+        {
+            long changeLsn = getLogAnchor().getLogLSN();
+            Iterator<DataChange> dit = changes.iterator();
+            DataChange nextChange;
+
+            while ( dit.hasNext() )
+            {
+                nextChange = dit.next();
+
+                if ( nextChange instanceof EntryModification )
+                {
+                    EntryModification entryModification = ( EntryModification ) nextChange;
+
+                    if ( needToCloneOnChange )
+                    {
+                        if ( curEntry != null )
+                        {
+                            curEntry = curEntry.clone();
+                        }
+
+                        needToCloneOnChange = false;
+                    }
+
+                    entryModification.applyModification( partition, curEntry, id, changeLsn, true );
+                }
+            }
+
+        }
+
+        return curEntry;
+    }
+
+
     @Override
     public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
     {

Modified: directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryAddDelete.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryAddDelete.java?rev=1203122&r1=1203121&r2=1203122&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryAddDelete.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryAddDelete.java Thu Nov 17 09:23:57 2011
@@ -22,16 +22,22 @@ package org.apache.directory.server.core
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.util.UUID;
 
+import org.apache.directory.server.core.api.partition.Partition;
+import org.apache.directory.server.core.api.partition.index.MasterTable;
 import org.apache.directory.server.core.api.txn.logedit.AbstractDataChange;
+import org.apache.directory.server.core.api.txn.logedit.EntryModification;
+import org.apache.directory.shared.ldap.model.entry.AttributeUtils;
 import org.apache.directory.shared.ldap.model.entry.Entry;
 import org.apache.directory.shared.ldap.model.entry.DefaultEntry;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
 
 /**
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class EntryAddDelete extends AbstractDataChange
+public class EntryAddDelete extends AbstractDataChange implements EntryModification
 {
     /** Added or deleted entry */
     private Entry entry;
@@ -64,6 +70,51 @@ public class EntryAddDelete extends Abst
     }
     
     
+    /**
+     * {@inheritDoc}
+     */
+    public Entry applyModification( Partition partition, Entry curEntry, UUID entryId, long changeLsn, boolean recovery )
+    {
+        if ( type == EntryAddDelete.Type.ADD )
+        {
+            if ( curEntry != null )
+            {
+                if ( recovery == false )
+                {
+                    throw new IllegalStateException( "Entry is being added while it already exists:" + entryId
+                        + " curEntry:" + curEntry + " entry:" + entry );
+                }
+                else
+                {
+                    // TODO verify the curEnty is more recent
+                    return curEntry;
+                }
+            }
+
+            curEntry = entry;
+        }
+        else
+        {
+            if ( curEntry == null )
+            {
+                if ( recovery == false )
+                {
+                    throw new IllegalStateException( "Entry is being delete while it doesnt exist:" + entryId
+                        + " curEntry:" + curEntry + " entry:" + entry );
+                }
+                else
+                {
+                    return null;
+                }
+            }
+
+            curEntry = null;
+        }
+
+        return curEntry;
+    }
+    
+    
     @Override
     public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
     {

Modified: directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryChange.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryChange.java?rev=1203122&r1=1203121&r2=1203122&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryChange.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-shared/src/main/java/org/apache/directory/server/core/shared/txn/logedit/EntryChange.java Thu Nov 17 09:23:57 2011
@@ -22,16 +22,23 @@ package org.apache.directory.server.core
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.util.UUID;
 
+import org.apache.directory.server.core.api.partition.Partition;
+import org.apache.directory.server.core.api.partition.index.MasterTable;
 import org.apache.directory.server.core.api.txn.logedit.AbstractDataChange;
+import org.apache.directory.server.core.api.txn.logedit.EntryModification;
+import org.apache.directory.shared.ldap.model.entry.AttributeUtils;
+import org.apache.directory.shared.ldap.model.entry.Entry;
 import org.apache.directory.shared.ldap.model.entry.Modification;
 import org.apache.directory.shared.ldap.model.entry.DefaultModification;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
 
 /**
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class EntryChange extends AbstractDataChange
+public class EntryChange extends AbstractDataChange implements EntryModification
 {
     /** redo change */
     private Modification redoChange;
@@ -63,6 +70,41 @@ public class EntryChange extends Abstrac
     }
     
     
+    /**
+     * {@inheritDoc}
+     */
+    public Entry applyModification( Partition partition, Entry curEntry, UUID entryId, long changeLsn, boolean recovery )
+    {
+        if ( curEntry == null )
+        {   
+            if( recovery == false )
+            {
+                throw new IllegalStateException( "Entry with id:" + entryId + " not found while applying changes to it" + this );
+            }
+            else
+            {
+                // In recovery mode, null might be a more future version of the entry
+                return null;
+            }
+        }
+            
+        // TODO in reovery mode, check the version of the entry. 
+        
+        try
+        {
+            AttributeUtils.applyModification( curEntry, redoChange );
+        }
+        catch ( LdapException e )
+        {
+            // Shouldnt happen as this change is already verified
+            e.printStackTrace();
+            throw new IllegalStateException( "Application of redo change failed:" + entryId + "curEntry:" + curEntry + "change:" + redoChange, e );
+        }
+        
+        return curEntry;
+    }
+    
+    
     @Override
     public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
     {