You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ka...@apache.org on 2018/09/26 08:33:59 UTC

[directory-server] branch bdb-je-partition created (now e52b473)

This is an automated email from the ASF dual-hosted git repository.

kayyagari pushed a change to branch bdb-je-partition
in repository https://gitbox.apache.org/repos/asf/directory-server.git.


      at e52b473  commit the open txns after using them

This branch includes the following new commits:

     new 6504a6f  added txn implementation classes
     new 2e5221b  BDB JE based partition implmentation
     new e19f504  used new txn impl classes
     new fbb996d  added dependency on apacheds-bdbje-partition
     new 70d81b1  use txn impl classes
     new e52b473  commit the open txns after using them

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[directory-server] 04/06: added dependency on apacheds-bdbje-partition

Posted by ka...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kayyagari pushed a commit to branch bdb-je-partition
in repository https://gitbox.apache.org/repos/asf/directory-server.git

commit fbb996d530b6f9fac8806f63379428b5930748b4
Author: kayyagari <ka...@apache.org>
AuthorDate: Wed Sep 26 14:01:00 2018 +0530

    added dependency on apacheds-bdbje-partition
---
 core-annotations/pom.xml | 5 +++++
 pom.xml                  | 7 +++++++
 2 files changed, 12 insertions(+)

diff --git a/core-annotations/pom.xml b/core-annotations/pom.xml
index 58b0bd5..b6d8fd7 100644
--- a/core-annotations/pom.xml
+++ b/core-annotations/pom.xml
@@ -67,6 +67,11 @@
       <groupId>${project.groupId}</groupId>
       <artifactId>apacheds-jdbm-partition</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-bdbje-partition</artifactId>
+    </dependency>
     
     <dependency>
       <groupId>${project.groupId}</groupId>
diff --git a/pom.xml b/pom.xml
index abec467..262a21d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,6 +106,7 @@
     <module>all</module>
     <module>jdbm-partition</module>
     <module>mavibot-partition</module>
+    <module>bdbje-partition</module>
     <!--module>lmdb-partition</module-->
     <!--module>mavibotv2-partition</module-->
     <module>xdbm-partition</module>
@@ -749,6 +750,12 @@
       
       <dependency>
         <groupId>${project.groupId}</groupId>
+        <artifactId>apacheds-bdbje-partition</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>${project.groupId}</groupId>
         <artifactId>apacheds-kerberos-shared</artifactId>
         <version>${project.version}</version>
       </dependency>


[directory-server] 02/06: BDB JE based partition implmentation

Posted by ka...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kayyagari pushed a commit to branch bdb-je-partition
in repository https://gitbox.apache.org/repos/asf/directory-server.git

commit 2e5221b4fff9a6eae90d97a4ec43c208f81dcf26
Author: kayyagari <ka...@apache.org>
AuthorDate: Wed Sep 26 13:57:58 2018 +0530

    BDB JE based partition implmentation
---
 bdbje-partition/pom.xml                            | 128 +++
 .../core/partition/impl/btree/je/BdbJeDnIndex.java |  80 ++
 .../core/partition/impl/btree/je/BdbJeIndex.java   | 480 +++++++++++
 .../partition/impl/btree/je/BdbJeMasterTable.java  |  54 ++
 .../partition/impl/btree/je/BdbJePartition.java    | 535 ++++++++++++
 .../impl/btree/je/BdbJePartitionEnviroment.java    | 228 +++++
 .../partition/impl/btree/je/BdbJeRdnIndex.java     |  93 ++
 .../core/partition/impl/btree/je/BdbJeTable.java   | 607 +++++++++++++
 .../partition/impl/btree/je/BdbJeTableCursor.java  | 270 ++++++
 .../impl/btree/je/BdbJeTableDupKeyCursor.java      | 197 +++++
 .../impl/btree/je/ForwardBdbJeIndexCursor.java     | 350 ++++++++
 .../partition/impl/btree/je/JeTransaction.java     |  90 ++
 .../btree/je/ParentIdAndRdnByteComparator.java     |  78 ++
 .../btree/je/serializers/ByteArraySerializer.java  |  47 ++
 .../je/serializers/ComparatorSerializerMap.java    |  72 ++
 .../impl/btree/je/serializers/DnSerializer.java    | 126 +++
 .../impl/btree/je/serializers/EntrySerializer.java | 229 +++++
 .../impl/btree/je/serializers/LongSerializer.java  |  81 ++
 .../btree/je/serializers/ObjectSerializer.java     |  71 ++
 .../je/serializers/ParentIdAndRdnSerializer.java   | 182 ++++
 .../impl/btree/je/serializers/Serializer.java      |  38 +
 .../btree/je/serializers/StringSerializer.java     |  49 ++
 bdbje-partition/src/site/site.xml                  |  51 ++
 .../partition/impl/btree/je/BdbJeIndexTest.java    | 469 +++++++++++
 .../partition/impl/btree/je/BdbJeRdnIndexTest.java | 340 ++++++++
 .../partition/impl/btree/je/BdbJeStoreTest.java    | 935 +++++++++++++++++++++
 .../impl/btree/je/BdbJeTableNoDuplicatesTest.java  | 384 +++++++++
 .../btree/je/BdbJeTableWithDuplicatesTest.java     | 611 ++++++++++++++
 .../src/test/resources/log4j.properties            |  22 +
 .../server/core/factory/BdbJePartitionFactory.java |  78 ++
 30 files changed, 6975 insertions(+)

diff --git a/bdbje-partition/pom.xml b/bdbje-partition/pom.xml
new file mode 100644
index 0000000..938307f
--- /dev/null
+++ b/bdbje-partition/pom.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.apache.directory.server</groupId>
+		<artifactId>apacheds-parent</artifactId>
+		<version>2.0.0.AM26-SNAPSHOT</version>
+	</parent>
+	
+	<artifactId>apacheds-bdbje-partition</artifactId>
+	<name>ApacheDS BDB JE Partition</name>
+	
+	<description>BerkeleyDB JE backed partition</description>
+	
+	<dependencies>
+    <dependency>
+      <groupId>org.apache.directory.junit</groupId>
+      <artifactId>junit-addons</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-core-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-core-api</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-core-avl</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-i18n</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-xdbm-partition</artifactId>
+    </dependency>
+      
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-xdbm-partition</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.api</groupId>
+      <artifactId>api-ldap-model</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.api</groupId>
+      <artifactId>api-ldap-schema-data</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.api</groupId>
+      <artifactId>api-util</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.sleepycat</groupId>
+      <artifactId>je</artifactId>
+      <version>18.3.1</version>
+    </dependency>		
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+<!--         <configuration>
+          <archive>
+            <manifestFile>META-INF/MANIFEST.MF</manifestFile>
+            <addMavenDescriptor>false</addMavenDescriptor>
+          </archive>
+        </configuration>
+ -->      </plugin>
+    </plugins>
+  </build>
+</project>
+
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeDnIndex.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeDnIndex.java
new file mode 100644
index 0000000..76f340f
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeDnIndex.java
@@ -0,0 +1,80 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.util.Comparator;
+
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.DnComparator;
+import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.DnSerializer;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.StringSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Database;
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeDnIndex extends BdbJeIndex<Dn>
+{
+
+    /** A logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJeDnIndex.class );
+
+
+    public BdbJeDnIndex( String oid )
+    {
+        super( oid, true );
+        initialized = false;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void init( SchemaManager schemaManager, BdbJePartitionEnviroment environment )
+    {
+        LOG.debug( "Initializing RDN Index for attribute '{}'", attributeId );
+
+        attributeType = schemaManager.getAttributeType( attributeId );
+        attribute = attributeType;
+
+        String oid = attributeType.getOid();
+        Comparator comp = new DnComparator( oid );
+
+        UuidComparator.INSTANCE.setSchemaManager( schemaManager );
+
+        DnSerializer dnSerializer = new DnSerializer( schemaManager );
+
+        Database dnForwardDb = environment.createDb( oid + FORWARD_KEY );
+        forward = new BdbJeTable<Dn, String>( dnForwardDb, schemaManager, comp, dnSerializer, new StringSerializer() );
+
+        Database dnReverseDb = environment.createDb( oid + FORWARD_KEY );
+        reverse = new BdbJeTable<String, Dn>( dnReverseDb, schemaManager, UuidComparator.INSTANCE,
+            new StringSerializer(), dnSerializer );
+        initialized = true;
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeIndex.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeIndex.java
new file mode 100644
index 0000000..fa565cb
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeIndex.java
@@ -0,0 +1,480 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.apache.directory.api.ldap.model.cursor.Cursor;
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.EmptyCursor;
+import org.apache.directory.api.ldap.model.cursor.Tuple;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapOtherException;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.SerializableComparator;
+import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.partition.impl.btree.IndexCursorAdaptor;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.ByteArraySerializer;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.ComparatorSerializerMap;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.Serializer;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.StringSerializer;
+import org.apache.directory.server.xdbm.AbstractIndex;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Database;
+
+
+/**
+ * Index implementation backed by bdb je.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeIndex<K> extends AbstractIndex<K, String>
+{
+
+    public static final String REVERSE_KEY = "_reverse";
+
+    public static final String FORWARD_KEY = "_forward";
+
+    /** the logger */
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJeIndex.class );
+
+    /** flag to indicate if this index was initialized */
+    protected boolean initialized;
+
+    /** flag indicating if duplicate values are allowed in reverse database */
+    protected boolean revDupsAllowed = true;
+
+    /** flag indicating if duplicate values are allowed in forward datatbase */
+    protected boolean fwdDupsAllowed = true;
+
+    /** the forward database */
+    protected BdbJeTable<K, String> forward;
+
+    /** the reverse database */
+    protected BdbJeTable<String, K> reverse;
+
+    /** the forward database key serializer */
+    protected Serializer<K> forwardKeySerializer;
+
+    /** the forward database value serializer */
+    protected Serializer<String> forwardValueSerializer;
+
+    /** the reverse database key serializer */
+    protected Serializer<String> reverseKeySerializer;
+
+    /** the reverse database value serializer */
+    protected Serializer<K> reverseValueSerializer;
+
+    /** the indexed attribute type */
+    protected AttributeType attribute;
+
+    /** flag to indicate if the transactions are enabled */
+    protected boolean txnEnabled;
+
+
+    /**
+     * Creates a new instance of BdbJeIndex.
+     * @param attributeId the attribute's identifier
+     */
+    public BdbJeIndex( String attributeId, boolean withReverse )
+    {
+        super( attributeId, withReverse );
+        initialized = false;
+    }
+
+
+    public void init( SchemaManager schemaManager, BdbJePartitionEnviroment environment ) throws LdapException
+    {
+        if ( initialized )
+        {
+            return;
+        }
+
+        attribute = schemaManager.lookupAttributeTypeRegistry( attributeId );
+
+        if ( attribute.isSingleValued() )
+        {
+            revDupsAllowed = false;
+        }
+
+        SerializableComparator<K> atValueComparator = new SerializableComparator<K>( attribute.getEquality().getOid() );
+
+        atValueComparator.setSchemaManager( schemaManager );
+
+        forwardValueSerializer = new StringSerializer();
+
+        if ( !attribute.getSyntax().isHumanReadable() )
+        {
+            forwardKeySerializer = ( Serializer ) ByteArraySerializer.INSTANCE;
+        }
+        else
+        {
+            forwardKeySerializer = ComparatorSerializerMap.getSerializer( schemaManager.lookupComparatorRegistry(
+                attribute.getEquality().getOid() ).getClass() );
+        }
+
+        Database fdb = environment.createDb( attribute.getOid() + FORWARD_KEY, true );
+        forward = new BdbJeTable<>( fdb, schemaManager, atValueComparator, UuidComparator.INSTANCE,
+            forwardKeySerializer, new StringSerializer() );
+        reverseKeySerializer = forwardValueSerializer;
+        reverseValueSerializer = forwardKeySerializer;
+
+        Database rdb = environment.createDb( attribute.getOid() + REVERSE_KEY, revDupsAllowed );
+        reverse = new BdbJeTable<>( rdb, schemaManager, UuidComparator.INSTANCE, atValueComparator,
+            reverseKeySerializer, reverseValueSerializer );
+
+        txnEnabled = environment.isTransactional();
+
+        initialized = true;
+    }
+
+
+    @Override
+    public AttributeType getAttribute()
+    {
+        return attribute;
+    }
+
+
+    public void setFwdDupsAllowed( boolean fwdDupsAllowed )
+    {
+        if ( initialized )
+        {
+            throw new IllegalStateException( "cannot set the fwdDupsAllowed flag after initialization" );
+        }
+
+        this.fwdDupsAllowed = fwdDupsAllowed;
+    }
+
+
+    @Override
+    public void setAttributeId( String attributeId )
+    {
+        if ( initialized )
+        {
+            throw new IllegalStateException( "cannot set the attributeId after initialization" );
+        }
+
+        this.attributeId = attributeId;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Add/Drop Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void add( PartitionTxn partitionTxn, K attrVal, String id ) throws LdapException
+    {
+        // The pair to be added must exists
+        forward.put( partitionTxn, attrVal, id );
+
+        if ( withReverse )
+        {
+            reverse.put( partitionTxn, id, attrVal );
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void drop( PartitionTxn partitionTxn, K attrVal, String id ) throws LdapException
+    {
+        // The pair to be removed must exists
+        if ( forward.has( partitionTxn, attrVal, id ) )
+        {
+            forward.remove( partitionTxn, attrVal, id );
+
+            if ( withReverse )
+            {
+                reverse.remove( partitionTxn, id, attrVal );
+            }
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void drop( PartitionTxn partitionTxn, String entryId ) throws LdapException
+    {
+        if ( withReverse )
+        {
+            if ( isDupsEnabled() )
+            {
+                // Build a cursor to iterate on all the keys referencing
+                // this entryId
+                Cursor<Tuple<String, K>> values = reverse.cursor( partitionTxn, entryId );
+
+                try
+                {
+                    while ( values.next() )
+                    {
+                        // Remove the Key -> entryId from the index
+                        forward.remove( partitionTxn, values.get().getValue(), entryId );
+                    }
+
+                    values.close();
+                }
+                catch ( CursorException | IOException e )
+                {
+                    throw new LdapOtherException( e.getMessage(), e );
+                }
+            }
+            else
+            {
+                K key = reverse.get( partitionTxn, entryId );
+
+                forward.remove( partitionTxn, key );
+            }
+
+            // Remove the id -> key from the reverse index
+            reverse.remove( partitionTxn, entryId );
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Index Cursor Operations
+    // ------------------------------------------------------------------------
+
+
+    @SuppressWarnings("unchecked")
+    public Cursor<IndexEntry<K, String>> forwardCursor( PartitionTxn partitionTxn ) throws LdapException
+    {
+        return new IndexCursorAdaptor<>( partitionTxn, ( Cursor ) forward.cursor( partitionTxn ), true );
+    }
+
+
+    public Cursor<IndexEntry<K, String>> forwardCursor( PartitionTxn partitionTxn, K key ) throws LdapException
+    {
+        return new IndexCursorAdaptor<>( partitionTxn, ( Cursor ) forward.cursor( partitionTxn, key ), true );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Cursor<K> reverseValueCursor( PartitionTxn partitionTxn, String id ) throws LdapException
+    {
+        if ( withReverse )
+        {
+            return reverse.valueCursor( partitionTxn, id );
+        }
+        else
+        {
+            return new EmptyCursor<>();
+        }
+    }
+
+
+    public Cursor<String> forwardValueCursor( PartitionTxn partitionTxn, K key ) throws LdapException
+    {
+        return forward.valueCursor( partitionTxn, key );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Value Assertion (a.k.a Index Lookup) Methods //
+    // ------------------------------------------------------------------------
+    /**
+     * {@inheritDoc}
+     */
+    public boolean forward( PartitionTxn partitionTxn, K attrVal ) throws LdapException
+    {
+        return forward.has( partitionTxn, attrVal );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean forward( PartitionTxn partitionTxn, K attrVal, String id ) throws LdapException
+    {
+        return forward.has( partitionTxn, attrVal, id );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean reverse( PartitionTxn partitionTxn, String id ) throws LdapException
+    {
+        if ( withReverse )
+        {
+            return reverse.has( partitionTxn, id );
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean reverse( PartitionTxn partitionTxn, String id, K attrVal ) throws LdapException
+    {
+        return forward.has( partitionTxn, attrVal, id );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Maintenance Methods
+    // ------------------------------------------------------------------------
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void close( PartitionTxn partitionTxn ) throws LdapException, IOException
+    {
+        if ( forward != null )
+        {
+            forward.close( partitionTxn );
+        }
+
+        if ( reverse != null )
+        {
+            reverse.close( partitionTxn );
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isDupsEnabled()
+    {
+        if ( withReverse )
+        {
+            return reverse.isDupsEnabled();
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Scan Count Methods
+    // ------------------------------------------------------------------------
+    /**
+     * {@inheritDoc}
+     */
+    public long count( PartitionTxn partitionTxn ) throws LdapException
+    {
+        return forward.count( partitionTxn );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public long count( PartitionTxn partitionTxn, K attrVal ) throws LdapException
+    {
+        return forward.count( partitionTxn, attrVal );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long greaterThanCount( PartitionTxn partitionTxn, K attrVal ) throws LdapException
+    {
+        return forward.greaterThanCount( partitionTxn, attrVal );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long lessThanCount( PartitionTxn partitionTxn, K attrVal ) throws LdapException
+    {
+        return forward.lessThanCount( partitionTxn, attrVal );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Forward and Reverse Lookups
+    // ------------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public String forwardLookup( PartitionTxn partitionTxn, K attrVal ) throws LdapException
+    {
+        return forward.get( partitionTxn, attrVal );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public K reverseLookup( PartitionTxn partitionTxn, String id ) throws LdapException
+    {
+        if ( withReverse )
+        {
+            return reverse.get( partitionTxn, id );
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    @Override
+    public void setWkDirPath( URI wkDirPath )
+    {
+    }
+
+
+    @Override
+    public URI getWkDirPath()
+    {
+        return null;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "Index<" + attributeId + ">";
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeMasterTable.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeMasterTable.java
new file mode 100644
index 0000000..2b19021
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeMasterTable.java
@@ -0,0 +1,54 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.util.UUID;
+
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.EntrySerializer;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.StringSerializer;
+import org.apache.directory.server.xdbm.MasterTable;
+
+import com.sleepycat.je.Database;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeMasterTable extends BdbJeTable<String, Entry> implements MasterTable
+{
+
+    public BdbJeMasterTable( Database db, SchemaManager schemaManager )
+    {
+        super( db, schemaManager, UuidComparator.INSTANCE,
+            new StringSerializer(), new EntrySerializer( schemaManager ) );
+    }
+
+
+    @Override
+    public String getNextId( Entry entry )
+    {
+        return UUID.randomUUID().toString();
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJePartition.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJePartition.java
new file mode 100644
index 0000000..e5f0983
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJePartition.java
@@ -0,0 +1,535 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.api.ldap.model.cursor.Cursor;
+import org.apache.directory.api.ldap.model.cursor.Tuple;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapOtherException;
+import org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.api.DnFactory;
+import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
+import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.ParentIdAndRdn;
+import org.apache.directory.server.xdbm.search.impl.CursorBuilder;
+import org.apache.directory.server.xdbm.search.impl.DefaultOptimizer;
+import org.apache.directory.server.xdbm.search.impl.DefaultSearchEngine;
+import org.apache.directory.server.xdbm.search.impl.EvaluatorBuilder;
+import org.apache.directory.server.xdbm.search.impl.NoOpOptimizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.Transaction;
+
+
+/**
+ * A partition implementation backed by bdb je.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJePartition extends AbstractBTreePartition
+{
+
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJePartition.class );
+
+    private BdbJePartitionEnviroment partitionEnv;
+
+
+    /**
+     * Creates a new instance of BdbJePartition.
+     */
+    public BdbJePartition( SchemaManager schemaManager, DnFactory dnFactory )
+    {
+        super( schemaManager, dnFactory );
+    }
+
+
+    @Override
+    protected void doInit() throws LdapException
+    {
+        if ( initialized )
+        {
+            return;
+        }
+
+        // setup optimizer and registries for parent
+        if ( !optimizerEnabled )
+        {
+            setOptimizer( new NoOpOptimizer() );
+        }
+        else
+        {
+            setOptimizer( new DefaultOptimizer( this ) );
+        }
+
+        EvaluatorBuilder evaluatorBuilder = new EvaluatorBuilder( this, schemaManager );
+        CursorBuilder cursorBuilder = new CursorBuilder( this, evaluatorBuilder );
+
+        setSearchEngine( new DefaultSearchEngine( this, cursorBuilder, evaluatorBuilder, getOptimizer() ) );
+
+        File partitionDir = getPartitionDir();
+        partitionDir.mkdirs();
+
+        partitionEnv = new BdbJePartitionEnviroment( schemaManager, partitionDir );
+
+        Database db = partitionEnv.createDb( "masterDb" );
+        master = new BdbJeMasterTable( db, schemaManager );
+
+        Set<String> existingIndices = partitionEnv.getIndexOids();
+
+        // Initialize the indexes
+        super.doInit();
+
+        if ( cacheSize < 0 )
+        {
+            cacheSize = DEFAULT_CACHE_SIZE;
+            LOG.debug( "Using the default entry cache size of {} for {} partition", cacheSize, id );
+        }
+        else
+        {
+            LOG.debug( "Using the custom configured cache size of {} for {} partition", cacheSize, id );
+        }
+
+        //entryCache = partitionEnv.createCache( "entryCache" );
+
+        // then add all index objects to a list
+        List<String> allIndices = new ArrayList<String>();
+        for ( Index i : systemIndices.values() )
+        {
+            allIndices.add( i.getAttribute().getOid() );
+        }
+
+        Set<Index> newUserIndices = new HashSet<Index>();
+
+        for ( Index i : userIndices.values() )
+        {
+            allIndices.add( i.getAttribute().getOid() );
+
+            String name = i.getAttribute().getOid();
+
+            // if the name doesn't exist in the list of indexed OIDs
+            // this is a new index and we need to build it
+            if ( !existingIndices.contains( name ) )
+            {
+                newUserIndices.add( i );
+            }
+        }
+
+        if ( !newUserIndices.isEmpty() )
+        {
+            try
+            {
+                buildNewIndices( newUserIndices );
+            }
+            catch ( Exception e )
+            {
+                throw new LdapException( e );
+            }
+        }
+
+        deleteUnusedIndexData( allIndices, existingIndices );
+
+        //printDb( masterDb, masterKeySer, masterValSer );
+        //printDb( ( BdbJeIndex ) getNdnIndex() );
+        initialized = true;
+    }
+
+
+    private File getPartitionDir()
+    {
+        return new File( getPartitionPath() );
+    }
+
+
+    @Override
+    public void sync()
+    {
+    }
+
+
+    @Override
+    protected void doDestroy( PartitionTxn txn ) throws LdapException
+    {
+        if ( !initialized )
+        {
+            return;
+        }
+
+        // FIXME this code MUST be commented out before releasing
+        if ( !JeTransaction.txnStack.isEmpty() )
+        {
+            System.out.println( "there are " + JeTransaction.txnStack.size() + " open txns" );
+            int i = 0;
+            for ( StackTraceElement[] st : JeTransaction.txnStack )
+            {
+                System.out.println( ">>>>>>>>>>>>>>>>>>>>> " + i );
+                for ( StackTraceElement se : st )
+                {
+                    String s = se.toString();
+                    if ( s.startsWith( "org.eclipse." ) )
+                    {
+                        break;
+                    }
+                    System.out.println( se );
+                }
+                System.out.println( "<<<<<<<<<<<<<<<<<<<<< " + i );
+                i++;
+            }
+        }
+        super.doDestroy( txn );
+
+        try
+        {
+            partitionEnv.close();
+            LOG.debug( "Closed environment for {} partition.", suffixDn );
+        }
+        catch ( Throwable t )
+        {
+            LOG.error( I18n.err( I18n.ERR_127 ), t );
+        }
+    }
+
+
+    /**
+     * builds a user defined index on a attribute by browsing all the entries present in master db
+     * 
+     * @param userIdx then user defined index
+     * @throws Exception in case of any problems while building the index
+     */
+    private void buildNewIndices( Set<Index> userIndices ) throws Exception
+    {
+        LOG.info( "building the index for attribute type(s) {}", userIndices );
+
+        PartitionTxn tx = beginWriteTransaction();
+        Cursor<Tuple<String, Entry>> cursor = ( ( BdbJeMasterTable ) master ).cursor( tx );
+        while ( cursor.next() )
+        {
+            Tuple<String, Entry> t = cursor.get();
+            Entry entry = t.getValue();
+
+            BdbJeIndex prIdx = ( BdbJeIndex ) presenceIdx;
+
+            for ( Index bji : userIndices )
+            {
+                AttributeType atType = bji.getAttribute();
+                Attribute entryAttr = entry.get( atType );
+                if ( entryAttr != null )
+                {
+                    for ( Value value : entryAttr )
+                    {
+                        bji.add( tx, value.getValue(), id );
+                    }
+
+                    // Adds only those attributes that are indexed
+                    prIdx.add( tx, atType.getOid(), id );
+                }
+            }
+        }
+
+        cursor.close();
+        tx.commit();
+    }
+
+
+    /**
+     * removes any unused/removed user attribute index data present in the partition's database
+     * Note: only removes user defined indices
+     */
+    private void deleteUnusedIndexData( List<String> allIndices, Set<String> existingIndexOids )
+    {
+        for ( String oid : existingIndexOids )
+        {
+            // remove the index data if not found in the list of names of indices
+            if ( !allIndices.contains( oid ) )
+            {
+                try
+                {
+                    partitionEnv.deleteIndexDb( oid );
+                    LOG.info( "Deleted data of index with oid {}", oid );
+                }
+                catch ( Exception e )
+                {
+                    LOG.warn( "", e );
+                }
+            }
+        }
+    }
+
+
+    protected BdbJePartitionEnviroment getPartitionEnv()
+    {
+        return partitionEnv;
+    }
+
+
+    /**
+     * Rebuild the indexes 
+     */
+    private int rebuildIndexes( PartitionTxn partitionTxn ) throws LdapException, IOException
+    {
+        Cursor<Tuple<String, Entry>> cursor = getMasterTable().cursor();
+
+        int masterTableCount = 0;
+        int repaired = 0;
+
+        System.out.println( "Re-building indices..." );
+
+        boolean ctxEntryLoaded = false;
+
+        try
+        {
+            while ( cursor.next() )
+            {
+                masterTableCount++;
+                Tuple<String, Entry> tuple = cursor.get();
+                String id = tuple.getKey();
+
+                Entry entry = tuple.getValue();
+
+                // Start with the RdnIndex
+                String parentId = entry.get( ApacheSchemaConstants.ENTRY_PARENT_ID_OID ).getString();
+                System.out.println( "Read entry " + entry.getDn() + " with ID " + id + " and parent ID " + parentId );
+
+                Dn dn = entry.getDn();
+
+                ParentIdAndRdn parentIdAndRdn = null;
+
+                // context entry may have more than one RDN
+                if ( !ctxEntryLoaded && getSuffixDn().getName().startsWith( dn.getName() ) )
+                {
+                    // If the read entry is the context entry, inject a tuple that have one or more RDNs
+                    parentIdAndRdn = new ParentIdAndRdn( parentId, getSuffixDn().getRdns() );
+                    ctxEntryLoaded = true;
+                }
+                else
+                {
+                    parentIdAndRdn = new ParentIdAndRdn( parentId, dn.getRdn() );
+                }
+
+                // Inject the parentIdAndRdn in the rdnIndex
+                rdnIdx.add( partitionTxn, parentIdAndRdn, id );
+
+                // Process the ObjectClass index
+                // Update the ObjectClass index
+                Attribute objectClass = entry.get( objectClassAT );
+
+                if ( objectClass == null )
+                {
+                    String msg = I18n.err( I18n.ERR_217, dn, entry );
+                    ResultCodeEnum rc = ResultCodeEnum.OBJECT_CLASS_VIOLATION;
+                    throw new LdapSchemaViolationException( rc, msg );
+                }
+
+                for ( Value value : objectClass )
+                {
+                    String valueStr = value.getValue();
+
+                    if ( valueStr.equals( SchemaConstants.TOP_OC ) )
+                    {
+                        continue;
+                    }
+
+                    objectClassIdx.add( partitionTxn, valueStr, id );
+                }
+
+                // The Alias indexes
+                if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
+                {
+                    Attribute aliasAttr = entry.get( aliasedObjectNameAT );
+                    addAliasIndices( partitionTxn, id, dn, new Dn( schemaManager, aliasAttr.getString() ) );
+                }
+
+                // The entryCSN index
+                // Update the EntryCsn index
+                Attribute entryCsn = entry.get( entryCsnAT );
+
+                if ( entryCsn == null )
+                {
+                    String msg = I18n.err( I18n.ERR_219, dn, entry );
+                    throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION, msg );
+                }
+
+                entryCsnIdx.add( partitionTxn, entryCsn.getString(), id );
+
+                // The AdministrativeRole index
+                // Update the AdministrativeRole index, if needed
+                if ( entry.containsAttribute( administrativeRoleAT ) )
+                {
+                    // We may have more than one role
+                    Attribute adminRoles = entry.get( administrativeRoleAT );
+
+                    for ( Value value : adminRoles )
+                    {
+                        adminRoleIdx.add( partitionTxn, value.getValue(), id );
+                    }
+
+                    // Adds only those attributes that are indexed
+                    presenceIdx.add( partitionTxn, administrativeRoleAT.getOid(), id );
+                }
+
+                // And the user indexess
+                // Now work on the user defined userIndices
+                for ( Attribute attribute : entry )
+                {
+                    AttributeType attributeType = attribute.getAttributeType();
+                    String attributeOid = attributeType.getOid();
+
+                    if ( hasUserIndexOn( attributeType ) )
+                    {
+                        Index<Object, String> idx = ( Index<Object, String> ) getUserIndex( attributeType );
+
+                        // here lookup by attributeId is OK since we got attributeId from
+                        // the entry via the enumeration - it's in there as is for sure
+
+                        for ( Value value : attribute )
+                        {
+                            idx.add( partitionTxn, value.getValue(), id );
+                        }
+
+                        // Adds only those attributes that are indexed
+                        presenceIdx.add( partitionTxn, attributeOid, id );
+                    }
+                }
+            }
+
+        }
+        catch ( Exception e )
+        {
+            System.out.println( "Exiting after fetching entries " + repaired );
+            throw new LdapOtherException( e.getMessage(), e );
+        }
+        finally
+        {
+            cursor.close();
+        }
+
+        return masterTableCount;
+    }
+
+
+    @Override
+    public PartitionReadTxn beginReadTransaction()
+    {
+        return beginTransaction();
+    }
+
+
+    @Override
+    public PartitionWriteTxn beginWriteTransaction()
+    {
+        return beginTransaction();
+    }
+
+
+    private JeTransaction beginTransaction()
+    {
+        Transaction txn = partitionEnv.createTxn();
+        JeTransaction jeTxn = new JeTransaction( txn );
+        System.out.println( "************ txn created" );
+        return jeTxn;
+    }
+
+
+    @Override
+    protected Index<?, String> convertAndInit( Index<?, String> index )
+        throws LdapException
+    {
+        BdbJeIndex jeIndex;
+
+        if ( index.getAttributeId().equals( ApacheSchemaConstants.APACHE_RDN_AT_OID ) )
+        {
+            jeIndex = new BdbJeRdnIndex();
+            jeIndex.setAttributeId( ApacheSchemaConstants.APACHE_RDN_AT_OID );
+            jeIndex.setCacheSize( index.getCacheSize() );
+        }
+        else if ( index instanceof BdbJeIndex )
+        {
+            jeIndex = ( BdbJeIndex<?> ) index;
+        }
+        else
+        {
+            LOG.debug( "Supplied index {} is not a BdbJeIndex.  "
+                + "Will create new BdbJeIndex using copied configuration parameters.", index );
+            jeIndex = new BdbJeIndex<>( index.getAttributeId(), true );
+            jeIndex.setCacheSize( index.getCacheSize() );
+        }
+
+        jeIndex.init( schemaManager, partitionEnv );
+
+        return jeIndex;
+    }
+
+
+    @Override
+    protected Index createSystemIndex( String oid, URI path, boolean withReverse ) throws LdapException
+    {
+        LOG.debug( "Creating a bdb JE based system Index for attribute {}", oid );
+        BdbJeIndex<?> jeIndex = null;
+
+        if ( oid.equals( ApacheSchemaConstants.APACHE_RDN_AT_OID ) )
+        {
+            jeIndex = new BdbJeRdnIndex();
+            jeIndex.setAttributeId( ApacheSchemaConstants.APACHE_RDN_AT_OID );
+        }
+        else if ( oid.equals( ApacheSchemaConstants.APACHE_ALIAS_AT_OID ) )
+        {
+            jeIndex = new BdbJeDnIndex( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
+            jeIndex.setAttributeId( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
+        }
+        else
+        {
+            jeIndex = new BdbJeIndex( oid, withReverse );
+        }
+
+        jeIndex.setWkDirPath( path );
+
+        return jeIndex;
+    }
+
+
+    @Override
+    protected void doRepair() throws LdapException
+    {
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJePartitionEnviroment.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJePartitionEnviroment.java
new file mode 100644
index 0000000..94fa36b
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJePartitionEnviroment.java
@@ -0,0 +1,228 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.io.File;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.Durability;
+import com.sleepycat.je.Durability.ReplicaAckPolicy;
+import com.sleepycat.je.Durability.SyncPolicy;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.Transaction;
+
+
+/**
+ * A helper class which contains a reference to the bdb je Enviroment 
+ * instance and the associated configuration.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJePartitionEnviroment
+{
+
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJePartitionEnviroment.class );
+
+    private Environment jeEnv;
+
+    private EnvironmentConfig defaultConfig;
+
+    /**
+     * a set holding the new names created for each cache name present in the cacheMap
+     * This is done to prevent name collision when multiple bdb je partitions are present
+     * and they all use(for the sake of simplicity and ease) copies of the config cache 
+     * file with same cache names
+     */
+    private Set<String> usedCacheNameSet = new HashSet<String>();
+
+
+    public BdbJePartitionEnviroment( SchemaManager schemaManager, File partitionDir )
+    {
+        defaultConfig = new EnvironmentConfig();
+        defaultConfig.setAllowCreate( true );
+        defaultConfig.setTransactional( true );
+
+        Durability durability = new Durability( SyncPolicy.WRITE_NO_SYNC, SyncPolicy.NO_SYNC, ReplicaAckPolicy.NONE );
+
+        defaultConfig.setDurability( durability );
+
+        jeEnv = new Environment( partitionDir, defaultConfig );
+    }
+
+
+    public Database createDb( String name )
+    {
+        DatabaseConfig dbConfig = new DatabaseConfig();
+        //dbConfig.setBtreeComparator( keyComparator );
+
+        return createDb( name, dbConfig );
+    }
+
+
+    public Database createDb( String name, Comparator keyComparator )
+    {
+        DatabaseConfig dbConfig = new DatabaseConfig();
+        dbConfig.setBtreeComparator( keyComparator );
+
+        return createDb( name, dbConfig );
+    }
+
+
+    public Database createDb( String name, Comparator keyComparator, Comparator valueComparator, boolean allowDups )
+    {
+        DatabaseConfig dbConfig = new DatabaseConfig();
+
+        if ( keyComparator != null )
+        {
+            dbConfig.setBtreeComparator( keyComparator );
+        }
+
+        if ( valueComparator != null )
+        {
+            dbConfig.setDuplicateComparator( valueComparator );
+        }
+
+        dbConfig.setSortedDuplicates( allowDups );
+
+        return createDb( name, dbConfig );
+
+    }
+
+
+    public Database createDb( String name, boolean allowDups )
+    {
+        DatabaseConfig dbConfig = new DatabaseConfig();
+        dbConfig.setSortedDuplicates( allowDups );
+
+        return createDb( name, dbConfig );
+    }
+
+
+    public Database createDb( String name, Comparator keyComparator, Comparator valueComparator )
+    {
+        return createDb( name, keyComparator, valueComparator, false );
+    }
+
+
+    public Database createDb( String name, DatabaseConfig dbConfig )
+    {
+        dbConfig.setAllowCreate( true );
+        dbConfig.setTransactional( true );
+
+        Transaction txn = createTxn();
+
+        Database db = jeEnv.openDatabase( txn, name, dbConfig );
+
+        txn.commit();
+
+        return db;
+    }
+
+
+    public Transaction createTxn()
+    {
+        return jeEnv.beginTransaction( null, null );
+    }
+
+
+    public void commitTxn( Transaction txn )
+    {
+        txn.commit();
+    }
+
+
+    public void abortTxn( Transaction txn )
+    {
+        if ( txn != null )
+        {
+            txn.abort();
+        }
+    }
+
+
+    public boolean isTransactional()
+    {
+        return jeEnv.getConfig().getTransactional();
+    }
+
+
+    public void close()
+    {
+        if ( jeEnv != null )
+        {
+            LOG.debug( "closing je environemt" );
+            jeEnv.close();
+        }
+    }
+
+
+    /**
+     * gives a set of OIDs of the attributes that are indexed  in the partition
+     * Warn: should only be called after opening the environment
+     * 
+     * @return set of index OIDs
+     */
+    public Set<String> getIndexOids()
+    {
+        List<String> names = jeEnv.getDatabaseNames();
+        Set<String> oids = new HashSet<String>();
+
+        for ( String s : names )
+        {
+            int pos = s.indexOf( BdbJeIndex.FORWARD_KEY );
+            if ( pos > 0 )
+            {
+                oids.add( s.substring( 0, pos ) );
+            }
+        }
+
+        return oids;
+    }
+
+
+    /**
+     * delete the databases of the index for the given attribute id
+     *  
+     * @param atOid the OID of the attribute which is indexed
+     */
+    public void deleteIndexDb( String atOid )
+    {
+        deleteDb( atOid + BdbJeIndex.FORWARD_KEY );
+        deleteDb( atOid + BdbJeIndex.REVERSE_KEY );
+    }
+
+
+    public void deleteDb( String name )
+    {
+        jeEnv.removeDatabase( null, name );
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeRdnIndex.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeRdnIndex.java
new file mode 100644
index 0000000..dd4cb40
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeRdnIndex.java
@@ -0,0 +1,93 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import org.apache.directory.api.ldap.model.schema.MatchingRule;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator;
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.xdbm.ParentIdAndRdn;
+import org.apache.directory.server.xdbm.ParentIdAndRdnComparator;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.ParentIdAndRdnSerializer;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.StringSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Database;
+
+
+/**
+ * RDN index implementation based on BdbJeIndex.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeRdnIndex<E> extends BdbJeIndex<ParentIdAndRdn>
+{
+
+    /** A logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJeRdnIndex.class );
+
+
+    public BdbJeRdnIndex()
+    {
+        super( ApacheSchemaConstants.APACHE_RDN_AT_OID, true );
+        initialized = false;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void init( SchemaManager schemaManager, BdbJePartitionEnviroment environment )
+    {
+        LOG.debug( "Initializing RDN Index for attribute '{}'", attributeId );
+
+        attributeType = schemaManager.getAttributeType( attributeId );
+        attribute = attributeType;
+        MatchingRule mr = attributeType.getEquality();
+
+        ParentIdAndRdnComparator<String> comp = new ParentIdAndRdnComparator<>( mr.getOid() );
+
+        UuidComparator.INSTANCE.setSchemaManager( schemaManager );
+
+        ParentIdAndRdnSerializer parentIdAndSerializer = new ParentIdAndRdnSerializer( schemaManager );
+
+        // it is mandatory to set the BTree key comparator for RDN index due to the way ParentIdAndRdn is serialized
+        // and compared. This incurs a significant penalty here because we are unable to use JE's byte-by-byte comparison
+        // that we use for all other indices.
+        Database rdnForwardDb = environment.createDb( ApacheSchemaConstants.APACHE_RDN_AT_OID + FORWARD_KEY,
+            new ParentIdAndRdnByteComparator() );
+        // it is import to set the serializer because it is a volatile field
+        ( ( ParentIdAndRdnByteComparator ) rdnForwardDb.getConfig().getBtreeComparator() )
+            .setSerializer( parentIdAndSerializer );
+
+        forward = new BdbJeTable<ParentIdAndRdn, String>( rdnForwardDb, schemaManager, comp, parentIdAndSerializer,
+            new StringSerializer() );
+
+        Database rdnReverseDb = environment.createDb( ApacheSchemaConstants.APACHE_RDN_AT_OID + REVERSE_KEY );
+        reverse = new BdbJeTable<String, ParentIdAndRdn>( rdnReverseDb, schemaManager, UuidComparator.INSTANCE,
+            new StringSerializer(), parentIdAndSerializer );
+        initialized = true;
+    }
+
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTable.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTable.java
new file mode 100644
index 0000000..ced7303
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTable.java
@@ -0,0 +1,607 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import static com.sleepycat.je.OperationStatus.SUCCESS;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.directory.api.ldap.model.cursor.Cursor;
+import org.apache.directory.api.ldap.model.cursor.EmptyCursor;
+import org.apache.directory.api.ldap.model.cursor.SingletonCursor;
+import org.apache.directory.api.ldap.model.cursor.Tuple;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapOtherException;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.Serializer;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.server.xdbm.AbstractTable;
+
+import com.sleepycat.je.CursorConfig;
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
+import com.sleepycat.je.Transaction;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeTable<K, V> extends AbstractTable<K, V>
+{
+    private Database db;
+    private Serializer<K> keySerializer;
+    private Serializer<V> valueSerializer;
+    private final DatabaseEntry discaredVE = new DatabaseEntry();
+
+
+    public BdbJeTable( Database db, SchemaManager schemaManager, Comparator<K> keyComparator,
+        Serializer<K> keySerializer, Serializer<V> valueSerializer )
+    {
+        super( schemaManager, db.getDatabaseName(), keyComparator, null );
+        UuidComparator.INSTANCE.setSchemaManager( schemaManager );
+        this.db = db;
+        this.keySerializer = keySerializer;
+        this.valueSerializer = valueSerializer;
+
+        this.allowsDuplicates = false;
+    }
+
+
+    public BdbJeTable( Database db, SchemaManager schemaManager,
+        Comparator<K> keyComparator, Comparator<V> valueComparator,
+        Serializer<K> keySerializer, Serializer<V> valueSerializer )
+    {
+        super( schemaManager, db.getDatabaseName(), keyComparator, valueComparator );
+        UuidComparator.INSTANCE.setSchemaManager( schemaManager );
+        this.db = db;
+        this.keySerializer = keySerializer;
+        this.valueSerializer = valueSerializer;
+
+        this.allowsDuplicates = true;
+    }
+
+
+    @Override
+    public boolean has( PartitionTxn transaction, K key ) throws LdapException
+    {
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            Transaction tx = getTxn( transaction );
+            OperationStatus status = db.get( tx, de, discaredVE, LockMode.READ_COMMITTED );
+            return ( status == SUCCESS );
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapException( ioe );
+        }
+    }
+
+
+    @Override
+    public boolean has( PartitionTxn transaction, K key, V value ) throws LdapException
+    {
+        if ( key == null || value == null )
+        {
+            return false;
+        }
+
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            DatabaseEntry ve = new DatabaseEntry( valueSerializer.serialize( value ) );
+            Transaction tx = getTxn( transaction );
+            OperationStatus status = db.getSearchBoth( tx, de, ve, LockMode.READ_COMMITTED );
+            return ( status == SUCCESS );
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapException( ioe );
+        }
+    }
+
+
+    @Override
+    public boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException
+    {
+        if ( key == null )
+        {
+            return false;
+        }
+
+        try
+        {
+            byte[] keyBytes = keySerializer.serialize( key );
+            DatabaseEntry de = new DatabaseEntry( keyBytes );
+            Transaction tx = getTxn( transaction );
+            CursorConfig cc = new CursorConfig();
+            cc.setReadCommitted( true );
+            com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+            OperationStatus status = cursor.getSearchKey( de, discaredVE, LockMode.DEFAULT );
+            if ( status != SUCCESS )
+            {
+                status = cursor.getNext( de, discaredVE, LockMode.DEFAULT );
+            }
+            cursor.close();
+
+            if ( status != SUCCESS )
+            {
+                return false;
+            }
+
+            if ( Arrays.equals( keyBytes, de.getData() ) )
+            {
+                return true;
+            }
+
+            K fetched = keySerializer.deserialize( de.getData() );
+            int cmpVal = keyComparator.compare( fetched, key );
+            return ( cmpVal > 0 );
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapOtherException( ioe.getMessage() );
+        }
+    }
+
+
+    @Override
+    public boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException
+    {
+        if ( key == null )
+        {
+            return false;
+        }
+
+        try
+        {
+            byte[] keyBytes = keySerializer.serialize( key );
+            DatabaseEntry de = new DatabaseEntry( keyBytes );
+            Transaction tx = getTxn( transaction );
+            CursorConfig cc = new CursorConfig();
+            cc.setReadCommitted( true );
+            com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+            OperationStatus status = cursor.getSearchKey( de, discaredVE, LockMode.DEFAULT );
+            if ( status != SUCCESS )
+            {
+                status = cursor.getPrev( de, discaredVE, LockMode.DEFAULT );
+            }
+            cursor.close();
+
+            if ( status != SUCCESS )
+            {
+                return false;
+            }
+
+            if ( Arrays.equals( keyBytes, de.getData() ) )
+            {
+                return true;
+            }
+
+            K fetched = keySerializer.deserialize( de.getData() );
+            int cmpVal = keyComparator.compare( fetched, key );
+            return ( cmpVal == -1 );
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapOtherException( ioe.getMessage() );
+        }
+    }
+
+
+    @Override
+    public boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
+    {
+
+        if ( key == null || val == null )
+        {
+            return false;
+        }
+
+        if ( !allowsDuplicates )
+        {
+            throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
+        }
+
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            DatabaseEntry ve = new DatabaseEntry();
+            Transaction tx = getTxn( transaction );
+            CursorConfig cc = new CursorConfig();
+            cc.setReadCommitted( true );
+            com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+            OperationStatus status = cursor.getSearchKey( de, ve, LockMode.DEFAULT );
+            if ( status != SUCCESS )
+            {
+                cursor.close();
+                return false;
+            }
+
+            boolean found = false;
+            do
+            {
+                V fetched = valueSerializer.deserialize( ve.getData() );
+                int cmpVal = valueComparator.compare( fetched, val );
+                if ( cmpVal >= 0 )
+                {
+                    found = true;
+                    break;
+                }
+            }
+            while ( cursor.getNextDup( de, ve, LockMode.DEFAULT ) == SUCCESS );
+
+            cursor.close();
+
+            return found;
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapOtherException( ioe.getMessage() );
+        }
+    }
+
+
+    @Override
+    public boolean hasLessOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
+    {
+        if ( key == null || val == null )
+        {
+            return false;
+        }
+
+        if ( !allowsDuplicates )
+        {
+            throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
+        }
+
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            DatabaseEntry ve = new DatabaseEntry();
+            Transaction tx = getTxn( transaction );
+            CursorConfig cc = new CursorConfig();
+            cc.setReadCommitted( true );
+            com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+            OperationStatus status = cursor.getSearchKey( de, ve, LockMode.DEFAULT );
+            if ( status != SUCCESS )
+            {
+                cursor.close();
+                return false;
+            }
+
+            boolean found = false;
+            do
+            {
+                V fetched = valueSerializer.deserialize( ve.getData() );
+                int cmpVal = valueComparator.compare( fetched, val );
+                if ( cmpVal <= 0 )
+                {
+                    found = true;
+                    break;
+                }
+            }
+            while ( cursor.getNextDup( de, ve, LockMode.DEFAULT ) == SUCCESS );
+
+            cursor.close();
+
+            return found;
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapOtherException( ioe.getMessage() );
+        }
+    }
+
+
+    @Override
+    public V get( PartitionTxn transaction, K key ) throws LdapException
+    {
+        if ( key == null )
+        {
+            return null;
+        }
+
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            DatabaseEntry ve = new DatabaseEntry();
+            Transaction tx = getTxn( transaction );
+            OperationStatus status = db.get( tx, de, ve, LockMode.READ_COMMITTED );
+            if ( status == SUCCESS )
+            {
+                return valueSerializer.deserialize( ve.getData() );
+            }
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapException( ioe );
+        }
+
+        return null;
+    }
+
+
+    @Override
+    public void put( PartitionTxn writeTransaction, K key, V value ) throws LdapException
+    {
+        if ( ( value == null ) || ( key == null ) )
+        {
+            throw new IllegalArgumentException( I18n.err( I18n.ERR_594 ) );
+        }
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            DatabaseEntry ve = new DatabaseEntry( valueSerializer.serialize( value ) );
+            Transaction tx = getTxn( writeTransaction );
+            OperationStatus status;
+            if ( allowsDuplicates )
+            {
+                status = db.putNoDupData( tx, de, ve );
+            }
+            else
+            {
+                status = db.put( tx, de, ve );
+            }
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapException( ioe );
+        }
+    }
+
+
+    @Override
+    public void remove( PartitionTxn writeTransaction, K key ) throws LdapException
+    {
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            Transaction tx = getTxn( writeTransaction );
+            db.delete( tx, de );
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapException( ioe );
+        }
+    }
+
+
+    @Override
+    public void remove( PartitionTxn writeTransaction, K key, V value )
+        throws LdapException
+    {
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            Transaction tx = getTxn( writeTransaction );
+            if ( allowsDuplicates )
+            {
+                DatabaseEntry ve = new DatabaseEntry( valueSerializer.serialize( value ) );
+                CursorConfig cc = new CursorConfig();
+                cc.setReadCommitted( true );
+                com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+                OperationStatus status = cursor.getSearchBoth( de, ve, LockMode.DEFAULT );
+                if ( status == SUCCESS )
+                {
+                    cursor.delete();
+                }
+                cursor.close();
+            }
+            else
+            {
+                db.delete( tx, de );
+            }
+        }
+        catch ( IOException ioe )
+        {
+            throw new LdapException( ioe );
+        }
+    }
+
+
+    @Override
+    public Cursor<Tuple<K, V>> cursor()
+    {
+        throw new UnsupportedOperationException( "cursor cannot be obtained without a transaction" );
+    }
+
+
+    public Cursor<Tuple<K, V>> cursor( PartitionTxn partitionTxn ) throws LdapException
+    {
+        Transaction tx = getTxn( partitionTxn );
+        CursorConfig cc = new CursorConfig();
+        cc.setReadCommitted( true );
+        com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+        return new BdbJeTableCursor<>( cursor, keySerializer, valueSerializer, allowsDuplicates );
+    }
+
+
+    @Override
+    public Cursor<Tuple<K, V>> cursor( PartitionTxn partitionTxn, K key ) throws LdapException
+    {
+        Transaction tx = getTxn( partitionTxn );
+        CursorConfig cc = new CursorConfig();
+        cc.setReadCommitted( true );
+        com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+        return new BdbJeTableCursor<>( cursor, keySerializer, valueSerializer, allowsDuplicates );
+    }
+
+
+    @Override
+    public Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException
+    {
+        if ( key == null )
+        {
+            return new EmptyCursor<>();
+        }
+
+        try
+        {
+            Transaction tx = getTxn( transaction );
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            DatabaseEntry ve = new DatabaseEntry();
+            if ( !allowsDuplicates )
+            {
+                OperationStatus status = db.get( tx, de, ve, LockMode.READ_COMMITTED );
+                if ( status == OperationStatus.SUCCESS )
+                {
+                    V value = valueSerializer.deserialize( ve.getData() );
+                    return new SingletonCursor<>( value );
+                }
+            }
+            else
+            {
+                CursorConfig cc = new CursorConfig();
+                cc.setReadCommitted( true );
+                com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+                return new BdbJeTableDupKeyCursor<>( cursor, keySerializer, valueSerializer, key );
+            }
+        }
+        catch ( IOException e )
+        {
+            throw new LdapException( e );
+        }
+
+        return new EmptyCursor<>();
+    }
+
+
+    @Override
+    public long count( PartitionTxn transaction, K key ) throws LdapException
+    {
+        if ( key == null )
+        {
+            return 0;
+        }
+
+        Transaction tx = getTxn( transaction );
+        try
+        {
+            DatabaseEntry de = new DatabaseEntry( keySerializer.serialize( key ) );
+            OperationStatus status = db.get( tx, de, discaredVE, LockMode.READ_COMMITTED );
+            if ( status == SUCCESS )
+            {
+                if ( allowsDuplicates )
+                {
+                    CursorConfig cc = new CursorConfig();
+                    cc.setReadCommitted( true );
+                    com.sleepycat.je.Cursor cursor = db.openCursor( tx, cc );
+                    cursor.getSearchKey( de, discaredVE, LockMode.DEFAULT );
+                    int c = cursor.count();
+                    cursor.close();
+                    return c;
+                }
+                else
+                {
+                    return 1;
+                }
+            }
+        }
+        catch ( IOException e )
+        {
+            throw new LdapException( e );
+        }
+
+        return 0;
+    }
+
+
+    @Override
+    public void close( PartitionTxn transaction ) throws LdapException
+    {
+        db.close();
+    }
+
+
+    @Override
+    public long count( PartitionTxn transaction ) throws LdapException
+    {
+        return db.count();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long greaterThanCount( PartitionTxn transaction, K key ) throws LdapException
+    {
+        // take a best guess
+        return Math.min( db.count(), 10L );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long lessThanCount( PartitionTxn transaction, K key ) throws LdapException
+    {
+        // take a best guess
+        return Math.min( db.count(), 10L );
+    }
+
+
+    private Transaction getTxn( PartitionTxn transaction )
+    {
+        return ( ( JeTransaction ) transaction ).getTxn();
+    }
+
+
+    void printDb()
+    {
+        com.sleepycat.je.Cursor cursor = null;
+        try
+        {
+            CursorConfig cc = new CursorConfig();
+            cc.setReadCommitted( true );
+            cursor = db.openCursor( null, cc );
+            DatabaseEntry key = new DatabaseEntry();
+            DatabaseEntry value = new DatabaseEntry();
+            while ( cursor.getNext( key, value, LockMode.DEFAULT ) == OperationStatus.SUCCESS )
+            {
+                Object k = keySerializer.deserialize( key.getData() );
+                Object v = valueSerializer.deserialize( value.getData() );
+
+                System.out.println( k + " = " + v );
+            }
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+        finally
+        {
+            if ( cursor != null )
+            {
+                cursor.close();
+            }
+        }
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableCursor.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableCursor.java
new file mode 100644
index 0000000..bd0d5e3
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableCursor.java
@@ -0,0 +1,270 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import static com.sleepycat.je.OperationStatus.NOTFOUND;
+import static com.sleepycat.je.OperationStatus.SUCCESS;
+
+import java.io.IOException;
+
+import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
+import org.apache.directory.api.ldap.model.cursor.Tuple;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.Serializer;
+
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeTableCursor<K, V> extends AbstractCursor<Tuple<K, V>>
+{
+    /**
+     * Whether or not a value is available when get() is called.
+     */
+    private OperationStatus status;
+
+    private DatabaseEntry ke = new DatabaseEntry();
+    private DatabaseEntry ve = new DatabaseEntry();
+
+    private com.sleepycat.je.Cursor wrapped;
+    private Serializer<K> keySerializer;
+    private Serializer<V> valueSerializer;
+    private boolean allowsDuplicates;
+
+    private boolean bf;
+    private boolean al;
+
+
+    public BdbJeTableCursor( com.sleepycat.je.Cursor wrapped, Serializer<K> keySerializer,
+        Serializer<V> valueSerializer, boolean allowsDuplicates )
+    {
+        this.wrapped = wrapped;
+        this.keySerializer = keySerializer;
+        this.valueSerializer = valueSerializer;
+        this.allowsDuplicates = allowsDuplicates;
+    }
+
+
+    @Override
+    public boolean available()
+    {
+        return ( status == SUCCESS );
+    }
+
+
+    @Override
+    public void before( Tuple<K, V> element ) throws LdapException, CursorException
+    {
+        try
+        {
+            ke.setData( keySerializer.serialize( element.getKey() ) );
+            if ( allowsDuplicates )
+            {
+                ve.setData( valueSerializer.serialize( element.getValue() ) );
+                wrapped.getSearchBoth( ke, ve, LockMode.DEFAULT );
+                wrapped.getPrevDup( ke, ve, LockMode.DEFAULT );
+            }
+            else
+            {
+                wrapped.getSearchKey( ke, ve, LockMode.DEFAULT );
+                wrapped.getPrev( ke, ve, LockMode.DEFAULT );
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new CursorException( e );
+        }
+    }
+
+
+    @Override
+    public void after( Tuple<K, V> element ) throws LdapException, CursorException
+    {
+        try
+        {
+            ke.setData( keySerializer.serialize( element.getKey() ) );
+            if ( allowsDuplicates )
+            {
+                ve.setData( valueSerializer.serialize( element.getValue() ) );
+                wrapped.getSearchBoth( ke, ve, LockMode.DEFAULT );
+                wrapped.getNextDup( ke, ve, LockMode.DEFAULT );
+            }
+            else
+            {
+                wrapped.getSearchKey( ke, ve, LockMode.DEFAULT );
+                wrapped.getNext( ke, ve, LockMode.DEFAULT );
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new CursorException( e );
+        }
+    }
+
+
+    @Override
+    public void beforeFirst() throws LdapException, CursorException
+    {
+        al = false;
+        status = wrapped.getFirst( ke, ve, LockMode.DEFAULT );
+        bf = true;
+    }
+
+
+    @Override
+    public void afterLast() throws LdapException, CursorException
+    {
+        bf = false;
+        status = wrapped.getLast( ke, ve, LockMode.DEFAULT );
+        al = true;
+    }
+
+
+    @Override
+    public boolean first() throws LdapException, CursorException
+    {
+        if ( bf )
+        {
+            bf = false;
+            al = false;
+            return bf;
+        }
+        status = wrapped.getFirst( ke, ve, LockMode.DEFAULT );
+        return ( status == OperationStatus.SUCCESS );
+    }
+
+
+    @Override
+    public boolean last() throws LdapException, CursorException
+    {
+        if ( al )
+        {
+            al = false;
+            bf = false;
+            return al;
+        }
+        status = wrapped.getLast( ke, ve, LockMode.DEFAULT );
+        return ( status == OperationStatus.SUCCESS );
+    }
+
+
+    @Override
+    public boolean previous() throws LdapException, CursorException
+    {
+        if ( al )
+        {
+            al = false;
+            return ( status == OperationStatus.SUCCESS );
+        }
+        else if ( bf )
+        {
+            return false;
+        }
+
+        if ( allowsDuplicates )
+        {
+            status = wrapped.getPrevDup( ke, ve, LockMode.DEFAULT );
+            if ( status == NOTFOUND )
+            {
+                status = wrapped.getPrev( ke, ve, LockMode.DEFAULT );
+            }
+        }
+        else
+        {
+            status = wrapped.getPrev( ke, ve, LockMode.DEFAULT );
+        }
+        return ( status == OperationStatus.SUCCESS );
+    }
+
+
+    @Override
+    public boolean next() throws LdapException, CursorException
+    {
+        if ( bf )
+        {
+            bf = false;
+            return ( status == OperationStatus.SUCCESS );
+        }
+        else if ( al )
+        {
+            return false;
+        }
+
+        if ( allowsDuplicates )
+        {
+            status = wrapped.getNextDup( ke, ve, LockMode.DEFAULT );
+            if ( status == NOTFOUND )
+            {
+                status = wrapped.getNext( ke, ve, LockMode.DEFAULT );
+            }
+        }
+        else
+        {
+            status = wrapped.getNext( ke, ve, LockMode.DEFAULT );
+        }
+        return ( status == OperationStatus.SUCCESS );
+    }
+
+
+    @Override
+    public Tuple<K, V> get() throws CursorException
+    {
+        if ( status != SUCCESS )
+        {
+            throw new InvalidCursorPositionException();
+        }
+
+        try
+        {
+            K key = keySerializer.deserialize( ke.getData() );
+            V value = valueSerializer.deserialize( ve.getData() );
+            Tuple<K, V> tuple = new Tuple<>( key, value );
+            return tuple;
+        }
+        catch ( IOException e )
+        {
+            throw new CursorException( e );
+        }
+    }
+
+
+    @Override
+    public void close( Exception cause ) throws IOException
+    {
+        wrapped.close();
+        super.close( cause );
+    }
+
+
+    @Override
+    public void close() throws IOException
+    {
+        wrapped.close();
+        super.close();
+    }
+
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableDupKeyCursor.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableDupKeyCursor.java
new file mode 100644
index 0000000..3ce7e81
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableDupKeyCursor.java
@@ -0,0 +1,197 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import static com.sleepycat.je.OperationStatus.SUCCESS;
+
+import java.io.IOException;
+
+import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.Serializer;
+
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeTableDupKeyCursor<K, V> extends AbstractCursor<V>
+{
+    private DatabaseEntry ke = new DatabaseEntry();
+    private DatabaseEntry ve = new DatabaseEntry();
+
+    private com.sleepycat.je.Cursor wrapped;
+    private Serializer<V> valueSerializer;
+
+    private boolean bf;
+
+    /**
+     * Whether or not a value is available when get() is called.
+     */
+    private OperationStatus status;
+
+
+    public BdbJeTableDupKeyCursor( com.sleepycat.je.Cursor wrapped, Serializer<K> keySerializer,
+        Serializer<V> valueSerializer, K key ) throws IOException
+    {
+        this.wrapped = wrapped;
+        this.valueSerializer = valueSerializer;
+        ke.setData( keySerializer.serialize( key ) );
+    }
+
+
+    @Override
+    public boolean available()
+    {
+        return ( status == SUCCESS );
+    }
+
+
+    @Override
+    public void before( V element ) throws LdapException, CursorException
+    {
+        try
+        {
+            ve.setData( valueSerializer.serialize( element ) );
+            status = wrapped.getSearchBoth( ke, ve, LockMode.DEFAULT );
+            if ( status == SUCCESS )
+            {
+                status = wrapped.getPrevDup( ke, ve, LockMode.DEFAULT );
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new CursorException( e );
+        }
+    }
+
+
+    @Override
+    public void after( V element ) throws LdapException, CursorException
+    {
+        try
+        {
+            ve.setData( valueSerializer.serialize( element ) );
+            status = wrapped.getSearchBoth( ke, ve, LockMode.DEFAULT );
+            if ( status == SUCCESS )
+            {
+                status = wrapped.getNextDup( ke, ve, LockMode.DEFAULT );
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new CursorException( e );
+        }
+    }
+
+
+    @Override
+    public void beforeFirst() throws LdapException, CursorException
+    {
+        status = wrapped.getSearchKey( ke, ve, LockMode.DEFAULT );
+        bf = true;
+    }
+
+
+    @Override
+    public void afterLast() throws LdapException, CursorException
+    {
+        throw new UnsupportedOperationException( "afterlast is not supported on a duplicate key cursor" );
+    }
+
+
+    @Override
+    public boolean first() throws LdapException, CursorException
+    {
+        if ( bf )
+        {
+            bf = false;
+            return bf;
+        }
+        status = wrapped.getSearchKey( ke, ve, LockMode.DEFAULT );
+        return ( status == OperationStatus.SUCCESS );
+    }
+
+
+    @Override
+    public boolean last() throws LdapException, CursorException
+    {
+        throw new UnsupportedOperationException( "navigating to last is not supported" );
+    }
+
+
+    @Override
+    public boolean previous() throws LdapException, CursorException
+    {
+        status = wrapped.getPrevDup( ke, ve, LockMode.DEFAULT );
+        return ( status == OperationStatus.SUCCESS );
+    }
+
+
+    @Override
+    public boolean next() throws LdapException, CursorException
+    {
+        status = wrapped.getNextDup( ke, ve, LockMode.DEFAULT );
+        return ( status == OperationStatus.SUCCESS );
+    }
+
+
+    @Override
+    public V get() throws CursorException
+    {
+        if ( status != SUCCESS )
+        {
+            throw new InvalidCursorPositionException();
+        }
+
+        try
+        {
+            V value = valueSerializer.deserialize( ve.getData() );
+            return value;
+        }
+        catch ( IOException e )
+        {
+            throw new CursorException( e );
+        }
+    }
+
+
+    @Override
+    public void close( Exception cause ) throws IOException
+    {
+        wrapped.close();
+        super.close( cause );
+    }
+
+
+    @Override
+    public void close() throws IOException
+    {
+        wrapped.close();
+        super.close();
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/ForwardBdbJeIndexCursor.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/ForwardBdbJeIndexCursor.java
new file mode 100644
index 0000000..a2cffe3
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/ForwardBdbJeIndexCursor.java
@@ -0,0 +1,350 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.util.exception.NotImplementedException;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.Serializer;
+import org.apache.directory.server.xdbm.AbstractIndexCursor;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Cursor;
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
+
+
+/**
+ * An IndexCursor for BdbJeIndex.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ForwardBdbJeIndexCursor<K> extends AbstractIndexCursor<K>
+{
+
+    private static final Logger LOG = LoggerFactory.getLogger( ForwardBdbJeIndexCursor.class );
+
+    /** the underlying cursor */
+    private Cursor wrapped;
+
+    /** search key */
+    private K searchKey;
+
+    /** key serializer */
+    private Serializer<K> keySerializer;
+
+    /** value serializer */
+    private Serializer<String> valueSerializer;
+
+    /** container to store the key */
+    private DatabaseEntry key;
+
+    /** container to store the value */
+    private DatabaseEntry value;
+
+    private OperationStatus status;
+
+    private IndexEntry<K, String> entry;
+
+    private IndexEntry<K, String> nextEntry, prevEntry;
+
+    private boolean closed;
+
+    //private boolean forward;
+
+    /**
+     * this is the flag used to avoid java.lang.IllegalStateException: Cursor not initialized.
+     * when the given searchKey is not found and we call previous() after next()
+     */
+    private boolean keyDoesNotExist;
+
+
+    public ForwardBdbJeIndexCursor( com.sleepycat.je.Cursor wrapped, Serializer<K> keySerializer,
+        Serializer<String> valueSerializer, boolean forward ) throws LdapException
+    {
+        this( wrapped, keySerializer, valueSerializer, forward, null );
+    }
+
+
+    public ForwardBdbJeIndexCursor( com.sleepycat.je.Cursor wrapped, Serializer<K> keySerializer,
+        Serializer<String> valueSerializer, boolean forward, K searchKey ) throws LdapException
+    {
+        this.wrapped = wrapped;
+        this.keySerializer = keySerializer;
+        this.valueSerializer = valueSerializer;
+        this.searchKey = searchKey;
+        //this.forward = forward;
+
+        key = new DatabaseEntry();
+        value = new DatabaseEntry();
+
+        if ( searchKey != null )
+        {
+            try
+            {
+                key.setData( keySerializer.serialize( searchKey ) );
+            }
+            catch ( IOException e )
+            {
+                throw new LdapException( e );
+            }
+        }
+    }
+
+
+    public boolean next() throws LdapException
+    {
+        if ( nextEntry != null )
+        {
+            entry = nextEntry;
+            nextEntry = null;
+
+            return true;
+        }
+
+        OperationStatus tmpStatus = status;
+
+        if ( searchKey != null )
+        {
+            if ( tmpStatus == null ) //first search
+            {
+                tmpStatus = wrapped.getSearchKey( key, value, LockMode.DEFAULT );
+            }
+            else
+            {
+                tmpStatus = wrapped.getNextDup( key, value, LockMode.DEFAULT );
+            }
+        }
+        else
+        {
+            tmpStatus = wrapped.getNext( key, value, LockMode.DEFAULT );
+        }
+
+        if ( ( status == null ) && ( tmpStatus == OperationStatus.NOTFOUND ) )
+        {
+            //to reproduce this IllegalStateException comment the below line
+            keyDoesNotExist = true;
+        }
+
+        status = tmpStatus;
+
+        boolean next = false;
+
+        if ( status == OperationStatus.SUCCESS )
+        {
+            next = true;
+
+            readData();
+        }
+        else
+        {
+            prevEntry = entry;
+            entry = null;
+        }
+
+        return next;
+    }
+
+
+    public boolean previous() throws LdapException
+    {
+        // if previous is the first call return false
+        if ( status == null || keyDoesNotExist )
+        {
+            return false;
+        }
+
+        if ( prevEntry != null )
+        {
+            entry = prevEntry;
+            prevEntry = null;
+            return true;
+        }
+
+        OperationStatus tmpStatus;
+
+        if ( searchKey != null )
+        {
+            tmpStatus = wrapped.getPrevDup( key, value, LockMode.DEFAULT );
+        }
+        else
+        {
+            tmpStatus = wrapped.getPrev( key, value, LockMode.DEFAULT );
+        }
+
+        if ( tmpStatus == OperationStatus.SUCCESS )
+        {
+            status = tmpStatus;
+            readData();
+            return true;
+        }
+        else
+        {
+            nextEntry = entry;
+            entry = null;
+        }
+
+        return false;
+    }
+
+
+    private void readData() throws LdapException
+    {
+        try
+        {
+            K k = keySerializer.deserialize( key.getData() );
+            String val = valueSerializer.deserialize( value.getData() );
+            entry = new IndexEntry<K, String>();
+            // need to set in reverse way for forward
+            entry.setId( val );
+            entry.setKey( k );
+        }
+        catch ( Exception e )
+        {
+            throw new LdapException( e );
+        }
+    }
+
+
+    public IndexEntry<K, String> get()
+    {
+        return entry;
+    }
+
+
+    public boolean available()
+    {
+        return ( entry != null );
+    }
+
+
+    public void close()
+    {
+        closed = true;
+        wrapped.close();
+    }
+
+
+    public void close( Exception arg0 )
+    {
+        close();
+    }
+
+
+    public boolean isElementReused()
+    {
+        return false;
+    }
+
+
+    public void beforeFirst() throws LdapException
+    {
+        // just ignore this call, cause the cursor will be first anyway before using
+        // one more reason is at many places beforeFirst() is called
+        // on the Cursor interface because of some issues in the way JDBM backed
+        // cursors position themselves after opening
+    }
+
+
+    public boolean first() throws LdapException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public boolean isAfterLast()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public boolean isBeforeFirst()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public boolean isFirst()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public boolean isLast()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public boolean last()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public Iterator<IndexEntry<K, String>> iterator()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public void beforeValue( K id, String indexValue ) throws Exception
+    {
+        //throw new NotImplementedException( "beforeValue( ID, V ) not yet supported" );
+    }
+
+
+    public void afterValue( K id, String indexValue ) throws Exception
+    {
+        throw new NotImplementedException( "afterValue( ID, V ) not yet supported" );
+    }
+
+
+    public void after( IndexEntry<K, String> arg0 ) throws LdapException
+    {
+        throw new NotImplementedException( "after( IndexEntry<V, ID> ) not yet supported" );
+    }
+
+
+    public void afterLast() throws LdapException
+    {
+        throw new NotImplementedException( "afterLast() not yet supported" );
+    }
+
+
+    public void before( IndexEntry<K, String> arg0 ) throws LdapException
+    {
+        throw new NotImplementedException( "before( IndexEntry<V, ID> ) not yet supported" );
+    }
+
+
+    @Override
+    protected String getUnsupportedMessage()
+    {
+        return "";
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/JeTransaction.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/JeTransaction.java
new file mode 100644
index 0000000..67f088e
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/JeTransaction.java
@@ -0,0 +1,90 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.io.IOException;
+import java.util.Stack;
+
+import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
+
+import com.sleepycat.je.Transaction;
+
+
+/**
+ * PartitionTxn implementation with encapsulated JE transaction handle.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class JeTransaction implements PartitionReadTxn, PartitionWriteTxn
+{
+    private Transaction txn;
+    private boolean closed;
+
+    // FIXME this txnStack and its usage MUST be commented out before releasing
+    static Stack<StackTraceElement[]> txnStack = new Stack<>();
+
+
+    public JeTransaction( Transaction txn )
+    {
+        this.txn = txn;
+        txnStack.push( Thread.currentThread().getStackTrace() );
+    }
+
+
+    public Transaction getTxn()
+    {
+        return txn;
+    }
+
+
+    @Override
+    public void commit() throws IOException
+    {
+        txn.commit();
+        closed = true;
+        txnStack.pop();
+    }
+
+
+    @Override
+    public void abort() throws IOException
+    {
+        txn.abort();
+        closed = true;
+        txnStack.pop();
+    }
+
+
+    @Override
+    public boolean isClosed()
+    {
+        return closed;
+    }
+
+
+    @Override
+    public void close() throws IOException
+    {
+        commit();
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/ParentIdAndRdnByteComparator.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/ParentIdAndRdnByteComparator.java
new file mode 100644
index 0000000..98b73f8
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/ParentIdAndRdnByteComparator.java
@@ -0,0 +1,78 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.ParentIdAndRdnSerializer;
+import org.apache.directory.server.xdbm.ParentIdAndRdn;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * TODO ParentIdAndRdnByteComparator.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ParentIdAndRdnByteComparator implements Comparator<byte[]>, Serializable
+{
+
+    private static final Logger LOG = LoggerFactory.getLogger( ParentIdAndRdnByteComparator.class );
+
+    private transient ParentIdAndRdnSerializer serializer;
+
+
+    public ParentIdAndRdnByteComparator()
+    {
+    }
+
+
+    public void setSerializer( ParentIdAndRdnSerializer serializer )
+    {
+        this.serializer = serializer;
+    }
+
+
+    public int compare( byte[] rdnBytes1, byte[] rdnBytes2 )
+    {
+        int val = 0;
+        try
+        {
+            ParentIdAndRdn rdn1 = serializer.deserialize( rdnBytes1 );
+            ParentIdAndRdn rdn2 = serializer.deserialize( rdnBytes2 );
+
+            val = rdn1.compareTo( rdn2 );
+        }
+        catch ( IOException e )
+        {
+            // shound't happen
+            LOG.error( "exception while comparing ParentIdAndRdn objects "
+                + "after deserializing from byte arrays, something must be wrong with RDN index", e );
+        }
+
+        return val;
+    }
+
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ByteArraySerializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ByteArraySerializer.java
new file mode 100644
index 0000000..f421df4
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ByteArraySerializer.java
@@ -0,0 +1,47 @@
+/*
+ *   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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.IOException;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ByteArraySerializer implements Serializer<byte[]>
+{
+
+    public static final ByteArraySerializer INSTANCE = new ByteArraySerializer();
+
+
+    public byte[] serialize( byte[] object ) throws IOException
+    {
+        return object;
+    }
+
+
+    public byte[] deserialize( byte[] bytes ) throws IOException
+    {
+        return bytes;
+    }
+
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ComparatorSerializerMap.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ComparatorSerializerMap.java
new file mode 100644
index 0000000..a763beb
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ComparatorSerializerMap.java
@@ -0,0 +1,72 @@
+/*
+ *   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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.api.ldap.model.schema.LdapComparator;
+import org.apache.directory.api.ldap.model.schema.comparators.IntegerComparator;
+import org.apache.directory.api.ldap.model.schema.comparators.StringComparator;
+import org.apache.directory.server.core.partition.impl.btree.LongComparator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A class holding the mappings of comparator to the respective serializer.
+ * If no mapping fornd for a comparator then the default serializer will be returned
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public final class ComparatorSerializerMap
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ComparatorSerializerMap.class );
+
+    private static final Map<Class<? extends LdapComparator<?>>, Serializer> MAP = new HashMap<Class<? extends LdapComparator<?>>, Serializer>();
+
+    static
+    {
+        MAP.put( LongComparator.class, new LongSerializer() );
+        MAP.put( StringComparator.class, new StringSerializer() );
+        MAP.put( IntegerComparator.class, new LongSerializer() );
+    }
+
+    private ComparatorSerializerMap()
+    {
+    }
+
+    public static Serializer getSerializer( Class compClass )
+    {
+        Serializer sz = MAP.get( compClass );
+
+        if ( sz == null )
+        {
+            LOG.info( "there is no configured serializer for the class " + compClass.getName() );
+            LOG.info( "returning the object serializer instead" );
+
+            sz = ObjectSerializer.INSTANCE;
+        }
+
+        return sz;
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/DnSerializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/DnSerializer.java
new file mode 100644
index 0000000..97412f1
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/DnSerializer.java
@@ -0,0 +1,126 @@
+/*
+ *  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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.server.i18n.I18n;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Serialize and deserialize a Dn.
+ * <br><br>
+ * <b>This class must *not* be used outside of the server.</b>
+ *  
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DnSerializer implements Serializer<Dn>
+{
+    /** The serialVersionUID */
+    private static final long serialVersionUID = 1L;
+
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( DnSerializer.class );
+
+    /**
+     * Speedup for logs
+     */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** The schemaManager reference */
+    private transient SchemaManager schemaManager;
+
+
+    /**
+     * Creates a new instance of DnSerializer.
+     * 
+     * @param schemaManager The reference to the global schemaManager
+     */
+    public DnSerializer( SchemaManager schemaManager )
+    {
+        this.schemaManager = schemaManager;
+    }
+
+
+    /**
+     * This is the place where we serialize Dn
+     * 
+     * @param object The element to serialize
+     * @return a byte[] containing the serialized element
+     * @throws IOException If teh serialization failed
+     */
+    public byte[] serialize( Dn dn ) throws IOException
+    {
+        try ( ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutput out = new ObjectOutputStream( baos ) )
+        {
+
+            // First, the Dn
+            dn.writeExternal( out );
+
+            out.flush();
+
+            if ( IS_DEBUG )
+            {
+                LOG.debug( ">------------------------------------------------" );
+                LOG.debug( "Serialized {}", dn );
+            }
+
+            return baos.toByteArray();
+        }
+    }
+
+
+    /**
+     *  Deserialize a Dn.
+     *  
+     *  @param bytes the byte array containing the serialized Dn
+     *  @return An instance of a Dn object 
+     *  @throws IOException if we can't deserialize the Dn
+     */
+    public Dn deserialize( byte[] bytes ) throws IOException
+    {
+        ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( bytes ) );
+
+        try
+        {
+            Dn dn = new Dn();
+
+            dn.readExternal( in );
+
+            return dn;
+        }
+        catch ( ClassNotFoundException cnfe )
+        {
+            LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
+            throw new IOException( cnfe.getLocalizedMessage() );
+        }
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/EntrySerializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/EntrySerializer.java
new file mode 100644
index 0000000..3ed5423
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/EntrySerializer.java
@@ -0,0 +1,229 @@
+/*
+ *   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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.name.Rdn;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.server.i18n.I18n;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * TODO EntrySerializer.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class EntrySerializer implements Serializer<Entry>
+{
+    /** The serialVersionUID */
+    private static final long serialVersionUID = 1L;
+
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( EntrySerializer.class );
+
+    /**
+     * Speedup for logs
+     */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** The schemaManager reference */
+    private transient SchemaManager schemaManager;
+
+
+    /**
+     * Creates a new instance of ServerEntrySerializer.
+     *
+     * @param schemaManager The reference to the global schemaManager
+     */
+    public EntrySerializer( SchemaManager schemaManager )
+    {
+        this.schemaManager = schemaManager;
+    }
+
+
+    /**
+     * <p>
+     * 
+     * This is the place where we serialize entries, and all theirs
+     * elements. the reason why we don't call the underlying methods
+     * (<code>ServerAttribute.write(), Value.write()</code>) is that we need
+     * access to the registries to read back the values.
+     * <p>
+     * The structure used to store the entry is the following :
+     * <ul>
+     *   <li><b>[a byte]</b> : if the Dn is empty 0 will be written else 1</li>
+     *   <li><b>[Rdn]</b> : The entry's Rdn.</li>
+     *   <li><b>[numberAttr]</b> : the bumber of attributes. Can be 0</li>
+     *   <li>For each Attribute :
+     *     <ul>
+     *       <li><b>[attribute's oid]</b> : The attribute's OID to get back
+     *       the attributeType on deserialization</li>
+     *       <li><b>[Attribute]</b> The attribute</li>
+     *     </ul>
+     *   </li>
+     * </ul>
+     */
+    public byte[] serialize( Entry entry ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream( baos );
+
+        // First, the Dn
+        Dn dn = entry.getDn();
+
+        // Write the Rdn of the Dn
+        if ( dn.isEmpty() )
+        {
+            out.writeByte( 0 );
+        }
+        else
+        {
+            out.writeByte( 1 );
+            Rdn rdn = dn.getRdn();
+            rdn.writeExternal( out );
+        }
+
+        // Then the attributes.
+        out.writeInt( entry.getAttributes().size() );
+
+        // Iterate through the keys. We store the Attribute
+        // here, to be able to restore it in the readExternal :
+        // we need access to the registries, which are not available
+        // in the ServerAttribute class.
+        for ( Attribute attribute : entry.getAttributes() )
+        {
+            // Write the oid to be able to restore the AttributeType when deserializing
+            // the attribute
+            String oid = attribute.getAttributeType().getOid();
+
+            out.writeUTF( oid );
+
+            // Write the attribute
+            attribute.writeExternal( out );
+        }
+
+        out.flush();
+
+        // Note : we don't store the ObjectClassAttribute. It has already
+        // been stored as an attribute.
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( ">------------------------------------------------" );
+            LOG.debug( "Serialize " + entry );
+        }
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     *  Deserialize a Entry.
+     *  
+     *  @param bytes the byte array containing the serialized entry
+     *  @return An instance of a Entry object 
+     *  @throws IOException if we can't deserialize the Entry
+     */
+    public Entry deserialize( byte[] bytes ) throws IOException
+    {
+        ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( bytes ) );
+
+        try
+        {
+            Entry entry = new DefaultEntry( schemaManager );
+
+            // Read the Dn, if any
+            byte hasDn = in.readByte();
+
+            if ( hasDn == 1 )
+            {
+                Rdn rdn = new Rdn( schemaManager );
+                rdn.readExternal( in );
+
+                try
+                {
+                    entry.setDn( new Dn( schemaManager, rdn ) );
+                }
+                catch ( LdapInvalidDnException lide )
+                {
+                    IOException ioe = new IOException( lide.getMessage() );
+                    ioe.initCause( lide );
+                    throw ioe;
+                }
+            }
+            else
+            {
+                entry.setDn( Dn.EMPTY_DN );
+            }
+
+            // Read the number of attributes
+            int nbAttributes = in.readInt();
+
+            // Read the attributes
+            for ( int i = 0; i < nbAttributes; i++ )
+            {
+                // Read the attribute's OID
+                String oid = in.readUTF();
+
+                try
+                {
+                    AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( oid );
+
+                    // Create the attribute we will read
+                    Attribute attribute = new DefaultAttribute( attributeType );
+
+                    // Read the attribute
+                    attribute.readExternal( in );
+
+                    entry.add( attribute );
+                }
+                catch ( LdapException ne )
+                {
+                    // We weren't able to find the OID. The attribute will not be added
+                    throw new ClassNotFoundException( ne.getMessage(), ne );
+                }
+            }
+
+            return entry;
+        }
+        catch ( ClassNotFoundException cnfe )
+        {
+            LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
+            throw new IOException( cnfe.getLocalizedMessage() );
+        }
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/LongSerializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/LongSerializer.java
new file mode 100644
index 0000000..d13c718
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/LongSerializer.java
@@ -0,0 +1,81 @@
+/*
+ *   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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.IOException;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LongSerializer implements Serializer<Long>
+{
+
+    public static final LongSerializer INSTANCE = new LongSerializer();
+
+
+    public byte[] serialize( Long obj ) throws IOException
+    {
+        long n = obj.longValue();
+
+        n = ( n ^ 0x8000000000000000L ); // flip MSB because "long" is signed
+        byte[] bites = new byte[8];
+
+        bites[0] = ( byte ) ( n >> 56 );
+        bites[1] = ( byte ) ( n >> 48 );
+        bites[2] = ( byte ) ( n >> 40 );
+        bites[3] = ( byte ) ( n >> 32 );
+        bites[4] = ( byte ) ( n >> 24 );
+        bites[5] = ( byte ) ( n >> 16 );
+        bites[6] = ( byte ) ( n >> 8 );
+        bites[7] = ( byte ) n;
+
+        return bites;
+    }
+
+
+    public Long deserialize( byte[] bites ) throws IOException
+    {
+        long id;
+
+        id = bites[0] + ( ( bites[0] < 0 ) ? 256 : 0 );
+        id <<= 8;
+        id += bites[1] + ( ( bites[1] < 0 ) ? 256 : 0 );
+        id <<= 8;
+        id += bites[2] + ( ( bites[2] < 0 ) ? 256 : 0 );
+        id <<= 8;
+        id += bites[3] + ( ( bites[3] < 0 ) ? 256 : 0 );
+        id <<= 8;
+        id += bites[4] + ( ( bites[4] < 0 ) ? 256 : 0 );
+        id <<= 8;
+        id += bites[5] + ( ( bites[5] < 0 ) ? 256 : 0 );
+        id <<= 8;
+        id += bites[6] + ( ( bites[6] < 0 ) ? 256 : 0 );
+        id <<= 8;
+        id += bites[7] + ( ( bites[7] < 0 ) ? 256 : 0 );
+
+        id = ( id ^ 0x8000000000000000L ); // flip MSB because "long" is signed
+
+        return id;
+    }
+
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ObjectSerializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ObjectSerializer.java
new file mode 100644
index 0000000..320909d
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ObjectSerializer.java
@@ -0,0 +1,71 @@
+/*
+ *   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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+
+/**
+ * Serializes any serializable/externalizable object to byte array.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ObjectSerializer<E> implements Serializer<E>
+{
+
+    public static final ObjectSerializer INSTANCE = new ObjectSerializer();
+
+
+    public byte[] serialize( E object ) throws IOException
+    {
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ObjectOutputStream objOut = new ObjectOutputStream( byteOut );
+
+        objOut.writeObject( object );
+        objOut.close();
+
+        return byteOut.toByteArray();
+    }
+
+
+    public E deserialize( byte[] bytes ) throws IOException
+    {
+        ByteArrayInputStream byteIn = new ByteArrayInputStream( bytes );
+        ObjectInputStream objIn = new ObjectInputStream( byteIn );
+
+        try
+        {
+            return ( E ) objIn.readObject();
+        }
+        catch ( ClassNotFoundException e )
+        {
+            IOException ioe = new IOException();
+            ioe.initCause( e );
+            throw ioe;
+        }
+    }
+
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ParentIdAndRdnSerializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ParentIdAndRdnSerializer.java
new file mode 100644
index 0000000..1f33b74
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/ParentIdAndRdnSerializer.java
@@ -0,0 +1,182 @@
+/*
+ *  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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+
+import org.apache.directory.api.ldap.model.name.Rdn;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.server.xdbm.ParentIdAndRdn;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Serialize and deserialize a ParentidAndRdn.
+ * <br><br>
+ * <b>This class must *not* be used outside of the server.</b>
+ *  
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ParentIdAndRdnSerializer implements Serializer<ParentIdAndRdn>
+{
+    /** The serialVersionUID */
+    private static final long serialVersionUID = 1L;
+
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( ParentIdAndRdnSerializer.class );
+
+    /**
+     * Speedup for logs
+     */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** The schemaManager reference */
+    private transient SchemaManager schemaManager;
+
+
+    /**
+     * Creates a new instance of ParentIdAndRdnSerializer.
+     * 
+     * @param schemaManager The reference to the global schemaManager
+     */
+    public ParentIdAndRdnSerializer( SchemaManager schemaManager )
+    {
+        this.schemaManager = schemaManager;
+    }
+
+
+    /**
+     * This is the place where we serialize ParentIdAndRdn
+     * 
+     * @param object The element to serialize
+     * @return the byte] containing the serialized element
+     * @throws IOException If the serialization failed
+     */
+    public byte[] serialize( ParentIdAndRdn parentIdAndRdn ) throws IOException
+    {
+        try ( ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutput out = new ObjectOutputStream( baos ) )
+        {
+
+            // First, the Dn
+            Rdn[] rdns = parentIdAndRdn.getRdns();
+
+            // Write the Rdn of the Dn
+            if ( ( rdns == null ) || ( rdns.length == 0 ) )
+            {
+                out.writeByte( 0 );
+            }
+            else
+            {
+                out.writeByte( rdns.length );
+
+                for ( Rdn rdn : rdns )
+                {
+                    rdn.writeExternal( out );
+                }
+            }
+
+            // Then the parentId.
+            out.writeUTF( parentIdAndRdn.getParentId() );
+
+            // The number of children
+            out.writeInt( parentIdAndRdn.getNbChildren() );
+
+            // The number of descendants
+            out.writeInt( parentIdAndRdn.getNbDescendants() );
+
+            out.flush();
+
+            if ( IS_DEBUG )
+            {
+                LOG.debug( ">------------------------------------------------" );
+                LOG.debug( "Serialize {}", parentIdAndRdn );
+            }
+
+            return baos.toByteArray();
+        }
+    }
+
+
+    /**
+     *  Deserialize a ParentIdAndRdn.
+     *  
+     *  @param bytes the byte array containing the serialized ParentIdAndRdn
+     *  @return An instance of a ParentIdAndRdn object 
+     *  @throws IOException if we can't deserialize the ParentIdAndRdn
+     */
+    public ParentIdAndRdn deserialize( byte[] bytes ) throws IOException
+    {
+        ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( bytes ) );
+
+        try
+        {
+            ParentIdAndRdn parentIdAndRdn = new ParentIdAndRdn();
+
+            // Read the number of rdns, if any
+            byte nbRdns = in.readByte();
+
+            if ( nbRdns == 0 )
+            {
+                parentIdAndRdn.setRdns( new Rdn[0] );
+            }
+            else
+            {
+                Rdn[] rdns = new Rdn[nbRdns];
+
+                for ( int i = 0; i < nbRdns; i++ )
+                {
+                    Rdn rdn = new Rdn( schemaManager );
+                    rdn.readExternal( in );
+                    rdns[i] = rdn;
+                }
+
+                parentIdAndRdn.setRdns( rdns );
+            }
+
+            // Read the parent ID
+            String uuid = in.readUTF();
+
+            parentIdAndRdn.setParentId( uuid );
+
+            // Read the nulber of children and descendants
+            int nbChildren = in.readInt();
+            int nbDescendants = in.readInt();
+
+            parentIdAndRdn.setNbChildren( nbChildren );
+            parentIdAndRdn.setNbDescendants( nbDescendants );
+
+            return parentIdAndRdn;
+        }
+        catch ( ClassNotFoundException cnfe )
+        {
+            LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
+            throw new IOException( cnfe.getLocalizedMessage() );
+        }
+    }
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/Serializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/Serializer.java
new file mode 100644
index 0000000..c584ffe
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/Serializer.java
@@ -0,0 +1,38 @@
+/*
+ *   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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.IOException;
+
+
+/**
+ * TODO Serializer.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface Serializer<E>
+{
+    byte[] serialize( E object ) throws IOException;
+
+
+    E deserialize( byte[] bytes ) throws IOException;
+}
diff --git a/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/StringSerializer.java b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/StringSerializer.java
new file mode 100644
index 0000000..291971f
--- /dev/null
+++ b/bdbje-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/je/serializers/StringSerializer.java
@@ -0,0 +1,49 @@
+/*
+ *   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.server.core.partition.impl.btree.je.serializers;
+
+
+import java.io.IOException;
+
+
+/**
+ * TODO StringSerializer.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class StringSerializer implements Serializer<String>
+{
+
+    private static final String UTF_8 = "UTF-8";
+
+
+    public byte[] serialize( String object ) throws IOException
+    {
+        return object.getBytes( UTF_8 );
+    }
+
+
+    public String deserialize( byte[] bytes ) throws IOException
+    {
+        return new String( bytes, UTF_8 );
+    }
+
+}
diff --git a/bdbje-partition/src/site/site.xml b/bdbje-partition/src/site/site.xml
new file mode 100644
index 0000000..6277924
--- /dev/null
+++ b/bdbje-partition/src/site/site.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+  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.
+
+-->
+
+<!--
+
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+
+-->
+
+<project name="${project.name}">
+
+  <body>
+
+    <menu ref="parent" />
+
+    <menu ref="reports" />
+
+  </body>
+
+</project>
diff --git a/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeIndexTest.java b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeIndexTest.java
new file mode 100644
index 0000000..c607316
--- /dev/null
+++ b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeIndexTest.java
@@ -0,0 +1,469 @@
+/*
+ *  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.server.core.partition.impl.btree.je;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.api.ldap.model.cursor.Cursor;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.schema.extractor.SchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
+import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
+import org.apache.directory.api.util.FileUtils;
+import org.apache.directory.api.util.Strings;
+import org.apache.directory.api.util.exception.Exceptions;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeIndexTest
+{
+    private Index<String, String> idx;
+    private static SchemaManager schemaManager;
+    private PartitionTxn partitionTxn;
+
+    /** The temporary directory the files will be created in */
+    private static Path tempDir;
+
+    static BdbJePartitionEnviroment env;
+
+
+    @BeforeClass
+    public static void init() throws Exception
+    {
+        tempDir = Files.createTempDirectory( BdbJeIndexTest.class.getSimpleName() );
+
+        File schemaRepository = new File( tempDir.toFile(), "schema" );
+        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor( tempDir.toFile() );
+        extractor.extractOrCopy( true );
+        LdifSchemaLoader loader = new LdifSchemaLoader( schemaRepository );
+        schemaManager = new DefaultSchemaManager( loader );
+
+        boolean loaded = schemaManager.loadAllEnabled();
+
+        if ( !loaded )
+        {
+            fail( "Schema load failed : " + Exceptions.printErrors( schemaManager.getErrors() ) );
+        }
+
+        env = new BdbJePartitionEnviroment( schemaManager, tempDir.toFile() );
+    }
+
+
+    @Before
+    public void setup() throws IOException
+    {
+        renewTxn();
+    }
+
+
+    private void renewTxn()
+    {
+        if ( partitionTxn != null )
+        {
+            try
+            {
+                partitionTxn.commit();
+            }
+            catch ( IOException e )
+            {
+                throw new RuntimeException( e );
+            }
+        }
+        partitionTxn = new JeTransaction( env.createTxn() );
+    }
+
+
+    @After
+    public void teardown() throws Exception
+    {
+        destroyIndex();
+    }
+
+
+    @AfterClass
+    public static void cleanup() throws Exception
+    {
+        FileUtils.deleteDirectory( tempDir.toFile() );
+    }
+
+
+    void destroyIndex() throws Exception
+    {
+        if ( idx != null )
+        {
+            idx.close( partitionTxn );
+            env.deleteIndexDb( idx.getAttribute().getOid() );
+        }
+
+        idx = null;
+    }
+
+
+    void initIndex() throws Exception
+    {
+        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.OU_AT );
+        BdbJeIndex<String> index = new BdbJeIndex<String>( attributeType.getName(), false );
+        initIndex( index );
+    }
+
+
+    void initIndex( BdbJeIndex<String> bdbJeIdx ) throws Exception
+    {
+        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.OU_AT );
+
+        if ( bdbJeIdx == null )
+        {
+            bdbJeIdx = new BdbJeIndex<String>( attributeType.getName(), false );
+        }
+
+        bdbJeIdx.init( schemaManager, env );
+        this.idx = bdbJeIdx;
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Property Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test(expected = LdapException.class)
+    public void testAttributeId() throws Exception
+    {
+        // uninitialized index
+        BdbJeIndex<Object> jdbmIndex1 = new BdbJeIndex<Object>( "foo", false );
+        assertEquals( "foo", jdbmIndex1.getAttributeId() );
+
+        BdbJeIndex<Object> jdbmIndex2 = new BdbJeIndex<Object>( "bar", false );
+        assertEquals( "bar", jdbmIndex2.getAttributeId() );
+
+        // initialized index
+        initIndex();
+
+        try
+        {
+            idx.setAttributeId( "foo" );
+            fail( "Should not be able to set attributeId after initialization." );
+        }
+        catch ( Exception e )
+        {
+        }
+
+        assertEquals( "ou", idx.getAttributeId() );
+
+        destroyIndex();
+        BdbJeIndex<String> index = new BdbJeIndex<String>( "foo", false );
+        initIndex( index );
+        assertEquals( "foo", idx.getAttributeId() );
+    }
+
+
+    @Test
+    public void testGetAttribute() throws Exception
+    {
+        // uninitialized index
+        BdbJeIndex<Object> jdbmIndex = new BdbJeIndex<Object>( "ou", false );
+        assertNull( jdbmIndex.getAttribute() );
+
+        initIndex();
+        assertEquals( schemaManager.lookupAttributeTypeRegistry( "ou" ), idx.getAttribute() );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Count Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testCount() throws Exception
+    {
+        initIndex();
+        assertEquals( 0, idx.count( partitionTxn ) );
+
+        idx.add( partitionTxn, "foo", Strings.getUUID( 1234L ) );
+        assertEquals( 1, idx.count( partitionTxn ) );
+
+        idx.add( partitionTxn, "foo", Strings.getUUID( 333L ) );
+        assertEquals( 2, idx.count( partitionTxn ) );
+
+        idx.add( partitionTxn, "bar", Strings.getUUID( 555L ) );
+        assertEquals( 3, idx.count( partitionTxn ) );
+    }
+
+
+    @Test
+    public void testCountOneArg() throws Exception
+    {
+        initIndex();
+        assertEquals( 0, idx.count( partitionTxn, " foo " ) );
+
+        idx.add( partitionTxn, "bar", Strings.getUUID( 1234L ) );
+        assertEquals( 0, idx.count( partitionTxn, " foo " ) );
+
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 1234L ) );
+        assertEquals( 1, idx.count( partitionTxn, " foo " ) );
+
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 333L ) );
+        assertEquals( 2, idx.count( partitionTxn, " foo " ) );
+    }
+
+
+    @Test
+    public void testGreaterThanCount() throws Exception
+    {
+        initIndex();
+        assertEquals( 0, idx.greaterThanCount( partitionTxn, "a" ) );
+
+        for ( char ch = 'a'; ch <= 'z'; ch++ )
+        {
+            idx.add( partitionTxn, String.valueOf( ch ), Strings.getUUID( ch ) );
+        }
+
+        // We should not go above the magic limit of 10
+        assertEquals( 10, idx.greaterThanCount( partitionTxn, "a" ) );
+    }
+
+
+    @Test
+    public void testLessThanCount() throws Exception
+    {
+        initIndex();
+        assertEquals( 0, idx.lessThanCount( partitionTxn, "z" ) );
+
+        for ( char ch = 'a'; ch <= 'z'; ch++ )
+        {
+            idx.add( partitionTxn, String.valueOf( ch ), Strings.getUUID( ch ) );
+        }
+
+        // We should not go above the magic limit of 10
+        assertEquals( 10, idx.lessThanCount( partitionTxn, "z" ) );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Add, Drop and Lookup Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testLookupsToo() throws Exception
+    {
+        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( "seeAlso" );
+        BdbJeIndex<String> index = new BdbJeIndex<String>( attributeType.getName(), false );
+        index.init( schemaManager, env );
+        this.idx = index;
+
+        String foobarDn = "uid=foo,ou=bar";
+        String bazbarDn = "uid=baz,ou=bar";
+
+        assertNull( idx.forwardLookup( partitionTxn, foobarDn ) );
+        assertNull( idx.forwardLookup( partitionTxn, bazbarDn ) );
+        idx.add( partitionTxn, foobarDn, Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, foobarDn ) );
+        assertNull( idx.forwardLookup( partitionTxn, bazbarDn ) );
+        idx.add( partitionTxn, bazbarDn, Strings.getUUID( 24L ) );
+        assertEquals( Strings.getUUID( 24L ), idx.forwardLookup( partitionTxn, bazbarDn ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, foobarDn ) );
+    }
+
+
+    @Test
+    public void testLookups() throws Exception
+    {
+        initIndex();
+        assertNull( idx.forwardLookup( partitionTxn, " foo " ) );
+        assertNull( idx.forwardLookup( partitionTxn, " bar " ) );
+
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " foo " ) );
+        assertTrue( idx.forward( partitionTxn, " foo ", Strings.getUUID( 0L ) ) );
+
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 1L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " foo " ) );
+        assertTrue( idx.forward( partitionTxn, " foo ", Strings.getUUID( 0L ) ) );
+        assertTrue( idx.forward( partitionTxn, " foo ", Strings.getUUID( 1L ) ) );
+
+        idx.add( partitionTxn, " bar ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " bar " ) );
+        assertTrue( idx.forward( partitionTxn, " bar ", Strings.getUUID( 0L ) ) );
+        assertTrue( idx.forward( partitionTxn, " foo ", Strings.getUUID( 0L ) ) );
+        assertTrue( idx.forward( partitionTxn, " foo ", Strings.getUUID( 1L ) ) );
+    }
+
+
+    @Test
+    public void testAddDropById() throws Exception
+    {
+        initIndex();
+        assertNull( idx.forwardLookup( partitionTxn, " foo " ) );
+        assertNull( idx.forwardLookup( partitionTxn, " bar " ) );
+
+        // test add/drop without adding any duplicates
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " foo " ) );
+
+        idx.drop( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        assertNull( idx.forwardLookup( partitionTxn, " foo " ) );
+
+        // test add/drop with duplicates in bulk
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 1L ) );
+        idx.add( partitionTxn, " bar ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " foo " ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " bar " ) );
+
+        idx.drop( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        idx.drop( partitionTxn, " bar ", Strings.getUUID( 0L ) );
+        assertFalse( idx.forward( partitionTxn, " bar ", Strings.getUUID( 0L ) ) );
+        assertFalse( idx.forward( partitionTxn, " foo ", Strings.getUUID( 0L ) ) );
+
+        idx.drop( partitionTxn, " bar ", Strings.getUUID( 1L ) );
+        idx.drop( partitionTxn, " foo ", Strings.getUUID( 1L ) );
+        assertNull( idx.forwardLookup( partitionTxn, " foo " ) );
+        assertNull( idx.forwardLookup( partitionTxn, " bar " ) );
+        assertEquals( 0, idx.count( partitionTxn ) );
+    }
+
+
+    @Test
+    public void testAddDropOneByOne() throws Exception
+    {
+        initIndex();
+        assertNull( idx.forwardLookup( partitionTxn, " foo " ) );
+        assertNull( idx.forwardLookup( partitionTxn, " bar " ) );
+
+        // test add/drop without adding any duplicates
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " foo " ) );
+
+        idx.drop( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        assertNull( idx.forwardLookup( partitionTxn, " foo " ) );
+
+        // test add/drop with duplicates but one at a time
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 1L ) );
+        idx.add( partitionTxn, " bar ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " foo " ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " bar " ) );
+
+        idx.drop( partitionTxn, " bar ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, " foo " ) );
+        assertFalse( idx.forward( partitionTxn, " bar ", Strings.getUUID( 0L ) ) );
+
+        idx.drop( partitionTxn, " foo ", Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 1L ), idx.forwardLookup( partitionTxn, " foo " ) );
+        assertFalse( idx.forward( partitionTxn, " foo ", Strings.getUUID( 0L ) ) );
+
+        idx.drop( partitionTxn, " foo ", Strings.getUUID( 1L ) );
+        assertNull( idx.forwardLookup( partitionTxn, " foo " ) );
+        assertNull( idx.forwardLookup( partitionTxn, " bar " ) );
+        assertEquals( 0, idx.count( partitionTxn ) );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Miscellaneous Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testCursors() throws Exception
+    {
+        initIndex();
+        assertEquals( 0, idx.count( partitionTxn ) );
+
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 1234L ) );
+        assertEquals( 1, idx.count( partitionTxn ) );
+
+        idx.add( partitionTxn, " foo ", Strings.getUUID( 333L ) );
+        assertEquals( 2, idx.count( partitionTxn ) );
+
+        idx.add( partitionTxn, "bar", Strings.getUUID( 555L ) );
+        assertEquals( 3, idx.count( partitionTxn ) );
+
+        // use forward index's cursor
+        Cursor<IndexEntry<String, String>> cursor = idx.forwardCursor( partitionTxn );
+        cursor.beforeFirst();
+
+        assertEquals( 3, idx.count( partitionTxn ) );
+
+        cursor.next();
+        IndexEntry<String, String> e1 = cursor.get();
+        assertEquals( Strings.getUUID( 555L ), e1.getId() );
+        assertEquals( "bar", e1.getKey() );
+
+        cursor.next();
+        IndexEntry<String, String> e2 = cursor.get();
+        assertEquals( Strings.getUUID( 333L ), e2.getId() );
+        assertEquals( " foo ", e2.getKey() );
+
+        cursor.next();
+        IndexEntry<String, String> e3 = cursor.get();
+        assertEquals( Strings.getUUID( 1234L ), e3.getId() );
+        assertEquals( " foo ", e3.getKey() );
+
+        cursor.close();
+    }
+
+
+    @Test(expected = LdapException.class)
+    public void testNoEqualityMatching() throws Exception
+    {
+        BdbJeIndex<Object> jdbmIndex = new BdbJeIndex<Object>( "1.1", false );
+
+        AttributeType noEqMatchAttribute = new AttributeType( "1.1" );
+        jdbmIndex.init( schemaManager, env );
+        fail( "should not get here" );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Failing Tests
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testSingleValuedAttribute() throws Exception
+    {
+        BdbJeIndex<Object> jdbmIndex = new BdbJeIndex<Object>( SchemaConstants.CREATORS_NAME_AT, false );
+        jdbmIndex.init( schemaManager, env );
+        jdbmIndex.close( partitionTxn );
+    }
+}
diff --git a/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeRdnIndexTest.java b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeRdnIndexTest.java
new file mode 100644
index 0000000..fb77519
--- /dev/null
+++ b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeRdnIndexTest.java
@@ -0,0 +1,340 @@
+/*
+ *  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.server.core.partition.impl.btree.je;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.directory.api.util.FileUtils;
+import org.apache.directory.api.ldap.model.cursor.Cursor;
+import org.apache.directory.api.ldap.model.name.Rdn;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.schema.extractor.SchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
+import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
+import org.apache.directory.api.util.Strings;
+import org.apache.directory.api.util.exception.Exceptions;
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.apache.directory.server.xdbm.MockPartitionReadTxn;
+import org.apache.directory.server.xdbm.ParentIdAndRdn;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import jdbm.recman.BaseRecordManager;
+import jdbm.recman.TransactionManager;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeRdnIndexTest
+{
+    private static File dbFileDir;
+    Index<ParentIdAndRdn, String> idx;
+    private static SchemaManager schemaManager;
+    private PartitionTxn partitionTxn;
+
+    /** The temporary directory the files will be created in */
+    private static Path tempDir;
+
+    static BdbJePartitionEnviroment env;
+
+
+    @BeforeClass
+    public static void init() throws Exception
+    {
+        tempDir = Files.createTempDirectory( BdbJeRdnIndexTest.class.getSimpleName() );
+
+        File schemaRepository = new File( tempDir.toFile(), "schema" );
+        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor( tempDir.toFile() );
+        extractor.extractOrCopy( true );
+        LdifSchemaLoader loader = new LdifSchemaLoader( schemaRepository );
+        schemaManager = new DefaultSchemaManager( loader );
+
+        boolean loaded = schemaManager.loadAllEnabled();
+
+        if ( !loaded )
+        {
+            fail( "Schema load failed : " + Exceptions.printErrors( schemaManager.getErrors() ) );
+        }
+
+        env = new BdbJePartitionEnviroment( schemaManager, tempDir.toFile() );
+    }
+
+
+    @Before
+    public void setup() throws IOException
+    {
+        renewTxn();
+    }
+
+
+    private void renewTxn()
+    {
+        if ( partitionTxn != null )
+        {
+            try
+            {
+                partitionTxn.commit();
+            }
+            catch ( IOException e )
+            {
+                throw new RuntimeException( e );
+            }
+        }
+        partitionTxn = new JeTransaction( env.createTxn() );
+    }
+
+
+    @After
+    public void teardown() throws Exception
+    {
+        destroyIndex();
+    }
+
+
+    @AfterClass
+    public static void cleanup() throws Exception
+    {
+        FileUtils.deleteDirectory( tempDir.toFile() );
+    }
+
+
+    void destroyIndex() throws Exception
+    {
+        if ( idx != null )
+        {
+            idx.close( partitionTxn );
+            env.deleteIndexDb( idx.getAttribute().getOid() );
+        }
+
+        idx = null;
+    }
+
+
+    void initIndex() throws Exception
+    {
+        BdbJeRdnIndex index = new BdbJeRdnIndex();
+        initIndex( index );
+        partitionTxn = new JeTransaction( env.createTxn() );
+    }
+
+
+    void initIndex( BdbJeRdnIndex bdbJeIdx ) throws Exception
+    {
+        if ( bdbJeIdx == null )
+        {
+            bdbJeIdx = new BdbJeRdnIndex();
+        }
+
+        bdbJeIdx.init( schemaManager, env );
+        this.idx = bdbJeIdx;
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Property Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testGetAttribute() throws Exception
+    {
+        // uninitialized index
+        BdbJeRdnIndex rdnIndex = new BdbJeRdnIndex();
+        assertNull( rdnIndex.getAttribute() );
+
+        initIndex();
+        assertEquals( schemaManager.lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_RDN_AT ),
+            idx.getAttribute() );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Count Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testCount() throws Exception
+    {
+        initIndex();
+        assertEquals( 0, idx.count( partitionTxn ) );
+
+        ParentIdAndRdn key = new ParentIdAndRdn( Strings.getUUID( 0L ), new Rdn( "cn=key" ) );
+
+        idx.add( partitionTxn, key, Strings.getUUID( 0L ) );
+        assertEquals( 1, idx.count( partitionTxn ) );
+
+        // setting a different parentId should make this key a different key
+        key = new ParentIdAndRdn( Strings.getUUID( 1L ), new Rdn( "cn=key" ) );
+
+        idx.add( partitionTxn, key, Strings.getUUID( 1L ) );
+        assertEquals( 2, idx.count( partitionTxn ) );
+
+        //count shouldn't get affected cause of inserting the same key
+        idx.add( partitionTxn, key, Strings.getUUID( 2L ) );
+        assertEquals( 2, idx.count( partitionTxn ) );
+
+        key = new ParentIdAndRdn( Strings.getUUID( 2L ), new Rdn( "cn=key" ) );
+        idx.add( partitionTxn, key, Strings.getUUID( 3L ) );
+        assertEquals( 3, idx.count( partitionTxn ) );
+    }
+
+
+    @Test
+    public void testCountOneArg() throws Exception
+    {
+        initIndex();
+
+        ParentIdAndRdn key = new ParentIdAndRdn( Strings.getUUID( 0L ), new Rdn( "cn=key" ) );
+
+        assertEquals( 0, idx.count( partitionTxn, key ) );
+
+        idx.add( partitionTxn, key, Strings.getUUID( 0L ) );
+        assertEquals( 1, idx.count( partitionTxn, key ) );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Add, Drop and Lookup Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testLookups() throws Exception
+    {
+        initIndex();
+
+        ParentIdAndRdn key = new ParentIdAndRdn( Strings.getUUID( 0L ), new Rdn( schemaManager, "cn=key" ) );
+
+        assertNull( idx.forwardLookup( partitionTxn, key ) );
+
+        idx.add( partitionTxn, key, Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, key ) );
+        assertEquals( key, idx.reverseLookup( partitionTxn, Strings.getUUID( 0L ) ) );
+
+        // check with the different case in UP name, this ensures that the custom
+        // key comparator is used
+        key = new ParentIdAndRdn( Strings.getUUID( 0L ), new Rdn( schemaManager, "cn=KEY" ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, key ) );
+        assertEquals( key, idx.reverseLookup( partitionTxn, Strings.getUUID( 0L ) ) );
+    }
+
+
+    @Test
+    public void testAddDropById() throws Exception
+    {
+        initIndex();
+
+        ParentIdAndRdn key = new ParentIdAndRdn( Strings.getUUID( 0L ), new Rdn( "cn=key" ) );
+
+        assertNull( idx.forwardLookup( partitionTxn, key ) );
+
+        // test add/drop without adding any duplicates
+        idx.add( partitionTxn, key, Strings.getUUID( 0L ) );
+        assertEquals( Strings.getUUID( 0L ), idx.forwardLookup( partitionTxn, key ) );
+
+        idx.drop( partitionTxn, key, Strings.getUUID( 0L ) );
+        assertNull( idx.forwardLookup( partitionTxn, key ) );
+        assertNull( idx.reverseLookup( partitionTxn, Strings.getUUID( 0L ) ) );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Miscellaneous Test Methods
+    // -----------------------------------------------------------------------
+
+    @Test
+    public void testCursors() throws Exception
+    {
+        initIndex();
+
+        ParentIdAndRdn key = new ParentIdAndRdn( Strings.getUUID( 0L ), new Rdn( "cn=key" ) );
+
+        assertEquals( 0, idx.count( partitionTxn ) );
+
+        idx.add( partitionTxn, key, Strings.getUUID( 0L ) );
+        assertEquals( 1, idx.count( partitionTxn ) );
+
+        for ( long i = 1; i < 5; i++ )
+        {
+            key = new ParentIdAndRdn( Strings.getUUID( i ), new Rdn( "cn=key" + i ) );
+
+            idx.add( partitionTxn, key, Strings.getUUID( i ) );
+        }
+
+        assertEquals( 5, idx.count( partitionTxn ) );
+
+        // use forward index's cursor
+        Cursor<IndexEntry<ParentIdAndRdn, String>> cursor = idx.forwardCursor( partitionTxn );
+        cursor.beforeFirst();
+
+        cursor.next();
+        IndexEntry<ParentIdAndRdn, String> e1 = cursor.get();
+        assertEquals( Strings.getUUID( 0L ), e1.getId() );
+        assertEquals( "cn=key", e1.getKey().getRdns()[0].getName() );
+        assertEquals( Strings.getUUID( 0L ), e1.getKey().getParentId() );
+
+        cursor.next();
+        IndexEntry<ParentIdAndRdn, String> e2 = cursor.get();
+        assertEquals( Strings.getUUID( 1L ), e2.getId() );
+        assertEquals( "cn=key1", e2.getKey().getRdns()[0].getName() );
+        assertEquals( Strings.getUUID( 1L ), e2.getKey().getParentId() );
+
+        cursor.next();
+        IndexEntry<ParentIdAndRdn, String> e3 = cursor.get();
+        assertEquals( Strings.getUUID( 2L ), e3.getId() );
+        assertEquals( "cn=key2", e3.getKey().getRdns()[0].getName() );
+        assertEquals( Strings.getUUID( 2 ), e3.getKey().getParentId() );
+
+        cursor.close();
+    }
+
+    //    @Test
+    //    public void testStoreRdnWithTwoATAVs() throws Exception
+    //    {
+    //        initIndex();
+    //        
+    //        Dn dn = new Dn( "dc=example,dc=com" );
+    //        dn.normalize( schemaManager.getNormalizerMapping() );
+    //        
+    //        Rdn rdn = new Rdn( dn.getName() );
+    //        rdn._setParentId( 1 );
+    //        idx.add( rdn, 0l );
+    //        
+    //        Rdn rdn2 = idx.reverseLookup( 0l );
+    //        System.out.println( rdn2 );
+    //        InternalRdnComparator rdnCom = new InternalRdnComparator( "" );
+    //        assertEquals( 0, rdnCom.compare( rdn, rdn2 ) );
+    //    }
+}
diff --git a/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeStoreTest.java b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeStoreTest.java
new file mode 100644
index 0000000..38f735b
--- /dev/null
+++ b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeStoreTest.java
@@ -0,0 +1,935 @@
+/*
+ *   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.server.core.partition.impl.btree.je;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.api.ldap.model.csn.CsnFactory;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.DefaultModification;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
+import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
+import org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.name.Rdn;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.schema.extractor.SchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
+import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
+import org.apache.directory.api.util.FileUtils;
+import org.apache.directory.api.util.Strings;
+import org.apache.directory.api.util.exception.Exceptions;
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.api.CacheService;
+import org.apache.directory.server.core.api.CoreSession;
+import org.apache.directory.server.core.api.DirectoryService;
+import org.apache.directory.server.core.api.DnFactory;
+import org.apache.directory.server.core.api.LdapPrincipal;
+import org.apache.directory.server.core.api.MockCoreSession;
+import org.apache.directory.server.core.api.MockDirectoryService;
+import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.ModDnAva;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.shared.DefaultDnFactory;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexNotFoundException;
+import org.apache.directory.server.xdbm.Store;
+import org.apache.directory.server.xdbm.StoreUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jdbm.recman.BaseRecordManager;
+
+
+/**
+ * TODO BdbJeStoreTest.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeStoreTest
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJeStoreTest.class );
+
+    BdbJePartition partition;
+    CoreSession session;
+
+    private static SchemaManager schemaManager = null;
+    private static DnFactory dnFactory;
+    private static LdifSchemaLoader loader;
+    private static Dn EXAMPLE_COM;
+
+    /** The OU AttributeType instance */
+    private static AttributeType OU_AT;
+
+    /** The ApacheAlias AttributeType instance */
+    private static AttributeType APACHE_ALIAS_AT;
+
+    /** The DC AttributeType instance */
+    private static AttributeType DC_AT;
+
+    /** The SN AttributeType instance */
+    private static AttributeType SN_AT;
+
+    private static CacheService cacheService;
+    private PartitionTxn partitionTxn;
+
+    /** The recordManager used */
+    private BaseRecordManager recMan;
+
+    /** The temporary directory the files will be created in */
+    private static Path tempDir;
+
+
+    @BeforeClass
+    public static void setup() throws Exception
+    {
+        tempDir = Files.createTempDirectory( BdbJeStoreTest.class.getSimpleName() );
+
+        File schemaRepository = new File( tempDir.toFile(), "schema" );
+        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor( tempDir.toFile() );
+        extractor.extractOrCopy( true );
+        loader = new LdifSchemaLoader( schemaRepository );
+        schemaManager = new DefaultSchemaManager( loader );
+
+        boolean loaded = schemaManager.loadAllEnabled();
+
+        if ( !loaded )
+        {
+            fail( "Schema load failed : " + Exceptions.printErrors( schemaManager.getErrors() ) );
+        }
+
+        EXAMPLE_COM = new Dn( schemaManager, "dc=example,dc=com" );
+
+        OU_AT = schemaManager.getAttributeType( SchemaConstants.OU_AT );
+        DC_AT = schemaManager.getAttributeType( SchemaConstants.DC_AT );
+        SN_AT = schemaManager.getAttributeType( SchemaConstants.SN_AT );
+        APACHE_ALIAS_AT = schemaManager.getAttributeType( ApacheSchemaConstants.APACHE_ALIAS_AT );
+
+        cacheService = new CacheService();
+        cacheService.initialize( null );
+        dnFactory = new DefaultDnFactory( schemaManager, cacheService.getCache( "dnCache" ) );
+    }
+
+
+    @Before
+    public void createStore() throws Exception
+    {
+        // setup the working directory for the store
+        StoreUtils.createdExtraAttributes( schemaManager );
+
+        // initialize the store
+        partition = new BdbJePartition( schemaManager, dnFactory );
+        partition.setId( "example" );
+        partition.setCacheSize( 10 );
+        partition.setPartitionPath( tempDir.toUri() );
+        partition.setSyncOnWrite( false );
+
+        BdbJeIndex ouIndex = new BdbJeIndex( SchemaConstants.OU_AT_OID, false );
+        ouIndex.setWkDirPath( tempDir.toUri() );
+        partition.addIndex( ouIndex );
+
+        BdbJeIndex uidIndex = new BdbJeIndex( SchemaConstants.UID_AT_OID, false );
+        uidIndex.setWkDirPath( tempDir.toUri() );
+        partition.addIndex( uidIndex );
+
+        Dn suffixDn = new Dn( schemaManager, "o=Good Times Co." );
+        partition.setSuffixDn( suffixDn );
+
+        partition.setCacheService( cacheService );
+        partition.initialize();
+
+        StoreUtils.loadExampleData( partition, schemaManager );
+
+        DirectoryService directoryService = new MockDirectoryService();
+        directoryService.setSchemaManager( schemaManager );
+        session = new MockCoreSession( new LdapPrincipal(), directoryService );
+
+        partitionTxn = partition.beginReadTransaction();
+
+        LOG.debug( "Created new store" );
+    }
+
+
+    @After
+    public void destroyStore() throws Exception
+    {
+        if ( partition != null )
+        {
+            partitionTxn.commit();
+            // make sure all files are closed so that they can be deleted on Windows.
+            partition.destroy( partitionTxn );
+        }
+
+        File[] files = tempDir.toFile().listFiles();
+
+        for ( File file : files )
+        {
+            if ( !file.isDirectory() )
+            {
+                file.delete();
+            }
+        }
+
+        partition = null;
+    }
+
+
+    @AfterClass
+    public static void cleanup() throws Exception
+    {
+        FileUtils.deleteDirectory( tempDir.toFile() );
+    }
+
+
+    /**
+     * Tests a suffix with two name components: dc=example,dc=com.
+     * When reading this entry back from the store the Dn must
+     * consist of two RDNs.
+     */
+    @Test
+    public void testTwoComponentSuffix() throws Exception
+    {
+        // setup the working directory for the 2nd store
+        Path wkdir2 = Files.createTempDirectory( BdbJeStoreTest.class.getSimpleName() + "_db2" );
+
+        // initialize the 2nd partition
+        BdbJePartition store2 = new BdbJePartition( schemaManager, dnFactory );
+        store2.setId( "example2" );
+        store2.setCacheSize( 10 );
+        store2.setPartitionPath( wkdir2.toFile().toURI() );
+        store2.setSyncOnWrite( false );
+        store2.addIndex( new BdbJeIndex( SchemaConstants.OU_AT_OID, false ) );
+        store2.addIndex( new BdbJeIndex( SchemaConstants.UID_AT_OID, false ) );
+        store2.setSuffixDn( EXAMPLE_COM );
+        store2.setCacheService( cacheService );
+        store2.initialize();
+
+        // inject context entry
+        Dn suffixDn = new Dn( schemaManager, "dc=example,dc=com" );
+        Entry entry = new DefaultEntry( schemaManager, suffixDn,
+            "objectClass: top",
+            "objectClass: domain",
+            "dc: example",
+            SchemaConstants.ENTRY_CSN_AT, new CsnFactory( 0 ).newInstance().toString(),
+            SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
+
+        AddOperationContext addContext = new AddOperationContext( null, entry );
+        addContext.setPartition( store2 );
+        addContext.setTransaction( store2.beginWriteTransaction() );
+
+        store2.add( addContext );
+        addContext.getTransaction().commit();
+        PartitionTxn tx = store2.beginWriteTransaction();
+        // lookup the context entry
+        String id = store2.getEntryId( partitionTxn, suffixDn );
+        Entry lookup = store2.fetch( partitionTxn, id, suffixDn );
+        assertEquals( 2, lookup.getDn().size() );
+        tx.commit();
+        // make sure all files are closed so that they can be deleted on Windows.
+        store2.destroy( partitionTxn );
+    }
+
+
+    @Test
+    public void testSimplePropertiesUnlocked() throws Exception
+    {
+        BdbJePartition BdbJePartition = new BdbJePartition( schemaManager, dnFactory );
+        BdbJePartition.setSyncOnWrite( true ); // for code coverage
+
+        assertNull( BdbJePartition.getAliasIndex() );
+        Index<Dn, String> index = new BdbJeIndex<Dn>( ApacheSchemaConstants.APACHE_ALIAS_AT_OID,
+            true );
+        ( ( Store ) BdbJePartition ).addIndex( index );
+        assertNotNull( BdbJePartition.getAliasIndex() );
+
+        assertEquals( BdbJePartition.DEFAULT_CACHE_SIZE, BdbJePartition.getCacheSize() );
+        BdbJePartition.setCacheSize( 24 );
+        assertEquals( 24, BdbJePartition.getCacheSize() );
+
+        assertNull( BdbJePartition.getPresenceIndex() );
+        BdbJePartition.addIndex( new BdbJeIndex<String>( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID, false ) );
+        assertNotNull( BdbJePartition.getPresenceIndex() );
+
+        assertNull( BdbJePartition.getId() );
+        BdbJePartition.setId( "foo" );
+        assertEquals( "foo", BdbJePartition.getId() );
+
+        assertNull( BdbJePartition.getRdnIndex() );
+        BdbJePartition.addIndex( new BdbJeRdnIndex() );
+        assertNotNull( BdbJePartition.getRdnIndex() );
+
+        assertNull( BdbJePartition.getOneAliasIndex() );
+        ( ( Store ) BdbJePartition ).addIndex( new BdbJeIndex<Long>(
+            ApacheSchemaConstants.APACHE_ONE_ALIAS_AT_OID, true ) );
+        assertNotNull( BdbJePartition.getOneAliasIndex() );
+
+        assertNull( BdbJePartition.getSubAliasIndex() );
+        BdbJePartition.addIndex( new BdbJeIndex<Long>( ApacheSchemaConstants.APACHE_SUB_ALIAS_AT_OID, true ) );
+        assertNotNull( BdbJePartition.getSubAliasIndex() );
+
+        assertNull( BdbJePartition.getSuffixDn() );
+        BdbJePartition.setSuffixDn( EXAMPLE_COM );
+        assertEquals( "dc=example,dc=com", BdbJePartition.getSuffixDn().getName() );
+
+        assertNotNull( BdbJePartition.getSuffixDn() );
+
+        assertFalse( BdbJePartition.getUserIndices().hasNext() );
+        BdbJePartition.addIndex( new BdbJeIndex<Object>( "2.5.4.3", false ) );
+        assertEquals( true, BdbJePartition.getUserIndices().hasNext() );
+
+        assertNull( BdbJePartition.getPartitionPath() );
+        BdbJePartition.setPartitionPath( new File( "." ).toURI() );
+        assertEquals( new File( "." ).toURI(), BdbJePartition.getPartitionPath() );
+
+        assertFalse( BdbJePartition.isInitialized() );
+        assertTrue( BdbJePartition.isSyncOnWrite() );
+        BdbJePartition.setSyncOnWrite( false );
+        assertFalse( BdbJePartition.isSyncOnWrite() );
+
+        BdbJePartition.sync();
+        // make sure all files are closed so that they can be deleted on Windows.
+        BdbJePartition.destroy( partitionTxn );
+    }
+
+
+    @Test
+    public void testSimplePropertiesLocked() throws Exception
+    {
+        assertNotNull( partition.getAliasIndex() );
+        try
+        {
+            partition.addIndex( new BdbJeIndex<Dn>( ApacheSchemaConstants.APACHE_ALIAS_AT_OID, true ) );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertEquals( 10, partition.getCacheSize() );
+        try
+        {
+            partition.setCacheSize( 24 );
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertNotNull( partition.getPresenceIndex() );
+        try
+        {
+            partition.addIndex( new BdbJeIndex<String>( ApacheSchemaConstants.APACHE_PRESENCE_AT_OID, false ) );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertNotNull( partition.getId() );
+        try
+        {
+            partition.setId( "foo" );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertNotNull( partition.getRdnIndex() );
+        try
+        {
+            partition.addIndex( new BdbJeRdnIndex() );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertNotNull( partition.getOneAliasIndex() );
+        try
+        {
+            partition.addIndex( new BdbJeIndex<Long>( ApacheSchemaConstants.APACHE_ONE_ALIAS_AT_OID, true ) );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertNotNull( partition.getSubAliasIndex() );
+        try
+        {
+            partition.addIndex( new BdbJeIndex<Long>( ApacheSchemaConstants.APACHE_SUB_ALIAS_AT_OID, true ) );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertNotNull( partition.getSuffixDn() );
+        try
+        {
+            partition.setSuffixDn( EXAMPLE_COM );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        Iterator<String> systemIndices = partition.getSystemIndices();
+
+        for ( int i = 0; i < 8; i++ )
+        {
+            assertTrue( systemIndices.hasNext() );
+            assertNotNull( systemIndices.next() );
+        }
+
+        assertFalse( systemIndices.hasNext() );
+        assertNotNull( partition.getSystemIndex( APACHE_ALIAS_AT ) );
+
+        try
+        {
+            partition.getSystemIndex( SN_AT );
+            fail();
+        }
+        catch ( IndexNotFoundException e )
+        {
+        }
+        try
+        {
+            partition.getSystemIndex( DC_AT );
+            fail();
+        }
+        catch ( IndexNotFoundException e )
+        {
+        }
+
+        assertNotNull( partition.getSuffixDn() );
+
+        Iterator<String> userIndices = partition.getUserIndices();
+        int count = 0;
+
+        while ( userIndices.hasNext() )
+        {
+            userIndices.next();
+            count++;
+        }
+
+        assertEquals( 2, count );
+        assertFalse( partition.hasUserIndexOn( DC_AT ) );
+        assertTrue( partition.hasUserIndexOn( OU_AT ) );
+        assertTrue( partition.hasSystemIndexOn( APACHE_ALIAS_AT ) );
+        userIndices = partition.getUserIndices();
+        assertTrue( userIndices.hasNext() );
+        assertNotNull( userIndices.next() );
+        assertTrue( userIndices.hasNext() );
+        assertNotNull( userIndices.next() );
+        assertFalse( userIndices.hasNext() );
+        assertNotNull( partition.getUserIndex( OU_AT ) );
+
+        try
+        {
+            partition.getUserIndex( SN_AT );
+            fail();
+        }
+        catch ( IndexNotFoundException e )
+        {
+        }
+        try
+        {
+            partition.getUserIndex( DC_AT );
+            fail();
+        }
+        catch ( IndexNotFoundException e )
+        {
+        }
+
+        assertNotNull( partition.getPartitionPath() );
+        try
+        {
+            partition.setPartitionPath( new File( "." ).toURI() );
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+        }
+
+        assertTrue( partition.isInitialized() );
+        assertFalse( partition.isSyncOnWrite() );
+
+        partition.sync();
+    }
+
+
+    @Test
+    public void testFreshStore() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "o=Good Times Co." );
+        assertEquals( Strings.getUUID( 1L ), partition.getEntryId( partitionTxn, dn ) );
+        assertEquals( 11, partition.count( partitionTxn ) );
+        assertEquals( "o=Good Times Co.", partition.getEntryDn( partitionTxn, Strings.getUUID( 1L ) ).getName() );
+        assertEquals( dn.getName(), partition.getEntryDn( partitionTxn, Strings.getUUID( 1L ) ).getName() );
+        assertEquals( dn.getName(), partition.getEntryDn( partitionTxn, Strings.getUUID( 1L ) ).getName() );
+
+        // note that the suffix entry returns 0 for it's parent which does not exist
+        assertEquals( Strings.getUUID( 0L ),
+            partition.getParentId( partitionTxn, partition.getEntryId( partitionTxn, dn ) ) );
+        assertNull( partition.getParentId( partitionTxn, Strings.getUUID( 0L ) ) );
+
+        // should NOW be allowed
+        partition.delete( partitionTxn, Strings.getUUID( 1L ) );
+    }
+
+
+    /*
+    @Test
+    public void testEntryOperations() throws Exception
+    {
+        assertEquals( 3, store.getChildCount( Strings.getUUID( 1L ) ) );
+    
+        Cursor<IndexEntry<String, String>> cursor = store.list( Strings.getUUID( 1L ) );
+        assertNotNull( cursor );
+        cursor.beforeFirst();
+        assertTrue( cursor.next() );
+        assertEquals( Strings.getUUID( 3L ), cursor.get().getId() );
+        assertTrue( cursor.next() );
+        assertEquals( Strings.getUUID( 4L ), cursor.get().getId() );
+        assertTrue( cursor.next() );
+        assertEquals( Strings.getUUID( 2L ), cursor.get().getId() );
+        assertFalse( cursor.next() );
+    
+        cursor.close();
+    
+        assertEquals( 3, store.getChildCount( Strings.getUUID( 1L ) ) );
+    
+        store.delete( Strings.getUUID( 2L ) );
+        assertEquals( 2, store.getChildCount( Strings.getUUID( 1L ) ) );
+        assertEquals( 10, store.count() );
+    
+        // add an alias and delete to test dropAliasIndices method
+        Dn dn = new Dn( schemaManager, "commonName=Jack Daniels,ou=Apache,ou=Board of Directors,o=Good Times Co." );
+        Entry entry = new DefaultEntry( schemaManager, dn,
+            "objectClass: top",
+            "objectClass: alias",
+            "objectClass: extensibleObject",
+            "ou: Apache",
+            "commonName: Jack Daniels",
+            "aliasedObjectName: cn=Jack Daniels,ou=Engineering,o=Good Times Co.",
+            "entryCSN", new CsnFactory( 1 ).newInstance().toString(),
+            "entryUUID", Strings.getUUID( 12L ).toString() );
+    
+        AddOperationContext addContext = new AddOperationContext( null, entry );
+        addContext.setPartition( store2 );
+        PartitionTxn partitionTxn = null;
+        
+        try
+        {
+            partitionTxn = store2.beginWriteTransaction();
+            addContext.setTransaction( partitionTxn );
+        
+            store2.add( addContext );
+            partitionTxn.commit();
+        }
+        catch ( Exception e )
+        {
+            partitionTxn.abort();
+        }
+    
+        store.delete( Strings.getUUID( 12L ) ); // drops the alias indices
+    }
+    */
+
+    @Test(expected = LdapNoSuchObjectException.class)
+    public void testAddWithoutParentId() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=Marting King,ou=Not Present,o=Good Times Co." );
+        Entry entry = new DefaultEntry( schemaManager, dn,
+            "objectClass: top",
+            "objectClass: person",
+            "objectClass: organizationalPerson",
+            "ou: Not Present",
+            "cn: Martin King" );
+
+        AddOperationContext addContext = new AddOperationContext( null, entry );
+        addContext.setPartition( partition );
+        PartitionTxn partitionTxn = null;
+
+        try
+        {
+            partitionTxn = partition.beginWriteTransaction();
+            addContext.setTransaction( partitionTxn );
+
+            partition.add( addContext );
+            partitionTxn.commit();
+        }
+        catch ( Exception e )
+        {
+            partitionTxn.abort();
+            throw e;
+        }
+    }
+
+
+    @Test(expected = LdapSchemaViolationException.class)
+    public void testAddWithoutObjectClass() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=Martin King,ou=Sales,o=Good Times Co." );
+        Entry entry = new DefaultEntry( schemaManager, dn,
+            "ou: Sales",
+            "cn: Martin King" );
+        AddOperationContext addContext = new AddOperationContext( null, entry );
+        addContext.setPartition( partition );
+        PartitionTxn partitionTxn = null;
+
+        try
+        {
+            partitionTxn = partition.beginWriteTransaction();
+            addContext.setTransaction( partitionTxn );
+
+            partition.add( addContext );
+            partitionTxn.commit();
+        }
+        catch ( Exception e )
+        {
+            partitionTxn.abort();
+            throw e;
+        }
+    }
+
+
+    @Test
+    public void testModifyAddOUAttrib() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=JOhnny WAlkeR,ou=Sales,o=Good Times Co." );
+
+        Attribute attrib = new DefaultAttribute( SchemaConstants.OU_AT, OU_AT );
+        attrib.add( "Engineering" );
+
+        Modification add = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, attrib );
+
+        partition.modify( partitionTxn, dn, add );
+    }
+
+
+    @Test
+    public void testRename() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=Private Ryan,ou=Engineering,o=Good Times Co." );
+        Entry entry = new DefaultEntry( schemaManager, dn,
+            "objectClass: top",
+            "objectClass: person",
+            "objectClass: organizationalPerson",
+            "ou: Engineering",
+            "cn: Private Ryan",
+            "entryCSN", new CsnFactory( 1 ).newInstance().toString(),
+            "entryUUID", UUID.randomUUID().toString() );
+
+        AddOperationContext addContext = new AddOperationContext( null, entry );
+        addContext.setPartition( partition );
+        PartitionTxn partitionTxn = null;
+
+        try
+        {
+            partitionTxn = partition.beginWriteTransaction();
+            addContext.setTransaction( partitionTxn );
+
+            partition.add( addContext );
+            partitionTxn.commit();
+        }
+        catch ( Exception e )
+        {
+            partitionTxn.abort();
+        }
+
+        Rdn rdn = new Rdn( schemaManager, "sn=James" );
+
+        partitionTxn = partition.beginWriteTransaction();
+        partition.rename( partitionTxn, dn, rdn, true, null );
+        partitionTxn.commit();
+
+        dn = new Dn( schemaManager, "sn=James,ou=Engineering,o=Good Times Co." );
+        LookupOperationContext lookupContext = new LookupOperationContext( session, dn );
+        lookupContext.setPartition( partition );
+
+        PartitionTxn partitionTxn2 = partition.beginReadTransaction();
+        {
+            lookupContext.setTransaction( partitionTxn2 );
+
+            Entry renamed = partition.lookup( lookupContext );
+            assertNotNull( renamed );
+            assertEquals( "James", renamed.getDn().getRdn().getValue() );
+        }
+        partitionTxn2.abort();
+    }
+
+
+    @Test
+    public void testRenameEscaped() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=Private Ryan,ou=Engineering,o=Good Times Co." );
+        Entry entry = new DefaultEntry( schemaManager, dn,
+            "objectClass: top",
+            "objectClass: person",
+            "objectClass: organizationalPerson",
+            "ou: Engineering",
+            "cn: Private Ryan",
+            "entryCSN", new CsnFactory( 1 ).newInstance().toString(),
+            "entryUUID", UUID.randomUUID().toString() );
+
+        AddOperationContext addContext = new AddOperationContext( null, entry );
+        addContext.setPartition( partition );
+        PartitionTxn partitionTxn = null;
+
+        try
+        {
+            partitionTxn = partition.beginWriteTransaction();
+            addContext.setTransaction( partitionTxn );
+
+            partition.add( addContext );
+            partitionTxn.commit();
+        }
+        catch ( Exception e )
+        {
+            partitionTxn.abort();
+        }
+
+        Rdn rdn = new Rdn( schemaManager, "sn=Ja\\+es" );
+
+        partitionTxn = partition.beginWriteTransaction();
+        partition.rename( partitionTxn, dn, rdn, true, null );
+
+        Dn dn2 = new Dn( schemaManager, "sn=Ja\\+es,ou=Engineering,o=Good Times Co." );
+        String id = partition.getEntryId( partitionTxn, dn2 );
+        assertNotNull( id );
+        Entry entry2 = partition.fetch( partitionTxn, id, dn2 );
+        assertEquals( "Ja+es", entry2.get( "sn" ).getString() );
+        partitionTxn.commit();
+    }
+
+
+    @Test
+    public void testMove() throws Exception
+    {
+        Dn childDn = new Dn( schemaManager, "cn=Private Ryan,ou=Engineering,o=Good Times Co." );
+        Entry childEntry = new DefaultEntry( schemaManager, childDn,
+            "objectClass: top",
+            "objectClass: person",
+            "objectClass: organizationalPerson",
+            "ou", "Engineering",
+            "cn", "Private Ryan",
+            "entryCSN", new CsnFactory( 1 ).newInstance().toString(),
+            "entryUUID", UUID.randomUUID().toString() );
+
+        AddOperationContext addContext = new AddOperationContext( null, childEntry );
+        addContext.setPartition( partition );
+        PartitionTxn partitionTxn = null;
+
+        try
+        {
+            partitionTxn = partition.beginWriteTransaction();
+            addContext.setTransaction( partitionTxn );
+
+            partition.add( addContext );
+            partitionTxn.commit();
+        }
+        catch ( Exception e )
+        {
+            partitionTxn.abort();
+        }
+
+        Dn parentDn = new Dn( schemaManager, "ou=Sales,o=Good Times Co." );
+
+        Rdn rdn = new Rdn( schemaManager, "cn=Ryan" );
+
+        // The cn=Ryan RDN that will be added. The cn=Private Ryan RDN will be removed
+        Map<String, List<ModDnAva>> modDnAvas = new HashMap<>();
+
+        List<ModDnAva> modAvas = new ArrayList<>();
+        modAvas.add( new ModDnAva( ModDnAva.ModDnType.ADD, rdn.getAva() ) );
+        modAvas.add( new ModDnAva( ModDnAva.ModDnType.DELETE, childDn.getRdn().getAva() ) );
+        modDnAvas.put( SchemaConstants.CN_AT_OID, modAvas );
+
+        partitionTxn = partition.beginWriteTransaction();
+        partition.moveAndRename( partitionTxn, childDn, parentDn, rdn, modDnAvas, childEntry );
+
+        // to drop the alias indices
+        childDn = new Dn( schemaManager, "commonName=Jim Bean,ou=Apache,ou=Board of Directors,o=Good Times Co." );
+
+        parentDn = new Dn( schemaManager, "ou=Engineering,o=Good Times Co." );
+
+        assertEquals( 3, partition.getSubAliasIndex().count( partitionTxn ) );
+
+        Dn newDn = parentDn.add( childDn.getRdn() );
+
+        partition.move( partitionTxn, childDn, parentDn, newDn, null );
+
+        assertEquals( 3, partition.getSubAliasIndex().count( partitionTxn ) );
+        partitionTxn.commit();
+    }
+
+
+    @Test
+    public void testModifyAdd() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=JOhnny WAlkeR,ou=Sales,o=Good Times Co." );
+
+        Attribute attrib = new DefaultAttribute( "sn", SN_AT );
+
+        String attribVal = "Walker";
+        attrib.add( attribVal );
+
+        Modification add = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, attrib );
+
+        Entry lookedup = partition.fetch( partitionTxn, partition.getEntryId( partitionTxn, dn ), dn );
+
+        partition.modify( partitionTxn, dn, add );
+        assertTrue( lookedup.get( "sn" ).contains( attribVal ) );
+    }
+
+
+    @Test
+    public void testModifyReplace() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=JOhnny WAlkeR,ou=Sales,o=Good Times Co." );
+
+        Attribute attrib = new DefaultAttribute( SchemaConstants.SN_AT, SN_AT );
+
+        String attribVal = "Johnny";
+        attrib.add( attribVal );
+
+        Modification add = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrib );
+
+        Entry lookedup = partition.fetch( partitionTxn, partition.getEntryId( partitionTxn, dn ), dn );
+
+        assertEquals( "WAlkeR", lookedup.get( "sn" ).get().getValue() ); // before replacing
+
+        lookedup = partition.modify( partitionTxn, dn, add );
+        assertEquals( attribVal, lookedup.get( "sn" ).get().getValue() );
+
+        // testing the store.modify( dn, mod, entry ) API
+        Modification replace = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, SN_AT, "JWalker" );
+
+        lookedup = partition.modify( partitionTxn, dn, replace );
+        assertEquals( "JWalker", lookedup.get( "sn" ).get().getValue() );
+        assertEquals( 1, lookedup.get( "sn" ).size() );
+    }
+
+
+    @Test
+    public void testModifyRemove() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=JOhnny WAlkeR,ou=Sales,o=Good Times Co." );
+
+        Attribute attrib = new DefaultAttribute( SchemaConstants.SN_AT, SN_AT );
+
+        Modification add = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attrib );
+
+        Entry lookedup = partition.fetch( partitionTxn, partition.getEntryId( partitionTxn, dn ), dn );
+
+        assertNotNull( lookedup.get( "sn" ).get() );
+
+        lookedup = partition.modify( partitionTxn, dn, add );
+        assertNull( lookedup.get( "sn" ) );
+
+        // add an entry for the sake of testing the remove operation
+        add = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, SN_AT, "JWalker" );
+        lookedup = partition.modify( partitionTxn, dn, add );
+        assertNotNull( lookedup.get( "sn" ) );
+
+        Modification remove = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, SN_AT );
+        lookedup = partition.modify( partitionTxn, dn, remove );
+        assertNull( lookedup.get( "sn" ) );
+    }
+
+
+    @Test
+    public void testModifyReplaceNonExistingIndexAttribute() throws Exception
+    {
+        Dn dn = new Dn( schemaManager, "cn=Tim B,ou=Sales,o=Good Times Co." );
+        Entry entry = new DefaultEntry( schemaManager, dn,
+            "objectClass: top",
+            "objectClass: person",
+            "objectClass: organizationalPerson",
+            "cn", "Tim B",
+            "entryCSN", new CsnFactory( 1 ).newInstance().toString(),
+            "entryUUID", UUID.randomUUID().toString() );
+
+        AddOperationContext addContext = new AddOperationContext( null, entry );
+        addContext.setPartition( partition );
+        PartitionTxn partitionTxn = null;
+
+        try
+        {
+            partitionTxn = partition.beginWriteTransaction();
+            addContext.setTransaction( partitionTxn );
+
+            partition.add( addContext );
+            partitionTxn.commit();
+        }
+        catch ( Exception e )
+        {
+            partitionTxn.abort();
+        }
+
+        partitionTxn = partition.beginWriteTransaction();
+        Attribute attrib = new DefaultAttribute( SchemaConstants.OU_AT, OU_AT );
+
+        String attribVal = "Marketing";
+        attrib.add( attribVal );
+
+        Modification add = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrib );
+
+        Entry lookedup = partition.fetch( partitionTxn, partition.getEntryId( partitionTxn, dn ), dn );
+
+        assertNull( lookedup.get( "ou" ) ); // before replacing
+
+        lookedup = partition.modify( partitionTxn, dn, add );
+        assertEquals( attribVal, lookedup.get( "ou" ).get().getValue() );
+        partitionTxn.commit();
+    }
+}
diff --git a/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableNoDuplicatesTest.java b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableNoDuplicatesTest.java
new file mode 100644
index 0000000..4c25527
--- /dev/null
+++ b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableNoDuplicatesTest.java
@@ -0,0 +1,384 @@
+/*
+ * 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.server.core.partition.impl.btree.je;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.SerializableComparator;
+import org.apache.directory.api.ldap.schema.extractor.SchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
+import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
+import org.apache.directory.api.util.FileUtils;
+import org.apache.directory.api.util.exception.Exceptions;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.StringSerializer;
+import org.apache.directory.server.xdbm.Table;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Database;
+
+
+/**
+ * Document me!
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeTableNoDuplicatesTest
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJeTableNoDuplicatesTest.class );
+    private static final String TEST_OUTPUT_PATH = "test.output.path";
+
+    Table<String, String> table;
+    private static SchemaManager schemaManager;
+    private PartitionTxn partitionTxn;
+    static BdbJePartitionEnviroment env;
+
+    static File dbDir;
+
+
+    @BeforeClass
+    public static void init() throws Exception
+    {
+        String workingDirectory = System.getProperty( "workingDirectory" );
+
+        if ( workingDirectory == null )
+        {
+            String path = BdbJeTableNoDuplicatesTest.class.getResource( "" ).getPath();
+            int targetPos = path.indexOf( "target" );
+            workingDirectory = path.substring( 0, targetPos + 6 );
+        }
+
+        File schemaRepository = new File( workingDirectory, "schema" );
+        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor( new File( workingDirectory ) );
+        extractor.extractOrCopy( true );
+        LdifSchemaLoader loader = new LdifSchemaLoader( schemaRepository );
+        schemaManager = new DefaultSchemaManager( loader );
+
+        boolean loaded = schemaManager.loadAllEnabled();
+
+        if ( !loaded )
+        {
+            fail( "Schema load failed : " + Exceptions.printErrors( schemaManager.getErrors() ) );
+        }
+
+        dbDir = new File( workingDirectory + "/" + BdbJeTableNoDuplicatesTest.class.getSimpleName() );
+        if ( dbDir.exists() )
+        {
+            FileUtils.deleteDirectory( dbDir );
+        }
+
+        dbDir.mkdirs();
+        env = new BdbJePartitionEnviroment( schemaManager, dbDir );
+    }
+
+
+    @Before
+    public void createTable() throws Exception
+    {
+        destroyTable();
+
+        SerializableComparator<String> comparator = new SerializableComparator<String>(
+            SchemaConstants.INTEGER_ORDERING_MATCH_MR_OID );
+        StringSerializer serializer = new StringSerializer();
+        comparator.setSchemaManager( schemaManager );
+        Database db = env.createDb( "test", false );
+        table = new BdbJeTable<String, String>( db, schemaManager, comparator, serializer, serializer );
+        LOG.debug( "Created new table and populated it with data" );
+
+        renewTxn();
+    }
+
+
+    private void renewTxn()
+    {
+        if ( partitionTxn != null )
+        {
+            try
+            {
+                partitionTxn.commit();
+            }
+            catch ( IOException e )
+            {
+                throw new RuntimeException( e );
+            }
+        }
+        partitionTxn = new JeTransaction( env.createTxn() );
+    }
+
+
+    @After
+    public void destroyTable() throws Exception
+    {
+        if ( table != null )
+        {
+            table.close( partitionTxn );
+            env.deleteDb( table.getName() );
+            table = null;
+        }
+    }
+
+
+    @Test
+    public void testCloseReopen() throws Exception
+    {
+        table.put( partitionTxn, "1", "2" );
+        table.close( partitionTxn );
+        SerializableComparator<String> comparator = new SerializableComparator<String>(
+            SchemaConstants.INTEGER_ORDERING_MATCH_MR_OID );
+        comparator.setSchemaManager( schemaManager );
+        Database db = env.createDb( "test", false );
+        StringSerializer serializer = new StringSerializer();
+        table = new BdbJeTable<String, String>( db, schemaManager, comparator, serializer, serializer );
+        assertEquals( "2", table.get( partitionTxn, "1" ) );
+    }
+
+
+    @Test
+    public void testConfigMethods() throws Exception
+    {
+        assertFalse( table.isDupsEnabled() );
+        assertEquals( "test", table.getName() );
+        assertNotNull( table.getKeyComparator() );
+    }
+
+
+    @Test
+    public void testWhenEmpty() throws Exception
+    {
+        // Test the count methods
+        assertEquals( 0, table.count( partitionTxn ) );
+        assertEquals( 0, table.count( partitionTxn, "1" ) );
+
+        // Test get method
+        assertNull( table.get( partitionTxn, "0" ) );
+
+        // Test remove methods
+        table.remove( partitionTxn, "1" );
+        assertNull( table.get( partitionTxn, "1" ) );
+
+        // Test has operations
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1" ) );
+        assertFalse( table.has( partitionTxn, "1", "0" ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "1" ) );
+
+        try
+        {
+            assertFalse( table.hasGreaterOrEqual( partitionTxn, "1", "0" ) );
+            fail( "Should never get here." );
+        }
+        catch ( UnsupportedOperationException e )
+        {
+        }
+
+        try
+        {
+            assertFalse( table.hasLessOrEqual( partitionTxn, "1", "0" ) );
+            fail( "Should never get here." );
+        }
+        catch ( UnsupportedOperationException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testLoadData() throws Exception
+    {
+        // add some data to it
+        for ( int i = 0; i < 10; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, istr, istr );
+        }
+
+        assertEquals( 10, table.count( partitionTxn ) );
+        assertEquals( 1, table.count( partitionTxn, "0" ) );
+
+        /*
+         * If counts are exact then we can test for exact values.  Again this 
+         * is not a critical function but one used for optimization so worst 
+         * case guesses are allowed.
+         */
+
+        assertEquals( 10, table.lessThanCount( partitionTxn, "5" ) );
+        assertEquals( 10, table.greaterThanCount( partitionTxn, "5" ) );
+    }
+
+
+    /**
+     * Let's test keys with a null or lack of any values.
+     * @throws Exception on error
+     */
+    @Test
+    public void testNullOrEmptyKeyValue() throws Exception
+    {
+        assertEquals( 0, table.count( partitionTxn ) );
+
+        try
+        {
+            table.put( partitionTxn, "1", null );
+            fail( "should never get here due to IllegalArgumentException" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            table.put( partitionTxn, null, "2" );
+            fail( "should never get here due to IllegalArgumentException" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        assertEquals( 0, table.count( partitionTxn ) );
+        assertEquals( null, table.get( partitionTxn, "1" ) );
+
+        // Let's add the key with a valid value and remove just the value
+        assertEquals( 0, table.count( partitionTxn, "1" ) );
+        table.remove( partitionTxn, "1" );
+        assertEquals( 0, table.count( partitionTxn, "1" ) );
+        table.put( partitionTxn, "1", "1" );
+        assertEquals( 1, table.count( partitionTxn, "1" ) );
+        table.remove( partitionTxn, "1", "1" );
+        assertEquals( 0, table.count( partitionTxn, "1" ) );
+        assertNull( table.get( partitionTxn, "1" ) );
+        assertFalse( table.has( partitionTxn, "1" ) );
+    }
+
+
+    @Test
+    public void testRemove() throws Exception
+    {
+        table.put( partitionTxn, "1", "1" );
+        table.remove( partitionTxn, "1" );
+        assertNull( table.get( partitionTxn, "1" ) );
+
+        table.put( partitionTxn, "10", "10" );
+
+        table.remove( partitionTxn, "10", "11" );
+        assertFalse( table.has( partitionTxn, "10", "11" ) );
+
+        //        assertNull( table.remove( null ) );
+        //        assertNull( table.remove( null, null ) );
+    }
+
+
+    @Test
+    public void testPut() throws Exception
+    {
+        final int SIZE = 15;
+
+        for ( int i = 0; i < SIZE; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, istr, istr );
+        }
+
+        assertEquals( SIZE, table.count( partitionTxn ) );
+        table.put( partitionTxn, "0", "0" );
+        assertTrue( table.has( partitionTxn, "0", "0" ) );
+    }
+
+
+    @Test
+    public void testHas() throws Exception
+    {
+        assertFalse( table.has( partitionTxn, "1" ) );
+        final int SIZE = 15;
+
+        for ( int i = 0; i < SIZE; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, istr, istr );
+        }
+
+        assertEquals( SIZE, table.count( partitionTxn ) );
+
+        assertFalse( table.has( partitionTxn, "-1" ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "-1" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "-1" ) );
+
+        assertTrue( table.has( partitionTxn, "0" ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "0" ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "0" ) );
+
+        assertTrue( table.has( partitionTxn, Integer.toString( SIZE - 1 ) ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, Integer.toString( SIZE - 1 ) ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, Integer.toString( SIZE - 1 ) ) );
+
+        assertFalse( table.has( partitionTxn, Integer.toString( SIZE ) ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, Integer.toString( SIZE ) ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, Integer.toString( SIZE ) ) );
+        table.remove( partitionTxn, "10" );
+        table.remove( partitionTxn, "11" );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "11" ) );
+
+        try
+        {
+            assertFalse( table.hasGreaterOrEqual( partitionTxn, "1", "1" ) );
+            fail( "Should never get here." );
+        }
+        catch ( UnsupportedOperationException e )
+        {
+        }
+
+        try
+        {
+            assertFalse( table.hasLessOrEqual( partitionTxn, "1", "1" ) );
+            fail( "Should never get here." );
+        }
+        catch ( UnsupportedOperationException e )
+        {
+        }
+
+        try
+        {
+            assertTrue( table.hasLessOrEqual( partitionTxn, "1", "2" ) );
+            fail( "Should never get here since no dups tables " +
+                "freak when they cannot find a value comparator" );
+        }
+        catch ( UnsupportedOperationException e )
+        {
+            assertNotNull( e );
+        }
+    }
+}
diff --git a/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableWithDuplicatesTest.java b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableWithDuplicatesTest.java
new file mode 100644
index 0000000..1eaf42f
--- /dev/null
+++ b/bdbje-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/je/BdbJeTableWithDuplicatesTest.java
@@ -0,0 +1,611 @@
+/*
+ * 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.server.core.partition.impl.btree.je;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.api.ldap.model.cursor.Cursor;
+import org.apache.directory.api.ldap.model.cursor.Tuple;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.SerializableComparator;
+import org.apache.directory.api.ldap.schema.extractor.SchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
+import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
+import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
+import org.apache.directory.api.util.FileUtils;
+import org.apache.directory.api.util.exception.Exceptions;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.partition.impl.btree.je.serializers.StringSerializer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.je.Database;
+
+
+/**
+ * Tests BdbJeTable operations with duplicates.  Does not test Cursor capabilities.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJeTableWithDuplicatesTest
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BdbJeTableWithDuplicatesTest.class );
+    private static final int SIZE = 15;
+    private static final int SIZE2 = 30;
+
+    private static final String SIZE_MINUS_ONE_STR = "14";
+    private static final String SIZE_STR = "15";
+    private static final String SIZE_PLUS_ONE_STR = "16";
+
+    private static final String SIZE2_MINUS_ONE_STR = "29";
+    private static final String SIZE2_STR = "30";
+    private static final String SIZE2_PLUS_ONE_STR = "31";
+
+    BdbJeTable<String, String> table;
+    static File dbDir;
+    private static SchemaManager schemaManager;
+    private PartitionTxn partitionTxn;
+    static BdbJePartitionEnviroment env;
+
+
+    @BeforeClass
+    public static void init() throws Exception
+    {
+        String workingDirectory = System.getProperty( "workingDirectory" );
+
+        if ( workingDirectory == null )
+        {
+            String path = BdbJeTableNoDuplicatesTest.class.getResource( "" ).getPath();
+            int targetPos = path.indexOf( "target" );
+            workingDirectory = path.substring( 0, targetPos + 6 );
+        }
+
+        File schemaRepository = new File( workingDirectory, "schema" );
+        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor( new File( workingDirectory ) );
+        extractor.extractOrCopy( true );
+        LdifSchemaLoader loader = new LdifSchemaLoader( schemaRepository );
+        schemaManager = new DefaultSchemaManager( loader );
+
+        boolean loaded = schemaManager.loadAllEnabled();
+
+        if ( !loaded )
+        {
+            fail( "Schema load failed : " + Exceptions.printErrors( schemaManager.getErrors() ) );
+        }
+
+        dbDir = new File( workingDirectory + "/" + BdbJeTableWithDuplicatesTest.class.getSimpleName() );
+        if ( dbDir.exists() )
+        {
+            FileUtils.deleteDirectory( dbDir );
+        }
+
+        dbDir.mkdirs();
+        env = new BdbJePartitionEnviroment( schemaManager, new File( workingDirectory ) );
+    }
+
+
+    @Before
+    public void createTable() throws Exception
+    {
+        destroyTable();
+        LOG.debug( "Created new table and populated it with data" );
+        table = _createTable();
+        renewTxn();
+    }
+
+
+    private void renewTxn()
+    {
+        if ( partitionTxn != null )
+        {
+            try
+            {
+                partitionTxn.commit();
+            }
+            catch ( IOException e )
+            {
+                throw new RuntimeException( e );
+            }
+        }
+        partitionTxn = new JeTransaction( env.createTxn() );
+    }
+
+
+    private BdbJeTable<String, String> _createTable()
+    {
+        SerializableComparator<String> comparator = new SerializableComparator<String>(
+            SchemaConstants.INTEGER_ORDERING_MATCH_MR_OID );
+        StringSerializer serializer = new StringSerializer();
+        comparator.setSchemaManager( schemaManager );
+        Database db = env.createDb( "test", true );
+        return new BdbJeTable<String, String>( db, schemaManager, comparator, comparator, serializer, serializer );
+    }
+
+
+    @After
+    public void destroyTable() throws Exception
+    {
+        if ( table != null )
+        {
+            table.close( partitionTxn );
+            env.deleteDb( table.getName() );
+        }
+
+        table = null;
+    }
+
+
+    @Test
+    public void testCountOneArg() throws Exception
+    {
+        assertEquals( 0, table.count( partitionTxn, "3" ) );
+        assertEquals( 0, table.count( partitionTxn, null ) );
+    }
+
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullKeyComparator() throws Exception
+    {
+        assertNotNull( table.getKeyComparator() );
+
+        SerializableComparator<String> comparator = new SerializableComparator<String>(
+            SchemaConstants.INTEGER_ORDERING_MATCH_MR_OID );
+        comparator.setSchemaManager( schemaManager );
+
+        Database db = env.createDb( "testNullKeyComparator", true );
+        new BdbJeTable<String, String>( db, schemaManager, null, comparator, null, new StringSerializer() );
+    }
+
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullValueComparator() throws Exception
+    {
+        assertNotNull( table.getValueComparator() );
+
+        SerializableComparator<String> comparator = new SerializableComparator<String>(
+            SchemaConstants.INTEGER_ORDERING_MATCH_MR_OID );
+        comparator.setSchemaManager( schemaManager );
+
+        Database db = env.createDb( "testNullValueComparator", true );
+        new BdbJeTable<String, String>( db, schemaManager, null, comparator, null, new StringSerializer() );
+    }
+
+
+    @Test
+    public void testCloseReopen() throws Exception
+    {
+        table.put( partitionTxn, "1", "2" );
+        assertEquals( "2", table.get( partitionTxn, "1" ) );
+        table.close( partitionTxn );
+        table = _createTable();
+        assertEquals( "2", table.get( partitionTxn, "1" ) );
+    }
+
+
+    @Test
+    public void testConfigMethods() throws Exception
+    {
+        assertTrue( table.isDupsEnabled() );
+        assertEquals( "test", table.getName() );
+        assertNotNull( table.getKeyComparator() );
+    }
+
+
+    @Test
+    public void testWhenEmpty() throws Exception
+    {
+        // Test the count methods
+        assertEquals( 0, table.count( partitionTxn ) );
+        assertEquals( 0, table.count( partitionTxn, "1" ) );
+
+        // Test get method
+        assertNull( table.get( partitionTxn, "0" ) );
+        assertNull( table.get( partitionTxn, null ) );
+
+        // Test remove methods
+        table.remove( partitionTxn, "1" );
+        assertFalse( table.has( partitionTxn, "1" ) );
+
+        // Test has operations
+        assertFalse( table.has( partitionTxn, "1" ) );
+        assertFalse( table.has( partitionTxn, "1", "0" ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "1" ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1", "0" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "1", "0" ) );
+    }
+
+
+    @Test
+    public void testPut() throws Exception
+    {
+        final int SIZE = 15;
+
+        for ( int i = 0; i < SIZE; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, istr, istr );
+        }
+
+        assertEquals( SIZE, table.count( partitionTxn ) );
+        table.put( partitionTxn, "0", "0" );
+        assertTrue( table.has( partitionTxn, "0", "0" ) );
+
+        // add some duplicates
+        for ( int i = 0; i < SIZE * 2; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, SIZE2_STR, istr );
+        }
+
+        assertEquals( SIZE * 3, table.count( partitionTxn ) );
+
+        table.put( partitionTxn, "0", "0" );
+        assertTrue( table.has( partitionTxn, "0", "0" ) );
+
+        table.put( partitionTxn, SIZE2_STR, "0" );
+        assertTrue( table.has( partitionTxn, SIZE2_STR, "0" ) );
+    }
+
+
+    @Test
+    public void testHas() throws Exception
+    {
+        assertFalse( table.has( partitionTxn, "1" ) );
+
+        for ( int i = 0; i < SIZE * 2; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, "1", istr );
+        }
+
+        assertEquals( SIZE2, table.count( partitionTxn ) );
+
+        assertTrue( table.has( partitionTxn, "1" ) );
+        assertTrue( table.has( partitionTxn, "1", "0" ) );
+        assertFalse( table.has( partitionTxn, "1", SIZE2_STR ) );
+
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "1", "0" ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "1", "0" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "1", "-1" ) );
+
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "1", SIZE2_MINUS_ONE_STR ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "1", SIZE2_MINUS_ONE_STR ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "1", SIZE2_MINUS_ONE_STR ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "1", SIZE2_STR ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1", SIZE2_STR ) );
+        assertFalse( table.has( partitionTxn, "1", SIZE2_STR ) );
+
+        // let's go over the this limit now and ask the same questions
+        table.put( partitionTxn, "1", SIZE2_STR );
+
+        assertTrue( table.has( partitionTxn, "1" ) );
+        assertTrue( table.has( partitionTxn, "1", "0" ) );
+        assertTrue( table.has( partitionTxn, "1", SIZE2_STR ) );
+        assertFalse( table.has( partitionTxn, null, null ) );
+
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "1", "0" ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "1", "0" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "1", "-1" ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, null, null ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, null, null ) );
+
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "1", SIZE2_STR ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "1", SIZE2_STR ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "1", SIZE2_STR ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "1", SIZE2_STR ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1", SIZE2_PLUS_ONE_STR ) );
+        assertFalse( table.has( partitionTxn, "1", SIZE2_PLUS_ONE_STR ) );
+
+        // now do not add duplicates and check has( partitionTxn, key, boolean )
+        for ( int i = 0; i < SIZE; i++ )
+        {
+            // note we are not adding duplicates not put( partitionTxn, 1, i )
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, istr, istr );
+        }
+
+        assertFalse( table.has( partitionTxn, "-1" ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "-1" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "-1" ) );
+
+        assertTrue( table.has( partitionTxn, "0" ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, "0" ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, "0" ) );
+
+        assertTrue( table.has( partitionTxn, SIZE_MINUS_ONE_STR ) );
+        assertTrue( table.hasGreaterOrEqual( partitionTxn, SIZE_MINUS_ONE_STR ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, SIZE_MINUS_ONE_STR ) );
+
+        assertFalse( table.has( partitionTxn, SIZE_STR ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, SIZE_STR ) );
+        assertTrue( table.hasLessOrEqual( partitionTxn, SIZE_STR ) );
+
+        for ( int i = 0; i < SIZE; i++ )
+        {
+            if ( i == 1 ) // don't delete the node which had multiple values
+            {
+                continue;
+            }
+
+            String istr = Integer.toString( i );
+            table.remove( partitionTxn, istr, istr );
+        }
+
+        // delete all values of the duplicate key one by one
+        for ( int i = 0; i < SIZE * 2 + 1; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.remove( partitionTxn, "1", istr );
+        }
+
+        Cursor<Tuple<String, String>> cursor = table.cursor( partitionTxn );
+
+        cursor.beforeFirst();
+
+        while ( cursor.next() )
+        {
+            //System.out.println( cursor.get() );
+        }
+
+        cursor.close();
+
+        assertFalse( table.hasLessOrEqual( partitionTxn, "1" ) );
+        assertFalse( table.hasLessOrEqual( partitionTxn, "1", "10" ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1" ) );
+        assertFalse( table.hasGreaterOrEqual( partitionTxn, "1", "0" ) );
+
+        table.put( partitionTxn, "1", "0" );
+
+    }
+
+
+    @Test
+    public void testRemove() throws Exception
+    {
+        assertEquals( 0, table.count( partitionTxn ) );
+
+        table.put( partitionTxn, "1", "1" );
+        table.put( partitionTxn, "1", "2" );
+        assertEquals( 2, table.count( partitionTxn ) );
+        table.remove( partitionTxn, "1" );
+        assertFalse( table.has( partitionTxn, "1" ) );
+        assertEquals( 0, table.count( partitionTxn ) );
+
+        table.put( partitionTxn, "10", "10" );
+        assertEquals( 1, table.count( partitionTxn ) );
+        table.remove( partitionTxn, "10", "11" );
+        assertFalse( table.has( partitionTxn, "10", "11" ) );
+        assertEquals( 1, table.count( partitionTxn ) );
+        table.remove( partitionTxn, "10", "10" );
+        assertFalse( table.has( partitionTxn, "10", "10" ) );
+        assertEquals( 0, table.count( partitionTxn ) );
+
+        // add duplicates
+        for ( int i = 0; i < SIZE * 2; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, "0", istr );
+        }
+
+        assertEquals( SIZE * 2, table.count( partitionTxn ) );
+        table.remove( partitionTxn, "0", "100" );
+        assertFalse( table.has( partitionTxn, "0", "100" ) );
+        assertEquals( SIZE * 2, table.count( partitionTxn ) );
+
+        table.remove( partitionTxn, "0" );
+        assertNull( table.get( partitionTxn, "0" ) );
+    }
+
+
+    @Test
+    public void testLoadData() throws Exception
+    {
+        // add some data to it
+        for ( int i = 0; i < SIZE; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, istr, istr );
+        }
+
+        assertEquals( 15, table.count( partitionTxn ) );
+        assertEquals( 1, table.count( partitionTxn, "0" ) );
+
+        /*
+         * If counts are exact then we can test for exact values.  Again this 
+         * is not a critical function but one used for optimization so worst 
+         * case guesses are allowed.
+         */
+
+        assertEquals( 10, table.lessThanCount( partitionTxn, "5" ) );
+        assertEquals( 10, table.greaterThanCount( partitionTxn, "5" ) );
+    }
+
+
+    @Test
+    public void testDuplicateLimit() throws Exception
+    {
+        for ( int i = 0; i < SIZE; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, "1", istr );
+        }
+        assertEquals( SIZE, table.count( partitionTxn ) );
+        assertEquals( SIZE, table.count( partitionTxn, "1" ) );
+
+        // this switches to B+Trees from AvlTree
+        table.put( partitionTxn, "1", SIZE_STR );
+        assertEquals( SIZE + 1, table.count( partitionTxn ) );
+        assertEquals( SIZE + 1, table.count( partitionTxn, "1" ) );
+
+        // go one more over still a B+Tree
+        table.put( partitionTxn, "1", SIZE_PLUS_ONE_STR );
+        assertEquals( SIZE + 2, table.count( partitionTxn ) );
+        assertEquals( SIZE + 2, table.count( partitionTxn, "1" ) );
+        assertEquals( "0", table.get( partitionTxn, "1" ) );
+
+        // now start removing and see what happens 
+        table.remove( partitionTxn, "1", SIZE_PLUS_ONE_STR );
+        assertFalse( table.has( partitionTxn, "1", SIZE_PLUS_ONE_STR ) );
+        assertTrue( table.has( partitionTxn, "1", SIZE_STR ) );
+        assertEquals( SIZE + 1, table.count( partitionTxn ) );
+        assertEquals( SIZE + 1, table.count( partitionTxn, "1" ) );
+
+        // this switches to AvlTree from B+Trees
+        table.remove( partitionTxn, "1", SIZE_STR );
+        assertFalse( table.has( partitionTxn, "1", SIZE_STR ) );
+        assertEquals( SIZE, table.count( partitionTxn ) );
+        assertEquals( SIZE, table.count( partitionTxn, "1" ) );
+        assertEquals( "0", table.get( partitionTxn, "1" ) );
+
+        for ( int i = SIZE - 1; i >= 0; i-- )
+        {
+            String istr = Integer.toString( i );
+            table.remove( partitionTxn, "1", istr );
+        }
+
+        assertEquals( 0, table.count( partitionTxn ) );
+
+        for ( int i = 0; i < SIZE - 1; i++ )
+        {
+            String istr = Integer.toString( i );
+            table.put( partitionTxn, "1", istr );
+        }
+
+        // this switches back to using B+Trees from AvlTree
+        table.put( partitionTxn, "1", SIZE_STR );
+        table.put( partitionTxn, "1", SIZE_PLUS_ONE_STR );
+
+        assertEquals( SIZE + 1, table.count( partitionTxn ) );
+        table.remove( partitionTxn, "1" );
+        assertEquals( 0, table.count( partitionTxn ) );
+    }
+
+
+    /**
+     * Let's test keys with a null or lack of any values.
+     * @throws Exception on error
+     */
+    @Test
+    public void testNullOrEmptyKeyValueAfterDuplicateLimit() throws Exception
+    {
+        testDuplicateLimit();
+        assertEquals( 0, table.count( partitionTxn ) );
+
+        try
+        {
+            table.put( partitionTxn, "1", null );
+            fail( "should never get here due to IllegalArgumentException" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            table.put( partitionTxn, null, "1" );
+            fail( "should never get here due to IllegalArgumentException" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        assertEquals( 0, table.count( partitionTxn ) );
+        assertEquals( null, table.get( partitionTxn, "1" ) );
+
+        // Let's add the key with two valid values and remove all values
+        table.remove( partitionTxn, "1" );
+        table.put( partitionTxn, "1", "1" );
+        table.put( partitionTxn, "1", "2" );
+        assertEquals( 2, table.count( partitionTxn, "1" ) );
+        table.remove( partitionTxn, "1", "1" );
+        assertEquals( 1, table.count( partitionTxn, "1" ) );
+        assertEquals( "2", table.get( partitionTxn, "1" ) );
+
+        table.remove( partitionTxn, "1", "2" );
+        assertNull( table.get( partitionTxn, "1" ) );
+        assertEquals( 0, table.count( partitionTxn, "1" ) );
+        assertFalse( table.has( partitionTxn, "1" ) );
+    }
+
+
+    /**
+     * Let's test keys with a null or lack of any values.
+     * @throws Exception on error
+     */
+    @Test
+    public void testNullOrEmptyKeyValue() throws Exception
+    {
+        assertEquals( 0, table.count( partitionTxn ) );
+
+        try
+        {
+            table.put( partitionTxn, "1", null );
+            fail( "should never get here due to IllegalArgumentException" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            table.put( partitionTxn, null, "2" );
+            fail( "should never get here due to IllegalArgumentException" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        assertEquals( 0, table.count( partitionTxn ) );
+        assertEquals( null, table.get( partitionTxn, "1" ) );
+
+        // Let's add the key with two valid values and remove all values
+        table.remove( partitionTxn, "1" );
+        table.put( partitionTxn, "1", "1" );
+        table.put( partitionTxn, "1", "2" );
+        assertEquals( 2, table.count( partitionTxn, "1" ) );
+        table.remove( partitionTxn, "1", "1" );
+        assertEquals( 1, table.count( partitionTxn, "1" ) );
+        assertEquals( "2", table.get( partitionTxn, "1" ) );
+
+        table.remove( partitionTxn, "1", "2" );
+        assertNull( table.get( partitionTxn, "1" ) );
+        assertEquals( 0, table.count( partitionTxn, "1" ) );
+        assertFalse( table.has( partitionTxn, "1" ) );
+    }
+}
diff --git a/bdbje-partition/src/test/resources/log4j.properties b/bdbje-partition/src/test/resources/log4j.properties
new file mode 100644
index 0000000..69203a4
--- /dev/null
+++ b/bdbje-partition/src/test/resources/log4j.properties
@@ -0,0 +1,22 @@
+#############################################################################
+#    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.
+#############################################################################
+log4j.rootCategory=WARN, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
+
diff --git a/core-annotations/src/main/java/org/apache/directory/server/core/factory/BdbJePartitionFactory.java b/core-annotations/src/main/java/org/apache/directory/server/core/factory/BdbJePartitionFactory.java
new file mode 100644
index 0000000..c952b61
--- /dev/null
+++ b/core-annotations/src/main/java/org/apache/directory/server/core/factory/BdbJePartitionFactory.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.core.factory;
+
+
+import java.io.File;
+import java.util.Set;
+
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.server.core.api.DnFactory;
+import org.apache.directory.server.core.api.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.je.BdbJeIndex;
+import org.apache.directory.server.core.partition.impl.btree.je.BdbJePartition;
+import org.apache.directory.server.xdbm.Index;
+
+
+/**
+ * A factory used to generate {@link BdbJePartition}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BdbJePartitionFactory implements PartitionFactory
+{
+
+    /**
+     * {@inheritDoc}
+     */
+    public BdbJePartition createPartition( SchemaManager schemaManager, DnFactory dnFactory, String id, String suffix,
+        int cacheSize,
+        File workingDirectory )
+        throws Exception
+    {
+        BdbJePartition partition = new BdbJePartition( schemaManager, dnFactory );
+        partition.setId( id );
+        partition.setSuffixDn( new Dn( suffix ) );
+        partition.setPartitionPath( workingDirectory.toURI() );
+
+        return partition;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addIndex( Partition partition, String attributeId, int cacheSize ) throws Exception
+    {
+        if ( !( partition instanceof BdbJePartition ) )
+        {
+            throw new IllegalArgumentException( "Partition must be a BdbJePartition" );
+        }
+
+        BdbJePartition bdbJePartition = ( BdbJePartition ) partition;
+
+        Set<Index<?, String>> indexedAttributes = bdbJePartition.getIndexedAttributes();
+
+        BdbJeIndex<?> idx = new BdbJeIndex<>( attributeId, false );
+        indexedAttributes.add( idx );
+        bdbJePartition.setIndexedAttributes( indexedAttributes );
+    }
+
+}


[directory-server] 01/06: added txn implementation classes

Posted by ka...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kayyagari pushed a commit to branch bdb-je-partition
in repository https://gitbox.apache.org/repos/asf/directory-server.git

commit 6504a6fc45a80436984d5eb7034ab970b2b944c8
Author: kayyagari <ka...@apache.org>
AuthorDate: Wed Sep 26 13:57:04 2018 +0530

    added txn implementation classes
---
 .../core/api/partition/PartitionReadTxn.java       | 39 +---------------------
 ...itionReadTxn.java => PartitionReadTxnImpl.java} | 31 ++++++-----------
 .../core/api/partition/PartitionWriteTxn.java      | 39 +---------------------
 ...tionReadTxn.java => PartitionWriteTxnImpl.java} | 28 +++++-----------
 4 files changed, 22 insertions(+), 115 deletions(-)

diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
index 62e7797..f5a0cd2 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
@@ -19,48 +19,11 @@
  */
 package org.apache.directory.server.core.api.partition;
 
-import java.io.IOException;
-
 /**
  * The Read Transaction interface
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class PartitionReadTxn implements PartitionTxn
+public interface PartitionReadTxn extends PartitionTxn
 {
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void commit() throws IOException
-    {
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void abort() throws IOException
-    {
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean isClosed()
-    {
-        return false;
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void close() throws IOException
-    {
-    }
 }
diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxnImpl.java
similarity index 82%
copy from core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
copy to core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxnImpl.java
index 62e7797..ede6cfd 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxnImpl.java
@@ -6,59 +6,50 @@
  *  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.server.core.api.partition;
 
+
 import java.io.IOException;
 
+
 /**
- * The Read Transaction interface
- * 
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory
+ *         Project</a>
  */
-public class PartitionReadTxn implements PartitionTxn
+public class PartitionReadTxnImpl implements PartitionReadTxn
 {
-    /**
-     * {@inheritDoc}
-     */
+
     @Override
     public void commit() throws IOException
     {
     }
 
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public void abort() throws IOException
     {
     }
 
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public boolean isClosed()
     {
         return false;
     }
 
-    
-    /**
-     * {@inheritDoc}
-     */
+
     @Override
     public void close() throws IOException
     {
diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxn.java b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxn.java
index 2c1f7f0..dec7f3b 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxn.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxn.java
@@ -19,48 +19,11 @@
  */
 package org.apache.directory.server.core.api.partition;
 
-import java.io.IOException;
-
 /**
  * The Write Transaction interface
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class PartitionWriteTxn implements PartitionTxn
+public interface PartitionWriteTxn extends PartitionTxn
 {
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void commit() throws IOException
-    {
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void abort() throws IOException
-    {
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean isClosed()
-    {
-        return false;
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void close() throws IOException
-    {
-    }
 }
diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxnImpl.java
similarity index 83%
copy from core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
copy to core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxnImpl.java
index 62e7797..67217d7 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxnImpl.java
@@ -6,59 +6,49 @@
  *  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.server.core.api.partition;
 
+
 import java.io.IOException;
 
+
 /**
- * The Read Transaction interface
- * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class PartitionReadTxn implements PartitionTxn
+public class PartitionWriteTxnImpl implements PartitionWriteTxn
 {
-    /**
-     * {@inheritDoc}
-     */
+
     @Override
     public void commit() throws IOException
     {
     }
 
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public void abort() throws IOException
     {
     }
 
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public boolean isClosed()
     {
         return false;
     }
 
-    
-    /**
-     * {@inheritDoc}
-     */
+
     @Override
     public void close() throws IOException
     {


[directory-server] 06/06: commit the open txns after using them

Posted by ka...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kayyagari pushed a commit to branch bdb-je-partition
in repository https://gitbox.apache.org/repos/asf/directory-server.git

commit e52b4730aa3984adef731b9c6fe99fe181110c26
Author: kayyagari <ka...@apache.org>
AuthorDate: Wed Sep 26 14:02:34 2018 +0530

    commit the open txns after using them
---
 .../directory/server/core/api/filtering/CursorList.java    | 14 ++++++++++++++
 .../core/shared/partition/DefaultPartitionNexus.java       |  6 ++++--
 .../org/apache/directory/server/core/authz/GroupCache.java |  5 ++++-
 .../org/apache/directory/server/core/authz/TupleCache.java |  5 ++++-
 4 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/filtering/CursorList.java b/core-api/src/main/java/org/apache/directory/server/core/api/filtering/CursorList.java
index 7affbcd..923fe26 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/filtering/CursorList.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/filtering/CursorList.java
@@ -19,6 +19,7 @@
 package org.apache.directory.server.core.api.filtering;
 
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -442,6 +443,19 @@ public class CursorList extends AbstractCursor<Entry> implements EntryFilteringC
                 LOG.warn( "Failed to close the cursor" );
             }
         }
+        try
+        {
+            // FIXME this is incorrect instead close the txn associated with each cursor
+            // the below code only commits the last searched partition's txn
+            // the fix is to modify the cursor impls to accept a flag and a txn handle
+            // and then decide whether the caller wants the txn to be closed at the time of
+            // closing the cursor
+            searchContext.getTransaction().commit();
+        }
+        catch ( IOException e )
+        {
+            //ignore
+        }
     }
 
 
diff --git a/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultPartitionNexus.java b/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultPartitionNexus.java
index 208cf56..077838d 100644
--- a/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultPartitionNexus.java
+++ b/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/DefaultPartitionNexus.java
@@ -81,8 +81,10 @@ import org.apache.directory.server.core.api.partition.AbstractPartition;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.PartitionNexus;
 import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionReadTxnImpl;
 import org.apache.directory.server.core.api.partition.PartitionTxn;
 import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxnImpl;
 import org.apache.directory.server.core.api.partition.Subordinates;
 import org.apache.directory.server.i18n.I18n;
 import org.slf4j.Logger;
@@ -1121,13 +1123,13 @@ public class DefaultPartitionNexus extends AbstractPartition implements Partitio
     @Override
     public PartitionReadTxn beginReadTransaction()
     {
-        return new PartitionReadTxn();
+        return new PartitionReadTxnImpl();
     }
 
 
     @Override
     public PartitionWriteTxn beginWriteTransaction()
     {
-        return new PartitionWriteTxn();
+        return new PartitionWriteTxnImpl();
     }
 }
diff --git a/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/GroupCache.java b/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/GroupCache.java
index 1564c72..b764879 100644
--- a/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/GroupCache.java
+++ b/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/GroupCache.java
@@ -52,6 +52,7 @@ import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
 import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.PartitionNexus;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
 import org.apache.directory.server.i18n.I18n;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -156,11 +157,13 @@ public class GroupCache
                 baseDn, filter, ctls );
             searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS );
             searchOperationContext.setPartition( partition );
-            searchOperationContext.setTransaction( partition.beginReadTransaction() );
+            PartitionTxn tx = partition.beginReadTransaction();
+            searchOperationContext.setTransaction( tx );
             EntryFilteringCursor results = nexus.search( searchOperationContext );
 
             try
             {
+                tx.commit();
                 while ( results.next() )
                 {
                     Entry result = results.get();
diff --git a/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/TupleCache.java b/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/TupleCache.java
index d38cc49..e3132981 100644
--- a/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/TupleCache.java
+++ b/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/TupleCache.java
@@ -57,6 +57,7 @@ import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
 import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.PartitionNexus;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
 import org.apache.directory.server.i18n.I18n;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -133,12 +134,14 @@ public class TupleCache
                 baseDn, filter, ctls );
             searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
             searchOperationContext.setPartition( partition );
-            searchOperationContext.setTransaction( partition.beginReadTransaction() );
+            PartitionTxn tx = partition.beginReadTransaction();
+            searchOperationContext.setTransaction( tx );
 
             EntryFilteringCursor results = nexus.search( searchOperationContext );
 
             try
             {
+                tx.commit();
                 while ( results.next() )
                 {
                     Entry result = results.get();


[directory-server] 05/06: use txn impl classes

Posted by ka...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kayyagari pushed a commit to branch bdb-je-partition
in repository https://gitbox.apache.org/repos/asf/directory-server.git

commit 70d81b1b1be7b0d3481a5b44e28b6dca69c16f19
Author: kayyagari <ka...@apache.org>
AuthorDate: Wed Sep 26 14:02:03 2018 +0530

    use txn impl classes
---
 .../apache/directory/server/core/api/schema/SchemaPartition.java    | 6 ++++--
 .../directory/server/core/shared/partition/RootPartition.java       | 6 ++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/schema/SchemaPartition.java b/core-api/src/main/java/org/apache/directory/server/core/api/schema/SchemaPartition.java
index a0cf514..2125a75 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/schema/SchemaPartition.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/schema/SchemaPartition.java
@@ -61,8 +61,10 @@ import org.apache.directory.server.core.api.interceptor.context.UnbindOperationC
 import org.apache.directory.server.core.api.partition.AbstractPartition;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionReadTxnImpl;
 import org.apache.directory.server.core.api.partition.PartitionTxn;
 import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxnImpl;
 import org.apache.directory.server.core.api.partition.Subordinates;
 import org.apache.directory.server.core.api.schema.registries.synchronizers.RegistrySynchronizerAdaptor;
 import org.apache.directory.server.i18n.I18n;
@@ -590,13 +592,13 @@ public final class SchemaPartition extends AbstractPartition
     @Override
     public PartitionReadTxn beginReadTransaction()
     {
-        return new PartitionReadTxn();
+        return new PartitionReadTxnImpl();
     }
 
 
     @Override
     public PartitionWriteTxn beginWriteTransaction()
     {
-        return new PartitionWriteTxn();
+        return new PartitionWriteTxnImpl();
     }
 }
diff --git a/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/RootPartition.java b/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/RootPartition.java
index e2a1ae5..1fa8a4c 100644
--- a/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/RootPartition.java
+++ b/core-shared/src/main/java/org/apache/directory/server/core/shared/partition/RootPartition.java
@@ -38,8 +38,10 @@ import org.apache.directory.server.core.api.interceptor.context.SearchOperationC
 import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext;
 import org.apache.directory.server.core.api.partition.AbstractPartition;
 import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionReadTxnImpl;
 import org.apache.directory.server.core.api.partition.PartitionTxn;
 import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxnImpl;
 import org.apache.directory.server.core.api.partition.Subordinates;
 
 /**
@@ -59,14 +61,14 @@ public class RootPartition extends AbstractPartition
     @Override
     public PartitionReadTxn beginReadTransaction()
     {
-        return new PartitionReadTxn();
+        return new PartitionReadTxnImpl();
     }
 
     
     @Override
     public PartitionWriteTxn beginWriteTransaction()
     {
-        return new PartitionWriteTxn();
+        return new PartitionWriteTxnImpl();
     }
 
     @Override


[directory-server] 03/06: used new txn impl classes

Posted by ka...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kayyagari pushed a commit to branch bdb-je-partition
in repository https://gitbox.apache.org/repos/asf/directory-server.git

commit e19f50432b6b07ee86ff427fcdc956ce5f273d3f
Author: kayyagari <ka...@apache.org>
AuthorDate: Wed Sep 26 14:00:02 2018 +0530

    used new txn impl classes
---
 .../core/partition/impl/btree/jdbm/JdbmPartition.java | 19 ++++++++++---------
 .../impl/btree/jdbm/JdbmPartitionWriteTxn.java        |  2 +-
 .../impl/btree/mavibot/MavibotPartition.java          |  6 ++++--
 .../org/apache/directory/MockPartitionReadTxn.java    |  2 +-
 .../server/core/partition/impl/avl/AvlPartition.java  |  6 ++++--
 .../directory/server/xdbm/MockPartitionReadTxn.java   |  2 +-
 .../directory/server/xdbm/MockPartitionWriteTxn.java  |  2 +-
 .../org/apache/directory/server/xdbm/StoreUtils.java  |  8 ++++++--
 8 files changed, 28 insertions(+), 19 deletions(-)

diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartition.java b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartition.java
index f0e9418..c0a20a8 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartition.java
+++ b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartition.java
@@ -29,14 +29,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 
-import jdbm.RecordManager;
-import jdbm.helper.MRU;
-import jdbm.recman.BaseRecordManager;
-import jdbm.recman.CacheRecordManager;
-import jdbm.recman.TransactionManager;
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
 import org.apache.directory.api.ldap.model.csn.CsnFactory;
 import org.apache.directory.api.ldap.model.cursor.Cursor;
@@ -67,6 +59,7 @@ import org.apache.directory.server.core.api.interceptor.context.OperationContext
 import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionReadTxnImpl;
 import org.apache.directory.server.core.api.partition.PartitionTxn;
 import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
 import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
@@ -81,6 +74,14 @@ import org.apache.directory.server.xdbm.search.impl.NoOpOptimizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import jdbm.RecordManager;
+import jdbm.helper.MRU;
+import jdbm.recman.BaseRecordManager;
+import jdbm.recman.CacheRecordManager;
+import jdbm.recman.TransactionManager;
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
 
 /**
  * A {@link Partition} that stores entries in
@@ -1016,7 +1017,7 @@ public class JdbmPartition extends AbstractBTreePartition
     @Override
     public PartitionReadTxn beginReadTransaction()
     {
-        return new PartitionReadTxn();
+        return new PartitionReadTxnImpl();
     }
 
 
diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartitionWriteTxn.java b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartitionWriteTxn.java
index c731e3c..1f4bc16 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartitionWriteTxn.java
+++ b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartitionWriteTxn.java
@@ -32,7 +32,7 @@ import jdbm.recman.CacheRecordManager;
  *  
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class JdbmPartitionWriteTxn extends PartitionWriteTxn
+public class JdbmPartitionWriteTxn implements PartitionWriteTxn
 {
     /** The associated record manager */
     private RecordManager recordManager;
diff --git a/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotPartition.java b/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotPartition.java
index 3240429..7f52b6e 100644
--- a/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotPartition.java
+++ b/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotPartition.java
@@ -56,8 +56,10 @@ import org.apache.directory.server.core.api.interceptor.context.OperationContext
 import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionReadTxnImpl;
 import org.apache.directory.server.core.api.partition.PartitionTxn;
 import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxnImpl;
 import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
 import org.apache.directory.server.i18n.I18n;
 import org.apache.directory.server.xdbm.Index;
@@ -549,13 +551,13 @@ public class MavibotPartition extends AbstractBTreePartition
     @Override
     public PartitionReadTxn beginReadTransaction()
     {
-        return new PartitionReadTxn();
+        return new PartitionReadTxnImpl();
     }
 
 
     @Override
     public PartitionWriteTxn beginWriteTransaction()
     {
-        return new PartitionWriteTxn();
+        return new PartitionWriteTxnImpl();
     }
 }
\ No newline at end of file
diff --git a/server-integ/src/test/java/org/apache/directory/MockPartitionReadTxn.java b/server-integ/src/test/java/org/apache/directory/MockPartitionReadTxn.java
index e4af47b..d5a78f1 100644
--- a/server-integ/src/test/java/org/apache/directory/MockPartitionReadTxn.java
+++ b/server-integ/src/test/java/org/apache/directory/MockPartitionReadTxn.java
@@ -28,7 +28,7 @@ import org.apache.directory.server.core.api.partition.PartitionReadTxn;
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class MockPartitionReadTxn extends PartitionReadTxn
+public class MockPartitionReadTxn implements PartitionReadTxn
 {
 
     @Override
diff --git a/xdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/avl/AvlPartition.java b/xdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/avl/AvlPartition.java
index aba0128..23b606d 100644
--- a/xdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/avl/AvlPartition.java
+++ b/xdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/avl/AvlPartition.java
@@ -29,7 +29,9 @@ import org.apache.directory.server.constants.ApacheSchemaConstants;
 import org.apache.directory.server.core.api.DnFactory;
 import org.apache.directory.server.core.api.partition.Partition;
 import org.apache.directory.server.core.api.partition.PartitionReadTxn;
+import org.apache.directory.server.core.api.partition.PartitionReadTxnImpl;
 import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxnImpl;
 import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
 import org.apache.directory.server.xdbm.Index;
 import org.apache.directory.server.xdbm.impl.avl.AvlIndex;
@@ -227,13 +229,13 @@ public class AvlPartition extends AbstractBTreePartition
     @Override
     public PartitionReadTxn beginReadTransaction()
     {
-        return new PartitionReadTxn();
+        return new PartitionReadTxnImpl();
     }
 
 
     @Override
     public PartitionWriteTxn beginWriteTransaction()
     {
-        return new PartitionWriteTxn();
+        return new PartitionWriteTxnImpl();
     }
 }
diff --git a/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionReadTxn.java b/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionReadTxn.java
index 3ad6866..f5f139c 100644
--- a/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionReadTxn.java
+++ b/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionReadTxn.java
@@ -28,7 +28,7 @@ import org.apache.directory.server.core.api.partition.PartitionReadTxn;
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class MockPartitionReadTxn extends PartitionReadTxn
+public class MockPartitionReadTxn implements PartitionReadTxn
 {
 
     @Override
diff --git a/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionWriteTxn.java b/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionWriteTxn.java
index 7378842..51cc794 100644
--- a/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionWriteTxn.java
+++ b/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/MockPartitionWriteTxn.java
@@ -28,7 +28,7 @@ import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class MockPartitionWriteTxn extends PartitionWriteTxn
+public class MockPartitionWriteTxn implements PartitionWriteTxn
 {
 
     @Override
diff --git a/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/StoreUtils.java b/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/StoreUtils.java
index 551cf1a..77e7c33 100644
--- a/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/StoreUtils.java
+++ b/xdbm-partition/src/test/java/org/apache/directory/server/xdbm/StoreUtils.java
@@ -30,6 +30,7 @@ import org.apache.directory.api.ldap.model.schema.SchemaManager;
 import org.apache.directory.api.util.Strings;
 import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
 import org.apache.directory.server.core.api.partition.Partition;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
 
 
 /**
@@ -293,7 +294,10 @@ public class StoreUtils
         entry.add( SchemaConstants.ENTRY_UUID_AT, Strings.getUUID( index ).toString() );
 
         AddOperationContext addContext = new AddOperationContext( null, entry );
-        addContext.setTransaction( new MockPartitionWriteTxn() );
-        ( ( Partition ) store ).add( addContext );
+        Partition p = ( Partition ) store;
+        PartitionWriteTxn tx = p.beginWriteTransaction();
+        addContext.setTransaction( tx );
+        p.add( addContext );
+        tx.commit();
     }
 }