directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From elecha...@apache.org
Subject svn commit: r1784232 [7/9] - in /directory/mavibot/branches/single-value/mavibot/src: main/java/org/apache/directory/mavibot/btree/ main/java/org/apache/directory/mavibot/btree/serializer/ test/java/org/apache/directory/mavibot/btree/
Date Fri, 24 Feb 2017 07:16:38 GMT
Added: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManagerHeader.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManagerHeader.java?rev=1784232&view=auto
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManagerHeader.java (added)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManagerHeader.java Fri Feb 24 07:16:37 2017
@@ -0,0 +1,274 @@
+package org.apache.directory.mavibot.btree;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class store the recordManager header, ie the data associated with the current revision.
+ * Here is a description of its content : 
+ * <pre>
+ * +---------------------+
+ * | PageSize            | 4 bytes : The size of a physical page (default to 4096)
+ * +---------------------+
+ * | NbTree              | 4 bytes : The number of managed B-trees (at least 1)
+ * +---------------------+
+ * | idCounter           | 8 bytes : The page ID counter (an incremental counter)
+ * +---------------------+
+ * | Revision            | 8 bytes : The current revision
+ * +---------------------+
+ * | FirstFree           | 8 bytes : The offset of the first free page
+ * +---------------------+
+ * | current BoB offset  | 8 bytes : The offset of the current B-tree of B-trees
+ * +---------------------+
+ * | current CP offset   | 8 bytes : The offset of the current CopiedPages B-tree
+ * +---------------------+
+ * </pre>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class RecordManagerHeader
+{
+    /** The LoggerFactory used by this class */
+    protected static final Logger LOG = LoggerFactory.getLogger( RecordManagerHeader.class );
+
+    /** The LoggerFactory used for Pages operations logging */
+    protected static final Logger LOG_PAGES = LoggerFactory.getLogger( "org.apache.directory.mavibot.LOG_PAGES" );
+
+    /** The current revision *
+    /* no qualifier */long revision;
+    
+    /** The RecordManager underlying page size. */
+    /* no qualifier */int pageSize = RecordManager.DEFAULT_PAGE_SIZE;
+    
+    /** The number of managed B-trees */
+    /* no qualifier */int nbBtree;
+
+    /** The first and last free page */
+    /* no qualifier */long firstFreePage;
+
+    /** The b-tree of b-trees, where we store user's b-trees. */
+    /* no qualifier */BTree<NameRevision, Long> btreeOfBtrees;
+
+    /** The b-tree of copied pages, where we store the page that have been modified. */
+    /* no qualifier */BTree<RevisionName, long[]> copiedPagesBtree;
+
+    /** The current B-tree of B-trees header offset */
+    /* no qualifier */long currentBtreeOfBtreesOffset;
+
+    /** The offset on the current copied pages B-tree */
+    /* no qualifier */long currentCopiedPagesBtreeOffset = RecordManager.NO_PAGE;
+    
+    /** The page ID incremental counter */
+    /* no qualifier */long idCounter = 0;
+    
+    /** The offset of the end of the file */
+    /* no qualifier */long lastOffset = RecordManager.NO_PAGE;
+    
+    
+    /** The lock used to protect the recordManagerHeader while accessing it */
+    private ReadWriteLock lock = new ReentrantReadWriteLock();
+    
+    /**
+     * Copy the current data structure and returns it.
+     * 
+     * @return A copy of the current data structure
+     */
+    public RecordManagerHeader copy()
+    {
+        lock.readLock().lock();
+        
+        try
+        {
+            RecordManagerHeader copy = new RecordManagerHeader();
+            copy.revision = revision;
+            copy.nbBtree = nbBtree;
+            copy.currentBtreeOfBtreesOffset = currentBtreeOfBtreesOffset;
+            copy.currentCopiedPagesBtreeOffset = currentCopiedPagesBtreeOffset;
+            copy.firstFreePage = firstFreePage;
+            copy.btreeOfBtrees = btreeOfBtrees;
+            copy.copiedPagesBtree = copiedPagesBtree;
+            copy.lastOffset = lastOffset;
+            copy.idCounter = idCounter;
+            
+            return copy;
+        }
+        finally
+        {
+            lock.readLock().unlock();
+        }
+    }
+    
+    
+    /**
+     * Update the current revision
+     * 
+     * @param update The new revision
+     */
+    public void update( RecordManagerHeader update )
+    {
+        lock.writeLock().lock();
+        
+        try
+        {
+            revision = update.revision;
+            nbBtree = update.nbBtree;
+            currentBtreeOfBtreesOffset = update.currentBtreeOfBtreesOffset;
+            currentCopiedPagesBtreeOffset = update.currentCopiedPagesBtreeOffset;
+            firstFreePage = update.firstFreePage;
+            lastOffset = update.lastOffset;
+        }
+        finally
+        {
+            lock.writeLock().unlock();
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     * @return
+     */
+    /* No Qualifier */ long getRevision()
+    {
+        return revision;
+    }
+    
+    
+    /* No qualifier */ BTree<NameRevision, Long> getBtreeOfBtrees()
+    {
+        return btreeOfBtrees;
+    }
+    
+    
+    /* No qualifier */ BTree<RevisionName, long[]> getCopiedPagesBtree()
+    {
+        return copiedPagesBtree;
+    }
+
+
+    /**
+     * Update the RecordManager header, injecting the following data :
+     *
+     * <pre>
+     * +---------------------+
+     * | PageSize            | 4 bytes : The size of a physical page (default to 4096)
+     * +---------------------+
+     * | NbTree              | 4 bytes : The number of managed B-trees (at least 1)
+     * +---------------------+
+     * | Revision            | 8 bytes : The current revision
+     * +---------------------+
+     * | FirstFree           | 8 bytes : The offset of the first free page
+     * +---------------------+
+     * | current BoB offset  | 8 bytes : The offset of the current B-tree of B-trees
+     * +---------------------+
+     * | current CP offset   | 8 bytes : The offset of the current CopiedPages B-tree
+     * +---------------------+
+     * </pre>
+     */
+    /* No qualifier */ ByteBuffer serialize( RecordManager recordManager )
+    {
+        byte[] recordManagerHeaderBytes = recordManager.getRecordManagerHeaderBytes();
+        
+        // The page size
+        int position = recordManager.writeData( recordManagerHeaderBytes, 0, pageSize );
+
+        // The number of managed b-tree
+        position = recordManager.writeData( recordManagerHeaderBytes, position, nbBtree );
+
+        // The current revision
+        position = recordManager.writeData( recordManagerHeaderBytes, position, revision );
+
+        // The first free page
+        position = recordManager.writeData( recordManagerHeaderBytes, position, firstFreePage );
+
+        // The offset of the current B-tree of B-trees
+        position = recordManager.writeData( recordManagerHeaderBytes, position, currentBtreeOfBtreesOffset );
+
+        // The offset of the current B-tree of B-trees
+        recordManager.writeData( recordManagerHeaderBytes, position, currentCopiedPagesBtreeOffset );
+
+        // Write the RecordManager header on disk
+        recordManager.getRecordManagerHeaderBuffer().put( recordManagerHeaderBytes );
+        recordManager.getRecordManagerHeaderBuffer().flip();
+
+        LOG.debug( "Update RM header" );
+
+        if ( LOG_PAGES.isDebugEnabled() )
+        {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append( "revision            : " );
+            sb.append( revision );
+            sb.append( "First free page     : 0x" );
+            sb.append( String.format( "%16x", firstFreePage ) );
+            sb.append( "\n" );
+            sb.append( "Current BOB header  : 0x" );
+            sb.append( String.format( "%16x", currentBtreeOfBtreesOffset ) );
+            sb.append( "\n" );
+            sb.append( "Current CPB header  : 0x" );
+            sb.append( String.format( "%16x", currentCopiedPagesBtreeOffset ) );
+            sb.append( "\n" );
+
+            if ( firstFreePage != RecordManager.NO_PAGE )
+            {
+                long freePage = firstFreePage;
+                sb.append( "free pages list : " );
+
+                boolean isFirst = true;
+
+                while ( freePage != RecordManager.NO_PAGE )
+                {
+                    if ( isFirst )
+                    {
+                        isFirst = false;
+                    }
+                    else
+                    {
+                        sb.append( " -> " );
+                    }
+
+                    sb.append( "0x" ).append( String.format( "%16x", freePage ) );
+
+                    try
+                    {
+                        PageIO[] freePageIO = recordManager.readPageIOs( this, freePage, 8 );
+
+                        freePage = freePageIO[0].getNextPage();
+                    }
+                    catch ( IOException e )
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            LOG_PAGES.debug( "Update RM Header : \n{}", sb.toString() );
+        }
+
+        return recordManager.getRecordManagerHeaderBuffer();
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        sb.append( "RecordManagerHeader :\n" );
+        sb.append( "    ID counter :           " ).append( idCounter ).append( '\n' );
+        sb.append( "    revision :            " ).append( revision ).append( '\n' );
+        sb.append( "    nbTrees :             " ).append( nbBtree ).append( '\n' );
+        sb.append( "    pageSize :            " ).append( pageSize ).append( '\n' );
+        sb.append( "    BOB current offset :  " ).append( String.format( "%16x", currentBtreeOfBtreesOffset ) ).append( '\n' );
+        sb.append( "    CPB current offset :  " ).append( String.format( "%16x", currentCopiedPagesBtreeOffset ) ).append( '\n' );
+        sb.append( "    last offset :         " ).append( String.format( "%16x", lastOffset ) ).append( '\n' );
+        return sb.toString();
+    }
+}

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RemoveResult.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RemoveResult.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RemoveResult.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RemoveResult.java Fri Feb 24 07:16:37 2017
@@ -62,6 +62,7 @@ import java.util.List;
     /**
      * @see Object#toString()
      */
+    @Override
     public String toString()
     {
         StringBuilder sb = new StringBuilder();

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Result.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Result.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Result.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Result.java Fri Feb 24 07:16:37 2017
@@ -26,8 +26,7 @@ import java.util.List;
 /**
  * The result of an insert or delete operation.
  * 
- * @param <K> The type for the Key
- * @param <V> The type for the stored value
+ * @param <P> The type of Page
 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionNameSerializer.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionNameSerializer.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionNameSerializer.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionNameSerializer.java Fri Feb 24 07:16:37 2017
@@ -42,7 +42,7 @@ import org.apache.directory.mavibot.btre
 /* no qualifier*/class RevisionNameSerializer extends AbstractElementSerializer<RevisionName>
 {
     /** A static instance of a RevisionNameSerializer */
-    /*No qualifier*/ final static RevisionNameSerializer INSTANCE = new RevisionNameSerializer();
+    /*No qualifier*/ static final RevisionNameSerializer INSTANCE = new RevisionNameSerializer();
 
     /**
      * Create a new instance of a RevisionNameSerializer
@@ -83,9 +83,7 @@ import org.apache.directory.mavibot.btre
         long revision = LongSerializer.deserialize( in, start );
         String name = StringSerializer.deserialize( in, 8 + start );
 
-        RevisionName revisionName = new RevisionName( revision, name );
-
-        return revisionName;
+        return new RevisionName( revision, name );
     }
 
 
@@ -95,6 +93,7 @@ import org.apache.directory.mavibot.btre
      * @param in The byte array containing the RevisionName
      * @return A RevisionName instance
      */
+    @Override
     public RevisionName fromBytes( byte[] in )
     {
         return deserialize( in, 0 );
@@ -108,6 +107,7 @@ import org.apache.directory.mavibot.btre
      * @param start the position in the byte[] we will deserialize the RevisionName from
      * @return A RevisionName instance
      */
+    @Override
     public RevisionName fromBytes( byte[] in, int start )
     {
         // The buffer must be 8 bytes plus 4 bytes long (the revision is a long, and the name is a String
@@ -119,9 +119,7 @@ import org.apache.directory.mavibot.btre
         long revision = LongSerializer.deserialize( in, start );
         String name = StringSerializer.deserialize( in, 8 + start );
 
-        RevisionName revisionName = new RevisionName( revision, name );
-
-        return revisionName;
+        return new RevisionName( revision, name );
     }
 
 
@@ -136,25 +134,37 @@ import org.apache.directory.mavibot.btre
             throw new SerializerCreationException( "The revisionName instance should not be null " );
         }
 
-        byte[] result = null;
+        byte[] result;
 
         if ( revisionName.getName() != null )
         {
             byte[] stringBytes = Strings.getBytesUtf8( revisionName.getName() );
-            int stringLen = stringBytes.length;
             result = new byte[8 + 4 + stringBytes.length];
+            
+            // The revision
             LongSerializer.serialize( result, 0, revisionName.getRevision() );
 
-            if ( stringLen > 0 )
+            if ( stringBytes.length > 0 )
             {
+                // The name
                 ByteArraySerializer.serialize( result, 8, stringBytes );
             }
+            else
+            {
+                // The empty name
+                IntSerializer.serialize( result, 4,  0 );
+            }
         }
         else
         {
             result = new byte[8 + 4];
+            
+            // The revision
             LongSerializer.serialize( result, 0, revisionName.getRevision() );
-            StringSerializer.serialize( result, 8, null );
+            
+            
+            // The null name
+            IntSerializer.serialize( result, 4, 0xFFFFFFFF );
         }
 
         return result;
@@ -174,10 +184,13 @@ import org.apache.directory.mavibot.btre
         if ( revisionName.getName() != null )
         {
             byte[] stringBytes = Strings.getBytesUtf8( revisionName.getName() );
-            int stringLen = stringBytes.length;
             LongSerializer.serialize( buffer, start, revisionName.getRevision() );
-            IntSerializer.serialize( buffer, 8 + start, stringLen );
-            ByteArraySerializer.serialize( buffer, 12 + start, stringBytes );
+            IntSerializer.serialize( buffer, 8 + start, stringBytes.length );
+            
+            if ( stringBytes.length > 0 )
+            {
+                ByteArraySerializer.serialize( buffer, 12 + start, stringBytes );
+            }
         }
         else
         {
@@ -195,9 +208,11 @@ import org.apache.directory.mavibot.btre
     @Override
     public RevisionName deserialize( BufferHandler bufferHandler ) throws IOException
     {
+        // The revision
         byte[] revisionBytes = bufferHandler.read( 8 );
         long revision = LongSerializer.deserialize( revisionBytes );
 
+        // The name, if any
         byte[] lengthBytes = bufferHandler.read( 4 );
 
         int len = IntSerializer.deserialize( lengthBytes );
@@ -240,7 +255,27 @@ import org.apache.directory.mavibot.btre
 
             default:
                 byte[] nameBytes = new byte[len];
-                buffer.get( nameBytes );
+                
+                try
+                {
+                    buffer.get( nameBytes );
+                }
+                catch ( Exception e )
+                {
+                    buffer.rewind();
+                    
+                    for ( int i = 0; i < buffer.limit(); i++ )
+                    {
+                        if ( i % 8 == 0 )
+                        {
+                            System.out.println();
+                        }
+                        
+                        byte b = buffer.get();
+                        System.out.print( String.format( "0x%02X ", b ) );
+                    }
+                    throw e;
+                }
 
                 return new RevisionName( revision, Strings.utf8ToString( nameBytes ) );
         }

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetComparator.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetComparator.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetComparator.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetComparator.java Fri Feb 24 07:16:37 2017
@@ -29,7 +29,7 @@ import java.util.Comparator;
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-/* no qualifier*/class RevisionOffsetComparator implements Comparator<RevisionOffset>
+/* no qualifier*/class RevisionOffsetComparator implements Comparator<RevisionOffsets>
 {
     /** A static instance of a RevisionOffsetComparator */
     public static final RevisionOffsetComparator INSTANCE = new RevisionOffsetComparator();
@@ -55,7 +55,7 @@ import java.util.Comparator;
     /**
      * {@inheritDoc}
      */
-    public int compare( RevisionOffset rn1, RevisionOffset rn2 )
+    public int compare( RevisionOffsets rn1, RevisionOffsets rn2 )
     {
         if ( rn1 == rn2 )
         {

Copied: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsets.java (from r1706445, directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffset.java)
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsets.java?p2=directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsets.java&p1=directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffset.java&r1=1706445&r2=1784232&rev=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffset.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsets.java Fri Feb 24 07:16:37 2017
@@ -28,7 +28,7 @@ import java.util.Arrays;
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class RevisionOffset
+public class RevisionOffsets
 {
     /** the revision number */
     private long revision;
@@ -43,7 +43,7 @@ public class RevisionOffset
      * @param revision the revision number
      * @param offsets array of copied page offsets
      */
-    public RevisionOffset( long revision, long[] offsets )
+    public RevisionOffsets( long revision, long[] offsets )
     {
         this.revision = revision;
         this.offsets = offsets;
@@ -84,6 +84,9 @@ public class RevisionOffset
     }
 
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public boolean equals( Object obj )
     {
@@ -92,12 +95,12 @@ public class RevisionOffset
             return true;
         }
         
-        if ( obj == null )
+        if ( !( obj instanceof RevisionOffsets ) )
         {
             return false;
         }
 
-        RevisionOffset other = ( RevisionOffset ) obj;
+        RevisionOffsets other = ( RevisionOffsets ) obj;
 
         if ( revision != other.revision )
         {
@@ -108,6 +111,9 @@ public class RevisionOffset
     }
 
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public String toString()
     {

Copied: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetsSerializer.java (from r1706445, directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetSerializer.java)
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetsSerializer.java?p2=directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetsSerializer.java&p1=directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetSerializer.java&r1=1706445&r2=1784232&rev=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetSerializer.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionOffsetsSerializer.java Fri Feb 24 07:16:37 2017
@@ -36,15 +36,15 @@ import org.apache.directory.mavibot.btre
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-/* no qualifier*/class RevisionOffsetSerializer extends AbstractElementSerializer<RevisionOffset>
+/* no qualifier*/class RevisionOffsetsSerializer extends AbstractElementSerializer<RevisionOffsets>
 {
     /** A static instance of a RevisionOffsetSerializer */
-    /*No qualifier*/ final static RevisionOffsetSerializer INSTANCE = new RevisionOffsetSerializer();
+    /*No qualifier*/ static final RevisionOffsetsSerializer INSTANCE = new RevisionOffsetsSerializer();
 
     /**
      * Create a new instance of a RevisionOffsetSerializer
      */
-    private RevisionOffsetSerializer()
+    private RevisionOffsetsSerializer()
     {
         super( RevisionOffsetComparator.INSTANCE );
     }
@@ -56,7 +56,7 @@ import org.apache.directory.mavibot.btre
      * @param in The byte array containing the RevisionOffset
      * @return A RevisionOffset instance
      */
-    /* no qualifier*/static RevisionOffset deserialize( byte[] in )
+    /* no qualifier*/static RevisionOffsets deserialize( byte[] in )
     {
         return deserialize( in, 0 );
     }
@@ -69,7 +69,7 @@ import org.apache.directory.mavibot.btre
      * @param start the position in the byte[] we will deserialize the RevisionOffset from
      * @return A RevisionOffset instance
      */
-    /* no qualifier*/static RevisionOffset deserialize( byte[] in, int start )
+    /* no qualifier*/static RevisionOffsets deserialize( byte[] in, int start )
     {
         // The buffer must be 8 bytes
         if ( ( in == null ) || ( in.length < 8 + start ) )
@@ -83,9 +83,7 @@ import org.apache.directory.mavibot.btre
         {
             long[] offsets = LongArraySerializer.INSTANCE.fromBytes( in, 8 + start );
 
-            RevisionOffset RevisionOffset = new RevisionOffset( revision, offsets );
-            
-            return RevisionOffset;
+            return new RevisionOffsets( revision, offsets );
         }
         catch( IOException e )
         {
@@ -100,7 +98,8 @@ import org.apache.directory.mavibot.btre
      * @param in The byte array containing the RevisionOffset
      * @return A RevisionOffset instance
      */
-    public RevisionOffset fromBytes( byte[] in )
+    @Override
+    public RevisionOffsets fromBytes( byte[] in )
     {
         return deserialize( in, 0 );
     }
@@ -113,7 +112,8 @@ import org.apache.directory.mavibot.btre
      * @param start the position in the byte[] we will deserialize the RevisionOffset from
      * @return A RevisionOffset instance
      */
-    public RevisionOffset fromBytes( byte[] in, int start )
+    @Override
+    public RevisionOffsets fromBytes( byte[] in, int start )
     {
         // The buffer must be 8 bytes long (the revision is a long, and the name is a String
         if ( ( in == null ) || ( in.length < 8 + start ) )
@@ -129,18 +129,18 @@ import org.apache.directory.mavibot.btre
      * {@inheritDoc}
      */
     @Override
-    public byte[] serialize( RevisionOffset RevisionOffset )
+    public byte[] serialize( RevisionOffsets revisionOffsets )
     {
-        if ( RevisionOffset == null )
+        if ( revisionOffsets == null )
         {
             throw new SerializerCreationException( "The RevisionOffset instance should not be null " );
         }
 
-        byte[] result = null;
+        byte[] result;
 
-        byte[] offsets = LongArraySerializer.INSTANCE.serialize( RevisionOffset.getOffsets() );
+        byte[] offsets = LongArraySerializer.INSTANCE.serialize( revisionOffsets.getOffsets() );
         result = new byte[8 + offsets.length];
-        LongSerializer.serialize( result, 0, RevisionOffset.getRevision() );
+        LongSerializer.serialize( result, 0, revisionOffsets.getRevision() );
 
         System.arraycopy( offsets, 0, result, 8, offsets.length );
         
@@ -156,11 +156,11 @@ import org.apache.directory.mavibot.btre
      * @param value the value to serialize
      * @return The byte[] containing the serialized RevisionOffset
      */
-    /* no qualifier*/static byte[] serialize( byte[] buffer, int start, RevisionOffset RevisionOffset )
+    /* no qualifier*/static byte[] serialize( byte[] buffer, int start, RevisionOffsets revisionOffsets )
     {
-        LongSerializer.serialize( buffer, start, RevisionOffset.getRevision() );
+        LongSerializer.serialize( buffer, start, revisionOffsets.getRevision() );
         
-        byte[] offsets = LongArraySerializer.INSTANCE.serialize( RevisionOffset.getOffsets() );
+        byte[] offsets = LongArraySerializer.INSTANCE.serialize( revisionOffsets.getOffsets() );
 
         System.arraycopy( offsets, 0, buffer, 8 + start, offsets.length );
         
@@ -172,14 +172,16 @@ import org.apache.directory.mavibot.btre
      * {@inheritDoc}
      */
     @Override
-    public RevisionOffset deserialize( BufferHandler bufferHandler ) throws IOException
+    public RevisionOffsets deserialize( BufferHandler bufferHandler ) throws IOException
     {
+        // The revision
         byte[] revisionBytes = bufferHandler.read( 8 );
         long revision = LongSerializer.deserialize( revisionBytes );
 
+        // The offsets
         long[] offsets = LongArraySerializer.INSTANCE.deserialize( bufferHandler );
 
-        return new RevisionOffset( revision, offsets );
+        return new RevisionOffsets( revision, offsets );
     }
 
 
@@ -187,13 +189,14 @@ import org.apache.directory.mavibot.btre
      * {@inheritDoc}
      */
     @Override
-    public RevisionOffset deserialize( ByteBuffer buffer ) throws IOException
+    public RevisionOffsets deserialize( ByteBuffer buffer ) throws IOException
     {
         // The revision
         long revision = buffer.getLong();
 
+        // The offsets
         long[] offsets = LongArraySerializer.INSTANCE.deserialize( buffer );
 
-        return new RevisionOffset( revision, offsets );
+        return new RevisionOffsets( revision, offsets );
     }
 }

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/SplitResult.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/SplitResult.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/SplitResult.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/SplitResult.java Fri Feb 24 07:16:37 2017
@@ -106,6 +106,7 @@ import java.util.List;
     /**
      * @see Object#toString()
      */
+    @Override
     public String toString()
     {
         StringBuilder sb = new StringBuilder();

Added: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Transaction.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Transaction.java?rev=1784232&view=auto
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Transaction.java (added)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/Transaction.java Fri Feb 24 07:16:37 2017
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.directory.mavibot.btree;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * <p>
+ * The Transaction is used to protect b-trees against concurrent modifications,
+ * and insure that a read is always done against one single revision. It's also
+ * used to apply many modifications under one single revision.
+ * </p
+ * <p>>
+ * A read Transaction should be committed or aborted when the user is done with it, 
+ * otherwise the pages associated with the given revision, and all the referenced pages, 
+ * will remain, leading to a database expansion.
+ * </p>
+ * <p>
+ * A Transaction can be hold for quite a long time, for instance while doing
+ * a browse against a big b-tree. At some point, transactions which are pending
+ * for too long will be closed by the transaction manager, unless an infinite
+ * timeout is used. By default, a transaction will last 30 seconds. 
+ * </p>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface Transaction extends Closeable
+{
+    /** The default transaction timeout : 30 seconds */
+    static final long DEFAULT_TIMEOUT = 30000L;
+    
+    /**
+     * Commit a write transaction. It will apply the changes on 
+     * the database.Last, not least, a new version will be created.
+     * If called by a Read transaction, it will simply close it.
+     */
+    void commit() throws IOException;
+    
+    
+    /**
+     * Abort a transaction. If it's a {@link ReadTransaction}, it will unlink this transaction
+     * from the version it used. If it's a {@link WriteTransaction}; it will drop all the pending
+     * changes. The latest version will remain the same.
+     */
+    void abort() throws IOException;
+    
+    
+    /**
+     * @return The current transaction revision
+     */
+    long getRevision();
+
+
+    /**
+     * @return the creationDate
+     */
+    long getCreationDate();
+
+    
+    /**
+     * Tells if the transaction has been committed/aborted or not.
+     *  
+     * @return <tt>true</tt> if the transaction has been completed.
+     */
+    boolean isClosed();
+    
+    
+    /**
+     * @return The associated {@link RecordManager}
+     */
+    RecordManager getRecordManager();
+    
+    
+    /**
+     * @return The associated {@link RecordManagerHeader}
+     */
+    RecordManagerHeader getRecordManagerHeader();
+}

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionContext.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionContext.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionContext.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionContext.java Fri Feb 24 07:16:37 2017
@@ -35,10 +35,10 @@ import java.util.Map;
 public class TransactionContext
 {
     /** The map containing all the modified pages, using their offset as a key */
-    private Map<Long, WALObject> pageMap = new HashMap<Long, WALObject>();
+    private Map<Long, WALObject> pageMap = new HashMap<>();
     
     /** The map containing all the copied pages, using their offset as a key */
-    private Map<Long, WALObject> copiedPageMap = new HashMap<Long, WALObject>();
+    private Map<Long, WALObject> copiedPageMap = new HashMap<>();
     
     /** The global revision */
     private long revision;
@@ -46,8 +46,11 @@ public class TransactionContext
     /** The last offset on disk */
     private long lastOffset;
     
+    /** The RecordManager header for the transaction */
+    private RecordManagerHeader recordManagerHeader;
+    
     /** The BOB header */
-    private BTreeHeader btreeOfBtreeHeader;
+    private BTreeHeader<NameRevision, Long> btreeOfBtreeHeader;
     
     /** The CPB header */
     private BTreeHeader copiedPageBtreeHeader;
@@ -58,12 +61,24 @@ public class TransactionContext
      * 
      * @param lastOffset The last offset of the associated file
      */
-    public TransactionContext( long lastOffset, long revision, BTreeHeader btreeOfBtreeHeader, BTreeHeader copiedPageBtreeHeader )
+    public TransactionContext( RecordManagerHeader recordManagerHeader )
+    {
+        this.recordManagerHeader = recordManagerHeader;
+    }
+    
+    
+    /**
+     * A constructor for the TransactionContext, which initialize the last offset to
+     * the file size. This is a Read Transaction context, we don't have to keep the copied page
+     * BTreeHeader, nor the last offset.
+     * 
+     * @param revision The current revision used for this transaction
+     * @param btreeOfBtreeHeader The BOB header
+     */
+    public TransactionContext( long revision, BTreeHeader btreeOfBtreeHeader )
     {
-        this.lastOffset = lastOffset;
         this.revision = revision;
         this.btreeOfBtreeHeader = btreeOfBtreeHeader;
-        this.copiedPageBtreeHeader = copiedPageBtreeHeader;
     }
 
 
@@ -150,7 +165,7 @@ public class TransactionContext
     /**
      * @return the btreeOfBtreeHeader
      */
-    public BTreeHeader getBtreeOfBtreeHeader() {
+    public BTreeHeader<NameRevision, Long> getBtreeOfBtreeHeader() {
         return btreeOfBtreeHeader;
     }
 
@@ -202,6 +217,7 @@ public class TransactionContext
     /**
      * @see Object#toString()
      */
+    @Override
     public String toString()
     {
         StringBuilder sb = new StringBuilder();

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionManager.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionManager.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionManager.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TransactionManager.java Fri Feb 24 07:16:37 2017
@@ -21,44 +21,67 @@ package org.apache.directory.mavibot.btr
 
 
 /**
- * An interface used to manage the transactions mechanism in B-trees. Transactions are cross
- * B-trees.
- *
+ * An interface used to manage the transactions mechanism in b-trees. Transactions span across
+ * all the managed b-trees. 
+ * <br>
+ * On can start a read or a write transaction, then commit or abort it. 
+ * <br>
+ * Many read transactions can be executed at the same time, and each one of them
+ * will use the version in use when creating during all their existence. That means we may have
+ * different read transactions using different versions.
+ * <br>
+ * One single write transaction can be executed, which means the caller will be blocked
+ * until the previous write transaction is completed.
+ * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public interface TransactionManager<K, V>
+public interface TransactionManager
 {
     /**
-     * Starts a transaction
-     */
-    void beginTransaction();
-
-
-    /**
-     * Commits a transaction
-     */
-    void commit();
-
-
-    /**
-     * Rollback a transaction
+     * Starts a Read transaction. One can start as many read transaction as needed. One can 
+     * start a read transaction even if a write transaction has been started.
+     * <br>
+     * A read transaction will use the latest revision available, and will work on this revision
+     * during all its life. New revision might be created in the mean time, but they won't be visible.
+     * <br>
+     * With respect to this rule, it's likely that what we get back from one B-tree might not be up to date.
+     * <br>
+     * When done, close the transaction by calling the {@link Transaction#commit()} or 
+     * {@link Transaction#abort()} method.
+     * <br>
+     * Note : holding a read revision for a long period of time might lead to a database expansion, as
+     * the hold revision will forbid the cleanup thread to reclaim any page that are used by this revision.
+     * Be sure to commit (or abort) your transaction when done.
+     * <br>
+     * We have a timeout that is used to avoid long lasting transaction, which defaults to 30 seconds. It's 
+     * possible to change this timeout by passing a specific timeout when starting a read transaction. 
+     * <br>
+     * The transaction is valid across all the b-trees.
+     * 
+     * @return The created read {@link Transaction}
      */
-    void rollback();
+    Transaction beginReadTransaction();
 
 
     /**
-     * Gets the current BtreeHeader for a given BTree.
-     * 
-     * @param btreeName The Btree name we are looking the BtreeHeader for
-     * @return the current BTreeHeader
+     * Starts a read transaction that has a limited live time. The default is 30 seconds.
+     *  
+     * @param timeout The delay after which the transaction will be automatically closed, in ms. 
+     * Passing 0 or a negative value will make the read transaction to last forever.
+     * @return The created read {@link Transaction}
      */
-    BTreeHeader<K, V> getBTreeHeader( String btreeName );
+    Transaction beginReadTransaction( long timeout );
 
 
     /**
-     * Updates the map of new BTreeHeaders
+     * Starts a Write transaction. One can only start one single write transaction. Any
+     * thread starting a write transaction will be blocked by an existing write transaction.
+     * <br>
+     * Once committed, a new revision will be available for the next read or write transaction.
+     * <br>
+     * The write transaction spans across all the b-trees.
      * 
-     * @param btreeHeader The new BtreeHeader
+     * @return The created write {@link Transaction}
      */
-    void updateNewBTreeHeaders( BTreeHeader<K, V> btreeHeader );
+    WriteTransaction beginWriteTransaction();
 }

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TupleCursor.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TupleCursor.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TupleCursor.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/TupleCursor.java Fri Feb 24 07:16:37 2017
@@ -20,21 +20,19 @@
 package org.apache.directory.mavibot.btree;
 
 
-import java.io.IOException;
+import java.io.Closeable;
+import java.util.Iterator;
 import java.util.NoSuchElementException;
 
-import org.apache.directory.mavibot.btree.exception.CursorException;
-import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
-
 
 /**
- * A Cursor is used to fetch elements in a BTree and is returned by the
+ * A TupleCursor is used to fetch elements in a B-tree and is returned by the
  * @see BTree#browse method. The cursor <strong>must</strong> be closed
  * when the user is done with it.
  * <p>
  * We keep a track of the current position at each level of the B-tree, so
  * we can navigate in this B-tree forward and backward. Two special positions
- * are defined : BEFORE_FIRST and AFTER_LAST which are befoe the leftmost and 
+ * are defined : BEFORE_FIRST and AFTER_LAST which are before the leftmost and 
  * after the rightmost elements in the B-tree
  *
  * @param <K> The type for the Key
@@ -42,13 +40,13 @@ import org.apache.directory.mavibot.btre
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class TupleCursor<K, V> //implements Iterator<Tuple<K,V>>
+public class TupleCursor<K, V> implements Iterator<Tuple<K,V>>, Closeable
 {
     /** A marker to tell that we are before the first element */
-    private static final int BEFORE_FIRST = -1;
+    /* No qualifier */ static final int BEFORE_FIRST = -1;
 
     /** A marker to tell that we are after the last element */
-    private static final int AFTER_LAST = -2;
+    /* No qualifier */ static final int AFTER_LAST = -2;
 
     /** The stack of pages from the root down to the leaf */
     protected ParentPos<K, V>[] stack;
@@ -57,11 +55,11 @@ public class TupleCursor<K, V> //impleme
     protected int depth = 0;
 
     /** The transaction used for this cursor */
-    protected ReadTransaction<K, V> transaction;
+    protected Transaction transaction;
 
 
     /**
-     * Creates a new instance of Cursor.
+     * Creates a new instance of TupleCursor.
      */
     protected TupleCursor()
     {
@@ -69,12 +67,13 @@ public class TupleCursor<K, V> //impleme
 
 
     /**
-     * Creates a new instance of Cursor, starting on a page at a given position.
+     * Creates a new instance of cursor, starting on a page at a given position.
      *
      * @param transaction The transaction this operation is protected by
      * @param stack The stack of parent's from root to this page
+     * @param depth The stack's depth
      */
-    public TupleCursor( ReadTransaction<K, V> transaction, ParentPos<K, V>[] stack, int depth )
+    public TupleCursor( Transaction transaction, ParentPos<K, V>[] stack, int depth )
     {
         this.transaction = transaction;
         this.stack = stack;
@@ -83,208 +82,106 @@ public class TupleCursor<K, V> //impleme
 
 
     /**
-     * Positions this Cursor after the last element. This will set the path to the
+     * Positions this cursor after the last element. This will set the path to the
      * rightmost page for each level, and the position to the rightmost element
      * of each page.
-     *
-     * @throws CursorException if there are problems positioning this Cursor or if
-     * this Cursor is closed
      */
-    public void afterLast() throws CursorException
+    public void afterLast()
     {
-        // First check that we have elements in the BTree
+        // First check that we have elements in the B-tree
         if ( ( stack == null ) || ( stack.length == 0 ) )
         {
             return;
         }
         
-        // Update the parent page.
-        ParentPos<K, V> parentPos = stack[0];
-        parentPos.pos = parentPos.page.getNbElems();
-        Page<K, V> childPage = null;
-
-        if ( parentPos.page instanceof Node )
-        {
-            childPage = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos );
-        }
-
-        // Go down the Tree, selecting the rightmost element in each Node
-        for ( int i = 1; i < depth; i++ )
-        {
-            parentPos = stack[i];
-
-            // We are in the middle of the tree, change the parentPos to be
-            // the one we have selected previously
-            // Set the parentPos to be at the rightmost position for this level.
-            parentPos.page = childPage;
-            parentPos.pos = childPage.getNbElems();
-
-            // Get the child page, and iterate
-            childPage = ( ( AbstractPage<K, V> ) childPage ).getPage( parentPos.pos );
-        }
-
-        // Now, we should handle the leaf
-        parentPos = stack[depth];
-
-        if ( childPage != null )
+        // We have to go down the stack, setting all the position to 0,
+        // changing the page,except for the leaf which position is set 
+        // to BEFORE_FIRST
+        for ( int i = 0; i < depth; i++ )
         {
-            // The tree had more than one level, so we make the current parentPos
-            // point on the previously selected leaf.
-            parentPos.page = childPage;
+            stack[i].pos = stack[i].page.getNbPageElems();
+            stack[i+1].page = ( ( Node<K, V> ) stack[i].page ).getPage( stack[i].pos );
         }
 
-        // Finally, set the position after the rightmost element
-        parentPos.pos = AFTER_LAST;
+        stack[depth].pos = AFTER_LAST;
     }
 
     
     /**
-     * Determines whether or not a call to get() will succeed.
+     * Determines whether or not a call to {@link #get()} will succeed.
      *
-     * @return true if a call to the get() method will succeed, false otherwise
+     * @return <tt>true</tt> if a call to the {@link #get()} method will succeed, <tt>false</tt> otherwise
      */
     public boolean available()
     {
-        // First check that we have elements in the BTree
-        if ( ( stack == null ) || ( stack.length == 0 ) )
+        // First check that we have elements in the B-tree
+        if ( ( stack == null ) || ( stack.length == 0 ) || ( stack[depth].page == null ) )
         {
             return false;
         }
 
-        // Take the leaf and check if we have no mare values
-        ParentPos<K, V> parentPos = stack[depth];
-
-        if ( parentPos.page == null )
-        {
-            // Empty BTree, get out
-            return false;
-        }
-        
         // We must not be AFTER_LAST or BEFORE_FIRST 
-        return ( parentPos.pos != BEFORE_FIRST ) && ( parentPos.pos != AFTER_LAST );
+        return ( stack[depth].pos != BEFORE_FIRST ) && ( stack[depth].pos != AFTER_LAST );
     }
 
 
     /**
-     * Positions this Cursor before the first element.
-     *
-     * @throws Exception if there are problems positioning this cursor or if
-     * this Cursor is closed
+     * Positions this cursor before the first element.
      */
-    public void beforeFirst() throws CursorException
+    public void beforeFirst()
     {
-        // First check that we have elements in the BTree
+        // First check that we have elements in the B-tree
         if ( ( stack == null ) || ( stack.length == 0 ) )
         {
             return;
         }
 
-        ParentPos<K, V> parentPos = stack[0];
-        parentPos.pos = 0;
-        Page<K, V> child = null;
-        
-        if ( parentPos.page instanceof Node )
-        {
-            child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( 0 );
-        }
-
-        for ( int i = 1; i < depth; i++ )
+        // We have to go down the stack, setting all the position to 0,
+        // changing the page,except for the leaf which position is set 
+        // to BEFORE_FIRST
+        for ( int i = 0; i < depth; i++ )
         {
-            parentPos = stack[i];
-            parentPos.pos = 0;
-            parentPos.page = child;
-
-            child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( 0 );
+            stack[i].pos = 0;
+            stack[i+1].page = ( ( Node<K, V> ) stack[i].page ).getPage( 0 );
         }
 
-        // and leaf
-        parentPos = stack[depth];
-        parentPos.pos = BEFORE_FIRST;
-
-        if ( child != null )
-        {
-            parentPos.page = child;
-        }
+        stack[depth].pos = BEFORE_FIRST;
     }
 
 
     /**
      * Tells if the cursor can return a next element
      *
-     * @return true if there are some more elements
-     * @throws IOException
-     * @throws EndOfFileExceededException
+     * @return <tt>true</tt> if there are some more elements
      */
-    public boolean hasNext() throws EndOfFileExceededException, IOException
+    @Override
+    public boolean hasNext()
     {
-        // First check that we have elements in the BTree
-        if ( ( stack == null ) || ( stack.length == 0 ) )
+        // First check that we have elements in the B-tree
+        if ( ( stack == null ) || ( stack.length == 0 ) || ( stack[depth].pos == AFTER_LAST ) || 
+            ( stack[depth].page == null ))
         {
             return false;
         }
 
         // Take the leaf and check if we have no mare values
-        ParentPos<K, V> parentPos = stack[depth];
-
-        if ( parentPos.page == null )
-        {
-            // Empty BTree, get out
-            return false;
-        }
-
-        if ( parentPos.pos == AFTER_LAST )
+        if ( ( stack[depth].pos + 1 < stack[depth].page.getNbPageElems() ) || ( stack[depth].pos == BEFORE_FIRST ) )
         {
-            // Ok, here, we have reached the last value in the leaf. We have to go up and
-            // see if we have some remaining values
-            int currentDepth = depth - 1;
-
-            while ( currentDepth >= 0 )
-            {
-                parentPos = stack[currentDepth];
-
-                if ( parentPos.pos < parentPos.page.getNbElems() )
-                {
-                    // The parent has some remaining values on the right, get out
-                    return true;
-                }
-                else
-                {
-                    currentDepth--;
-                }
-            }
-
-            return false;
-        }
-
-        if ( parentPos.pos < parentPos.page.getNbElems() - 1 )
-        {
-            // Not the last position, we have a next value
+            // get out, we have values on the right
             return true;
         }
-        else
-        {
-            // Ok, here, we have reached the last value in the leaf. We have to go up and
-            // see if we have some remaining values
-            int currentDepth = depth - 1;
 
-            while ( currentDepth >= 0 )
+        // Check that we are not at position 0 for all the pages in the stack 
+        for ( int i = depth - 1; i >= 0; i-- )
+        {
+            if ( stack[i].pos < stack[i].page.getNbPageElems() )
             {
-                parentPos = stack[currentDepth];
-
-                if ( parentPos.pos < parentPos.page.getNbElems() )
-                {
-                    // The parent has some remaining values on the right, get out
-                    return true;
-                }
-                else
-                {
-                    currentDepth--;
-                }
+                return true;
             }
-
-            // We are done, there are no more value left
-            return false;
         }
+        
+        // Sadly, all the pages on the stack are on pos 0 : no more prev 
+        return false;
     }
 
 
@@ -293,141 +190,149 @@ public class TupleCursor<K, V> //impleme
      * a NoSuchElementException will be thrown.
      *
      * @return A Tuple containing the found key and value
-     * @throws IOException
      * @throws NoSuchElementException When we have reached the end of the B-tree, or if 
      * the B-tree is empty
      */
-    public Tuple<K, V> next() throws NoSuchElementException
+    @Override
+    public Tuple<K, V> next()
     {
-        // First check that we have elements in the BTree
+        // First check that we have elements in the B-tree
         if ( ( stack == null ) || ( stack.length == 0 ) )
         {
-            throw new NoSuchElementException( "No tuple present" );
+            throw new NoSuchElementException( "No more tuple present" );
         }
 
         ParentPos<K, V> parentPos = stack[depth];
+        
+        if ( parentPos.page == null )
+        {
+            throw new NoSuchElementException( "No more tuple present" );
+        }
 
-        try
+        switch( parentPos.pos )
         {
-            if ( parentPos.pos == parentPos.page.getNbElems() )
-            {
-                // End of the leaf. We have to go back into the stack up to the
-                // parent, and down to the leaf
-                parentPos = findNextParentPos();
-    
-                // we also need to check for the type of page cause
-                // findNextParentPos will never return a null ParentPos
-                if ( parentPos == null )
+            case AFTER_LAST :
+                // We already are at the end of the cursor
+                throw new NoSuchElementException( "No more tuples present" );
+
+            case BEFORE_FIRST :
+                // Move the the first element
+                parentPos.pos = 0;
+                break;
+                
+            default :
+                // Check if we are at the end of each page, and if so, go up
+                // in the stack, finding the Node that has some more children
+                stack[depth].pos++;
+                parentPos = stack[depth];
+
+                if ( stack[depth].pos == stack[depth].page.getNbPageElems() )
                 {
-                    // This is the end : no more value
-                    throw new NoSuchElementException( "No more tuples present" );
+                    boolean hasNext = false;
+
+                    // Move up to find the next leaf
+                    for ( int i = depth; i >= 0; i-- )
+                    {
+                        if ( stack[i].pos != stack[i].page.getNbPageElems() )
+                        {
+                            // We have some more children on the right, move forward
+                            stack[i].pos++;
+    
+                            // and go down the stack if we weren't already at the bottom, setting the pos to 0
+                            for ( int j = i + 1; j < depth; j++ )
+                            {
+                                stack[j].page = ((Node<K, V>)stack[j - 1].page).getPage( stack[j - 1].pos );
+                                stack[j].pos = 0;
+                            }
+                            
+                            // The last page is a leaf
+                            stack[depth].page = ((Node<K, V>)stack[depth - 1].page).getPage( stack[depth - 1].pos );
+                            stack[depth].pos = 0;
+                            
+                            parentPos = stack[depth];
+                            hasNext = true;
+                            
+                            break;
+                        }
+                    }
+                    
+                    // No next leaf : set the pos to AFTER_LAST
+                    if ( !hasNext )
+                    {
+                        parentPos.pos = AFTER_LAST;
+                    }
                 }
-            }
-            else
-            {
-                parentPos.pos++;
-            }
-        }
-        catch ( IOException ioe )
-        {
-            throw new NoSuchElementException( ioe.getMessage());
         }
 
-        // Get the value
-        ValueHolder<V> valueHolder = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( parentPos.pos );
+        Leaf<K, V> leaf = ( Leaf<K, V> ) parentPos.page;
 
-        V value = valueHolder.get();
+        // Get the value
+        V value = leaf.getValue( parentPos.pos ).get();
 
         // Get the key
-        AbstractPage<K, V> leaf = ( AbstractPage<K, V> ) ( parentPos.page );
         K key = leaf.getKey( parentPos.pos );
         
         // Build the Tuple
-        Tuple<K, V> tuple = new Tuple<K, V>( key, value );
-
-        return tuple;
+        return new Tuple<>( key, value );
     }
 
 
     /**
      * Tells if the cursor can return a previous element
      *
-     * @return true if there are some more elements
-     * @throws IOException
-     * @throws EndOfFileExceededException
+     * @return <tt>true</tt> if there are some more elements before the current one
      */
-    public boolean hasPrev() throws EndOfFileExceededException, IOException
+    public boolean hasPrev()
     {
-        // First check that we have elements in the BTree
-        if ( ( stack == null ) || ( stack.length == 0 ) )
+        // First check that we have elements in the B-tree
+        if ( ( stack == null ) || ( stack.length == 0 ) || ( stack[depth].pos == BEFORE_FIRST ) || ( stack[depth].page == null ))
         {
             return false;
         }
 
         // Take the leaf and check if we have no mare values
-        ParentPos<K, V> parentPos = stack[depth];
-
-        if ( parentPos.page == null )
-        {
-            // Empty BTree, get out
-            return false;
-        }
-
-        if ( parentPos.pos > 0 )
+        if ( ( stack[depth].pos > 0 ) || ( stack[depth].pos == AFTER_LAST ) )
         {
             // get out, we have values on the left
             return true;
         }
-        else
-        {
-            // Check that we are not before the first value
-            if ( parentPos.pos == BEFORE_FIRST )
-            {
-                return false;
-            }
 
-            // Ok, here, we have reached the first value in the leaf. We have to go up and
-            // see if we have some remaining values
-            int currentDepth = depth - 1;
-
-            while ( currentDepth >= 0 )
+        // Check that we are not at position 0 for all the pages in the stack 
+        for ( int i = depth; i >= 0; i-- )
+        {
+            if ( stack[i].pos > 0 )
             {
-                parentPos = stack[currentDepth];
-
-                if ( parentPos.pos > 0 )
-                {
-                    // The parent has some remaining values on the right, get out
-                    return true;
-                }
-                else
-                {
-                    currentDepth--;
-                }
+                return true;
             }
-
-            return false;
         }
+        
+        // Sadly, all the pages on the stack are on pos 0 : no more prev 
+        return false;
     }
 
 
     /**
-     * Get the previous Tuple, from the current position. If we have no more new element, 
-     * a NoMoreElementException will be thrown.
+     * Get the previous {@link Tuple}, from the current position. If we have no more new element, 
+     * a {@link NoSuchElementException} will be thrown.
      *
-     * @return A Tuple containing the found key and value
-     * @throws IOException
+     * @return A {@link Tuple} containing the found key and value
      * @throws NoSuchElementException When we have reached the beginning of the B-tree, or if 
      * the B-tree is empty
      */
-    public Tuple<K, V> prev() throws EndOfFileExceededException, IOException
+    public Tuple<K, V> prev()
     {
-        // First check that we have elements in the BTree
+        // First check that we have elements in the B-tree
         if ( ( stack == null ) || ( stack.length == 0 ) )
         {
             throw new NoSuchElementException( "No more tuple present" );
         }
 
         ParentPos<K, V> parentPos = stack[depth];
+        
+        if ( parentPos.page == null )
+        {
+            throw new NoSuchElementException( "No more tuple present" );
+        }
 
         switch( parentPos.pos )
         {
@@ -436,26 +341,45 @@ public class TupleCursor<K, V> //impleme
                 throw new NoSuchElementException( "No more tuples present" );
 
             case AFTER_LAST :
-                // Move the the last element
-                parentPos.pos = parentPos.page.getNbElems() - 1;
+                // Move to the last element
+                parentPos.pos = parentPos.page.getNbPageElems() - 1;
                 break;
             
             case 0 :
                 // beginning of the leaf. We have to go back into the stack up to the
                 // parent, and down to the leaf on the left, if possible
-                parentPos = findPrevParentPos();
+                boolean hasPrev = false;
+                
+                for ( int i = depth - 1; i >= 0; i-- )
+                {
+                    if ( stack[i].pos != 0 )
+                    {
+                        // We have some more children on the left, move backward
+                        stack[i].pos--;
+
+                        // and go down the stack if we weren't already at the bottom, setting the pos to nbElems
+                        for ( int j = i + 1; j < depth; j++ )
+                        {
+                            stack[j].page = ((Node<K, V>)stack[j - 1].page).getPage( stack[j - 1].pos );
+                            stack[j].pos = stack[j].page.getNbPageElems();
+                        }
+
+                        // The last page is a leaf
+                        stack[depth].page = ((Node<K, V>)stack[depth - 1].page).getPage( stack[depth - 1].pos );
+                        stack[depth].pos = stack[depth].page.getNbPageElems() - 1;
+                        
+                        hasPrev = true;
+                        break;
+                    }
+                }
 
                 // we also need to check for the type of page cause
                 // findPrevParentPos will never return a null ParentPos
-                if ( parentPos == null )
+                if ( !hasPrev )
                 {
                     // This is the end : no more value
                     throw new NoSuchElementException( "No more tuples present" );
                 }
-                else
-                {
-                    parentPos.pos--;
-                }
                 
                 break;
                 
@@ -466,7 +390,7 @@ public class TupleCursor<K, V> //impleme
         }
 
         // Get the value
-        ValueHolder<V> valueHolder = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( parentPos.pos );
+        ValueHolder<V> valueHolder = ( ( Leaf<K, V> ) parentPos.page ).getValue( parentPos.pos );
         V value = valueHolder.get();
 
         // Get the key
@@ -474,262 +398,15 @@ public class TupleCursor<K, V> //impleme
         K key = leaf.getKey( parentPos.pos );
         
         // Construct the Tuple 
-        Tuple<K, V> tuple = new Tuple<K, V>( key, value );
-
-        return tuple;
+        return new Tuple<>( key, value );
     }
 
 
     /**
-     * Tells if there is a next ParentPos
-     *
-     * @return the new ParentPos instance, or null if we have no following leaf
-     * @throws IOException
-     * @throws EndOfFileExceededException
-     */
-    private boolean hasNextParentPos() throws EndOfFileExceededException, IOException
-    {
-        if ( depth == 0 )
-        {
-            // No need to go any further, there is only one leaf in the btree
-            return false;
-        }
-
-        int currentDepth = depth - 1;
-        Page<K, V> child = null;
-
-        // First, go up the tree until we find a Node which has some element on the right
-        while ( currentDepth >= 0 )
-        {
-            // We first go up the tree, until we reach a page whose current position
-            // is not the last one
-            ParentPos<K, V> parentPos = stack[currentDepth];
-
-            if ( parentPos.pos + 1 > parentPos.page.getNbElems() )
-            {
-                // No more element on the right : go up
-                currentDepth--;
-            }
-            else
-            {
-                // We can pick the next element at this level
-                child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos + 1 );
-
-                // and go down the tree through the nodes
-                while ( currentDepth < depth - 1 )
-                {
-                    currentDepth++;
-                    child = ( ( AbstractPage<K, V> ) child ).getPage( 0 );
-                }
-
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
-     * Find the leaf containing the following elements.
-     *
-     * @return the new ParentPos instance, or null if we have no following leaf
-     * @throws IOException
-     * @throws EndOfFileExceededException
-     */
-    private ParentPos<K, V> findNextParentPos() throws EndOfFileExceededException, IOException
-    {
-        if ( depth == 0 )
-        {
-            // No need to go any further, there is only one leaf in the btree
-            return null;
-        }
-
-        int currentDepth = depth - 1;
-        Page<K, V> child = null;
-
-        // First, go up the tree until we find a Node which has some element on the right
-        while ( currentDepth >= 0 )
-        {
-            // We first go up the tree, until we reach a page whose current position
-            // is not the last one
-            ParentPos<K, V> parentPos = stack[currentDepth];
-
-            if ( parentPos.pos + 1 > parentPos.page.getNbElems() )
-            {
-                // No more element on the right : go up
-                currentDepth--;
-            }
-            else
-            {
-                // We can pick the next element at this level
-                parentPos.pos++;
-                child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos );
-
-                // and go down the tree through the nodes
-                while ( currentDepth < depth - 1 )
-                {
-                    currentDepth++;
-                    parentPos = stack[currentDepth];
-                    parentPos.pos = 0;
-                    parentPos.page = child;
-                    child = ( ( AbstractPage<K, V> ) child ).getPage( 0 );
-                }
-
-                // and the leaf
-                parentPos = stack[depth];
-                parentPos.page = child;
-                parentPos.pos = 0;
-
-                return parentPos;
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Find the leaf containing the previous elements. We are in
-     * 
-     * if the B-tree content is like :
-     * <pre>
-     *                    .
-     *                   |
-     *                   v
-     *           +---+---+---+---+
-     *           |///| X | Y |///|               Level N-1
-     *           +---+---+---+---+
-     *           |   |   |   |   |
-     *   <-...---+   |   |   |   +---...-> 
-     *               |   |   |
-     *       <-...---+   |   |
-     *                   |   |
-     *         +---------+   +-------+
-     *         |                     | 
-     *         v                     v
-     * +---+---+---+---+     +---+---+---+---+
-     * |///|///| T |###|     | Y |///|///|///|   Level N
-     * +---+---+---+---+     +---+---+---+---+
-     *          pos           pos 
-     *   New ParentPos       Current ParentPos
-     * </pre>
-     * 
-     * and we are on Y, then the previous ParentPos instance will contain the page
-     * on the left, teh position beoing on the last element of this page, T
-     *
-     * @return the new ParentPos instance, or null if we have no previous leaf
-     * @throws IOException
-     * @throws EndOfFileExceededException
-     */
-    private ParentPos<K, V> findPrevParentPos() throws EndOfFileExceededException, IOException
-    {
-        if ( depth == 0 )
-        {
-            // No need to go any further, there is only one leaf in the btree
-            return null;
-        }
-
-        // Go up one step
-        int currentDepth = depth - 1;
-        Page<K, V> child = null;
-
-        // First, go up the tree until we find a Node which has some element on the left
-        while ( currentDepth >= 0 )
-        {
-            // We first go up the tree, until we reach a page whose current position
-            // is not the first one
-            ParentPos<K, V> parentPos = stack[currentDepth];
-
-            if ( parentPos.pos == 0 )
-            {
-                // No more element on the left : go up
-                currentDepth--;
-            }
-            else
-            {
-                // We can pick the previous element at this level
-                parentPos.pos--;
-                child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos );
-
-                // and go down the tree through the nodes, positioning on the rightmost element
-                while ( currentDepth < depth - 1 )
-                {
-                    currentDepth++;
-                    parentPos = stack[currentDepth];
-                    parentPos.pos = child.getNbElems();
-                    parentPos.page = child;
-                    child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.page.getNbElems() );
-                }
-
-                // and the leaf
-                parentPos = stack[depth];
-                parentPos.pos = child.getNbElems() - 1;
-                parentPos.page = child;
-
-                return parentPos;
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Tells if there is a prev ParentPos
-     *
-     * @return the new ParentPos instance, or null if we have no previous leaf
-     * @throws IOException
-     * @throws EndOfFileExceededException
-     */
-    private boolean hasPrevParentPos() throws EndOfFileExceededException, IOException
-    {
-        if ( depth == 0 )
-        {
-            // No need to go any further, there is only one leaf in the btree
-            return false;
-        }
-
-        int currentDepth = depth - 1;
-        Page<K, V> child = null;
-
-        // First, go up the tree until we find a Node which has some element on the right
-        while ( currentDepth >= 0 )
-        {
-            // We first go up the tree, until we reach a page whose current position
-            // is not the last one
-            ParentPos<K, V> parentPos = stack[currentDepth];
-
-            if ( parentPos.pos == 0 )
-            {
-                // No more element on the left : go up
-                currentDepth--;
-            }
-            else
-            {
-                // We can pick the previous element at this level
-                child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos - 1 );
-
-                // and go down the tree through the nodes
-                while ( currentDepth < depth - 1 )
-                {
-                    currentDepth++;
-                    child = ( ( AbstractPage<K, V> ) child ).getPage( child.getNbElems() );
-                }
-
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
-     * Checks if this Cursor is closed. Calls to this operation should not
+     * Checks if this cursor is closed. Calls to this operation should not
      * fail with exceptions if and only if the cursor is in the closed state.
      *
-     * @return true if this Cursor is closed, false otherwise
+     * @return <tt>true</tt> if this cursor is closed, <tt>false</tt> otherwise
      */
     public boolean isClosed()
     {
@@ -738,43 +415,50 @@ public class TupleCursor<K, V> //impleme
 
 
     /**
-     * Closes this Cursor and frees any resources it my have allocated.
-     * Repeated calls to this method after this Cursor has already been
+     * Closes this cursor and frees any resources it my have allocated.
+     * Repeated calls to this method after this cursor has already been
      * called should not fail with exceptions.
      */
+    @Override
     public void close()
     {
-        transaction.close();
+        //try 
+        {
+            //transaction.close();
+        }
+        //catch ( IOException ioe )
+        {
+            // There is nothing we can do
+        }
     }
 
 
     /**
-     * Closes this Cursor and frees any resources it my have allocated.
-     * Repeated calls to this method after this Cursor has already been
+     * Closes this cursor and frees any resources it my have allocated.
+     * Repeated calls to this method after this cursor has already been
      * called should not fail with exceptions.  The reason argument is 
      * the Exception instance thrown instead of the standard 
      * CursorClosedException.
      *
-     * @param reason exception thrown when this Cursor is accessed after close
+     * @param reason exception thrown when this cursor is accessed after close
      */
     public void close( Exception reason )
     {
-        transaction.close();
     }
 
 
     /**
-     * Gets the object at the current position.  Cursor implementations may
+     * Gets the object at the current position.  cursor implementations may
      * choose to reuse element objects by re-populating them on advances
      * instead of creating new objects on each advance.
      *
      * @return the object at the current position
-     * @throws NoSuchElementException if the object at this Cursor's current position
-     * cannot be retrieved, or if this Cursor is closed
+     * @throws NoSuchElementException if the object at this cursor's current position
+     * cannot be retrieved, or if this cursor is closed
      */
-    public Tuple<K, V> get() throws NoSuchElementException
+    public Tuple<K, V> get()
     {
-        // First check that we have elements in the BTree
+        // First check that we have elements in the B-tree
         if ( ( stack == null ) || ( stack.length == 0 ) )
         {
             throw new NoSuchElementException( "No tuple present" );
@@ -782,22 +466,26 @@ public class TupleCursor<K, V> //impleme
 
         ParentPos<K, V> parentPos = stack[depth];
         
+        // Check that we actually have elements
+        if ( parentPos.page == null )
+        {
+            // Empty B-tree, get out
+            throw new NoSuchElementException( "No tuple present" );
+        }
+        
         if ( ( parentPos.pos != BEFORE_FIRST ) && ( parentPos.pos != AFTER_LAST ) )
         {
             // We have some element, build the Tuple
-            // Get the value
-            ValueHolder<V> valueHolder = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( parentPos.pos );
+            Leaf<K, V> leaf = ( Leaf<K, V> ) ( parentPos.page );
 
-            V value = valueHolder.get();
+            // Get the value
+            V value = leaf.getValue( parentPos.pos ).get();
 
             // Get the key
-            AbstractPage<K, V> leaf = ( AbstractPage<K, V> ) ( parentPos.page );
             K key = leaf.getKey( parentPos.pos );
             
             // Build the Tuple
-            Tuple<K, V> tuple = new Tuple<K, V>( key, value );
-
-            return tuple;
+            return new Tuple<>( key, value );
         }
         
         throw new NoSuchElementException( "No element at this position : " + parentPos.pos );
@@ -810,7 +498,7 @@ public class TupleCursor<K, V> //impleme
      */
     public long getCreationDate()
     {
-        return transaction.getCreationDate();
+        return ((ReadTransaction)transaction).getCreationDate();
     }
 
 
@@ -825,6 +513,10 @@ public class TupleCursor<K, V> //impleme
     }
 
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public String toString()
     {
         StringBuilder sb = new StringBuilder();

Copied: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java (from r1711861, directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/AbstractValueHolder.java)
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java?p2=directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java&p1=directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/AbstractValueHolder.java&r1=1711861&r2=1784232&rev=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/AbstractValueHolder.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java Fri Feb 24 07:16:37 2017
@@ -20,9 +20,12 @@
 package org.apache.directory.mavibot.btree;
 
 
+import java.io.IOException;
 import java.util.Comparator;
 
 import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
 /**
@@ -31,22 +34,132 @@ import org.apache.directory.mavibot.btre
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @param <V> The value type
  */
-/* No qualifier*/abstract class AbstractValueHolder<V> implements ValueHolder<V>
+/* No qualifier*/class ValueHolder<V> implements Cloneable
 {
+    /** The LoggerFactory used by this class */
+    protected static final Logger LOG = LoggerFactory.getLogger( ValueHolder.class );
+
+    /** The parent BTree */
+    protected BTreeImpl parentBtree;
+
+    /** The serialized value */
+    private byte[] raw;
+
+    /** A flag set to true when the raw value has been deserialized */
+    private boolean isDeserialized = false;
+
+    /** A flag to signal that the raw value represent the serialized values in their last state */
+    private boolean isRawUpToDate = false;
+
     /** The array storing from 1 to N values */
-    protected V value;
+    private V value;
 
     /** The Value serializer */
     protected ElementSerializer<V> valueSerializer;
 
+    /**
+     * Creates a new instance of a ValueHolder, containing the serialized values.
+     *
+     * @param parentBtree the parent BTree
+     * @param raw The raw data containing the values
+     * @param nbValues the number of stored values
+     * @param raw the byte[] containing either the serialized array of values or the sub-btree offset
+     */
+    ValueHolder( BTree<?, V> parentBtree, byte[] raw )
+    {
+        this.parentBtree = ( BTreeImpl<V, V> ) parentBtree;
+        this.valueSerializer = parentBtree.getValueSerializer();
+        this.raw = raw;
+        isRawUpToDate = true;
+    }
+
+    
+    /**
+     * Creates a new instance of a ValueHolder, containing a Value. This constructor is called
+     * when we need to create a new ValueHolder with deserialized values.
+     *
+     * @param parentBtree The parent BTree
+     * @param values The Value stored in the ValueHolder
+     */
+    public ValueHolder( BTree parentBtree, V value )
+    {
+        this.parentBtree = ( BTreeImpl ) parentBtree;
+        this.valueSerializer = parentBtree.getValueSerializer();
+        set( value );
+
+        isDeserialized = true;
+    }
+
+
+    /**
+     * @return the raw representation of the value holder. The serialized value will not be the same
+     * if the values are stored in an array or in a btree. <br/>
+     * If they are stored in a BTree, the raw value will contain the offset of the btree, otherwise
+     * it will contain a byte[] which will contain each serialized value, prefixed by their length.
+     *
+     */
+    /* No qualifier*/byte[] getRaw()
+    {
+        if ( isRawUpToDate )
+        {
+            // Just have to return the raw value
+            return raw;
+        }
+
+        // Process the value
+        // Serialize the value
+        raw = valueSerializer.serialize( value );
+
+        // Update the flags
+        isRawUpToDate = true;
 
+        return raw;
+    }
+    
+    
+    /**
+     * Deserialize the stored raw value
+     * @throws IOException 
+     */
+    private void deserialize() throws IOException
+    {
+        // We haven't yet deserialized the values. Let's do it now.
+        value = valueSerializer.fromBytes( raw, 0 );
+    }
+
+
+    /**
+     * Check that the values are stored as raw value
+     * @throws IOException 
+     */
+    private void checkAndDeserialize() throws IOException
+    {
+        if ( !isDeserialized )
+        {
+                // The values are stored into an array. Deserialize it now
+            deserialize();
+        }
+
+        // Change the flag
+        isDeserialized = true;
+    }
+
+    
     /**
      * Create a clone of this instance
      */
+    @Override
     public ValueHolder<V> clone() throws CloneNotSupportedException
     {
         ValueHolder<V> copy = ( ValueHolder<V> ) super.clone();
 
+        // Also clone the raw value if its up to date
+        if ( isRawUpToDate )
+        {
+            copy.raw = new byte[raw.length];
+            System.arraycopy( raw, 0, copy.raw, 0, raw.length );
+        }
+
         return copy;
     }
 
@@ -56,6 +169,16 @@ import org.apache.directory.mavibot.btre
      */
     public boolean contains( V checkedValue )
     {
+        // First, deserialize the value if it's still a byte[]
+        try
+        {
+            checkAndDeserialize();
+        }
+        catch ( IOException e )
+        {
+            e.printStackTrace();
+        }
+
         Comparator<V> comparator = valueSerializer.getComparator();
 
         int result = comparator.compare( value,  checkedValue );
@@ -67,8 +190,65 @@ import org.apache.directory.mavibot.btre
     /**
      * {@inheritDoc}
      */
+    public V get()
+    {
+        return value;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
     public void set( V value )
     {
         this.value = value;
+
+        // The raw value is not anymore up to date with the content
+        isRawUpToDate = false;
+        raw = null;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public V remove( V value )
+    {
+        if ( contains( value ) )
+        {
+            V removedValue = this.value;
+            this.value = null;
+            
+            // The raw value is not anymore up to date with the content
+            isRawUpToDate = false;
+            raw = null;
+    
+            return removedValue;
+        }
+        else 
+        { 
+            return null;
+        }
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+
+        if ( !isDeserialized )
+        {
+            sb.append( "isRaw[" ).append( raw.length ).append( "]" );
+        }
+        else
+        {
+            sb.append( value );
+        }
+
+        return sb.toString();
     }
 }

Modified: directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/WALObject.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/WALObject.java?rev=1784232&r1=1784231&r2=1784232&view=diff
==============================================================================
--- directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/WALObject.java (original)
+++ directory/mavibot/branches/single-value/mavibot/src/main/java/org/apache/directory/mavibot/btree/WALObject.java Fri Feb 24 07:16:37 2017
@@ -20,28 +20,82 @@
 package org.apache.directory.mavibot.btree;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 
 /**
  * A WALObject is an object stored in the TransactionContext before weing written on disk.
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public interface WALObject
+public interface WALObject<K, V>
 {
     /**
-     * @return The ID associated with the Object
+     * @return the B-tree
      */
-    long getId();
+    public BTree<K, V> getBtree();
+
+    
+    /**
+     * @return The offset for this WALObject
+     */
+    long getOffset();
+    
     
+    /**
+     * @return the underlying B-tree name
+     */
+    String getName();
     
     /**
-     * @param id Set an ID to this Object
+     * @return The PageIOs associated with the Object
      */
-    void setId( long id );
+    PageIO[] getPageIOs();
+    
+    
+    /**
+     * @param pageIOs Store teh PageIOs for this object
+     */
+    void setPageIOs( PageIO[] pageIOs );
+    
+    
+    /**
+     * Serialize a WALObject into PageIO[]
+     * 
+     * @param transaction The Write Transaction in use
+     * @return The serialized WALObject into some PageIOs
+     * 
+     * @throws IOException If we got an error while serializing
+     */
+    PageIO[] serialize( WriteTransaction transaction ) throws IOException;
+    
+    
+    /**
+     * Deserialize a WALObject from a PageIO[]
+     * 
+     * @param transaction The Read Transaction in use
+     * @param byteBuffer The byteBuffer containing the page data
+     * @return The read page
+     * 
+     * @throws IOException If we got an error while serializing
+     */
+    WALObject<K, V> deserialize( Transaction transaction, ByteBuffer byteBuffer ) throws IOException;
+
+    
+    /**
+     * @return the revision
+     */
+    long getRevision();
+    
+    
+    /**
+     * @return the Page id
+     */
+    long getId();
     
     
     /**
-     * @return Serialize the Object into PageIOs
+     * Print the content of this WALObject in a dense format
+     * @return The pretty print for this instance
      */
-    PageIO[] serialize() throws IOException;
+    String prettyPrint();
 }



Mime
View raw message