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/10/18 15:11:43 UTC

svn commit: r1185638 [1/2] - in /directory/apacheds/branches/apacheds-txns: core-api/src/main/java/org/apache/directory/server/core/partition/index/ core-api/src/main/java/org/apache/directory/server/core/txn/ core-api/src/main/java/org/apache/director...

Author: saya
Date: Tue Oct 18 13:11:42 2011
New Revision: 1185638

URL: http://svn.apache.org/viewvc?rev=1185638&view=rev
Log:
changes to merge reads from master and index reads with txn log:

logedits: log edits to keep track entry modifications, entry add/delete and index changes. 

ReadWriteTxn and TxnLogManager: As data log edits are added a summary of index changes for the current txn is built. This summary is of the for<partitionDN, attributeOID, sorted set of index changes>

TxnIndexCursor: this implements a cursor interface for a single txn for a single index. Since index changes are kept memory, this cursor is built using an iterator.

IndexCursorWrapper.java: This wraps a partition index and creates a transactionally consistent view of it using the wrapped index and TxnIdexCursors for the txns that the current txn depends on.This basically implements a merge sort of various index cursors.

TODO: finish MastertableWrapper, add IndexWrapper

Added:
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ForwardIndexComparator.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ReverseIndexComparator.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Serializer.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/IndexCursorWrapper.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/MasterTableWrapper.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnIndexCursor.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerFactory.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/DataChangeContainer.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/EntryAddDelete.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/EntryChange.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/IndexChange.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/TxnStateChange.java
Removed:
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/TxnStateChange.java
Modified:
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/GenericIndex.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Index.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/AbstractTransaction.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnLogManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadOnlyTxn.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadWriteTxn.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/Transaction.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerInternal.java
    directory/apacheds/branches/apacheds-txns/core/src/test/java/org/apache/directory/server/core/log/LogFlushScanTest.java
    directory/apacheds/branches/apacheds-txns/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
    directory/apacheds/branches/apacheds-txns/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/impl/avl/AvlIndex.java

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ForwardIndexComparator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ForwardIndexComparator.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ForwardIndexComparator.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ForwardIndexComparator.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,48 @@
+
+package org.apache.directory.server.core.partition.index;
+
+import java.util.Comparator;
+
+public class ForwardIndexComparator<V, ID> implements Comparator<IndexEntry<V,ID>>
+{
+    Comparator<V> keyComparator;
+    Comparator<ID> valueComparator;
+    
+    public ForwardIndexComparator( Comparator<V> keyComparator, Comparator<ID> valueComparator )
+    {
+        this.keyComparator = keyComparator;
+        this.valueComparator = valueComparator;
+    }
+    
+    public int compare( IndexEntry<V, ID> entry1, IndexEntry<V, ID> entry2 )
+    {
+        V value1 = entry1.getValue();
+        V value2 = entry2.getValue();
+        ID id1 = entry1.getId();
+        ID id2 = entry2.getId();
+        
+        int result = keyComparator.compare( value1, value2 );
+        
+        if ( result == 0 )
+        {
+            if ( id1 == id2 )
+            {
+                result = 0;
+            }
+            else if ( id1 == null )
+            {
+                result = -1;
+            }
+            else if ( id2 == null )
+            {
+                result = 1;
+            }
+            else
+            {
+                result = valueComparator.compare( id1, id2 );
+            }
+        }
+        
+        return result;
+    }
+}

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/GenericIndex.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/GenericIndex.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/GenericIndex.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/GenericIndex.java Tue Oct 18 13:11:42 2011
@@ -262,6 +262,17 @@ public class GenericIndex<K, O, ID> exte
     }
 
 
+    public ForwardIndexComparator<K,ID> getForwardIndexEntryComparator()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public ReverseIndexComparator<K,ID> getReverseIndexEntryComparator()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    
     public void setWkDirPath( URI wkDirPath )
     {
         this.wkDirPath = wkDirPath;

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Index.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Index.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Index.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Index.java Tue Oct 18 13:11:42 2011
@@ -258,6 +258,9 @@ public interface Index<K, O, ID>
 
     boolean reverseLessOrEq( ID id, K attrVal ) throws Exception;
 
+    public ForwardIndexComparator<K,ID> getForwardIndexEntryComparator();
+
+    public ReverseIndexComparator<K,ID> getReverseIndexEntryComparator();
 
     void close() throws Exception;
 

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ReverseIndexComparator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ReverseIndexComparator.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ReverseIndexComparator.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/ReverseIndexComparator.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,48 @@
+
+package org.apache.directory.server.core.partition.index;
+
+import java.util.Comparator;
+
+public class ReverseIndexComparator<V, ID> implements Comparator<IndexEntry<V, ID>>
+{
+    Comparator<V> keyComparator;
+    Comparator<ID> valueComparator;
+    
+    public ReverseIndexComparator( Comparator<V> keyComparator, Comparator<ID> valueComparator )
+    {
+        this.keyComparator = keyComparator;
+        this.valueComparator = valueComparator;
+    }
+    
+    public int compare( IndexEntry<V, ID> entry1, IndexEntry<V, ID> entry2 )
+    {
+        V value1 = entry1.getValue();
+        V value2 = entry2.getValue();
+        ID id1 = entry1.getId();
+        ID id2 = entry2.getId();
+        
+        int result = valueComparator.compare( id1, id2 );
+        
+        if ( result == 0 )
+        {
+            if ( value1 == value2 )
+            {
+                result = 0;
+            }
+            else if ( value1 == null )
+            {
+                result = -1;
+            }
+            else if ( value2 == null )
+            {
+                result = 1;
+            }
+            else
+            {
+                result = keyComparator.compare( value1, value2 );
+            }
+        }
+        
+        return result;
+    }
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Serializer.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Serializer.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Serializer.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/partition/index/Serializer.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,25 @@
+
+package org.apache.directory.server.core.partition.index;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+public interface Serializer
+{
+    /**
+     * Serialize the content of an object into a byte array.
+     *
+     * @param obj Object to serialize
+     * @return a byte array representing the object's state
+     */
+    public byte[] serialize( Object obj ) throws IOException;
+
+
+    /**
+     * Deserialize the content of an object from a byte array.
+     *
+     * @param serialized Byte array representation of the object
+     * @return deserialized object
+     */
+    public Object deserialize( byte[] serialized ) throws IOException;
+}

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java Tue Oct 18 13:11:42 2011
@@ -4,12 +4,16 @@ package org.apache.directory.server.core
 import org.apache.directory.server.core.txn.logedit.LogEdit;
 
 import org.apache.directory.server.core.log.UserLogRecord;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.name.Dn;
 
 import java.io.IOException;
 
-public interface TxnLogManager
+public interface TxnLogManager<ID>
 {
-    public void log( LogEdit logEdit, boolean sync ) throws IOException;
+    public void log( LogEdit<ID> logEdit, boolean sync ) throws IOException;
     
     public void log( UserLogRecord logRecord, boolean sync ) throws IOException;
+    
+    public Entry mergeUpdates(Dn partitionDN, ID entryID,  Entry entry );
 }

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java Tue Oct 18 13:11:42 2011
@@ -2,12 +2,18 @@
 package org.apache.directory.server.core.txn;
 
 import java.io.IOException;
+import org.apache.directory.server.core.partition.index.Serializer;
+import java.util.Comparator;
 
-public interface TxnManager
+public interface TxnManager<ID>
 {
     public void beginTransaction( boolean readOnly ) throws IOException;
    
     public void commitTransaction() throws IOException;
     
     public void abortTransaction() throws IOException;
+    
+    public Comparator<ID> getIDComparator();
+    
+    public Serializer getIDSerializer();
 }

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/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/txn/logedit/AbstractLogEdit.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java Tue Oct 18 13:11:42 2011
@@ -3,7 +3,7 @@ package org.apache.directory.server.core
 
 import org.apache.directory.server.core.log.LogAnchor;
 
-public abstract class AbstractLogEdit implements LogEdit
+public abstract class AbstractLogEdit<ID> implements LogEdit<ID>
 {
     /** position in the wal */
     private transient LogAnchor logAnchor = new LogAnchor();

Modified: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/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/txn/logedit/LogEdit.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java (original)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java Tue Oct 18 13:11:42 2011
@@ -5,7 +5,7 @@ import org.apache.directory.server.core.
 
 import java.io.Externalizable;
 
-public interface LogEdit extends Externalizable 
+public interface LogEdit<ID> extends Externalizable 
 {
     /**
      * Returns the position the edit is inserted in the wal.

Modified: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/AbstractTransaction.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/AbstractTransaction.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/AbstractTransaction.java (original)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/AbstractTransaction.java Tue Oct 18 13:11:42 2011
@@ -3,8 +3,12 @@ package org.apache.directory.server.core
 
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Iterator;
 
-abstract class AbstractTransaction implements Transaction
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.name.Dn;
+
+abstract class AbstractTransaction<ID> implements Transaction<ID>
 {
     /** Logical time(LSN in the wal) when the txn began */ 
     long startTime;
@@ -16,7 +20,7 @@ abstract class AbstractTransaction imple
     State txnState;
     
     /** List of txns that this txn depends */
-    List<ReadWriteTxn> txnsToCheck = new ArrayList<ReadWriteTxn>();
+    List<ReadWriteTxn<ID>> txnsToCheck = new ArrayList<ReadWriteTxn<ID>>();
  
     
     public AbstractTransaction( )
@@ -70,7 +74,7 @@ abstract class AbstractTransaction imple
     /**
      * {@inheritDoc}
      */  
-    public List<ReadWriteTxn> getTxnsToCheck()
+    public List<ReadWriteTxn<ID>> getTxnsToCheck()
     {
         return this.txnsToCheck;
     }
@@ -91,5 +95,28 @@ abstract class AbstractTransaction imple
         this.txnState = newState;
     }
     
+    public Entry mergeUpdates( Dn partitionDn, ID entryID, Entry entry )
+    {
+        Entry prevEntry  = entry;
+        Entry curEntry = entry;
+        ReadWriteTxn<ID> curTxn;
+        boolean cloneOnChange = true;
+        
+        Iterator<ReadWriteTxn<ID>> it = txnsToCheck.iterator();
+        
+        while ( it.hasNext() )
+        {
+            curTxn = it.next();
+            curEntry = curTxn.applyUpdatesToEntry( partitionDn, entryID, curEntry, cloneOnChange );
+            
+            if ( curEntry != prevEntry )
+            {
+                cloneOnChange = false;
+            }
+        }
+        
+        return curEntry;
+    }
+    
     
 }
\ No newline at end of file

Modified: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnLogManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnLogManager.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnLogManager.java (original)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnLogManager.java Tue Oct 18 13:11:42 2011
@@ -9,20 +9,21 @@ import org.apache.directory.server.core.
 import org.apache.directory.server.core.log.Log;
 import org.apache.directory.server.core.log.InvalidLogException;
 
-
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.name.Dn;
 
 import org.apache.directory.server.core.txn.logedit.LogEdit;
 
 
-public class DefaultTxnLogManager implements TxnLogManager
+public class DefaultTxnLogManager<ID> implements TxnLogManager<ID>
 {
     /** Write ahea log */
     Log wal;
     
     /** Txn Manager */
-    TxnManagerInternal txnManager;
+    TxnManagerInternal<ID> txnManager;
     
-    public void init( Log logger, TxnManagerInternal txnManager )
+    public void init( Log logger, TxnManagerInternal<ID> txnManager )
     {
         this.wal = logger;
         this.txnManager = txnManager;
@@ -30,16 +31,16 @@ public class DefaultTxnLogManager implem
     /**
      * {@inheritDoc}
      */
-   public void log( LogEdit logEdit, boolean sync ) throws IOException
+   public void log( LogEdit<ID> logEdit, boolean sync ) throws IOException
    {
-       Transaction curTxn = txnManager.getCurTxn();
+       Transaction<ID> curTxn = txnManager.getCurTxn();
        
        if ( ( curTxn == null ) || ( ! ( curTxn instanceof ReadWriteTxn ) ) )
        {
            throw new IllegalStateException( "Trying to log logedit without ReadWriteTxn" );
        }
        
-       ReadWriteTxn txn = (ReadWriteTxn)curTxn;
+       ReadWriteTxn<ID> txn = (ReadWriteTxn<ID>)curTxn;
        UserLogRecord logRecord = txn.getUserLogRecord();
        
        
@@ -73,7 +74,7 @@ public class DefaultTxnLogManager implem
        this.log( logRecord, sync );
        
        logEdit.getLogAnchor().resetLogAnchor( logRecord.getLogAnchor() );
-       txn.getEdits().add( logEdit );
+       txn.addLogEdit( logEdit );
    }
     
    /**
@@ -92,4 +93,20 @@ public class DefaultTxnLogManager implem
    }
    
    
+   /**
+    * {@inheritDoc}
+    */
+   public Entry mergeUpdates(Dn partitionDn, ID entryID,  Entry entry )
+   {
+       Transaction<ID> curTxn = txnManager.getCurTxn();
+       
+       if ( ( curTxn == null ) )
+       {
+           throw new IllegalStateException( "Trying to merge with log wihout txn" );
+       }
+       
+       return curTxn.mergeUpdates( partitionDn, entryID, entry );
+   }
+   
+   
 }

Modified: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnManager.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnManager.java (original)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnManager.java Tue Oct 18 13:11:42 2011
@@ -1,14 +1,14 @@
 
 package org.apache.directory.server.core.txn;
 
+import org.apache.directory.server.core.partition.index.Serializer;
 import org.apache.directory.server.core.txn.logedit.TxnStateChange;
 import org.apache.directory.server.core.log.LogAnchor;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 
@@ -21,33 +21,38 @@ import java.util.concurrent.atomic.Atomi
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.directory.server.core.log.UserLogRecord;
-import org.apache.directory.server.core.log.LogAnchor;
 
 import java.io.IOException;
 
 
-public class DefaultTxnManager implements TxnManager, TxnManagerInternal
+public class DefaultTxnManager<ID> implements  TxnManagerInternal<ID>
 {
     /** wal log manager */
-    TxnLogManager txnLogManager;
+    private TxnLogManager<ID> txnLogManager;
     
     /** List of committed txns in commit LSN order */
-    ConcurrentLinkedQueue<ReadWriteTxn> committedQueue = new ConcurrentLinkedQueue<ReadWriteTxn>();
+    private ConcurrentLinkedQueue<ReadWriteTxn<ID>> committedQueue = new ConcurrentLinkedQueue<ReadWriteTxn<ID>>();
     
     /** Verify lock under which txn verification is done */
-    Lock verifyLock = new ReentrantLock();
+    private Lock verifyLock = new ReentrantLock();
     
     /** Used to assign start and commit version numbers to writeTxns */
-    Lock writeTxnsLock = new ReentrantLock();
+    private Lock writeTxnsLock = new ReentrantLock();
     
     /** Latest committed txn on which read only txns can depend */
-    AtomicReference<ReadWriteTxn> latestCommittedTxn = new AtomicReference<ReadWriteTxn>();
+    private AtomicReference<ReadWriteTxn<ID>> latestCommittedTxn = new AtomicReference<ReadWriteTxn<ID>>();
     
     /** Latest verified write txn */
-    AtomicReference<ReadWriteTxn> latestVerifiedTxn = new AtomicReference<ReadWriteTxn>();
+    private AtomicReference<ReadWriteTxn<ID>> latestVerifiedTxn = new AtomicReference<ReadWriteTxn<ID>>();
     
     /** Latest flushed txn's logical commit time */
-    AtomicLong latestFlushedTxnLSN = new AtomicLong( 0 );
+    private AtomicLong latestFlushedTxnLSN = new AtomicLong( 0 );
+    
+    /** ID comparator */
+    private Comparator<ID> idComparator;
+    
+    /** ID serializer */
+    private Serializer idSerializer ;
     
     /** Per thread txn context */
     static final ThreadLocal < Transaction > txnVar = 
@@ -60,9 +65,27 @@ public class DefaultTxnManager implement
              }
         };
     
-    public void init( TxnLogManager txnLogManager )
+    public void init( TxnLogManager<ID> txnLogManager, Comparator<ID> idComparator, Serializer idSerializer )
     {
         this.txnLogManager = txnLogManager;
+        this.idComparator = idComparator;
+        this.idSerializer = idSerializer;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Comparator<ID> getIDComparator()
+    {
+        return this.idComparator;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Serializer getIDSerializer()
+    {
+        return this.idSerializer;
     }
     
     /**
@@ -70,7 +93,7 @@ public class DefaultTxnManager implement
      */  
     public void beginTransaction( boolean readOnly ) throws IOException
     {
-        Transaction curTxn = txnVar.get();
+        Transaction<ID> curTxn = this.getCurTxn();
         
         if ( curTxn != null )
         {
@@ -94,7 +117,7 @@ public class DefaultTxnManager implement
      */
     public void commitTransaction() throws IOException
     {
-        Transaction txn = txnVar.get();
+        Transaction<ID> txn = this.getCurTxn();
         
         if ( txn == null )
         {
@@ -109,7 +132,7 @@ public class DefaultTxnManager implement
         }
         else
         {
-            this.commitReadWriteTxn( (ReadWriteTxn)txn );
+            this.commitReadWriteTxn( (ReadWriteTxn<ID>)txn );
         }
         
         txnVar.set( null );
@@ -121,7 +144,7 @@ public class DefaultTxnManager implement
      */
     public void abortTransaction() throws IOException
     {
-        Transaction txn = txnVar.get();
+        Transaction<ID> txn = this.getCurTxn();
         
         if ( txn == null )
         {
@@ -133,7 +156,7 @@ public class DefaultTxnManager implement
         
         if ( txn instanceof ReadWriteTxn )
         {
-            this.abortReadWriteTxn( (ReadWriteTxn)txn );
+            this.abortReadWriteTxn( (ReadWriteTxn<ID>)txn );
         }
         
         txn.abortTxn();
@@ -143,15 +166,16 @@ public class DefaultTxnManager implement
     /**
      * {@inheritDoc}
      */
-    public Transaction getCurTxn()
+    @SuppressWarnings("unchecked")
+    public Transaction<ID> getCurTxn()
     {
-       return txnVar.get(); 
+       return (Transaction<ID>)txnVar.get(); 
     }
     
     private void beginReadOnlyTxn()
     {
-        ReadOnlyTxn txn = new ReadOnlyTxn();
-        ReadWriteTxn lastTxnToCheck = null;
+        ReadOnlyTxn<ID> txn = new ReadOnlyTxn<ID>();
+        ReadWriteTxn<ID> lastTxnToCheck = null;
         
         do
         {
@@ -176,18 +200,19 @@ public class DefaultTxnManager implement
             startTime = LogAnchor.UNKNOWN_LSN;
         }
         
+        txn.startTxn( startTime );
+        
         this.buildCheckList( txn, lastTxnToCheck );
         txnVar.set( txn );
     }
     
     private void beginReadWriteTxn() throws IOException
     {
-        long txnID;
         
-        ReadWriteTxn txn = new ReadWriteTxn();
+        ReadWriteTxn<ID> txn = new ReadWriteTxn<ID>();
         UserLogRecord logRecord = txn.getUserLogRecord();
         
-        TxnStateChange txnRecord = new TxnStateChange( LogAnchor.UNKNOWN_LSN, 
+        TxnStateChange<ID> txnRecord = new TxnStateChange<ID>( LogAnchor.UNKNOWN_LSN, 
                 TxnStateChange.State.TXN_BEGIN );
         ObjectOutputStream out = null;
         ByteArrayOutputStream bout = null;
@@ -217,7 +242,7 @@ public class DefaultTxnManager implement
         
         logRecord.setData(  data, data.length );
         
-        ReadWriteTxn lastTxnToCheck = null; 
+        ReadWriteTxn<ID> lastTxnToCheck = null; 
         writeTxnsLock.lock();
         
         try
@@ -250,15 +275,15 @@ public class DefaultTxnManager implement
     
     
     
-    private void buildCheckList( Transaction txn, ReadWriteTxn lastTxnToCheck )
+    private void buildCheckList( Transaction<ID> txn, ReadWriteTxn<ID> lastTxnToCheck )
     {
         if ( lastTxnToCheck != null )
         {
             long lastLSN = lastTxnToCheck.getCommitTime();
-            ReadWriteTxn toAdd;
+            ReadWriteTxn<ID> toAdd;
 
-            List<ReadWriteTxn> toCheckList = txn.getTxnsToCheck();
-            Iterator<ReadWriteTxn> it = committedQueue.iterator();
+            List<ReadWriteTxn<ID>> toCheckList = txn.getTxnsToCheck();
+            Iterator<ReadWriteTxn<ID>> it = committedQueue.iterator();
             while ( it.hasNext() )
             {
                 toAdd = it.next();
@@ -277,7 +302,7 @@ public class DefaultTxnManager implement
             long flushedLSN = latestFlushedTxnLSN.get();
 
             it = toCheckList.iterator();
-            ReadWriteTxn toCheck;
+            ReadWriteTxn<ID> toCheck;
             while ( it.hasNext() )
             {
                 toCheck = it.next();
@@ -292,13 +317,13 @@ public class DefaultTxnManager implement
     }
     
     
-    private void prepareForEndingTxn( Transaction txn )
+    private void prepareForEndingTxn( Transaction<ID> txn )
     {
-        List<ReadWriteTxn> toCheck = txn.getTxnsToCheck();
+        List<ReadWriteTxn<ID>> toCheck = txn.getTxnsToCheck();
         
         if ( toCheck.size() > 0 )
         {
-            ReadWriteTxn lastTxnToCheck = toCheck.get( toCheck.size() - 1 );
+            ReadWriteTxn<ID> lastTxnToCheck = toCheck.get( toCheck.size() - 1 );
             
             if ( lastTxnToCheck.commitTime != txn.getStartTime() )
             {
@@ -316,11 +341,11 @@ public class DefaultTxnManager implement
         }
     }
     
-    private void commitReadWriteTxn( ReadWriteTxn txn ) throws IOException
+    private void commitReadWriteTxn( ReadWriteTxn<ID> txn ) throws IOException
     {
         UserLogRecord logRecord = txn.getUserLogRecord();
 
-        TxnStateChange txnRecord = new TxnStateChange( txn.getStartTime(),
+        TxnStateChange<ID> txnRecord = new TxnStateChange<ID>( txn.getStartTime(),
             TxnStateChange.State.TXN_COMMIT );
         ObjectOutputStream out = null;
         ByteArrayOutputStream bout = null;
@@ -374,11 +399,11 @@ public class DefaultTxnManager implement
     }
     
     
-    private void abortReadWriteTxn( ReadWriteTxn txn ) throws IOException
+    private void abortReadWriteTxn( ReadWriteTxn<ID> txn ) throws IOException
     {
         UserLogRecord logRecord = txn.getUserLogRecord();
 
-        TxnStateChange txnRecord = new TxnStateChange( txn.getStartTime(),
+        TxnStateChange<ID> txnRecord = new TxnStateChange<ID>( txn.getStartTime(),
             TxnStateChange.State.TXN_ABORT );
         ObjectOutputStream out = null;
         ByteArrayOutputStream bout = null;

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/IndexCursorWrapper.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/IndexCursorWrapper.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/IndexCursorWrapper.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/IndexCursorWrapper.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,570 @@
+
+package org.apache.directory.server.core.txn;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+
+import org.apache.directory.server.core.partition.index.AbstractIndexCursor;
+import org.apache.directory.server.core.partition.index.ForwardIndexEntry;
+import org.apache.directory.server.core.partition.index.IndexCursor;
+import org.apache.directory.server.core.partition.index.IndexEntry;
+import org.apache.directory.server.i18n.I18n;
+
+import org.apache.directory.shared.ldap.model.cursor.InvalidCursorPositionException;
+import org.apache.directory.shared.ldap.model.name.Dn;
+
+public class IndexCursorWrapper<V, E, ID> extends AbstractIndexCursor<V, E, ID>
+{
+    /** Cursors to merge */
+    private ArrayList<IndexCursor<V,E,ID>> cursors;
+    
+    /** list of values available per cursor */
+    private ArrayList<IndexEntry<V,ID>> values;
+    
+    /** index get should get the value from */
+    private int getIndex = -1;
+    
+    /** Dn of the partition */
+    private Dn partitionDn;
+    
+    /** Index attribute oid */
+    private String attributeOid;
+    
+    /** whether this is a cursor on forward or reverse index */
+    private boolean forwardIndex;
+    
+    /** List of txns that this cursor depends on */
+    private ArrayList<ReadWriteTxn<ID>> txns;
+    
+    /** True if cursor is positioned */
+    private boolean positioned;
+    
+    /** direction of the move */
+    boolean movingNext = true;
+    
+    /** Comparator used to order the index entries */
+    private Comparator<IndexEntry<V,ID>> comparator;
+    
+    /** unsupported operation message */
+    private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_722 );
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void after( IndexEntry<V, ID> element ) throws Exception
+    {
+        int idx;
+        positioned = true;
+        movingNext = true;
+        IndexCursor<V,E,ID> cursor;
+        
+        checkNotClosed( "after()" );
+        
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            values.set( idx, null );
+            cursor = cursors.get( idx );
+            if( cursor != null )
+            {
+             cursor.after( element );
+            }
+        }
+        
+        getIndex = -1;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void before( IndexEntry<V, ID> element ) throws Exception
+    {
+        int idx;
+        positioned = true;
+        movingNext = true;
+        IndexCursor<V,E,ID> cursor;
+        
+        checkNotClosed( "before()" );
+        
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            values.set( idx, null );
+            cursor = cursors.get( idx );
+            if( cursor != null )
+            {
+                cursor.before( element );
+            }
+        }
+        
+        getIndex = -1;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void afterValue( ID id, V value ) throws Exception
+    {
+        int idx;
+        positioned = true;
+        movingNext = true;
+        IndexCursor<V,E,ID> cursor;
+        
+        checkNotClosed( "afterValue()" );
+        
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            values.set( idx, null );
+            cursor = cursors.get( idx );
+            if( cursor != null )
+            {
+                cursor.afterValue( id, value );
+            }
+        }
+        
+        getIndex = -1;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void beforeValue( ID id, V value ) throws Exception
+    {
+        int idx;
+        positioned = true;
+        movingNext = true;
+        IndexCursor<V,E,ID> cursor;
+        
+        checkNotClosed( "beforeValue()" );
+        
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            values.set( idx, null );
+            cursor = cursors.get( idx );
+            if( cursor != null )
+            {
+                cursor.beforeValue( id, value );
+            }
+        }
+        
+        getIndex = -1;
+    }
+   
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void beforeFirst() throws Exception
+    {
+        int idx;
+        positioned = true;
+        movingNext = true;
+        IndexCursor<V,E,ID> cursor;
+        
+        checkNotClosed( "beforeFirst()" );
+        
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            values.set( idx, null );
+            cursor = cursors.get( idx );
+            if( cursor != null )
+            {
+                cursor.beforeFirst();
+            }
+        }
+        
+        getIndex = -1;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void afterLast() throws Exception
+    {
+        int idx;
+        positioned = true;
+        movingNext = false;
+        IndexCursor<V,E,ID> cursor;
+        
+        checkNotClosed( "afterLast()" );
+        
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            values.set( idx, null );
+            cursor = cursors.get( idx );
+            if( cursor != null )
+            {
+                cursor.afterLast( );
+            }
+            
+        }
+        
+        getIndex = -1;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean first() throws Exception
+    {
+        this.beforeFirst();
+        return this.next();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean last() throws Exception
+    {
+        this.afterLast();
+        return this.previous();
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public boolean next() throws Exception
+    {
+        IndexCursor<V,E,ID> cursor;
+        IndexEntry<V,ID> minValue;
+        IndexEntry<V,ID> value;
+        
+        checkNotClosed( "next()" );
+        
+        IndexEntry<V,ID> lastValue = null;
+        if ( getIndex >= 0 )
+        {
+            lastValue = values.get( getIndex );
+        }
+        
+        int idx;
+        if ( positioned == false )
+        {
+            afterLast();
+        }
+        
+        if ( movingNext == false || ( getIndex < 0 ) )
+        {
+            minValue = null;
+            getIndex = -1;
+            for ( idx = 0; idx < values.size(); idx++ )
+            {
+                cursor = cursors.get( idx );
+                if ( cursor != null && cursor.next() )
+                {
+                    value = cursor.get();
+                    if ( ( getIndex < 0 ) || ( comparator.compare( value, minValue ) < 0 ) )
+                    {
+                        minValue = value;
+                        getIndex = idx;
+                    }
+                    
+                    values.set( idx, value );
+                }
+                else
+                {
+                    values.set( idx, null );
+                }
+            }
+            
+        }
+        else
+        {
+            // Move the last cursor we did a get from and recompute minimum
+           this.recomputeMinimum();
+        }
+        
+        int txnIdx;
+        ReadWriteTxn<ID> curTxn;
+        boolean valueDeleted;
+        do
+        {
+            if ( getIndex < 0 )
+            {
+                break;
+            }
+            
+            value = values.get( getIndex );
+            
+            txnIdx = getIndex;
+            if ( txnIdx > 0 )
+            {
+                txnIdx--;
+            }
+            
+            valueDeleted = false;
+            for ( ; txnIdx < txns.size(); txnIdx++ )
+            {
+                curTxn = txns.get( txnIdx );
+                
+                // TODO check for index entry delete here
+                if ( curTxn!= null)
+                {
+                    valueDeleted = true;
+                    break;
+                }
+            }
+            
+            if ( valueDeleted == false && ( lastValue == null || ( comparator.compare( value, lastValue ) > 0 ) ) )
+            {
+                break;
+            }
+            
+            // Recompute minimum
+            this.recomputeMinimum();
+            
+        } while ( true );
+        
+        return ( getIndex >= 0 );
+
+    } 
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public boolean previous() throws Exception
+    {
+        IndexCursor<V,E,ID> cursor;
+        IndexEntry<V,ID> maxValue;
+        IndexEntry<V,ID> value;
+        
+        checkNotClosed( "previous()" );
+        
+        IndexEntry<V,ID> lastValue = null;
+        if ( getIndex >= 0 )
+        {
+            lastValue = values.get( getIndex );
+        }
+        
+        int idx;
+        if ( positioned == false )
+        {
+            afterLast();
+        }
+        
+        if ( movingNext == false || ( getIndex < 0 ) )
+        {
+            maxValue = null;
+            getIndex = -1;
+            for ( idx = 0; idx < values.size(); idx++ )
+            {
+                cursor = cursors.get( idx );
+                if ( cursor != null && cursor.next() )
+                {
+                    value = cursor.get();
+                    if ( ( getIndex < 0 ) || ( comparator.compare( value, maxValue ) > 0 ) )
+                    {
+                        maxValue = value;
+                        getIndex = idx;
+                    }
+                    
+                    values.set( idx, value );
+                }
+                else
+                {
+                    values.set( idx, null );
+                }
+            }
+            
+        }
+        else
+        {
+            // Move the last cursor we did a get from and recompute maximum
+           this.recomputeMaximum();
+        }
+        
+        int txnIdx;
+        ReadWriteTxn<ID> curTxn;
+        boolean valueDeleted;
+        do
+        {
+            if ( getIndex < 0 )
+            {
+                break;
+            }
+            
+            value = values.get( getIndex );
+            
+            txnIdx = getIndex;
+            if ( txnIdx > 0 )
+            {
+                txnIdx--;
+            }
+            
+            valueDeleted = false;
+            for ( ; txnIdx < txns.size(); txnIdx++ )
+            {
+                curTxn = txns.get( txnIdx );
+                
+                // TODO check for index entry delete here
+                if ( curTxn!= null)
+                {
+                    valueDeleted = true;
+                    break;
+                }
+            }
+            
+            if ( valueDeleted == false && ( lastValue == null || ( comparator.compare( value, lastValue ) < 0 ) ) )
+            {
+                break;
+            }
+            
+            // Recompute maximum
+            this.recomputeMaximum();
+            
+        } while ( true );
+        
+        return ( getIndex >= 0 );
+
+    }
+    
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public IndexEntry<V, ID> get() throws Exception
+    {
+        checkNotClosed( "get()" );
+        
+        if ( getIndex >= 0 )
+        {
+            IndexEntry<V,ID> value = values.get( getIndex );
+            
+            if ( value == null )
+            {
+                throw new IllegalStateException( "getIndex points to a null value" );
+            }
+            
+            return value;
+        }
+
+        throw new InvalidCursorPositionException();
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void close() throws Exception
+    {
+        
+        super.close();
+        
+        IndexCursor<V,E,ID> cursor;
+        int idx;
+        
+        for ( idx = 0; idx < cursors.size(); idx++ )
+        {
+            cursor = cursors.get( idx );
+            if ( cursor != null )
+            {
+                cursor.close();
+            }
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void close( Exception cause ) throws Exception
+    {
+        super.close( cause );
+        
+        IndexCursor<V,E,ID> cursor;
+        int idx;
+        
+        for ( idx = 0; idx < cursors.size(); idx++ )
+        {
+            cursor = cursors.get( idx );
+            if ( cursor != null )
+            {
+                cursor.close( cause );
+            }
+        }
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    protected String getUnsupportedMessage()
+    {
+        return UNSUPPORTED_MSG;
+    }
+
+    
+    private void recomputeMinimum() throws Exception
+    {
+        IndexCursor<V,E,ID> cursor;
+        IndexEntry<V,ID> minValue;
+        IndexEntry<V,ID> value;
+        int idx;
+        
+        cursor = cursors.get( getIndex );
+        if ( cursor.next() )
+        {
+            values.set( getIndex , cursor.get() );
+        }
+        else
+        {
+            values.set( getIndex, null );
+        }
+        
+        
+        minValue = null;
+        getIndex = -1;
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            value = values.get( idx );
+            if ( value != null )
+            {
+                if ( ( getIndex < 0 ) || ( comparator.compare( value, minValue ) < 0 ) )
+                {
+                    minValue = value;
+                    getIndex = idx;
+                }
+            }
+        }
+    }
+    
+    private void recomputeMaximum() throws Exception
+    {
+        IndexCursor<V,E,ID> cursor;
+        IndexEntry<V,ID> maxValue;
+        IndexEntry<V,ID> value;
+        int idx;
+        
+        cursor = cursors.get( getIndex );
+        if ( cursor.next() )
+        {
+            values.set( getIndex , cursor.get() );
+        }
+        else
+        {
+            values.set( getIndex, null );
+        }
+        
+        
+        maxValue = null;
+        getIndex = -1;
+        for ( idx = 0; idx < values.size(); idx++ )
+        {
+            value = values.get( idx );
+            if ( value != null )
+            {
+                if ( ( getIndex < 0 ) || ( comparator.compare( value, maxValue ) > 0 ) )
+                {
+                    maxValue = value;
+                    getIndex = idx;
+                }
+            }
+        }
+    }
+
+}

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/MasterTableWrapper.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/MasterTableWrapper.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/MasterTableWrapper.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/MasterTableWrapper.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,311 @@
+
+package org.apache.directory.server.core.txn;
+
+import java.util.Comparator;
+
+import org.apache.directory.server.core.partition.index.MasterTable;
+import org.apache.directory.shared.ldap.model.cursor.Cursor;
+import org.apache.directory.shared.ldap.model.cursor.Tuple;
+
+import org.apache.directory.shared.ldap.model.entry.Entry;
+
+public class MasterTableWrapper<ID, Entry> implements MasterTable<ID, Entry>
+{
+    private MasterTable<ID, Entry> wrappedTable;
+    
+    /**
+     * {@inheritDoc}
+     */
+    public ID getNextId( Entry entry ) throws Exception
+    {
+        return wrappedTable.getNextId( entry );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void resetCounter() throws Exception
+    {
+        wrappedTable.resetCounter();
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Comparator<ID> getKeyComparator()
+    {
+        return wrappedTable.getKeyComparator();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Comparator<Entry> getValueComparator()
+    {
+        return wrappedTable.getValueComparator();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getName()
+    {
+       return wrappedTable.getName(); 
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isDupsEnabled()
+    {
+        return wrappedTable.isDupsEnabled();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Simple Table Key/Value Assertions 
+    // ------------------------------------------------------------------------
+
+    /**
+     * Checks to see if this table has one or more tuples with a specific key:
+     * this is exactly the same as a get call with a check to see if the
+     * returned value is null or not.
+     *
+     * @param key the Object of the key to check for
+     * @return true if the key exists, false otherwise
+     * @throws Exception if there is a failure to read the underlying Db
+     */
+    boolean has( K key ) throws Exception;
+
+
+    /**
+     * Checks to see if this table has a key with a specific value.
+     *
+     * @param key the key to check for
+     * @param value the value to check for
+     * @return true if a record with the key and value exists, false otherwise
+     * @throws Exception if there is a failure to read the underlying Db
+     */
+    boolean has( K key, V value ) throws Exception;
+
+
+    /**
+     * Checks to see if this table has a record with a key greater than or
+     * equal to the key argument.  The key argument need not exist for this
+     * call to return true.  The underlying database must sort keys based on a
+     * key comparator because this method depends on key ordering.
+     *
+     * @param key the key to compare keys to
+     * @return true if a Tuple with a key greater than or equal to the key
+     * argument exists, false otherwise
+     * @throws Exception if there is a failure to read the underlying Db
+     */
+    boolean hasGreaterOrEqual( K key ) throws Exception;
+
+
+    /**
+     * Checks to see if this table has a record with a key less than or
+     * equal to the key argument.  The key argument need not exist for this
+     * call to return true.  The underlying database must sort keys based on a
+     * key comparator because this method depends on key ordering.
+     *
+     * @param key the key to compare keys to
+     * @return true if a Tuple with a key less than or equal to the key
+     * argument exists, false otherwise
+     * @throws Exception if there is a failure to read the underlying Db
+     */
+    boolean hasLessOrEqual( K key ) throws Exception;
+
+
+    /**
+     * Checks to see if this table has a Tuple with a key equal to the key
+     * argument, yet with a value greater than or equal to the value argument
+     * provided.  The key argument <strong>MUST</strong> exist for this call
+     * to return true and the underlying Db must allow for values of duplicate
+     * keys to be sorted.  The entire basis to this method depends on the fact
+     * that tuples of the same key have values sorted according to a valid
+     * value comparator.
+     *
+     * If the table does not support duplicates then an
+     * UnsupportedOperationException is thrown.
+     *
+     * @param key the key
+     * @param val the value to compare values to
+     * @return true if a Tuple with a key equal to the key argument and a
+     * value greater than the value argument exists, false otherwise
+     * @throws Exception if there is a failure to read the underlying Db
+     * or if the underlying Db is not of the Btree type that allows sorted
+     * duplicate values.
+     */
+    boolean hasGreaterOrEqual( K key, V val ) throws Exception;
+
+
+    /**
+     * Checks to see if this table has a Tuple with a key equal to the key
+     * argument, yet with a value less than or equal to the value argument
+     * provided.  The key argument <strong>MUST</strong> exist for this call
+     * to return true and the underlying Db must allow for values of duplicate
+     * keys to be sorted.  The entire basis to this method depends on the fact
+     * that tuples of the same key have values sorted according to a valid
+     * value comparator.
+     *
+     * If the table does not support duplicates then an
+     * UnsupportedOperationException is thrown.
+     *
+     * @param key the key
+     * @param val the value to compare values to
+     * @return true if a Tuple with a key equal to the key argument and a
+     * value less than the value argument exists, false otherwise
+     * @throws Exception if there is a failure to read the underlying Db
+     * or if the underlying Db is not of the Btree type that allows sorted
+     * duplicate values.
+     */
+    boolean hasLessOrEqual( K key, V val ) throws Exception;
+
+
+    /**
+     * {@inheritDoc}
+     */
+    Entry get( ID key ) throws Exception
+    {
+        Entry entry = wrappedTable.get( key ); 
+    }
+
+
+    /**
+     * Puts a record into this Table.  Null is not allowed for keys or values
+     * and should result in an IllegalArgumentException.
+     *
+     * @param key the key of the record
+     * @param value the value of the record.
+     * @throws Exception if there is a failure to read or write to the
+     * underlying Db
+     * @throws IllegalArgumentException if a null key or value is used
+     */
+    void put( K key, V value ) throws Exception;
+
+
+    /**
+     * Removes all records with a specified key from this Table.
+     *
+     * @param key the key of the records to remove
+     * @throws Exception if there is a failure to read or write to
+     * the underlying Db
+     */
+    void remove( K key ) throws Exception;
+
+
+    /**
+     * Removes a single key value pair with a specified key and value from
+     * this Table.
+     *
+     * @param key the key of the record to remove
+     * @param value the value of the record to remove
+     * @throws Exception if there is a failure to read or write to
+     * the underlying Db
+     */
+    void remove( K key, V value ) throws Exception;
+
+
+    /**
+     * Creates a Cursor that traverses Tuples in a Table.
+     *
+     * @return a Cursor over Tuples containing the key value pairs
+     * @throws Exception if there are failures accessing underlying stores
+     */
+    Cursor<Tuple<K, V>> cursor() throws Exception;
+
+
+    /**
+     * Creates a Cursor that traverses Table Tuples for the same key. Only
+     * Tuples with the provided key will be returned if the key exists at
+     * all.  If the key does not exist an empty Cursor is returned.  The
+     * motivation behind this method is to minimize the need for callers to
+     * actively constrain Cursor operations based on the Tuples they return
+     * to a specific key.  This Cursor is naturally limited to return only
+     * the tuples for the same key.
+     *
+     * @param key the duplicate key to return the Tuples of
+     * @return a Cursor over Tuples containing the same key
+     * @throws Exception if there are failures accessing underlying stores
+     */
+    Cursor<Tuple<K, V>> cursor( K key ) throws Exception;
+
+
+    /**
+     * Creates a Cursor that traverses Table values for the same key. Only
+     * Tuples with the provided key will have their values returned if the key
+     * exists at all.  If the key does not exist an empty Cursor is returned.
+     * The motivation behind this method is to minimize the need for callers
+     * to actively constrain Cursor operations to a specific key while
+     * removing overheads in creating new Tuples or population one that is
+     * reused to return key value pairs.  This Cursor is naturally limited to
+     * return only the values for the same key.
+     *
+     * @param key the duplicate key to return the values of
+     * @return a Cursor over values of a key
+     * @throws Exception if there are failures accessing underlying stores
+     */
+    Cursor<V> valueCursor( K key ) throws Exception;
+
+
+    // ------------------------------------------------------------------------
+    // Table Record Count Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Gets the count of the number of records in this Table.
+     *
+     * @return the number of records
+     * @throws Exception if there is a failure to read the underlying Db
+     */
+    int count() throws Exception;
+
+
+    /**
+     * Gets the count of the number of records in this Table with a specific
+     * key: returns the number of duplicates for a key.
+     *
+     * @param key the Object key to count.
+     * @return the number of duplicate records for a key.
+     * @throws Exception if there is a failure to read the underlying Db
+     */
+    int count( K key ) throws Exception;
+
+
+    /**
+     * Gets the number of records greater than or equal to a key value.  The 
+     * specific key argument provided need not exist for this call to return 
+     * a non-zero value.
+     *
+     * @param key the key to use in comparisons
+     * @return the number of keys greater than or equal to the key
+     * @throws Exception if there is a failure to read the underlying db
+     */
+    int greaterThanCount( K key ) throws Exception;
+
+
+    /**
+     * Gets the number of records less than or equal to a key value.  The 
+     * specific key argument provided need not exist for this call to return 
+     * a non-zero value.
+     *
+     * @param key the key to use in comparisons
+     * @return the number of keys less than or equal to the key
+     * @throws Exception if there is a failure to read the underlying db
+     */
+    int lessThanCount( K key ) throws Exception;
+
+
+    /**
+     * Closes the underlying Db of this Table.
+     *
+     * @throws Exception on any failures
+     */
+    void close() throws Exception;
+    
+}

Modified: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadOnlyTxn.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadOnlyTxn.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadOnlyTxn.java (original)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadOnlyTxn.java Tue Oct 18 13:11:42 2011
@@ -1,7 +1,7 @@
 
 package org.apache.directory.server.core.txn;
 
-public class ReadOnlyTxn extends AbstractTransaction
+class ReadOnlyTxn<ID> extends AbstractTransaction<ID>
 {
    
 }

Modified: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadWriteTxn.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadWriteTxn.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadWriteTxn.java (original)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadWriteTxn.java Tue Oct 18 13:11:42 2011
@@ -3,17 +3,41 @@ package org.apache.directory.server.core
 
 import java.util.List;
 import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.Map;
+import java.util.HashMap;
 
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.Comparator;
 
 import org.apache.directory.server.core.txn.logedit.LogEdit;
+import org.apache.directory.server.core.txn.logedit.IndexChange;
+import org.apache.directory.server.core.txn.logedit.DataChange;
+import org.apache.directory.server.core.txn.logedit.EntryAddDelete;
+import org.apache.directory.server.core.txn.logedit.EntryChange;
+import org.apache.directory.server.core.txn.logedit.DataChangeContainer;
 
 import org.apache.directory.server.core.log.UserLogRecord;
 
-public class ReadWriteTxn extends AbstractTransaction
+import org.apache.directory.server.core.partition.index.ForwardIndexEntry;
+import org.apache.directory.server.core.partition.index.IndexEntry;
+import org.apache.directory.server.core.partition.index.Index;
+import org.apache.directory.server.core.partition.index.Serializer;
+
+import org.apache.directory.shared.ldap.model.name.Dn;
+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.Value;
+
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+
+import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
+
+class ReadWriteTxn<ID> extends AbstractTransaction<ID>
 {  
     /** list of log edits by the txn */
-    List<LogEdit> logEdits = new LinkedList<LogEdit>();
+    private List<LogEdit<ID>> logEdits = new LinkedList<LogEdit<ID>>();
     
     /*
      * Number of txns that depend on this txn and previous committed
@@ -22,14 +46,19 @@ public class ReadWriteTxn extends Abstra
      * committed and ref count becomes zero for all the previously
      * committed txns.
      */
-    AtomicInteger txnRefCount = new AtomicInteger( 0 );
+    private AtomicInteger txnRefCount = new AtomicInteger( 0 );
     
     /** User record used to communicate data with log manager */
-    UserLogRecord logRecord = new UserLogRecord();
+    private UserLogRecord logRecord = new UserLogRecord();
+    
+    private Map<Dn, Map<String, TreeSet< IndexEntry<Object,ID> >>> forwardIndexAdds  = 
+        new HashMap<Dn,  Map<String, TreeSet< IndexEntry<Object,ID> >>>();
     
-    // TODO add a map of index changes 
-   
+    private Map<Dn, Map<String, TreeSet< IndexEntry<Object,ID> >>> reverseIndexAdds  = 
+        new HashMap<Dn,  Map<String, TreeSet< IndexEntry<Object,ID> >>>();
     
+    private Map<Dn, Map<String, TreeSet< IndexEntry<Object,ID> >>> indexDeletes  = 
+        new HashMap<Dn,  Map<String, TreeSet< IndexEntry<Object,ID> >>>();
       
     public AtomicInteger getRefCount()
     {
@@ -38,13 +67,227 @@ public class ReadWriteTxn extends Abstra
     
     public UserLogRecord getUserLogRecord()
     {
-        return this.getUserLogRecord();
+        return logRecord;
     }
     
-    public List<LogEdit> getEdits()
+    public List<LogEdit<ID>> getEdits()
     {
         return logEdits;
     }
     
+    @SuppressWarnings("unchecked")
+    public void addLogEdit( LogEdit<ID> edit )
+    {
+        logEdits.add( edit );
+        
+        /*
+         * Update the in memory summary of the index changes
+         */
+        if ( edit instanceof DataChangeContainer )
+        {
+            DataChangeContainer<ID> dEdit = (DataChangeContainer<ID>)edit; 
+            List<DataChange<ID>> dataChanges =  dEdit.getChanges();
+            Iterator<DataChange<ID>> it = dataChanges.iterator();
+            Dn partitionDn = dEdit.getPartitionDn();
+            
+            DataChange<ID> nextChange;            
+            IndexChange<ID> indexChange;
+            IndexChange.Type indexChangeType;
+            ForwardIndexEntry<Object,ID> indexEntry;
+            
+            Map<String, TreeSet<IndexEntry<Object,ID>>> forwardIndices = 
+                forwardIndexAdds.get( partitionDn );
+            
+            Map<String, TreeSet<IndexEntry<Object,ID>>> reverseIndices = 
+                reverseIndexAdds.get( partitionDn );
+            
+            if ( forwardIndices == null )
+            {
+                forwardIndices = new HashMap<String, TreeSet<IndexEntry<Object,ID>>>();
+                
+                // Reverse index changes should be null too
+                reverseIndices = new HashMap<String, TreeSet<IndexEntry<Object,ID>>>();
+                
+                forwardIndexAdds.put( partitionDn, forwardIndices );
+                reverseIndexAdds.put( partitionDn, reverseIndices );
+            }
+            
+            Map<String, TreeSet< IndexEntry<Object,ID>>> deletedIndices = 
+                    indexDeletes.get( partitionDn ); 
+            
+            if ( deletedIndices == null )
+            {
+                deletedIndices = new HashMap<String, TreeSet< IndexEntry<Object,ID>>>();
+                indexDeletes.put( partitionDn, deletedIndices );
+            }
+            
+            while( it.hasNext() )
+            {
+                nextChange = it.next();
+                if ( nextChange instanceof IndexChange )
+                {
+                    indexChange = (IndexChange<ID>) nextChange;
+                    indexChangeType = indexChange.getType();
+                    Index<Object,?,ID> index = (Index<Object,?,ID>)indexChange.getIndex();
+                    
+                    TreeSet<IndexEntry<Object,ID>> forwardAdds = 
+                        forwardIndices.get( indexChange.getOID() );
+                    
+                    TreeSet<IndexEntry<Object,ID>> reverseAdds = 
+                        reverseIndices.get( indexChange.getOID() );
+                    
+                    if ( forwardAdds == null )
+                    {
+                        forwardAdds = 
+                            new TreeSet<IndexEntry<Object, ID>>( index.getForwardIndexEntryComparator() );
+                        reverseAdds = 
+                            new TreeSet<IndexEntry<Object, ID>>( index.getReverseIndexEntryComparator() );
+                        
+                        forwardIndices.put( indexChange.getOID(), forwardAdds );
+                        reverseIndices.put( indexChange.getOID(), forwardAdds );
+                    }
+                    
+                    TreeSet<IndexEntry<Object,ID>> deletes = deletedIndices.get( indexChange.getOID() );
+                    if ( deletes == null )
+                    {
+                        deletes = new TreeSet<IndexEntry<Object,ID>>( index.getForwardIndexEntryComparator() );
+                        deletedIndices.put( indexChange.getOID(), deletes );
+                    }
+                    
+                    
+                    indexEntry = new ForwardIndexEntry<Object,ID>();
+                    indexEntry.setValue( indexChange.getKey() );
+                    indexEntry.setId( indexChange.getID() );
+                    
+                    if ( indexChangeType == IndexChange.Type.ADD )
+                    {
+                        deletes.remove( indexEntry );
+                        forwardAdds.add( indexEntry );
+                        reverseAdds.add( indexEntry );
+                    }
+                    else
+                    {
+                        deletes.add( indexEntry );
+                        forwardAdds.remove( indexEntry );
+                        reverseAdds.remove( indexEntry );
+                    }
+                }
+            }
+            
+        } 
+    }
+    
+    public Entry applyUpdatesToEntry( Dn partitionDn, ID entryID, Entry curEntry, boolean cloneOnChange )
+    {
+        boolean needToCloneOnChange = cloneOnChange;
+        LogEdit<ID> edit;
+        DataChangeContainer<ID> container;
+        
+        Iterator<LogEdit<ID>> it = logEdits.iterator();
+        
+        while ( it.hasNext() )
+        {
+            edit = it.next();
+            
+            if ( edit instanceof DataChangeContainer )
+            {
+                container = (DataChangeContainer<ID>)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. If uuid is not available,
+                // then match partitionDn as well.
+                String uuid = container.getUUID();
+                boolean applyChanges = false; 
+                
+                if ( uuid != null )
+                {
+                    /*
+                     * Container has changes for entry. Check if the entry change
+                     * affects out entry by comparing uuid if entry is available.
+                     * Otherwise compare partition dn and Id.
+                     */
+                    
+                    if ( curEntry!= null )
+                    {
+                        String curUuid = null;  
+                        try
+                        {
+                            curUuid = curEntry.get( SchemaConstants.ENTRY_UUID_AT ).getString();
+                            if ( curUuid.equals( uuid ) )
+                            {
+                                //TODO check the version here to see if the change should be applied
+                            }
+                        }
+                        catch( LdapException e )
+                        {
+                            //TODO decide whether to throw IOException or an internal exception here
+                        }
+                    }
+                    else
+                    {
+                        Comparator<ID> idComp = TxnManagerFactory.<ID>txnManagerInstance().getIDComparator();
+                        if ( partitionDn.equals( container.getPartitionDn() ) &&  ( idComp.compare( entryID, container.getEntryID() ) == 0 ))
+                        {
+                            applyChanges = true;
+                        }
+                    }
+                    
+                }
+                
+                if ( applyChanges )
+                {
+                    List<DataChange<ID>> dataChanges =  container.getChanges();
+                    Iterator<DataChange<ID>> dit = dataChanges.iterator();
+                    DataChange<ID> nextChange;
+                    
+                    while ( dit.hasNext() )
+                    {
+                        nextChange = dit.next();
+                        if ( nextChange instanceof EntryChange && ( curEntry != null ) )
+                        {
+                            EntryChange<ID> entryChange = (EntryChange<ID>)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<ID> addDelete = (EntryAddDelete<ID>)nextChange;
+                            needToCloneOnChange = false;
+                            
+                            if ( addDelete.getType() == EntryAddDelete.Type.ADD )
+                            {
+                                curEntry = addDelete.getChangedEntry();
+                            }
+                            else
+                            {
+                                curEntry = null;
+                            }
+                        }
+                    }
+                    
+                }
+            }
+        }
+        
+        return curEntry;
+        
+    }
     
 }

Modified: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/Transaction.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/Transaction.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/Transaction.java (original)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/Transaction.java Tue Oct 18 13:11:42 2011
@@ -3,10 +3,13 @@ package org.apache.directory.server.core
 
 import java.util.List;
 
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.name.Dn;
 
-interface Transaction
+
+interface Transaction<ID>
 {
-    public List<ReadWriteTxn> getTxnsToCheck();
+    public List<ReadWriteTxn<ID>> getTxnsToCheck();
     
     public long getStartTime();
     
@@ -18,8 +21,9 @@ interface Transaction
     
     public void abortTxn();
     
-    public State getState();    
+    public State getState();
     
+    public Entry mergeUpdates( Dn partitionDn, ID entryID, Entry entry );
     
     enum State
     {

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnIndexCursor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnIndexCursor.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnIndexCursor.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnIndexCursor.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,252 @@
+
+package org.apache.directory.server.core.txn;
+
+import org.apache.directory.server.core.partition.index.IndexCursor;
+import org.apache.directory.server.core.partition.index.AbstractIndexCursor;
+import org.apache.directory.server.core.partition.index.IndexEntry;
+
+import org.apache.directory.server.core.partition.index.ForwardIndexEntry;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.cursor.InvalidCursorPositionException;
+import org.apache.directory.shared.ldap.model.cursor.Tuple;
+
+import java.util.Iterator;
+import java.util.SortedSet;
+import java.util.NavigableSet;
+
+public class TxnIndexCursor <V, O, ID> extends AbstractIndexCursor<V, O, ID>
+{
+    /** list of changed index entries */
+    private NavigableSet<IndexEntry<V,ID>> changedEntries;
+    
+    /** forward or reverse index */
+    private boolean forwardIndex;
+    
+    /** whether cursor is explicitly positioned */
+    private boolean positioned;
+    
+    /** whether the moving direction is next */
+    private boolean movingNext = true;
+    
+    /** Iterator to move over the set */
+    private Iterator<IndexEntry<V,ID>> it;
+    
+    /** currently available value */
+    IndexEntry<V,ID> availableValue;
+    
+    /** unsupported operation message */
+    private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_722 );
+   
+    
+    public TxnIndexCursor( NavigableSet<IndexEntry<V,ID>> changedEntries, boolean forwardIndex )
+    {
+        this.changedEntries = changedEntries;
+        this.forwardIndex = forwardIndex;
+        
+        if ( changedEntries.size()  < 1 )
+        {
+            throw new IllegalArgumentException("TxnIndexCursor should not be constructed with no index  changes");
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void after( IndexEntry<V, ID> element ) throws Exception
+    {
+        positioned = true;
+        availableValue = null;
+        movingNext = true;
+        it = changedEntries.tailSet( element, false ).iterator();
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void before( IndexEntry<V, ID> element ) throws Exception
+    {
+        positioned = true;
+        availableValue = null;
+        movingNext = true;
+        it = changedEntries.tailSet( element, true ).iterator();
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void afterValue( ID id, V value ) throws Exception
+    {
+        ForwardIndexEntry<V,ID> indexEntry = new ForwardIndexEntry();
+        indexEntry.setId( id );
+        indexEntry.setValue( value );
+        this.after( indexEntry );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void beforeValue( ID id, V value ) throws Exception
+    {
+        ForwardIndexEntry<V,ID> indexEntry = new ForwardIndexEntry();
+        indexEntry.setId( id );
+        indexEntry.setValue( value );
+        this.before( indexEntry );
+    }
+   
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void beforeFirst() throws Exception
+    {
+        positioned = true;
+        availableValue = null;
+        movingNext = true;
+        it = changedEntries.iterator();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void afterLast() throws Exception
+    {
+        positioned = true;
+        availableValue = null;
+        movingNext = false;
+        it = changedEntries.descendingIterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean first() throws Exception
+    {
+        this.beforeFirst();
+        return this.next();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean last() throws Exception
+    {
+        this.afterLast();
+        return this.previous();
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public boolean previous() throws Exception
+    {
+        if ( positioned == false )
+        {
+            afterLast();
+        }
+        
+        if ( movingNext == true )
+        {
+            if ( availableValue == null )
+            {
+                if ( it.hasNext() )
+                {
+                    availableValue = it.next();
+                }
+            }
+            
+            if ( availableValue == null )
+            {
+                it = changedEntries.descendingIterator();
+            }
+            else
+            {
+                it = changedEntries.headSet( availableValue, false ).descendingIterator();
+            }
+            
+            availableValue = null;
+            movingNext = false;
+        }
+
+        if ( it.hasNext() )
+        {
+            availableValue = it.next();
+            return true;
+        }
+        else
+        {
+            availableValue = null;
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean next() throws Exception
+    {
+        if ( positioned == false )
+        {
+            afterLast();
+        }
+        
+        if ( movingNext == false )
+        {
+            if ( availableValue == null )
+            {
+                if ( it.hasNext() )
+                {
+                    availableValue = it.next();
+                }
+            }
+            
+            if ( availableValue == null )
+            {
+                it = changedEntries.iterator();
+            }
+            else
+            {
+                it = changedEntries.tailSet( availableValue, false ).descendingIterator();
+            }
+            
+            availableValue = null;
+            movingNext = true;
+        }
+
+        if ( it.hasNext() )
+        {
+            availableValue = it.next();
+            return true;
+        }
+        else
+        {
+            availableValue = null;
+            return false;
+        }
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public IndexEntry<V, ID> get() throws Exception
+    {
+        if ( availableValue != null )
+        {
+            return availableValue;
+        }
+
+        throw new InvalidCursorPositionException();
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    protected String getUnsupportedMessage()
+    {
+        return UNSUPPORTED_MSG;
+    }
+
+}

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerFactory.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerFactory.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerFactory.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerFactory.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,40 @@
+
+package org.apache.directory.server.core.txn;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import org.apache.directory.server.core.partition.index.Serializer;
+
+public class TxnManagerFactory
+{
+    private static TxnManager<?> txnManager;
+    
+    private static TxnLogManager<?> txnLogManager;
+    
+    public static <ID> void 
+        init(Comparator<ID> idComparator, Serializer idSerializer)
+    {
+        DefaultTxnManager<ID> dTxnManager;
+        dTxnManager = new DefaultTxnManager<ID>();
+        txnManager = dTxnManager;
+        
+        DefaultTxnLogManager<ID> dTxnLogManager;
+        dTxnLogManager = new DefaultTxnLogManager<ID>();
+        txnLogManager = dTxnLogManager;
+        
+        // TODO init txn manager and log manager
+        
+        dTxnManager.init( dTxnLogManager, idComparator, idSerializer );
+    }
+    
+    
+    public static <ID> TxnManager<ID> txnManagerInstance()
+    {
+        return ( (TxnManager<ID>) txnManager );
+    }
+    
+    public static <ID> TxnLogManager<ID> txnLogManagerInstance()
+    {
+        return ( (TxnLogManager<ID>) txnLogManager );
+    }
+}

Modified: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerInternal.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerInternal.java?rev=1185638&r1=1185637&r2=1185638&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerInternal.java (original)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerInternal.java Tue Oct 18 13:11:42 2011
@@ -1,7 +1,7 @@
 
 package org.apache.directory.server.core.txn;
 
-public interface TxnManagerInternal extends TxnManager
+public interface TxnManagerInternal<ID> extends TxnManager<ID>
 {
-    public Transaction getCurTxn();
+    public Transaction<ID> getCurTxn();
 }

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/DataChangeContainer.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/DataChangeContainer.java?rev=1185638&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/DataChangeContainer.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/logedit/DataChangeContainer.java Tue Oct 18 13:11:42 2011
@@ -0,0 +1,162 @@
+
+package org.apache.directory.server.core.txn.logedit;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
+
+import org.apache.directory.server.core.log.LogAnchor;
+
+import org.apache.directory.shared.ldap.model.name.Dn;
+
+import org.apache.directory.server.core.partition.index.Serializer;
+
+import org.apache.directory.server.core.txn.TxnManagerFactory;
+
+public class DataChangeContainer<ID> extends AbstractLogEdit<ID>
+{
+    /** Set to the uuid of the entry if the container contains a change for the entry, null otherwise */
+    private String uuid;
+    
+    /** id of the entry if the container contains a change for an entry */
+    private ID entryID;
+    
+    /** Transaction under which the change is done */
+    private long txnID;
+    
+    /** partition this change applies to */
+    private Dn partitionDn;
+    
+    /** List of data changes */
+    private List<DataChange<ID>> changes = new LinkedList<DataChange<ID>>();
+    
+    //For externalizable
+    public DataChangeContainer()
+    {
+        
+    }
+    
+    public DataChangeContainer( Dn partitionDn, long txnID)
+    {
+        this.partitionDn = partitionDn;
+        this.txnID = txnID;
+    }
+    
+    public String getUUID()
+    {
+        return uuid;
+    }
+    
+    public void setUUID( String entryUUID )
+    {
+        this.uuid = entryUUID;
+    }
+    
+    public long getTxnID()
+    {
+        return this.txnID;
+    }
+    
+    public Dn getPartitionDn()
+    {
+        return partitionDn;
+    }
+    
+    public ID getEntryID()
+    {
+        return entryID;
+    }
+    
+    public List<DataChange<ID>> getChanges()
+    {
+        return changes;
+    }
+    
+    @Override
+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+    {
+        Serializer idSerializer = TxnManagerFactory.txnManagerInstance().getIDSerializer();
+        boolean uuidNotNull = in.readBoolean();
+        
+        if ( uuidNotNull )
+        {
+            uuid = in.readUTF();
+        }
+        
+        int len = in.readInt(); 
+        if ( len < 0 )
+        {
+            entryID = null;
+        }
+        else
+        {
+            byte[] buf = new byte[len];
+            in.readFully( buf );
+            entryID = (ID)idSerializer.deserialize( buf );
+        }
+        
+        txnID = in.readLong();
+        
+        partitionDn = new Dn();
+        partitionDn.readExternal( in );
+        
+        
+        DataChange<ID> change;
+        int numChanges = in.readInt();
+        
+        for ( int idx = 0; idx < numChanges; idx++ )
+        {
+            change = (DataChange<ID>)in.readObject();
+            changes.add( change );
+        }
+    }
+
+
+    @Override
+    public void writeExternal( ObjectOutput out ) throws IOException
+    {
+        Serializer idSerializer = TxnManagerFactory.txnManagerInstance().getIDSerializer();
+        DataChange<ID> change;
+        
+        
+        if ( uuid != null )
+        {
+            out.writeBoolean( true );
+            out.writeUTF( uuid );
+        }
+        else
+        {
+            out.writeBoolean( false );
+        }
+        
+        if ( entryID == null )
+        {
+            out.writeInt( -1 );
+        }
+        else
+        {
+            byte[] buf = idSerializer.serialize( entryID );
+            out.writeInt( buf.length );
+            out.write( buf );
+        }
+        
+        out.writeLong( txnID );
+        
+        partitionDn.writeExternal( out );
+        
+        out.writeInt( changes.size() );
+        
+        Iterator<DataChange<ID>> it = changes.iterator();
+        
+        while( it.hasNext() )
+        {
+            change = it.next();
+            change.writeExternal( out );
+        }
+    }
+
+}