Same errors.

I guess we have to go a

On Thu, Aug 25, 2011 at 2:55 PM, Pierre-Arnaud Marcelot <pa@marcelot.net> wrote:
I'm getting the following test failures in jdbm-partition:
[INFO] ------------------------------------------------------------------------
[INFO] Building ApacheDS JDBM Partition 2.0.0-M3-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ apacheds-jdbm-partition ---
[INFO] Deleting /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target
[INFO] 
[INFO] --- maven-remote-resources-plugin:1.2:process (default) @ apacheds-jdbm-partition ---
[INFO] 
[INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ apacheds-jdbm-partition ---
[debug] execute contextualize
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/src/main/resources
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ apacheds-jdbm-partition ---
[INFO] Compiling 20 source files to /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.5:testResources (default-testResources) @ apacheds-jdbm-partition ---
[debug] execute contextualize
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ apacheds-jdbm-partition ---
[INFO] Compiling 20 source files to /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.8:test (default-test) @ apacheds-jdbm-partition ---
[INFO] Surefire report directory: /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.apache.directory.server.core.partition.impl.btree.jdbm.BTreeRedirectMarshallerTest
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.211 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.DupsContainerCursorTest
Tests run: 5, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.526 sec <<< FAILURE!
Running org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest
Tests run: 15, Failures: 0, Errors: 12, Skipped: 0, Time elapsed: 1.4 sec <<< FAILURE!
Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmBrowserBugTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndexTest
Tests run: 15, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.865 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmMasterTableTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.553 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmRdnIndexTest
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.583 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmStoreTest
Tests run: 18, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.443 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest
Tests run: 8, Failures: 0, Errors: 6, Skipped: 0, Time elapsed: 1.135 sec <<< FAILURE!
Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest
Tests run: 15, Failures: 0, Errors: 9, Skipped: 0, Time elapsed: 0.918 sec <<< FAILURE!
Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyBTreeCursorTest
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.071 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyCursorTest
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.06 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyTupleArrayCursorTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyTupleBTreeCursorTest
Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.558 sec <<< FAILURE!
Running org.apache.directory.server.core.partition.impl.btree.jdbm.NoDupsCursorTest
Tests run: 5, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.491 sec <<< FAILURE!
Running org.apache.directory.server.core.partition.impl.btree.jdbm.ServerEntrySerializerTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.466 sec
Running org.apache.directory.server.core.partition.impl.btree.jdbm.StringSerializerTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.058 sec
Running org.apache.directory.server.core.partition.tree.PartitionTreeTest
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.454 sec
Running org.apache.directory.server.core.schema.PartitionSchemaLoaderTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.377 sec

Results :

Tests in error: 
  testOnTableWithMultipleEntries(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsContainerCursorTest)
  testNextNoDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testPreviousNoDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testNextDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testPreviousDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testFirstLastUnderDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testFirstLastOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testFirstOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testLastOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testUnderDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testBeforeAfterBelowDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testBeforeAfterOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
  testCloseReopen(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
  testLoadData(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
  testNullOrEmptyKeyValue(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
  testRemove(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
  testPut(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
  testHas(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
  testMiscellaneous(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testCloseReopen(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testLoadData(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testNullOrEmptyKeyValue(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testRemove(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testPut(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testHas(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testDuplicateLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testNullOrEmptyKeyValueAfterDuplicateLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
  testNonEmptyCursor(org.apache.directory.server.core.partition.impl.btree.jdbm.KeyTupleBTreeCursorTest)
  testOnTableWithMultipleEntries(org.apache.directory.server.core.partition.impl.btree.jdbm.NoDupsCursorTest)

Tests run: 127, Failures: 0, Errors: 30, Skipped: 0

Did I miss something?!?
Is there another patch to apply, other than the 'jdbm1.diff' file?

Regards,
Pierre-Arnaud

On 25 aoűt 2011, at 14:49, Pierre-Arnaud Marcelot wrote:

OK,

I moved the two test classes to their dedicated package 'jdbm.helper'.

And fixed the compilation failure in Maven (strangely it was fine in Eclipse) by replacing this in:
lrus = (LRUCache<K,V>.LRU [] )new LRUCache.LRU[NUM_LRUS];
by this:
lrus = ( LRUCache.LRU[] ) new LRUCache.LRU[NUM_LRUS];

Compilation is now fine in Eclipse *and* Maven.

Now, running all tests.

Regards,
Pierre-Arnaud


On 25 aoűt 2011, at 14:42, Pierre-Arnaud Marcelot wrote:

I'm having trouble applying the 'jdbm1.diff' patch.

Patching went fine, however there are two test classes that are not in the correct package, TestActionVersioning and TestVersionedCache, and Maven is reporting a compilation failure:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project apacheds-jdbm: Compilation failure: Compilation failure:
[ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,28] ')' expected
[ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,29] ';' expected
[ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,33] illegal start of expression
[ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,34] ';' expected
[ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,36] illegal start of expression
[ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,37] ';' expected
[ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,54] not a statement

More to come...

Regards,
Pierre-Arnaud

On 25 aoűt 2011, at 13:58, Pierre-Arnaud Marcelot wrote:

Sure, I'm on it.

Regards,
Pierre-Arnaud

On 25 aoűt 2011, at 13:41, Emmanuel Lecharny wrote:

Strange... I did a pacth -p0 with your diff.

I may have not svn added the files.

i'm not connected right now (using a client internet connection, will do that tonite

Or maybe Pierre-Arnaud can apply the patch, do a svn add of the added files and commit ?

On Thu, Aug 25, 2011 at 9:41 AM, Selcuk AYA <ayaselcuk@gmail.com> wrote:
Hi,
there are alos a bunch of new filesn the patch I provided. Can you
also add them to the branch?

A    jdbm/src/test/java/jdbm/helper
A    jdbm/src/test/java/jdbm/btree/SnapshotBTree.java
A    jdbm/src/test/java/jdbm/TestActionVersioning.java
A    jdbm/src/test/java/jdbm/TestVersionedCache.java
A    jdbm/src/main/java/jdbm/helper/ExplicitList.java
A    jdbm/src/main/java/jdbm/helper/ActionVersioning.java
A    jdbm/src/main/java/jdbm/helper/ActionContext.java
A    jdbm/src/main/java/jdbm/helper/LRUCache.java
A    jdbm/src/main/java/jdbm/helper/EntryIO.java
A    jdbm/src/main/java/jdbm/recman/SnapshotRecordManager.java
A    jdbm/src/main/java/jdbm/ActionRecordManager.java

regards,
Selcuk

On Thu, Aug 25, 2011 at 9:58 AM,  <elecharny@apache.org> wrote:
> Author: elecharny
> Date: Thu Aug 25 06:58:36 2011
> New Revision: 1161416
>
> URL: http://svn.apache.org/viewvc?rev=1161416&view=rev
> Log:
> applied Selcuk's patch
>
> Modified:
>    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java
>    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java
>    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java
>    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java
>
> Modified: directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java?rev=1161416&r1=1161415&r2=1161416&view=diff
> ==============================================================================
> --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java (original)
> +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java Thu Aug 25 06:58:36 2011
> @@ -50,6 +50,7 @@ package jdbm.btree;
>  import jdbm.helper.Serializer;
>  import jdbm.helper.Tuple;
>  import jdbm.helper.TupleBrowser;
> +import jdbm.helper.ActionContext;
>
>  import java.io.IOException;
>  import java.io.ByteArrayOutputStream;
> @@ -259,10 +260,11 @@ public class BPage<K, V> implements Seri
>      *
>      * @param height Height of the current BPage (zero is leaf page)
>      * @param key The key
> +     * @param context action specific context. not null if record manager is action capable
>      * @return TupleBrowser positionned just before the given key, or before
>      *                      next greater key if key isn't found.
>      */
> -    TupleBrowser<K, V> find( int height, K key ) throws IOException
> +    TupleBrowser<K, V> find( int height, K key, ActionContext context ) throws IOException
>     {
>         int index = this.findChildren( key );
>
> @@ -285,26 +287,27 @@ public class BPage<K, V> implements Seri
>             }
>         }
>
> -        return new Browser( child, index );
> +        return new Browser( child, index, context );
>     }
>
>
>     /**
>      * Find first entry and return a browser positioned before it.
>      *
> +     * @param context action specific context. not null if record manager is action capable
>      * @return TupleBrowser positionned just before the first entry.
>      */
> -    TupleBrowser<K, V> findFirst() throws IOException
> +    TupleBrowser<K, V> findFirst(ActionContext context ) throws IOException
>     {
>         if ( isLeaf )
>         {
> -            return new Browser( this, first );
> +            return new Browser( this, first, context );
>         }
>         else
>         {
>             BPage<K, V> child = childBPage( first );
>
> -            return child.findFirst();
> +            return child.findFirst( context );
>         }
>     }
>
> @@ -337,9 +340,12 @@ public class BPage<K, V> implements Seri
>
>         height -= 1;
>
> +        BPage<K,V> pageNewCopy = null;
>         if ( height == 0 )
>         {
> +            pageNewCopy = btree.copyOnWrite( this );
>             result = new InsertResult<K, V>();
> +            result.pageNewCopy = pageNewCopy;
>
>             // inserting on a leaf BPage
>             overflow = -1;
> @@ -365,8 +371,8 @@ public class BPage<K, V> implements Seri
>
>                 if ( replace )
>                 {
> -                    values[index] = value;
> -                    btree.recordManager.update( recordId, this, this );
> +                    pageNewCopy.values[index] = value;
> +                    btree.recordManager.update( recordId, pageNewCopy, this );
>                 }
>
>                 // return the existing key
> @@ -378,6 +384,11 @@ public class BPage<K, V> implements Seri
>             // non-leaf BPage
>             BPage<K, V> child = childBPage( index );
>             result = child.insert( height, key, value, replace );
> +            if( result.pageNewCopy != null)
> +            {
> +                child = result.pageNewCopy;
> +                result.pageNewCopy = null;
> +            }
>
>             if ( result.existing != null )
>             {
> @@ -393,6 +404,9 @@ public class BPage<K, V> implements Seri
>
>             // there was an overflow, we need to insert the overflow page
>             // on this BPage
> +            pageNewCopy = btree.copyOnWrite( this );
> +            result.pageNewCopy = pageNewCopy;
> +
>             if ( DEBUG )
>             {
>                 System.out.println( "BPage.insert() Overflow page: " + result.overflow.recordId );
> @@ -402,7 +416,7 @@ public class BPage<K, V> implements Seri
>             overflow = result.overflow.recordId;
>
>             // update child's largest key
> -            keys[index] = child.getLargestKey();
> +            pageNewCopy.keys[index] = child.getLargestKey();
>
>             // clean result so we can reuse it
>             result.overflow = null;
> @@ -410,24 +424,25 @@ public class BPage<K, V> implements Seri
>
>         // if we get here, we need to insert a new entry on the BPage
>         // before children[ index ]
> -        if ( !isFull() )
> -        {
> +        if ( !pageNewCopy.isFull() )
> +        {
>             if ( height == 0 )
>             {
> -                insertEntry( this, index - 1, key, value );
> +                insertEntry( pageNewCopy, index - 1, key, value );
>             }
>             else
>             {
> -                insertChild( this, index - 1, key, overflow );
> +                insertChild( pageNewCopy, index - 1, key, overflow );
>             }
>
> -            btree.recordManager.update( recordId, this, this );
> +            btree.recordManager.update( recordId, pageNewCopy, this );
>             return result;
>         }
>
>         // page is full, we must divide the page
>         int half = btree.pageSize >> 1;
> -        BPage<K, V> newPage = new BPage<K, V>( btree, isLeaf );
> +        BPage<K, V> newPage = new BPage<K, V>( btree, pageNewCopy.isLeaf );
> +
>
>         if ( index < half )
>         {
> @@ -441,15 +456,15 @@ public class BPage<K, V> implements Seri
>
>             if ( height == 0 )
>             {
> -                copyEntries( this, 0, newPage, half, index );
> +                copyEntries( pageNewCopy, 0, newPage, half, index );
>                 setEntry( newPage, half + index, key, value );
> -                copyEntries( this, index, newPage, half + index + 1, half - index - 1 );
> +                copyEntries( pageNewCopy, index, newPage, half + index + 1, half - index - 1 );
>             }
>             else
>             {
> -                copyChildren( this, 0, newPage, half, index );
> +                copyChildren( pageNewCopy, 0, newPage, half, index );
>                 setChild( newPage, half + index, key, overflow );
> -                copyChildren( this, index, newPage, half + index + 1, half - index - 1 );
> +                copyChildren( pageNewCopy, index, newPage, half + index + 1, half - index - 1 );
>             }
>         }
>         else
> @@ -463,50 +478,51 @@ public class BPage<K, V> implements Seri
>
>             if ( height == 0 )
>             {
> -                copyEntries( this, 0, newPage, half, half );
> -                copyEntries( this, half, this, half - 1, index - half );
> -                setEntry( this, index - 1, key, value );
> +                copyEntries( pageNewCopy, 0, newPage, half, half );
> +                copyEntries( pageNewCopy, half, pageNewCopy, half - 1, index - half );
> +                setEntry( pageNewCopy, index - 1, key, value );
>             }
>             else
>             {
> -                copyChildren( this, 0, newPage, half, half );
> -                copyChildren( this, half, this, half - 1, index - half );
> -                setChild( this, index - 1, key, overflow );
> +                copyChildren( pageNewCopy, 0, newPage, half, half );
> +                copyChildren( pageNewCopy, half, pageNewCopy, half - 1, index - half );
> +                setChild( pageNewCopy, index - 1, key, overflow );
>             }
>         }
>
> -        first = half - 1;
> +        pageNewCopy.first = half - 1;
>
>         // nullify lower half of entries
> -        for ( int i = 0; i < first; i++ )
> +        for ( int i = 0; i < pageNewCopy.first; i++ )
>         {
>             if ( height == 0 )
>             {
> -                setEntry( this, i, null, null );
> +                setEntry( pageNewCopy, i, null, null );
>             }
>             else
>             {
> -                setChild( this, i, null, -1 );
> +                setChild( pageNewCopy, i, null, -1 );
>             }
>         }
>
> -        if ( isLeaf )
> +        if ( pageNewCopy.isLeaf )
>         {
>             // link newly created BPage
> -            newPage.previous = previous;
> -            newPage.next = recordId;
> +            newPage.previous = pageNewCopy.previous;
> +            newPage.next = pageNewCopy.recordId;
>
> -            if ( previous != 0 )
> +            if ( pageNewCopy.previous != 0 )
>             {
> -                BPage<K, V> previousBPage = loadBPage( previous );
> +                BPage<K, V> previousBPage = loadBPage( pageNewCopy.previous );
> +                previousBPage = btree.copyOnWrite( previousBPage );
>                 previousBPage.next = newPage.recordId;
> -                btree.recordManager.update( previous, previousBPage, this );
> +                btree.recordManager.update( pageNewCopy.previous, previousBPage, this );
>             }
>
> -            previous = newPage.recordId;
> +            pageNewCopy.previous = newPage.recordId;
>         }
>
> -        btree.recordManager.update( recordId, this, this );
> +        btree.recordManager.update( recordId, pageNewCopy, this );
>         btree.recordManager.update( newPage.recordId, newPage, this );
>
>         result.overflow = newPage;
> @@ -521,9 +537,9 @@ public class BPage<K, V> implements Seri
>      * @param key Removal key
>      * @return Remove result object
>      */
> -    RemoveResult<V> remove( int height, K key ) throws IOException
> +    RemoveResult<K, V> remove( int height, K key ) throws IOException
>     {
> -        RemoveResult<V> result;
> +        RemoveResult<K, V> result;
>
>         int half = btree.pageSize / 2;
>         int index = findChildren( key );
> @@ -536,6 +552,7 @@ public class BPage<K, V> implements Seri
>
>         height -= 1;
>
> +        BPage<K,V> pageNewCopy = btree.copyOnWrite( this );;
>         if ( height == 0 )
>         {
>             // remove leaf entry
> @@ -544,22 +561,32 @@ public class BPage<K, V> implements Seri
>                 throw new IllegalArgumentException( I18n.err( I18n.ERR_514, key ) );
>             }
>
> -            result = new RemoveResult<V>();
> -            result.value = values[index];
> -            removeEntry( this, index );
> +            result = new RemoveResult<K, V>();
> +            result.value = pageNewCopy.values[index];
> +            removeEntry( pageNewCopy, index );
>
>             // update this BPage
> -            btree.recordManager.update( recordId, this, this );
> +            btree.recordManager.update( recordId, pageNewCopy, this );
>         }
>         else
>         {
>             // recurse into Btree to remove entry on a children page
>             BPage<K, V> child = childBPage( index );
>             result = child.remove( height, key );
> +
> +            if ( result.pageNewCopy != null )
> +            {
> +                child = result.pageNewCopy;
> +                result.pageNewCopy = null;
> +            }
> +            else
> +            {
> +                child = btree.copyOnWrite( child );
> +            }
>
>             // update children
> -            keys[index] = child.getLargestKey();
> -            btree.recordManager.update( recordId, this, this );
> +            pageNewCopy.keys[index] = child.getLargestKey();
> +            btree.recordManager.update( recordId, pageNewCopy, this );
>
>             if ( result.underflow )
>             {
> @@ -569,10 +596,11 @@ public class BPage<K, V> implements Seri
>                     throw new IllegalStateException( I18n.err( I18n.ERR_513, "1" ) );
>                 }
>
> -                if ( index < children.length - 1 )
> +                if ( index < pageNewCopy.children.length - 1 )
>                 {
>                     // exists greater brother page
> -                    BPage<K, V> brother = childBPage( index + 1 );
> +                    BPage<K, V> brother = pageNewCopy.childBPage( index + 1 );
> +                    brother = btree.copyOnWrite( brother );
>                     int bfirst = brother.first;
>
>                     if ( bfirst < half )
> @@ -606,12 +634,12 @@ public class BPage<K, V> implements Seri
>                         }
>
>                         // update child's largest key
> -                        keys[index] = child.getLargestKey();
> +                        pageNewCopy.keys[index] = child.getLargestKey();
>
>                         // no change in previous/next BPage
>
>                         // update BPages
> -                        btree.recordManager.update( recordId, this, this );
> +                        btree.recordManager.update( recordId, pageNewCopy, this );
>                         btree.recordManager.update( brother.recordId, brother, this );
>                         btree.recordManager.update( child.recordId, child, this );
>
> @@ -638,24 +666,25 @@ public class BPage<K, V> implements Seri
>                         btree.recordManager.update( brother.recordId, brother, this );
>
>                         // remove "child" from current BPage
> -                        if ( isLeaf )
> +                        if ( pageNewCopy.isLeaf )
>                         {
> -                            copyEntries( this, first, this, first + 1, index - first );
> -                            setEntry( this, first, null, null );
> +                            copyEntries( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - pageNewCopy.first );
> +                            setEntry( pageNewCopy, pageNewCopy.first, null, null );
>                         }
>                         else
>                         {
> -                            copyChildren( this, first, this, first + 1, index - first );
> -                            setChild( this, first, null, -1 );
> +                            copyChildren( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - pageNewCopy.first );
> +                            setChild( pageNewCopy, pageNewCopy.first, null, -1 );
>                         }
>
> -                        first += 1;
> -                        btree.recordManager.update( recordId, this, this );
> +                        pageNewCopy.first += 1;
> +                        btree.recordManager.update( recordId, pageNewCopy, this );
>
>                         // re-link previous and next BPages
>                         if ( child.previous != 0 )
>                         {
>                             BPage<K, V> prev = loadBPage( child.previous );
> +                            prev = btree.copyOnWrite( prev );
>                             prev.next = child.next;
>                             btree.recordManager.update( prev.recordId, prev, this );
>                         }
> @@ -663,6 +692,7 @@ public class BPage<K, V> implements Seri
>                         if ( child.next != 0 )
>                         {
>                             BPage<K, V> next = loadBPage( child.next );
> +                            next = btree.copyOnWrite( next );
>                             next.previous = child.previous;
>                             btree.recordManager.update( next.recordId, next, this );
>                         }
> @@ -674,7 +704,8 @@ public class BPage<K, V> implements Seri
>                 else
>                 {
>                     // page "brother" is before "child"
> -                    BPage<K, V> brother = childBPage( index - 1 );
> +                    BPage<K, V> brother = pageNewCopy.childBPage( index - 1 );
> +                    brother = btree.copyOnWrite( brother );
>                     int bfirst = brother.first;
>
>                     if ( bfirst < half )
> @@ -708,12 +739,12 @@ public class BPage<K, V> implements Seri
>                         }
>
>                         // update brother's largest key
> -                        keys[index - 1] = brother.getLargestKey();
> +                        pageNewCopy.keys[index - 1] = brother.getLargestKey();
>
>                         // no change in previous/next BPage
>
>                         // update BPages
> -                        btree.recordManager.update( recordId, this, this );
> +                        btree.recordManager.update( recordId, pageNewCopy, this );
>                         btree.recordManager.update( brother.recordId, brother, this );
>                         btree.recordManager.update( child.recordId, child, this );
>
> @@ -740,24 +771,25 @@ public class BPage<K, V> implements Seri
>                         btree.recordManager.update( child.recordId, child, this );
>
>                         // remove "brother" from current BPage
> -                        if ( isLeaf )
> +                        if ( pageNewCopy.isLeaf )
>                         {
> -                            copyEntries( this, first, this, first + 1, index - 1 - first );
> -                            setEntry( this, first, null, null );
> +                            copyEntries( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - 1 - pageNewCopy.first );
> +                            setEntry( pageNewCopy, pageNewCopy.first, null, null );
>                         }
>                         else
>                         {
> -                            copyChildren( this, first, this, first + 1, index - 1 - first );
> -                            setChild( this, first, null, -1 );
> +                            copyChildren( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - 1 - pageNewCopy.first );
> +                            setChild( pageNewCopy, pageNewCopy.first, null, -1 );
>                         }
>
> -                        first += 1;
> -                        btree.recordManager.update( recordId, this, this );
> +                        pageNewCopy.first += 1;
> +                        btree.recordManager.update( recordId, pageNewCopy, this );
>
>                         // re-link previous and next BPages
>                         if ( brother.previous != 0 )
>                         {
>                             BPage<K, V> prev = loadBPage( brother.previous );
> +                            prev = btree.copyOnWrite( prev );
>                             prev.next = brother.next;
>                             btree.recordManager.update( prev.recordId, prev, this );
>                         }
> @@ -765,6 +797,7 @@ public class BPage<K, V> implements Seri
>                         if ( brother.next != 0 )
>                         {
>                             BPage<K, V> next = loadBPage( brother.next );
> +                            next = btree.copyOnWrite( next );
>                             next.previous = brother.previous;
>                             btree.recordManager.update( next.recordId, next, this );
>                         }
> @@ -777,7 +810,8 @@ public class BPage<K, V> implements Seri
>         }
>
>         // underflow if page is more than half-empty
> -        result.underflow = first > half;
> +        result.underflow = pageNewCopy.first > half;
> +        result.pageNewCopy = pageNewCopy;
>
>         return result;
>     }
> @@ -1316,13 +1350,18 @@ public class BPage<K, V> implements Seri
>          * Existing value for the insertion key.
>          */
>         V existing;
> +
> +        /**
> +         * New version of the page doing the insert
> +         */
> +        BPage<K, V> pageNewCopy;
>     }
>
>     /** STATIC INNER CLASS
>      *  Result from remove() method call. If we had to removed a BPage,
>      *  it will be stored into the underflow field.
>      */
> -    static class RemoveResult<V>
> +    static class RemoveResult<K, V>
>     {
>         /**
>          * Set to true if underlying pages underflowed
> @@ -1333,6 +1372,12 @@ public class BPage<K, V> implements Seri
>          * Removed entry value
>          */
>         V value;
> +
> +        /**
> +         * New version of the page doing the remove
> +         */
> +        BPage<K, V> pageNewCopy;
> +
>     }
>
>     /** PRIVATE INNER CLASS
> @@ -1342,6 +1387,9 @@ public class BPage<K, V> implements Seri
>     {
>         /** Current page. */
>         private BPage<K, V> page;
> +
> +        /** Browsing action's context in case of a action capable record manager */
> +        ActionContext context;
>
>         /**
>          * Current index in the page.  The index positionned on the next
> @@ -1354,12 +1402,14 @@ public class BPage<K, V> implements Seri
>          * Create a browser.
>          *
>          * @param page Current page
> +         * @param context Action specific context. Not null if part of an action
>          * @param index Position of the next tuple to return.
>          */
> -        Browser( BPage<K, V> page, int index )
> +        Browser( BPage<K, V> page, int index, ActionContext context )
>         {
>             this.page = page;
>             this.index = index;
> +            this.context = context;
>         }
>
>
> @@ -1373,6 +1423,7 @@ public class BPage<K, V> implements Seri
>          */
>         public boolean getNext( Tuple<K, V> tuple ) throws IOException
>         {
> +            btree.setAsCurrentAction( context );
>             // First, check that we are within a page
>             if ( index < page.btree.pageSize )
>             {
> @@ -1402,6 +1453,7 @@ public class BPage<K, V> implements Seri
>
>         public boolean getPrevious( Tuple<K, V> tuple ) throws IOException
>         {
> +            btree.setAsCurrentAction( context );
>             if ( index == page.first )
>             {
>                 if ( page.previous != 0 )
> @@ -1422,6 +1474,12 @@ public class BPage<K, V> implements Seri
>
>             return true;
>         }
> +
> +        @Override
> +        public void close()
> +        {
> +            btree.endAction( context );
> +        }
>     }
>
>
>
> Modified: directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java?rev=1161416&r1=1161415&r2=1161416&view=diff
> ==============================================================================
> --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java (original)
> +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java Thu Aug 25 06:58:36 2011
> @@ -52,13 +52,23 @@ import java.io.IOException;
>  import java.io.ObjectInput;
>  import java.io.ObjectOutput;
>  import java.io.Serializable;
> +import java.io.ByteArrayInputStream;
> +import java.io.ByteArrayOutputStream;
> +import java.io.ObjectInputStream;
> +import java.io.ObjectOutputStream;
>  import java.util.Comparator;
>  import java.util.concurrent.atomic.AtomicInteger;
>
>  import jdbm.RecordManager;
> +import jdbm.ActionRecordManager;
>  import jdbm.helper.Serializer;
>  import jdbm.helper.Tuple;
>  import jdbm.helper.TupleBrowser;
> +import jdbm.helper.WrappedRuntimeException;
> +import jdbm.helper.ActionContext;
> +
> +import java.util.concurrent.locks.Lock;
> +import java.util.concurrent.locks.ReentrantLock;
>
>  import org.apache.directory.server.i18n.I18n;
>
> @@ -131,7 +141,13 @@ public class BTree<K, V> implements Exte
>
>     /** Serializer used for BPages of this tree */
>     private transient BPage<K, V> bpageSerializer;
> -
> +
> +    /** TRUE if underlying record manager is snapshot capable */
> +    private transient boolean isActionCapable;
> +
> +
> +    /** Big lock snychronizing all actions */
> +    private transient Lock bigLock = new ReentrantLock();
>
>     /**
>      * No-argument constructor used by serialization.
> @@ -220,8 +236,27 @@ public class BTree<K, V> implements Exte
>         this.bpageSerializer = new BPage<K, V>();
>         this.bpageSerializer.btree = this;
>         this.nbEntries = new AtomicInteger( 0 );
> +
> +        this.isActionCapable = recordManager instanceof ActionRecordManager;
>
> -        this.recordId = recordManager.insert( this );
> +        boolean abortedAction = false;
> +        ActionContext context = this.beginAction( false );
> +
> +        try
> +        {
> +            this.recordId = recordManager.insert( this );
> +        }
> +        catch ( IOException e )
> +        {
> +            abortedAction = true;
> +            this.abortAction( context );
> +            throw e;
> +        }
> +        finally
> +        {
> +            if ( !abortedAction )
> +                this.endAction( context );
> +        }
>     }
>
>
> @@ -268,7 +303,7 @@ public class BTree<K, V> implements Exte
>      * @param replace Set to true to replace an existing key-value pair.
>      * @return Existing value, if any.
>      */
> -    public synchronized Object insert( K key, V value, boolean replace ) throws IOException
> +    public Object insert( K key, V value, boolean replace ) throws IOException
>     {
>         if ( key == null )
>         {
> @@ -280,58 +315,88 @@ public class BTree<K, V> implements Exte
>             throw new IllegalArgumentException( I18n.err( I18n.ERR_524 ) );
>         }
>
> -        BPage<K, V> rootPage = getRoot();
> +
> +        boolean abortedAction = false;
> +        ActionContext context  = this.beginAction( false );
> +
>
> -        if ( rootPage == null )
> -        {
> -            // BTree is currently empty, create a new root BPage
> -            if ( DEBUG )
> -            {
> -                System.out.println( "BTree.insert() new root BPage" );
> -            }
> -
> -            rootPage = new BPage<K, V>( this, key, value );
> -            rootId = rootPage.getRecordId();
> -            bTreeHeight = 1;
> -            nbEntries.set( 1 );
> -            recordManager.update( recordId, this );
> -
> -            return null;
> -        }
> -        else
> +        if ( !isActionCapable )
> +            bigLock.lock();
> +
> +        try
>         {
> -            BPage.InsertResult<K, V> insert = rootPage.insert( bTreeHeight, key, value, replace );
> -            boolean dirty = false;
> -
> -            if ( insert.overflow != null )
> +            BPage<K, V> rootPage = getRoot( false );
> +
> +            if ( rootPage == null )
>             {
> -                // current root page overflowed, we replace with a new root page
> +                // BTree is currently empty, create a new root BPage
>                 if ( DEBUG )
>                 {
> -                    System.out.println( "BTree.insert() replace root BPage due to overflow" );
> +                    System.out.println( "BTree.insert() new root BPage" );
>                 }
>
> -                rootPage = new BPage<K, V>( this, rootPage, insert.overflow );
> +                rootPage = new BPage<K, V>( this, key, value );
>                 rootId = rootPage.getRecordId();
> -                bTreeHeight += 1;
> -                dirty = true;
> -            }
> -
> -            if ( insert.existing == null )
> -            {
> -                nbEntries.getAndIncrement();
> -                dirty = true;
> +                bTreeHeight = 1;
> +                nbEntries.set( 1 );
> +                recordManager.update( recordId, this );
> +
> +                return null;
>             }
> -
> -            if ( dirty )
> +            else
>             {
> -                recordManager.update( recordId, this );
> +                BPage.InsertResult<K, V> insert = rootPage.insert( bTreeHeight, key, value, replace );
> +                if ( insert.pageNewCopy != null )
> +                    rootPage = insert.pageNewCopy;
> +
> +                boolean dirty = false;
> +
> +                if ( insert.overflow != null )
> +                {
> +                    // current root page overflowed, we replace with a new root page
> +                    if ( DEBUG )
> +                    {
> +                        System.out.println( "BTree.insert() replace root BPage due to overflow" );
> +                    }
> +
> +                    rootPage = new BPage<K, V>( this, rootPage, insert.overflow );
> +                    rootId = rootPage.getRecordId();
> +                    bTreeHeight += 1;
> +                    dirty = true;
> +                }
> +
> +                if ( insert.existing == null )
> +                {
> +                    nbEntries.getAndIncrement();
> +                    dirty = true;
> +                }
> +
> +                if ( dirty )
> +                {
> +                    recordManager.update( recordId, this );
> +                }
> +
> +                // insert might have returned an existing value
> +                return insert.existing;
>             }
> +        }
> +        catch ( IOException e )
> +        {
> +            abortedAction = true;
> +            this.abortAction( context );
> +            throw e;
> +        }
> +        finally
> +        {
> +            if ( !abortedAction )
> +                this.endAction( context );
>
> -            // insert might have returned an existing value
> -            return insert.existing;
> +            if ( !isActionCapable )
> +                bigLock.unlock();
>         }
> +
>     }
> +
>
>
>     /**
> @@ -341,52 +406,80 @@ public class BTree<K, V> implements Exte
>      * @return Value associated with the key, or null if no entry with given
>      *         key existed in the BTree.
>      */
> -    public synchronized V remove( K key ) throws IOException
> +    public V remove( K key ) throws IOException
>     {
>         if ( key == null )
>         {
>             throw new IllegalArgumentException( I18n.err( I18n.ERR_523 ) );
>         }
> -
> -        BPage<K, V> rootPage = getRoot();
> +
>
> -        if ( rootPage == null )
> -        {
> -            return null;
> -        }
> -
> -        boolean dirty = false;
> -        BPage.RemoveResult<V> remove = rootPage.remove( bTreeHeight, key );
> +        boolean abortedAction = false;
> +        ActionContext context = this.beginAction( false );
> +
> +        if ( !isActionCapable )
> +            bigLock.lock();
>
> -        if ( remove.underflow && rootPage.isEmpty() )
> +        try
>         {
> -            bTreeHeight -= 1;
> -            dirty = true;
>
> -            recordManager.delete( rootId );
> +            BPage<K, V> rootPage = getRoot( false );
>
> -            if ( bTreeHeight == 0 )
> +            if ( rootPage == null )
>             {
> -                rootId = 0;
> +                return null;
>             }
> -            else
> +
> +            boolean dirty = false;
> +            BPage.RemoveResult<K, V> remove = rootPage.remove( bTreeHeight, key );
> +            if ( remove.pageNewCopy != null )
> +                rootPage = remove.pageNewCopy;
> +
> +            if ( remove.underflow && rootPage.isEmpty() )
> +            {
> +                bTreeHeight -= 1;
> +                dirty = true;
> +
> +                recordManager.delete( rootId );
> +
> +                if ( bTreeHeight == 0 )
> +                {
> +                    rootId = 0;
> +                }
> +                else
> +                {
> +                    rootId = rootPage.childBPage( pageSize - 1 ).getRecordId();
> +                }
> +            }
> +
> +            if ( remove.value != null )
>             {
> -                rootId = rootPage.childBPage( pageSize - 1 ).getRecordId();
> +                nbEntries.getAndDecrement();
> +                dirty = true;
>             }
> +
> +            if ( dirty )
> +            {
> +                recordManager.update( recordId, this );
> +            }
> +
> +            return remove.value;
>         }
> -
> -        if ( remove.value != null )
> +        catch ( IOException e )
>         {
> -            nbEntries.getAndDecrement();
> -            dirty = true;
> +            abortedAction = true;
> +            this.abortAction( context );
> +            throw e;
>         }
> -
> -        if ( dirty )
> +        finally
>         {
> -            recordManager.update( recordId, this );
> +            if ( !abortedAction )
> +                this.endAction( context );
> +
> +            if ( !isActionCapable )
> +                bigLock.unlock();
>         }
>
> -        return remove.value;
>     }
>
>
> @@ -396,40 +489,52 @@ public class BTree<K, V> implements Exte
>      * @param key Lookup key.
>      * @return Value associated with the key, or null if not found.
>      */
> -    public synchronized V find( K key ) throws IOException
> +    public V find( K key ) throws IOException
>     {
> +        TupleBrowser<K, V> browser = null;
> +        Tuple<K, V> tuple = null;
> +
>         if ( key == null )
>         {
>             throw new IllegalArgumentException( I18n.err( I18n.ERR_523 ) );
>         }
>
> -        BPage<K, V> rootPage = getRoot();
> +        if ( !isActionCapable )
> +            bigLock.lock();
>
> -        if ( rootPage == null )
> -        {
> -            return null;
> -        }
> -
> -        Tuple<K, V> tuple = new Tuple<K, V>( null, null );
> -        TupleBrowser<K, V> browser = rootPage.find( bTreeHeight, key );
> -
> -        if ( browser.getNext( tuple ) )
> -        {
> -            // find returns the matching key or the next ordered key, so we must
> -            // check if we have an exact match
> -            if ( comparator.compare( key, tuple.getKey() ) != 0 )
> +        try
> +        {
> +            tuple = new Tuple<K, V>( null, null );
> +
> +            browser = browse( key );
> +
> +            if ( browser.getNext( tuple ) )
>             {
> -                return null;
> +                // find returns the matching key or the next ordered key, so we must
> +                // check if we have an exact match
> +                if ( comparator.compare( key, tuple.getKey() ) != 0 )
> +                {
> +                    return null;
> +                }
> +                else
> +                {
> +                    return tuple.getValue();
> +                }
>             }
>             else
>             {
> -                return tuple.getValue();
> +                return null;
>             }
>         }
> -        else
> +        finally
>         {
> -            return null;
> +            if ( browser != null )
> +                browser.close();
> +
> +            if ( !isActionCapable )
> +                bigLock.unlock();
>         }
> +
>     }
>
>
> @@ -441,10 +546,10 @@ public class BTree<K, V> implements Exte
>      * @return Value associated with the key, or a greater entry, or null if no
>      *         greater entry was found.
>      */
> -    public synchronized Tuple<K, V> findGreaterOrEqual( K key ) throws IOException
> +    public Tuple<K, V> findGreaterOrEqual( K key ) throws IOException
>     {
>         Tuple<K, V> tuple;
> -        TupleBrowser<K, V> browser;
> +        TupleBrowser<K, V> browser = null;
>
>         if ( key == null )
>         {
> @@ -453,16 +558,31 @@ public class BTree<K, V> implements Exte
>             return null;
>         }
>
> -        tuple = new Tuple<K, V>( null, null );
> -        browser = browse( key );
> +        if ( !isActionCapable )
> +            bigLock.lock();
>
> -        if ( browser.getNext( tuple ) )
> +        tuple = new Tuple<K, V>( null, null );
> +        try
>         {
> -            return tuple;
> +            browser = browse( key );
> +
> +            if ( browser.getNext( tuple ) )
> +            {
> +                return tuple;
> +            }
> +            else
> +            {
> +                return null;
> +            }
>         }
> -        else
> +        finally
>         {
> -            return null;
> +            if ( browser != null )
> +                browser.close();
> +
> +            if ( !isActionCapable )
> +                bigLock.unlock();
> +
>         }
>     }
>
> @@ -476,16 +596,26 @@ public class BTree<K, V> implements Exte
>      *
>      * @return Browser positionned at the beginning of the BTree.
>      */
> -    public synchronized TupleBrowser<K, V> browse() throws IOException
> +    public TupleBrowser<K, V> browse() throws IOException
>     {
> -        BPage<K, V> rootPage = getRoot();
> -
> -        if ( rootPage == null )
> +        TupleBrowser<K, V> browser = null;
> +        ActionContext context = this.beginAction( true );
> +        try
>         {
> -            return new EmptyBrowser(){};
> +            BPage<K, V> rootPage = getRoot( true );
> +
> +            if ( rootPage == null )
> +            {
> +                this.endAction( context );
> +                return new EmptyBrowser(){};
> +            }
> +            browser = rootPage.findFirst( context );
> +        }
> +        catch( IOException e )
> +        {
> +            this.abortAction( context );
> +            throw e;
>         }
> -
> -        TupleBrowser<K, V> browser = rootPage.findFirst();
>
>         return browser;
>     }
> @@ -503,19 +633,32 @@ public class BTree<K, V> implements Exte
>      *            (Null is considered to be an "infinite" key)
>      * @return Browser positioned just before the given key.
>      */
> -    public synchronized TupleBrowser<K, V> browse( K key ) throws IOException
> +    public TupleBrowser<K, V> browse( K key ) throws IOException
>     {
> -        BPage<K, V> rootPage = getRoot();
> +        TupleBrowser<K, V> browser = null;
> +        ActionContext context = this.beginAction( true );
>
> -        if ( rootPage == null )
> +        try
>         {
> -            return new EmptyBrowser(){};
> +            BPage<K, V> rootPage = getRoot( true );
> +
> +            if ( rootPage == null )
> +            {
> +                this.endAction( context );
> +                return new EmptyBrowser(){};
> +            }
> +
> +            browser  = rootPage.find( rootPage.btree.bTreeHeight, key, context );
> +        }
> +        catch( IOException e )
> +        {
> +            this.abortAction( context );
> +            throw e;
>         }
> -
> -        TupleBrowser<K, V> browser = rootPage.find( bTreeHeight, key );
>
>         return browser;
>     }
> +
>
>
>     /**
> @@ -539,16 +682,27 @@ public class BTree<K, V> implements Exte
>     /**
>      * Return the root BPage<Object, Object>, or null if it doesn't exist.
>      */
> -    private BPage<K, V> getRoot() throws IOException
> +    private BPage<K, V> getRoot( boolean readOnlyAction ) throws IOException
>     {
> -        if ( rootId == 0 )
> +        BTree<K, V> bTreeCopy;
> +
> +        if ( readOnlyAction )
> +        {
> +            bTreeCopy = ( BTree<K, V> )recordManager.fetch( recordId );
> +        }
> +        else
> +        {
> +            bTreeCopy = this;
> +        }
> +
> +        if ( bTreeCopy.rootId == 0 )
>         {
>             return null;
>         }
>
> -        BPage<K, V> root = ( BPage<K, V> ) recordManager.fetch( rootId, bpageSerializer );
> -        root.setRecordId( rootId );
> -        root.btree = this;
> +        BPage<K, V> root = ( BPage<K, V> ) recordManager.fetch( bTreeCopy.rootId, bpageSerializer );
> +        root.setRecordId( bTreeCopy.rootId );
> +        root.btree = bTreeCopy;
>
>         return root;
>     }
> @@ -616,6 +770,115 @@ public class BTree<K, V> implements Exte
>     }
>
>
> +    void setAsCurrentAction( ActionContext context )
> +    {
> +        if ( context != null )
> +        {
> +            assert( isActionCapable == true );
> +            ( ( ActionRecordManager )recordManager ).setCurrentActionContext( context );
> +        }
> +    }
> +
> +    void unsetAsCurrentAction( ActionContext context )
> +    {
> +        if ( context != null )
> +        {
> +            assert( isActionCapable == true );
> +            ( ( ActionRecordManager )recordManager ).setCurrentActionContext( context );
> +        }
> +    }
> +
> +
> +    ActionContext beginAction( boolean readOnly )
> +    {
> +        ActionContext context = null;
> +        if ( isActionCapable )
> +        {
> +            context = ( ( ActionRecordManager )recordManager ).beginAction( readOnly );
> +            this.setAsCurrentAction( context );
> +        }
> +        return context;
> +    }
> +
> +
> +    void endAction( ActionContext context )
> +    {
> +        if ( context != null )
> +        {
> +            assert( isActionCapable );
> +            ( ( ActionRecordManager )recordManager ).endAction( context );
> +            this.unsetAsCurrentAction( context );
> +        }
> +    }
> +
> +    void abortAction( ActionContext context )
> +    {
> +        if ( context != null )
> +        {
> +            assert( isActionCapable );
> +            this.abortAction( context );
> +            ( ( ActionRecordManager )recordManager ).abortAction( context );
> +            this.unsetAsCurrentAction( context );
> +        }
> +
> +    }
> +
> +
> +    BPage<K,V> copyOnWrite( BPage<K,V> page) throws IOException
> +    {
> +        byte[] array;
> +        array = this.bpageSerializer.serialize( page );
> +        BPage<K,V> pageCopy = this.bpageSerializer.deserialize( array );
> +        pageCopy.recordId = page.recordId;
> +        pageCopy.btree = page.btree;
> +        return pageCopy;
> +    }
> +
> +    @SuppressWarnings("unchecked")
> +    private BTree<K,V> copyOnWrite() throws IOException
> +    {
> +        ObjectOutputStream out = null;
> +        ObjectInputStream in = null;
> +        ByteArrayOutputStream bout = null;
> +        ByteArrayInputStream bin = null;
> +
> +        BTree<K,V> tree;
> +
> +        try
> +        {
> +            bout = new ByteArrayOutputStream();
> +            out = new ObjectOutputStream( bout );
> +            out.writeObject( this );
> +            out.flush();
> +            byte[]  arr = bout.toByteArray();
> +            bin = new ByteArrayInputStream( arr );
> +            in =new ObjectInputStream( bin );
> +            tree = ( BTree<K, V> )in.readObject();
> +        }
> +        catch ( ClassNotFoundException e )
> +        {
> +            throw new WrappedRuntimeException( e );
> +        }
> +        finally
> +        {
> +            if ( bout != null )
> +                bout.close();
> +
> +            if ( out != null )
> +                out.close();
> +
> +            if ( bin != null )
> +                bin.close();
> +
> +            if ( in != null )
> +                in.close();
> +        }
> +
> +        return tree;
> +    }
> +
> +
> +
>     public String toString()
>     {
>         StringBuilder sb = new StringBuilder();
>
> Modified: directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java?rev=1161416&r1=1161415&r2=1161416&view=diff
> ==============================================================================
> --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java (original)
> +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java Thu Aug 25 06:58:36 2011
> @@ -73,4 +73,11 @@ public abstract class TupleBrowser<K, V>
>      *         no previous tuple.
>      */
>     public abstract boolean getPrevious( Tuple<K, V> tuple ) throws IOException;
> +
> +    /**
> +     * Closes the browser and deallocates any resources it might have allocated.
> +     * Repeated calls of close are OK.
> +     */
> +    public void close() {}
> +
>  }
>
> Modified: directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java?rev=1161416&r1=1161415&r2=1161416&view=diff
> ==============================================================================
> --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java (original)
> +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java Thu Aug 25 06:58:36 2011
> @@ -52,6 +52,10 @@ import java.io.IOException;
>
>  import java.util.HashMap;
>  import java.util.Map;
> +import java.util.concurrent.ConcurrentHashMap;
> +import java.util.concurrent.locks.Lock;
> +import java.util.concurrent.locks.ReentrantLock;
> +import java.util.concurrent.locks.Condition;
>
>  import org.apache.directory.server.i18n.I18n;
>
> @@ -109,6 +113,102 @@ public final class BaseRecordManager
>      * the NAME_DIRECTORY_ROOT.
>      */
>     private Map<String,Long> nameDirectory;
> +
> +    private static enum IOType
> +    {
> +        READ_IO,
> +        WRITE_IO
> +    }
> +
> +    /** TODO add asserts to check internal consistency */
> +    private static class LockElement
> +    {
> +        private int readers;
> +        private int waiters;
> +        private boolean writer;
> +
> +        private Lock lock = new ReentrantLock();
> +        private Condition cv = lock.newCondition();
> +
> +
> +        public boolean anyReaders()
> +        {
> +            return readers > 0;
> +        }
> +
> +
> +        public boolean anyWaiters()
> +        {
> +            return waiters > 0;
> +        }
> +
> +
> +        public boolean beingWritten()
> +        {
> +            return writer;
> +        }
> +
> +
> +        public boolean anyUser()
> +        {
> +            return ( readers > 0 || waiters > 0 || writer );
> +        }
> +
> +
> +        public void bumpReaders()
> +        {
> +            readers++;
> +        }
> +
> +
> +        public void decrementReaders()
> +        {
> +            readers--;
> +        }
> +
> +
> +        public void bumpWaiters()
> +        {
> +            waiters++;
> +        }
> +
> +
> +        public void decrementWaiters()
> +        {
> +            waiters--;
> +        }
> +
> +
> +        public void setWritten()
> +        {
> +            writer = true;
> +        }
> +
> +
> +        public void unsetWritten()
> +        {
> +            writer = false;
> +        }
> +
> +
> +        public Lock getLock()
> +        {
> +            return lock;
> +        }
> +
> +
> +        public Condition getNoConflictingIOCondition()
> +        {
> +            return cv;
> +        }
> +
> +    }
> +
> +    /**
> +     * Map used to synchronize reads and writes on the same logical
> +     * recid.
> +     */
> +    private final ConcurrentHashMap<Long, LockElement> lockElements;
>
>
>     /**
> @@ -123,13 +223,14 @@ public final class BaseRecordManager
>         pageMgr = new PageManager( recordFile );
>         physMgr = new PhysicalRowIdManager( pageMgr );
>         logMgr = new LogicalRowIdManager( pageMgr );
> +        lockElements = new ConcurrentHashMap<Long, LockElement>();
>     }
>
>
>     /**
>      * Get the underlying Transaction Manager
>      */
> -    public synchronized TransactionManager getTransactionManager() throws IOException
> +    public TransactionManager getTransactionManager() throws IOException
>     {
>         checkIfClosed();
>         return recordFile.getTxnMgr();
> @@ -145,7 +246,7 @@ public final class BaseRecordManager
>      *  Only call this method directly after opening the file, otherwise
>      *  the results will be undefined.
>      */
> -    public synchronized void disableTransactions()
> +    public void disableTransactions()
>     {
>         checkIfClosed();
>         recordFile.disableTransactions();
> @@ -157,7 +258,7 @@ public final class BaseRecordManager
>      *
>      * @throws IOException when one of the underlying I/O operations fails.
>      */
> -    public synchronized void close() throws IOException
> +    public void close() throws IOException
>     {
>         checkIfClosed();
>
> @@ -190,7 +291,7 @@ public final class BaseRecordManager
>      * @return the rowid for the new record.
>      * @throws IOException when one of the underlying I/O operations fails.
>      */
> -    public synchronized long insert( Object obj, Serializer serializer ) throws IOException
> +    public long insert( Object obj, Serializer serializer ) throws IOException
>     {
>         byte[]    data;
>         long      recid;
> @@ -217,8 +318,9 @@ public final class BaseRecordManager
>      * @param recid the rowid for the record that should be deleted.
>      * @throws IOException when one of the underlying I/O operations fails.
>      */
> -    public synchronized void delete( long recid ) throws IOException
> +    public void delete( long recid ) throws IOException
>     {
> +       LockElement element;
>         checkIfClosed();
>
>         if ( recid <= 0 )
> @@ -231,10 +333,20 @@ public final class BaseRecordManager
>             System.out.println( "BaseRecordManager.delete() recid " + recid ) ;
>         }
>
> -        Location logRowId = new Location( recid );
> -        Location physRowId = logMgr.fetch( logRowId );
> -        physMgr.delete( physRowId );
> -        logMgr.delete( logRowId );
> +
> +        element = this.beginIO(recid, IOType.WRITE_IO);
> +
> +        try
> +        {
> +               Location logRowId = new Location( recid );
> +               Location physRowId = logMgr.fetch( logRowId );
> +               physMgr.delete( physRowId );
> +               logMgr.delete( logRowId );
> +        }
> +        finally
> +        {
> +               this.endIO(recid, element, IOType.WRITE_IO);
> +        }
>     }
>
>
> @@ -250,7 +362,6 @@ public final class BaseRecordManager
>         update( recid, obj, DefaultSerializer.INSTANCE );
>     }
>
> -
>     /**
>      * Updates a record using a custom serializer.
>      *
> @@ -259,33 +370,45 @@ public final class BaseRecordManager
>      * @param serializer a custom serializer
>      * @throws IOException when one of the underlying I/O operations fails.
>      */
> -    public synchronized void update( long recid, Object obj, Serializer serializer ) throws IOException
> +    public void update( long recid, Object obj, Serializer serializer ) throws IOException
>     {
> -        checkIfClosed();
> +       LockElement element;
> +
> +       checkIfClosed();
>
>         if ( recid <= 0 )
>         {
>             throw new IllegalArgumentException( I18n.err( I18n.ERR_536, recid ) );
>         }
>
> -        Location logRecid = new Location( recid );
> -        Location physRecid = logMgr.fetch( logRecid );
> -
> -        byte[] data = serializer.serialize( obj );
> -
> -        if ( DEBUG )
> -        {
> -            System.out.println( "BaseRecordManager.update() recid " + recid + " length " + data.length ) ;
> -        }
> -
> -        Location newRecid = physMgr.update( physRecid, data, 0, data.length );
> -
> -        if ( ! newRecid.equals( physRecid ) )
> -        {
> -            logMgr.update( logRecid, newRecid );
> -        }
> +        element = this.beginIO(recid, IOType.WRITE_IO);
> +
> +        try
> +       {
> +               Location logRecid = new Location( recid );
> +            Location physRecid = logMgr.fetch( logRecid );
> +
> +            byte[] data = serializer.serialize( obj );
> +
> +            if ( DEBUG )
> +            {
> +                System.out.println( "BaseRecordManager.update() recid " + recid + " length " + data.length ) ;
> +            }
> +
> +            Location newRecid = physMgr.update( physRecid, data, 0, data.length );
> +
> +            if ( ! newRecid.equals( physRecid ) )
> +            {
> +                logMgr.update( logRecid, newRecid );
> +            }
> +
> +       }
> +       finally
> +       {
> +           this.endIO(recid, element, IOType.WRITE_IO);
> +       }
>     }
> -
> +
>
>     /**
>      * Fetches a record using standard java object serialization.
> @@ -299,7 +422,7 @@ public final class BaseRecordManager
>         return fetch( recid, DefaultSerializer.INSTANCE );
>     }
>
> -
> +
>     /**
>      * Fetches a record using a custom serializer.
>      *
> @@ -308,26 +431,41 @@ public final class BaseRecordManager
>      * @return the object contained in the record.
>      * @throws IOException when one of the underlying I/O operations fails.
>      */
> -    public synchronized Object fetch( long recid, Serializer serializer )
> -        throws IOException
> +    public Object fetch( long recid, Serializer serializer ) throws IOException
>     {
> -        byte[] data;
> -
> -        checkIfClosed();
> -
> +       Object result;
> +       LockElement element;
> +
> +       checkIfClosed();
> +
>         if ( recid <= 0 )
>         {
>             throw new IllegalArgumentException( I18n.err( I18n.ERR_536, recid ) );
>         }
> -
> -        data = physMgr.fetch( logMgr.fetch( new Location( recid ) ) );
> -
> -        if ( DEBUG )
> -        {
> -            System.out.println( "BaseRecordManager.fetch() recid " + recid + " length " + data.length ) ;
> -        }
> -        return serializer.deserialize( data );
> +
> +       element = this.beginIO(recid, IOType.READ_IO);
> +
> +       try
> +       {
> +               byte[] data;
> +
> +            data = physMgr.fetch( logMgr.fetch( new Location( recid ) ) );
> +
> +            if ( DEBUG )
> +            {
> +                System.out.println( "BaseRecordManager.fetch() recid " + recid + " length " + data.length ) ;
> +            }
> +            result = serializer.deserialize( data );
> +       }
> +       finally
> +       {
> +               this.endIO(recid, element, IOType.READ_IO);
> +       }
> +
> +       return result;
> +
>     }
> +
>
>
>     /**
> @@ -347,7 +485,7 @@ public final class BaseRecordManager
>      *
>      *  @see #getRootCount
>      */
> -    public synchronized long getRoot( int id ) throws IOException
> +    public long getRoot( int id ) throws IOException
>     {
>         checkIfClosed();
>
> @@ -360,7 +498,7 @@ public final class BaseRecordManager
>      *
>      *  @see #getRootCount
>      */
> -    public synchronized void setRoot( int id, long rowid ) throws IOException
> +    public void setRoot( int id, long rowid ) throws IOException
>     {
>         checkIfClosed();
>
> @@ -411,7 +549,7 @@ public final class BaseRecordManager
>     /**
>      * Commit (make persistent) all changes since beginning of transaction.
>      */
> -    public synchronized void commit()
> +    public void commit()
>         throws IOException
>     {
>         checkIfClosed();
> @@ -423,7 +561,7 @@ public final class BaseRecordManager
>     /**
>      * Rollback (cancel) all changes since beginning of transaction.
>      */
> -    public synchronized void rollback() throws IOException
> +    public void rollback() throws IOException
>     {
>         checkIfClosed();
>
> @@ -478,4 +616,125 @@ public final class BaseRecordManager
>             throw new IllegalStateException( I18n.err( I18n.ERR_538 ) );
>         }
>     }
> +
> +
> +    /**
> +     * Used to serialize reads/write on a given logical rowid. Checks if there is a
> +     * ongoing conflicting IO to the same logical rowid and waits for the ongoing
> +     * write if there is one.
> +     *
> +     * @param recid the logical rowid for which the fetch will be done.
> +     * @param io type of the IO
> +     * @return lock element representing the logical lock gotten
> +     */
> +    private LockElement beginIO( Long recid, IOType io )
> +    {
> +        boolean lockVerified = false;
> +        LockElement element = null;
> +
> +        // loop until we successfully verify that there is no concurrent writer
> +/*
> +        element = lockElements.get( recid );
> +        do
> +        {
> +            if ( element == null )
> +            {
> +                element = new LockElement();
> +
> +                if ( io == IOType.READ_IO )
> +                    element.bumpReaders();
> +                else
> +                    element.setWritten();
> +
> +                LockElement existingElement = lockElements.putIfAbsent( recid, element );
> +
> +                if ( existingElement == null )
> +                    lockVerified = true;
> +                else
> +                    element = existingElement;
> +            }
> +            else
> +            {
> +                Lock lock = element.getLock();
> +                lock.lock();
> +                if ( element.anyUser() )
> +                {
> +                    if ( this.conflictingIOPredicate( io, element ) )
> +                    {
> +                        element.bumpWaiters();
> +                        do
> +                        {
> +                            element.getNoConflictingIOCondition()
> +                                .awaitUninterruptibly();
> +                        }
> +                        while ( this.conflictingIOPredicate( io, element ) );
> +
> +                        element.decrementWaiters();
> +                    }
> +
> +                    // no conflicting IO anymore..done
> +                    if ( io == IOType.READ_IO )
> +                        element.bumpReaders();
> +                    else
> +                        element.setWritten();
> +                    lockVerified = true;
> +                }
> +                else
> +                {
> +                    if ( io == IOType.READ_IO )
> +                        element.bumpReaders();
> +                    else
> +                        element.setWritten();
> +
> +                    LockElement existingElement = lockElements.get( recid );
> +
> +                    if ( element != existingElement )
> +                        element = existingElement;
> +                    else
> +                        lockVerified = true; // done
> +                }
> +                lock.unlock();
> +            }
> +        }
> +        while ( !lockVerified );
> +*/
> +        return element;
> +    }
> +
> +
> +    /**
> +     * Ends the IO by releasing the logical lock on the given recid
> +     *
> +     * @param recid logical recid for which the IO is being ended
> +     * @param element logical lock to be released
> +     * @param io type of the io
> +     */
> +    private void endIO( Long recid, LockElement element, IOType io )
> +    {
> +  /*      Lock lock = element.getLock();
> +        lock.lock();
> +
> +        if ( io == IOType.READ_IO )
> +            element.decrementReaders();
> +        else
> +            element.unsetWritten();
> +
> +        if ( element.anyWaiters() )
> +            element.getNoConflictingIOCondition().notifyAll();
> +
> +        if ( !element.anyUser() )
> +            lockElements.remove( recid );
> +
> +        lock.unlock();*/
> +    }
> +
> +
> +    private boolean conflictingIOPredicate( IOType io, LockElement element )
> +    {
> +        if ( io == IOType.READ_IO )
> +            return element.beingWritten();
> +        else
> +            return ( element.anyReaders() || element.beingWritten() );
> +    }
> +
>  }
>
>
>



--
Regards,
Cordialement,
Emmanuel Lécharny
www.iktek.com







--
Regards,
Cordialement,
Emmanuel Lécharny
www.iktek.com