You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2018/01/23 07:20:58 UTC
[directory-server] 03/05: Adding transaction to partitions
This is an automated email from the ASF dual-hosted git repository.
elecharny pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-server.git
commit be82b0ce2b78fa903a1818d27a351d07f2a977ce
Author: Emmanuel Lécharny <el...@symas.com>
AuthorDate: Wed Jan 10 07:05:52 2018 +0100
Adding transaction to partitions
---
.../server/core/api/LdapCoreSessionConnection.java | 2 +-
.../context/AbstractOperationContext.java | 22 +
.../server/core/api/partition/PartitionNexus.java | 11 +
.../core/api/partition/PartitionReadTxn.java | 6 +
.../server/core/api/partition/PartitionTxn.java | 30 +
.../core/api/partition/PartitionWriteTxn.java | 6 +
.../directory/server/core/jndi/ServerContext.java | 1 +
.../server/core/DefaultDirectoryService.java | 2 +
.../apache/directory/server/installers/config.ldif | 828 +++++++++++++++++++++
.../core/partition/impl/btree/jdbm/JdbmIndex.java | 4 +-
.../core/partition/impl/btree/jdbm/JdbmTable.java | 358 ++++-----
.../impl/btree/jdbm/KeyTupleBTreeCursor.java | 2 +-
.../btree/jdbm/JdbmTableWithDuplicatesTest.java | 9 -
.../partition/impl/btree/mavibot/MavibotTable.java | 159 ++--
mavibotv2-partition/pom.xml | 146 ++++
.../impl/btree/mavibot/AbstractMavibotTxn.java | 96 +++
.../partition/impl/btree/mavibot/DnSerializer.java | 206 +++++
.../impl/btree/mavibot/KeyTupleValueCursor.java | 239 ++----
.../impl/btree/mavibot/LdifTupleComparator.java | 57 ++
.../impl/btree/mavibot/LdifTupleReaderWriter.java | 162 ++++
.../impl/btree/mavibot/MavibotCursor.java | 292 ++++----
.../impl/btree/mavibot/MavibotDnIndex.java | 123 +++
.../impl/btree/mavibot/MavibotEntrySerializer.java | 382 ++++++++++
.../partition/impl/btree/mavibot/MavibotIndex.java | 261 +++----
.../impl/btree/mavibot/MavibotMasterTable.java | 58 ++
.../mavibot/MavibotParentIdAndRdnSerializer.java | 302 ++++++++
.../impl/btree/mavibot/MavibotPartition.java | 540 ++++++++++++++
.../impl/btree/mavibot/MavibotRdnIndex.java | 126 ++++
.../impl/btree/mavibot/MavibotReadTxn.java | 11 +
.../partition/impl/btree/mavibot/MavibotTable.java | 255 ++++---
.../partition/impl/btree/mavibot/MavibotTxn.java | 9 +
.../impl/btree/mavibot/MavibotWriteTxn.java | 18 +
.../impl/btree/mavibot/ValueTreeCursor.java | 155 ++++
pom.xml | 1 +
.../ldap/handlers/extended/PwdModifyHandler.java | 1 +
.../ldap/handlers/request/BindRequestHandler.java | 1 +
.../ldap/handlers/sasl/SimpleMechanismHandler.java | 1 +
.../ldap/handlers/sasl/ntlm/NtlmSaslServer.java | 1 +
.../ldap/handlers/sasl/plain/PlainSaslServer.java | 1 +
.../server/operations/modify/ModifyReferralIT.java | 14 +-
.../server/operations/search/PagedSearchApiIT.java | 7 +-
.../operations/search/PersistentSearchApiIT.java | 740 ++++++++++++++++++
.../directory/server/xdbm/AbstractTable.java | 36 +-
.../org/apache/directory/server/xdbm/Table.java | 95 ++-
.../directory/server/xdbm/impl/avl/AvlTable.java | 62 +-
45 files changed, 4877 insertions(+), 961 deletions(-)
diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/LdapCoreSessionConnection.java b/core-api/src/main/java/org/apache/directory/server/core/api/LdapCoreSessionConnection.java
index b2b6d12..74cf1db 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/LdapCoreSessionConnection.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/LdapCoreSessionConnection.java
@@ -1206,7 +1206,7 @@ public class LdapCoreSessionConnection extends AbstractLdapConnection
int newId = messageId.incrementAndGet();
- BindOperationContext bindContext = new BindOperationContext( null );
+ BindOperationContext bindContext = new BindOperationContext( directoryService, null );
bindContext.setCredentials( bindRequest.getCredentials() );
bindContext.setDn( bindRequest.getDn().apply( directoryService.getSchemaManager() ) );
diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/interceptor/context/AbstractOperationContext.java b/core-api/src/main/java/org/apache/directory/server/core/api/interceptor/context/AbstractOperationContext.java
index 8fffa95..4f04179 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/interceptor/context/AbstractOperationContext.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/interceptor/context/AbstractOperationContext.java
@@ -31,6 +31,7 @@ import org.apache.directory.api.ldap.model.message.Control;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.server.core.api.CoreSession;
import org.apache.directory.server.core.api.LdapPrincipal;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
/**
@@ -74,6 +75,9 @@ public abstract class AbstractOperationContext implements OperationContext
/** A flag used to tell if we should consider referrals as standard entries */
protected boolean throwReferral;
+
+ /** The transaction this operation is ran into */
+ protected PartitionTxn transaction;
/**
@@ -443,4 +447,22 @@ public abstract class AbstractOperationContext implements OperationContext
{
return !throwReferral;
}
+
+
+ /**
+ * @return the transaction
+ */
+ public PartitionTxn getTransaction()
+ {
+ return transaction;
+ }
+
+
+ /**
+ * @param transaction the transaction to set
+ */
+ public void setTransaction( PartitionTxn transaction )
+ {
+ this.transaction = transaction;
+ }
}
diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionNexus.java b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionNexus.java
index f648618..0534282 100644
--- a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionNexus.java
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionNexus.java
@@ -50,6 +50,17 @@ public interface PartitionNexus extends Partition
byte[] ADMIN_PASSWORD_BYTES = Strings.getBytesUtf8( ADMIN_PASSWORD_STRING );
+
+ /**
+ * Start a read transaction
+ */
+ PartitionReadTxn beginReadTransaction();
+
+
+ /**
+ * Start a write transaction
+ */
+ PartitionWriteTxn beginWriteTransaction();
/**
* Get's the RootDSE entry for the DSA.
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
new file mode 100644
index 0000000..d74c53f
--- /dev/null
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionReadTxn.java
@@ -0,0 +1,6 @@
+package org.apache.directory.server.core.api.partition;
+
+public interface PartitionReadTxn extends PartitionTxn
+{
+
+}
diff --git a/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionTxn.java b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionTxn.java
new file mode 100644
index 0000000..88841f9
--- /dev/null
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionTxn.java
@@ -0,0 +1,30 @@
+package org.apache.directory.server.core.api.partition;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public interface PartitionTxn extends Closeable
+{
+ /**
+ * Commit a write transaction. It will apply the changes on
+ * the database.Last, not least, a new version will be created.
+ * If called by a Read transaction, it will simply close it.
+ */
+ void commit() throws IOException;
+
+
+ /**
+ * Abort a transaction. If it's a {@link PartitionReadTxn}, it will unlink this transaction
+ * from the version it used. If it's a {@link PartitionWriteTxn}; it will drop all the pending
+ * changes. The latest version will remain the same.
+ */
+ void abort() throws IOException;
+
+
+ /**
+ * Tells if the transaction has been committed/aborted or not.
+ *
+ * @return <tt>true</tt> if the transaction has been completed.
+ */
+ boolean isClosed();
+}
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
new file mode 100644
index 0000000..effcd3d
--- /dev/null
+++ b/core-api/src/main/java/org/apache/directory/server/core/api/partition/PartitionWriteTxn.java
@@ -0,0 +1,6 @@
+package org.apache.directory.server.core.api.partition;
+
+public interface PartitionWriteTxn extends PartitionTxn
+{
+
+}
diff --git a/core-jndi/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java b/core-jndi/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java
index 5610114..6285de9 100644
--- a/core-jndi/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java
+++ b/core-jndi/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java
@@ -679,6 +679,7 @@ public abstract class ServerContext implements EventContext
{
// setup the op context and populate with request controls
BindOperationContext bindContext = new BindOperationContext( null );
+ bindContext.setTransaction( getDirectoryService().getPartitionNexus().beginReadTransaction() );
bindContext.setDn( bindDn );
bindContext.setCredentials( credentials );
bindContext.setSaslMechanism( saslMechanism );
diff --git a/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java b/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java
index dc4a7b8..5237313 100644
--- a/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java
+++ b/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java
@@ -994,6 +994,7 @@ public class DefaultDirectoryService implements DirectoryService
}
BindOperationContext bindContext = new BindOperationContext( null );
+ bindContext.setTransaction( partitionNexus.beginReadTransaction() );
bindContext.setCredentials( credentials );
bindContext.setDn( principalDn.apply( schemaManager ) );
bindContext.setInterceptors( getInterceptors( OperationEnum.BIND ) );
@@ -1020,6 +1021,7 @@ public class DefaultDirectoryService implements DirectoryService
}
BindOperationContext bindContext = new BindOperationContext( null );
+ bindContext.setTransaction( partitionNexus.beginReadTransaction() );
bindContext.setCredentials( credentials );
bindContext.setDn( principalDn.apply( schemaManager ) );
bindContext.setSaslMechanism( saslMechanism );
diff --git a/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/config.ldif b/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/config.ldif
new file mode 100644
index 0000000..8861253
--- /dev/null
+++ b/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/config.ldif
@@ -0,0 +1,828 @@
+version: 1
+dn: ou=config
+ou: config
+objectclass: top
+objectclass: organizationalUnit
+
+dn: ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-directoryService
+ads-directoryserviceid: default
+ads-dsreplicaid: 1
+ads-dssyncperiodmillis: 15000
+ads-dsPasswordHidden: FALSE
+ads-dsallowanonymousaccess: TRUE
+ads-dsaccesscontrolenabled: FALSE
+ads-dsdenormalizeopattrsenabled: FALSE
+ads-enabled: TRUE
+
+dn: ads-changeLogId=defaultChangeLog,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-changeLog
+ads-changeLogId: defaultChangeLog
+ads-changeLogExposed: FALSE
+ads-enabled: FALSE
+
+dn: ads-journalId=defaultJournal,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-journal
+ads-journalId: defaultJournal
+ads-journalFileName: Journal.txt
+ads-journalWorkingDir: /
+ads-journalRotation: 2
+ads-enabled: FALSE
+
+dn: ou=interceptors,ads-directoryServiceId=default,ou=config
+ou: interceptors
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-interceptorId=normalizationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 1
+ads-interceptorclassname: org.apache.directory.server.core.normalization.NormalizationInterceptor
+ads-interceptorid: normalizationInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+objectclass: ads-authenticationInterceptor
+ads-interceptororder: 2
+ads-interceptorclassname: org.apache.directory.server.core.authn.AuthenticationInterceptor
+ads-interceptorid: authenticationInterceptor
+ads-enabled: TRUE
+
+dn: ou=authenticators,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+ou: authenticators
+objectclass: top
+objectclass: organizationalUnit
+
+dn: ads-authenticatorid=anonymousauthenticator,ou=authenticators,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+ads-authenticatorid: anonymousauthenticator
+objectclass: top
+objectclass: ads-base
+objectClass: ads-authenticator
+objectClass: ads-authenticatorImpl
+ads-authenticatorClass: org.apache.directory.server.core.authn.AnonymousAuthenticator
+ads-baseDn:
+ads-enabled: TRUE
+
+dn: ads-authenticatorid=simpleauthenticator,ou=authenticators,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+ads-authenticatorid: simpleauthenticator
+objectclass: top
+objectclass: ads-base
+objectClass: ads-authenticator
+objectClass: ads-authenticatorImpl
+ads-authenticatorClass: org.apache.directory.server.core.authn.SimpleAuthenticator
+ads-baseDn:
+ads-enabled: TRUE
+
+dn: ads-authenticatorid=strongauthenticator,ou=authenticators,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+ads-authenticatorid: strongauthenticator
+objectclass: top
+objectclass: ads-base
+objectClass: ads-authenticator
+objectClass: ads-authenticatorImpl
+ads-authenticatorClass: org.apache.directory.server.core.authn.StrongAuthenticator
+ads-baseDn:
+ads-enabled: TRUE
+
+dn: ads-authenticatorid=delegatingauthenticator,ou=authenticators,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+ads-authenticatorid: delegatingauthenticator
+objectclass: top
+objectclass: ads-base
+objectClass: ads-authenticator
+objectClass: ads-authenticatorImpl
+ads-authenticatorClass: org.apache.directory.server.core.authn.DelegatingAuthenticator
+ads-baseDn:
+ads-enabled: FALSE
+
+dn: ou=passwordPolicies,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectClass: organizationalUnit
+objectClass: top
+ou: passwordPolicies
+
+dn: ads-pwdId=default,ou=passwordPolicies,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectClass: top
+objectClass: ads-base
+objectClass: ads-passwordPolicy
+ads-pwdId: default
+ads-pwdSafeModify: FALSE
+ads-pwdMaxAge: 0
+ads-pwdFailureCountInterval: 30
+ads-pwdAttribute: userPassword
+ads-pwdMaxFailure: 5
+ads-pwdLockout: TRUE
+ads-pwdMustChange: FALSE
+ads-pwdLockoutDuration: 0
+ads-pwdMinLength: 5
+ads-pwdInHistory: 5
+ads-pwdExpireWarning: 600
+ads-pwdMinAge: 0
+ads-pwdAllowUserChange: TRUE
+ads-pwdGraceAuthNLimit: 5
+ads-pwdCheckQuality: 1
+ads-pwdMaxLength: 0
+ads-pwdGraceExpire: 0
+ads-pwdMinDelay: 0
+ads-pwdMaxDelay: 0
+ads-pwdMaxIdle: 0
+ads-pwdValidator: org.apache.directory.server.core.api.authn.ppolicy.DefaultPasswordValidator
+ads-enabled: TRUE
+
+dn: ads-interceptorId=referralInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 3
+ads-interceptorclassname: org.apache.directory.server.core.referral.ReferralInterceptor
+ads-interceptorid: referralInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=aciAuthorizationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 4
+ads-interceptorclassname: org.apache.directory.server.core.authz.AciAuthorizationInterceptor
+ads-interceptorid: aciAuthorizationInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=defaultAuthorizationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 5
+ads-interceptorclassname: org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor
+ads-interceptorid: defaultAuthorizationInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=administrativePointInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 6
+ads-interceptorclassname: org.apache.directory.server.core.admin.AdministrativePointInterceptor
+ads-interceptorid: administrativePointInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=exceptionInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 7
+ads-interceptorclassname: org.apache.directory.server.core.exception.ExceptionInterceptor
+ads-interceptorid: exceptionInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=keyDerivationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-enabled: FALSE
+ads-interceptororder: 8
+ads-interceptorclassname: org.apache.directory.server.core.kerberos.KeyDerivationInterceptor
+ads-interceptorid: keyDerivationInterceptor
+
+dn: ads-interceptorId=passwordHashingInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+objectclass: ads-hashInterceptor
+ads-enabled: TRUE
+ads-interceptororder: 9
+ads-interceptorclassname: org.apache.directory.server.core.hash.ConfigurableHashingInterceptor
+ads-interceptorid: passwordHashingInterceptor
+ads-hashAlgorithm: SSHA
+ads-hashAttribute: 2.5.4.35
+
+dn: ads-interceptorId=schemaInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 10
+ads-interceptorclassname: org.apache.directory.server.core.schema.SchemaInterceptor
+ads-interceptorid: schemaInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=operationalAttributeInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 11
+ads-interceptorclassname: org.apache.directory.server.core.operational.OperationalAttributeInterceptor
+ads-interceptorid: operationalAttributeInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=collectiveAttributeInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 12
+ads-interceptorclassname: org.apache.directory.server.core.collective.CollectiveAttributeInterceptor
+ads-interceptorid: collectiveAttributeInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=subentryInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 13
+ads-interceptorclassname: org.apache.directory.server.core.subtree.SubentryInterceptor
+ads-interceptorid: subentryInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=eventInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 14
+ads-interceptorclassname: org.apache.directory.server.core.event.EventInterceptor
+ads-interceptorid: eventInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=triggerInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 15
+ads-interceptorclassname: org.apache.directory.server.core.trigger.TriggerInterceptor
+ads-interceptorid: triggerInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=journalInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 16
+ads-interceptorclassname: org.apache.directory.server.core.journal.JournalInterceptor
+ads-interceptorid: journalInterceptor
+ads-enabled: TRUE
+
+dn: ads-interceptorId=numberInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-interceptor
+ads-interceptororder: 17
+ads-interceptorclassname: org.apache.directory.server.core.number.NumberIncrementingInterceptor
+ads-interceptorId: numberInterceptor
+ads-enabled: FALSE
+
+dn: ou=partitions,ads-directoryServiceId=default,ou=config
+ou: partitions
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectClass: ads-base
+objectclass: ads-partition
+objectclass: ads-jdbmPartition
+ads-partitionSuffix: ou=system
+ads-jdbmpartitionoptimizerenabled: TRUE
+ads-partitioncachesize: 10000
+ads-partitionsynconwrite: TRUE
+ads-partitionid: system
+ads-enabled: TRUE
+
+dn: ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ou: indexes
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-indexAttributeId=apacheRdn,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheRdn
+ads-indexHasReverse: TRUE
+ads-indexcachesize: 1000
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apachePresence,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apachePresence
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apacheOneAlias,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheOneAlias
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apacheSubAlias,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheSubAlias
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apacheAlias,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheAlias
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=objectClass,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: objectClass
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=entryCSN,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: entryCSN
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=ou,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: ou
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=uid,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: uid
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=administrativeRole,ou=indexes,ads-partitionId=system,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: administrativeRole
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectClass: ads-base
+objectclass: ads-partition
+objectclass: ads-jdbmPartition
+ads-partitionSuffix: dc=example,dc=com
+ads-contextentry:: ZG46IGRjPWV4YW1wbGUsZGM9Y29tCmRjOiBleGFtcGxlCm9iamVjdGNsY
+ XNzOiBkb21haW4Kb2JqZWN0Y2xhc3M6IHRvcAoK
+ads-jdbmpartitionoptimizerenabled: TRUE
+ads-partitioncachesize: 10000
+ads-partitionsynconwrite: TRUE
+ads-partitionid: example
+ads-enabled: TRUE
+
+dn: ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ou: indexes
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-indexAttributeId=apacheRdn,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheRdn
+ads-indexHasReverse: TRUE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apachePresence,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apachePresence
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apacheOneAlias,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheOneAlias
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apacheSubAlias,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheSubAlias
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=apacheAlias,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: apacheAlias
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=dc,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: dc
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=krb5PrincipalName,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: krb5PrincipalName
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=objectClass,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: objectClass
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=entryCSN,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: entryCSN
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=ou,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: ou
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=uid,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: uid
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-indexAttributeId=administrativeRole,ou=indexes,ads-partitionId=example,ou=partitions,ads-directoryServiceId=default,ou=config
+ads-indexattributeid: administrativeRole
+ads-indexHasReverse: FALSE
+ads-indexcachesize: 100
+objectclass: ads-index
+objectclass: ads-jdbmIndex
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ou=servers,ads-directoryServiceId=default,ou=config
+ou: servers
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-serverId=changePasswordServer,ou=servers,ads-directoryServiceId=default,ou=config
+objectclass: ads-server
+objectclass: ads-changePasswordServer
+objectclass: ads-dsBasedServer
+objectclass: ads-base
+objectclass: top
+ads-serverid: changePasswordServer
+ads-enabled: FALSE
+
+dn: ou=transports,ads-serverId=changePasswordServer,ou=servers,ads-directoryServiceId=default,ou=config
+ou: transports
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-transportId=tcp,ou=transports,ads-serverId=changePasswordServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-systemport: 60464
+ads-transportnbthreads: 2
+ads-transportaddress: 0.0.0.0
+ads-transportid: tcp
+objectclass: ads-transport
+objectclass: ads-tcpTransport
+objectClass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-transportId=udp,ou=transports,ads-serverId=changePasswordServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-systemport: 60464
+ads-transportnbthreads: 2
+ads-transportaddress: 0.0.0.0
+ads-transportid: udp
+objectclass: ads-transport
+objectclass: ads-udpTransport
+objectClass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-serverId=httpServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-serverid: httpServer
+ads-enabled: FALSE
+objectclass: ads-server
+objectclass: ads-httpServer
+objectclass: ads-base
+objectclass: top
+
+dn: ou=transports,ads-serverId=httpServer,ou=servers,ads-directoryServiceId=default,ou=config
+ou: transports
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-transportid=http,ou=transports,ads-serverId=httpServer,ou=servers,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-transport
+objectclass: ads-tcpTransport
+ads-transportid: http
+ads-systemport: 8080
+ads-transportaddress: 0.0.0.0
+ads-enabled: TRUE
+
+dn: ads-transportid=https,ou=transports,ads-serverId=httpServer,ou=servers,ads-directoryServiceId=default,ou=config
+objectclass: top
+objectclass: ads-base
+objectclass: ads-transport
+objectclass: ads-tcpTransport
+ads-transportid: https
+ads-transportaddress: 0.0.0.0
+ads-systemport: 8443
+ads-enabled: TRUE
+
+dn: ou=httpWebApps,ads-serverId=httpServer,ou=servers,ads-directoryServiceId=default,ou=config
+objectclass: organizationalUnit
+objectclass: top
+ou: httpWebApps
+
+dn: ads-id=testapp,ou=httpWebApps,ads-serverId=httpServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-httpwarfile: /path/to/foo/war
+ads-httpappctxpath: /foo
+ads-id: testapp
+objectclass: ads-httpWebApp
+objectclass: ads-base
+objectclass: top
+ads-enabled: FALSE
+
+dn: ads-serverId=kerberosServer,ou=servers,ads-directoryServiceId=default,ou=config
+objectclass: ads-server
+objectclass: ads-kdcServer
+objectclass: ads-dsBasedServer
+objectclass: ads-base
+objectclass: top
+ads-serverid: kerberosServer
+ads-enabled: FALSE
+ads-krbAllowableClockSkew: 300000
+ads-krbBodyChecksumVerified: TRUE
+ads-krbEmptyAddressesAllowed: TRUE
+ads-krbEncryptionTypes: aes128-cts-hmac-sha1-96
+ads-krbEncryptionTypes: des3-cbc-sha1-kd
+ads-krbEncryptionTypes: des-cbc-md5
+ads-krbForwardableAllowed: TRUE
+ads-krbmaximumrenewablelifetime: 604800000
+ads-krbMaximumTicketLifetime: 86400000
+ads-krbPaEncTimestampRequired: TRUE
+ads-krbPostdatedAllowed: TRUE
+ads-krbPrimaryRealm: EXAMPLE.COM
+ads-krbProxiableAllowed: TRUE
+ads-krbRenewableAllowed: TRUE
+ads-searchBaseDN: ou=users,dc=example,dc=com
+
+dn: ou=transports,ads-serverId=kerberosServer,ou=servers,ads-directoryServiceId=default,ou=config
+ou: transports
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-transportid=tcp,ou=transports,ads-serverId=kerberosServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-systemport: 60088
+ads-transportnbthreads: 4
+ads-transportaddress: 0.0.0.0
+ads-transportid: tcp
+objectclass: ads-transport
+objectClass: ads-base
+objectclass: ads-tcpTransport
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-transportid=udp,ou=transports,ads-serverId=kerberosServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-systemport: 60088
+ads-transportnbthreads: 4
+ads-transportaddress: 0.0.0.0
+ads-transportid: udp
+objectclass: ads-transport
+objectclass: ads-udpTransport
+objectClass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+objectclass: ads-server
+objectclass: ads-ldapServer
+objectclass: ads-dsBasedServer
+objectclass: ads-base
+objectclass: top
+ads-serverId: ldapServer
+ads-confidentialityRequired: FALSE
+ads-maxSizeLimit: 1000
+ads-maxTimeLimit: 15000
+ads-maxpdusize: 2000000
+ads-saslHost: ldap.example.com
+ads-saslPrincipal: ldap/ldap.example.com@EXAMPLE.COM
+ads-saslRealms: example.com
+ads-saslRealms: apache.org
+ads-searchBaseDN: ou=users,ou=system
+ads-replEnabled: true
+ads-replPingerSleep: 5
+ads-enabled: TRUE
+
+dn: ou=replConsumers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+objectClass: organizationalUnit
+objectClass: top
+ou: replConsumers
+
+dn: ou=transports,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ou: transports
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-transportid=ldap,ou=transports,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-systemport: 10389
+ads-transportnbthreads: 8
+ads-transportaddress: 0.0.0.0
+ads-transportid: ldap
+objectclass: ads-transport
+objectclass: ads-tcpTransport
+objectClass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-transportid=ldaps,ou=transports,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-systemport: 10636
+ads-transportenablessl: TRUE
+ads-transportaddress: 0.0.0.0
+ads-transportid: ldaps
+objectclass: ads-transport
+objectclass: ads-tcpTransport
+objectClass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ou=extendedOpHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ou: extendedOpHandlers
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-extendedOpId=gracefulShutdownHandler,ou=extendedOpHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-extendedOpId: gracefulShutdownHandler
+ads-extendedOpHandlerclass: org.apache.directory.server.ldap.handlers.extended.GracefulShutdownHandler
+objectclass: ads-extendedOpHandler
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-extendedOpId=starttlshandler,ou=extendedOpHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-extendedOpId: starttlshandler
+ads-extendedOpHandlerclass: org.apache.directory.server.ldap.handlers.extended.StartTlsHandler
+objectclass: ads-extendedOpHandler
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-extendedOpId=storedprochandler,ou=extendedOpHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-enabled: FALSE
+ads-extendedOpId: storedprochandler
+ads-extendedOpHandlerclass: org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler
+objectclass: ads-extendedOpHandler
+objectclass: ads-base
+objectclass: top
+
+dn: ads-extendedOpId=pwdModifyHandler,ou=extendedOpHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-extendedOpId: pwdModifyHandler
+ads-extendedOpHandlerclass: org.apache.directory.server.ldap.handlers.extended.PwdModifyHandler
+objectclass: ads-extendedOpHandler
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ads-extendedOpId=whoAmIHandler,ou=extendedOpHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-extendedOpId: whoAmIHandler
+ads-extendedOpHandlerclass: org.apache.directory.server.ldap.handlers.extended.WhoAmIHandler
+objectclass: ads-extendedOpHandler
+objectclass: ads-base
+objectclass: top
+ads-enabled: TRUE
+
+dn: ou=saslMechHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ou: saslMechHandlers
+objectclass: organizationalUnit
+objectclass: top
+
+dn: ads-saslMechName=CRAM-MD5,ou=saslMechHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-saslMechClassName: org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler
+objectclass: ads-saslMechHandler
+objectclass: ads-base
+objectclass: top
+ads-saslMechName: CRAM-MD5
+ads-enabled: TRUE
+
+dn: ads-saslMechName=DIGEST-MD5,ou=saslMechHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-saslMechClassName: org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler
+objectclass: ads-saslMechHandler
+objectclass: ads-base
+objectclass: top
+ads-saslMechName: DIGEST-MD5
+ads-enabled: TRUE
+
+dn: ads-saslMechName=GSS-SPNEGO,ou=saslMechHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-saslMechClassName: org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler
+objectclass: ads-saslMechHandler
+objectclass: ads-base
+objectclass: top
+ads-saslMechName: GSS-SPNEGO
+ads-ntlmMechProvider: com.foo.Bar
+ads-enabled: TRUE
+
+dn: ads-saslMechName=GSSAPI,ou=saslMechHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-saslMechClassName: org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler
+objectclass: ads-saslMechHandler
+objectclass: ads-base
+objectclass: top
+ads-saslMechName: GSSAPI
+ads-enabled: TRUE
+
+dn: ads-saslMechName=NTLM,ou=saslMechHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-saslMechClassName: org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler
+objectclass: ads-saslMechHandler
+objectclass: ads-base
+objectclass: top
+ads-saslMechName: NTLM
+ads-ntlmMechProvider: com.foo.Bar
+ads-enabled: TRUE
+
+dn: ads-saslMechName=SIMPLE,ou=saslMechHandlers,ads-serverId=ldapServer,ou=servers,ads-directoryServiceId=default,ou=config
+ads-saslMechClassName: org.apache.directory.server.ldap.handlers.sasl.SimpleMechanismHandler
+objectclass: ads-saslMechHandler
+objectclass: ads-base
+objectclass: top
+ads-saslMechName: SIMPLE
+ads-enabled: TRUE
diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
index 90f84e4..b023652 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
+++ b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
@@ -60,9 +60,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
/** A logger for this class */
private static final Logger LOG = LoggerFactory.getLogger( JdbmIndex.class );
- /**
- * default duplicate limit before duplicate keys switch to using a btree for values
- */
+ /** default duplicate limit before duplicate keys switch to using a btree for values */
public static final int DEFAULT_DUPLICATE_LIMIT = 512;
/** the key used for the forward btree name */
diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTable.java b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTable.java
index 85554e1..df94782 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTable.java
+++ b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTable.java
@@ -33,6 +33,7 @@ import jdbm.recman.BaseRecordManager;
import jdbm.recman.CacheRecordManager;
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.SingletonCursor;
import org.apache.directory.api.ldap.model.exception.LdapException;
@@ -41,6 +42,8 @@ import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.api.ldap.model.schema.comparators.SerializableComparator;
import org.apache.directory.api.util.Strings;
import org.apache.directory.api.util.SynchronizedLRUMap;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
import org.apache.directory.server.core.avltree.ArrayMarshaller;
import org.apache.directory.server.core.avltree.ArrayTree;
import org.apache.directory.server.core.avltree.ArrayTreeCursor;
@@ -77,9 +80,6 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
/** a cache of duplicate BTrees */
private final Map<Long, BTree<K, V>> duplicateBtrees;
- /** A Key serializer */
- private final Serializer keySerializer;
-
/** A value serializer */
private final Serializer valueSerializer;
@@ -136,7 +136,6 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
this.numDupLimit = numDupLimit;
this.recMan = manager;
- this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.allowsDuplicates = true;
@@ -200,7 +199,6 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
this.numDupLimit = Integer.MAX_VALUE;
this.recMan = manager;
- this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.allowsDuplicates = false;
@@ -227,7 +225,7 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
}
else
{
- bt = new BTree<K, V>( recMan, keyComparator, keySerializer, valueSerializer );
+ bt = new BTree<>( recMan, keyComparator, keySerializer, valueSerializer );
recId = bt.getRecordId();
recMan.setNamedObject( name, recId );
recId = recMan.insert( 0 );
@@ -237,57 +235,13 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
// ------------------------------------------------------------------------
- // Simple Table Properties
- // ------------------------------------------------------------------------
-
- public Serializer getKeySerializer()
- {
- return keySerializer;
- }
-
-
- public Serializer getValueSerializer()
- {
- return valueSerializer;
- }
-
-
- /**
- * @see org.apache.directory.server.xdbm.Table#isDupsEnabled()
- */
- public boolean isDupsEnabled()
- {
- return allowsDuplicates;
- }
-
-
- // ------------------------------------------------------------------------
// Count Overloads
// ------------------------------------------------------------------------
/**
- * @see Table#greaterThanCount(Object)
- */
- public long greaterThanCount( K key ) throws IOException
- {
- // take a best guess
- return Math.min( count, 10L );
- }
-
-
- /**
- * @see Table#lessThanCount(Object)
- */
- public long lessThanCount( K key ) throws IOException
- {
- // take a best guess
- return Math.min( count, 10L );
- }
-
-
- /**
* @see org.apache.directory.server.xdbm.Table#count(java.lang.Object)
*/
- public long count( K key ) throws LdapException
+ @Override
+ public long count( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -328,8 +282,12 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
// get/has/put/remove Methods and Overloads
// ------------------------------------------------------------------------
+ /**
+ * {@inheritDoc}
+ */
@SuppressWarnings("unchecked")
- public V get( K key ) throws LdapException
+ @Override
+ public V get( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -375,9 +333,10 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
/**
- * @see Table#hasGreaterOrEqual(Object,Object)
+ * {@inheritDoc}
*/
- public boolean hasGreaterOrEqual( K key, V val ) throws LdapException
+ @Override
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -413,9 +372,10 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
/**
- * @see Table#hasLessOrEqual(Object,Object)
+ * {@inheritDoc}
*/
- public boolean hasLessOrEqual( K key, V val ) throws LdapException
+ @Override
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -451,86 +411,95 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
/**
- * @see org.apache.directory.server.xdbm.Table#hasGreaterOrEqual(Object)
+ * {@inheritDoc}
*/
+ @Override
@SuppressWarnings("unchecked")
- public boolean hasGreaterOrEqual( K key ) throws IOException
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
- // See if we can find the border between keys greater than and less
- // than in the set of keys. This will be the spot we search from.
- jdbm.helper.Tuple tuple = bt.findGreaterOrEqual( key );
-
- // Test for equality first since it satisfies both greater/less than
- if ( null != tuple && keyComparator.compare( ( K ) tuple.getKey(), key ) == 0 )
+ try
{
- return true;
+ // See if we can find the border between keys greater than and less
+ // than in the set of keys. This will be the spot we search from.
+ jdbm.helper.Tuple tuple = bt.findGreaterOrEqual( key );
+
+ // Test for equality first since it satisfies both greater/less than
+ if ( null != tuple && keyComparator.compare( ( K ) tuple.getKey(), key ) == 0 )
+ {
+ return true;
+ }
+
+ // Greater searches are easy and quick thanks to findGreaterOrEqual
+ // A null return above means there were no equal or greater keys
+ return ( null != tuple );
}
-
- // Greater searches are easy and quick thanks to findGreaterOrEqual
- // A null return above means there were no equal or greater keys
- if ( null == tuple )
+ catch ( IOException ioe )
{
- return false;
+ throw new LdapOtherException( ioe.getMessage() );
}
-
- // Not Null! - we found a tuple with equal or greater key value
- return true;
}
/**
- * @see Table#hasLessOrEqual(Object)
+ * {@inheritDoc}
*/
@SuppressWarnings("unchecked")
- public boolean hasLessOrEqual( K key ) throws IOException
+ @Override
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
- // Can only find greater than or equal to with JDBM so we find that
- // and work backwards to see if we can find one less than the key
- Tuple<K, V> tuple = bt.findGreaterOrEqual( key );
-
- // Test for equality first since it satisfies equal to condition
- if ( null != tuple && keyComparator.compare( tuple.getKey(), key ) == 0 )
- {
- return true;
- }
-
- if ( null == tuple )
- {
- /*
- * Jdbm failed to find a key greater than or equal to the argument
- * which means all the keys in the table are less than the
- * supplied key argument. We can hence return true if the table
- * contains any Tuples.
- */
- return count > 0;
- }
- else
+ try
{
- /*
- * We have the next tuple whose key is the next greater than the
- * key argument supplied. We use this key to advance a browser to
- * that tuple and scan down to lesser Tuples until we hit one
- * that is less than the key argument supplied. Usually this will
- * be the previous tuple if it exists.
- */
- TupleBrowser browser = bt.browse( tuple.getKey() );
-
- if ( browser.getPrevious( tuple ) )
+ // Can only find greater than or equal to with JDBM so we find that
+ // and work backwards to see if we can find one less than the key
+ Tuple<K, V> tuple = bt.findGreaterOrEqual( key );
+
+ // Test for equality first since it satisfies equal to condition
+ if ( null != tuple && keyComparator.compare( tuple.getKey(), key ) == 0 )
{
return true;
}
+
+ if ( null == tuple )
+ {
+ /*
+ * Jdbm failed to find a key greater than or equal to the argument
+ * which means all the keys in the table are less than the
+ * supplied key argument. We can hence return true if the table
+ * contains any Tuples.
+ */
+ return count > 0;
+ }
+ else
+ {
+ /*
+ * We have the next tuple whose key is the next greater than the
+ * key argument supplied. We use this key to advance a browser to
+ * that tuple and scan down to lesser Tuples until we hit one
+ * that is less than the key argument supplied. Usually this will
+ * be the previous tuple if it exists.
+ */
+ TupleBrowser browser = bt.browse( tuple.getKey() );
+
+ if ( browser.getPrevious( tuple ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ catch ( IOException ioe )
+ {
+ throw new LdapOtherException( ioe.getMessage() );
}
-
- return false;
}
/**
- * @see org.apache.directory.server.xdbm.Table#has(java.lang.Object,
- * java.lang.Object)
+ * {@inheritDoc}
*/
@SuppressWarnings("unchecked")
- public boolean has( K key, V value ) throws LdapException
+ public boolean has( PartitionTxn transaction, K key, V value ) throws LdapException
{
if ( key == null )
{
@@ -562,20 +531,28 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
/**
- * @see Table#has(java.lang.Object)
+ * {@inheritDoc}
*/
- public boolean has( K key ) throws IOException
+ @Override
+ public boolean has( PartitionTxn transaction, K key ) throws LdapException
{
- return key != null && bt.find( key ) != null;
+ try
+ {
+ return key != null && bt.find( key ) != null;
+ }
+ catch ( IOException ioe )
+ {
+ throw new LdapException( ioe );
+ }
}
/**
- * @see org.apache.directory.server.xdbm.Table#put(java.lang.Object,
- * java.lang.Object)
+ * {@inheritDoc}
*/
+ @Override
@SuppressWarnings("unchecked")
- public synchronized void put( K key, V value ) throws Exception
+ public synchronized void put( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
try
{
@@ -664,20 +641,20 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
commit( recMan );
}
- catch ( Exception e )
+ catch ( IOException | CursorException | LdapException e )
{
LOG.error( I18n.err( I18n.ERR_131, key, name ), e );
- throw e;
+ throw new LdapOtherException( e.getMessage(), e );
}
}
/**
- * @see org.apache.directory.server.xdbm.Table#remove(java.lang.Object,
- * java.lang.Object)
+ * {@inheritDoc}
*/
@SuppressWarnings("unchecked")
- public synchronized void remove( K key, V value ) throws IOException
+ @Override
+ public synchronized void remove( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
try
{
@@ -788,9 +765,10 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
/**
- * @see Table#remove(Object)
+ * {@inheritDoc}
*/
- public synchronized void remove( K key )
+ @Override
+ public synchronized void remove( PartitionWriteTxn transaction, K key ) throws LdapException
{
try
{
@@ -871,81 +849,107 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
}
- public Cursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>> cursor() throws LdapException
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Cursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>> cursor( PartitionTxn transaction ) throws LdapException
{
if ( allowsDuplicates )
{
- return new DupsCursor<K, V>( this );
+ return new DupsCursor<>( this );
}
- return new NoDupsCursor<K, V>( this );
+ return new NoDupsCursor<>( this );
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
@SuppressWarnings("unchecked")
- public Cursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>> cursor( K key ) throws Exception
+ public Cursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>> cursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
- return new EmptyCursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>>();
- }
-
- V raw = bt.find( key );
-
- if ( null == raw )
- {
- return new EmptyCursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>>();
+ return new EmptyCursor<>();
}
- if ( !allowsDuplicates )
- {
- return new SingletonCursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>>(
- new org.apache.directory.api.ldap.model.cursor.Tuple<K, V>( key, raw ) );
+ try
+ {
+ V raw = bt.find( key );
+
+ if ( null == raw )
+ {
+ return new EmptyCursor<>();
+ }
+
+ if ( !allowsDuplicates )
+ {
+ return new SingletonCursor<>(
+ new org.apache.directory.api.ldap.model.cursor.Tuple<K, V>( key, raw ) );
+ }
+
+ byte[] serialized = ( byte[] ) raw;
+
+ if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
+ {
+ BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
+ return new KeyTupleBTreeCursor<>( tree, key, valueComparator );
+ }
+
+ ArrayTree<V> set = marshaller.deserialize( serialized );
+
+ return new KeyTupleArrayCursor<>( set, key );
}
-
- byte[] serialized = ( byte[] ) raw;
-
- if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
+ catch ( IOException ioe )
{
- BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
- return new KeyTupleBTreeCursor<K, V>( tree, key, valueComparator );
+ throw new LdapOtherException( ioe.getMessage(), ioe );
}
-
- ArrayTree<V> set = marshaller.deserialize( serialized );
-
- return new KeyTupleArrayCursor<K, V>( set, key );
}
+ /**
+ * {@inheritDoc}
+ */
@SuppressWarnings("unchecked")
- public Cursor<V> valueCursor( K key ) throws Exception
+ @Override
+ public Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
- return new EmptyCursor<V>();
+ return new EmptyCursor<>();
}
- V raw = bt.find( key );
-
- if ( null == raw )
- {
- return new EmptyCursor<V>();
- }
-
- if ( !allowsDuplicates )
+ try
{
- return new SingletonCursor<V>( raw );
+ V raw = bt.find( key );
+
+ if ( null == raw )
+ {
+ return new EmptyCursor<>();
+ }
+
+ if ( !allowsDuplicates )
+ {
+ return new SingletonCursor<>( raw );
+ }
+
+ byte[] serialized = ( byte[] ) raw;
+
+ if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
+ {
+ BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
+ return new KeyBTreeCursor<>( tree, valueComparator );
+ }
+
+ return new ArrayTreeCursor<>( marshaller.deserialize( serialized ) );
}
-
- byte[] serialized = ( byte[] ) raw;
-
- if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
+ catch ( IOException ioe )
{
- BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
- return new KeyBTreeCursor<V>( tree, valueComparator );
+ throw new LdapOtherException( ioe.getMessage(), ioe );
}
-
- return new ArrayTreeCursor<V>( marshaller.deserialize( serialized ) );
}
@@ -956,9 +960,17 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
/**
* @see Table#close()
*/
- public synchronized void close() throws IOException
+ @Override
+ public synchronized void close( PartitionTxn transaction ) throws LdapException
{
- sync();
+ try
+ {
+ sync();
+ }
+ catch ( IOException ioe )
+ {
+ throw new LdapOtherException( ioe.getMessage() );
+ }
}
@@ -1143,7 +1155,7 @@ public class JdbmTable<K, V> extends AbstractTable<K, V>
}
- private BTree<V, K> convertToBTree( ArrayTree<V> arrayTree ) throws Exception
+ private BTree<V, K> convertToBTree( ArrayTree<V> arrayTree ) throws IOException, CursorException, LdapException
{
BTree<V, K> bTree;
diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java
index be164b2..02549d8 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java
+++ b/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java
@@ -69,7 +69,7 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
* @param comparator the Comparator used to determine <b>key</b> ordering
* @throws Exception of there are problems accessing the BTree
*/
- public KeyTupleBTreeCursor( BTree btree, K key, Comparator<V> comparator ) throws Exception
+ public KeyTupleBTreeCursor( BTree btree, K key, Comparator<V> comparator ) throws IOException
{
if ( IS_DEBUG )
{
diff --git a/jdbm-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableWithDuplicatesTest.java b/jdbm-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableWithDuplicatesTest.java
index fc24a71..91aa2f6 100644
--- a/jdbm-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableWithDuplicatesTest.java
+++ b/jdbm-partition/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableWithDuplicatesTest.java
@@ -159,14 +159,6 @@ public class JdbmTableWithDuplicatesTest
@Test
- public void testSerializers() throws Exception
- {
- assertNotNull( table.getKeySerializer() );
- assertNotNull( ( ( JdbmTable<?, ?> ) table ).getValueSerializer() );
- }
-
-
- @Test
public void testCountOneArg() throws Exception
{
assertEquals( 0, table.count( "3" ) );
@@ -590,7 +582,6 @@ public class JdbmTableWithDuplicatesTest
table = new JdbmTable<String, String>( schemaManager, "test", SIZE, recman,
comparator, comparator, new DefaultSerializer(), null );
- assertNull( table.getValueSerializer() );
for ( int i = 0; i < SIZE + 1; i++ )
{
diff --git a/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java b/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
index 124d865..20d78df 100644
--- a/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
+++ b/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
@@ -27,6 +27,7 @@ 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.mavibot.btree.BTree;
import org.apache.directory.mavibot.btree.BTreeFactory;
@@ -36,6 +37,8 @@ import org.apache.directory.mavibot.btree.ValueCursor;
import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
import org.apache.directory.server.core.avltree.ArrayMarshaller;
import org.apache.directory.server.core.avltree.ArrayTree;
import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
@@ -140,18 +143,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean isDupsEnabled()
- {
- return allowsDuplicates;
- }
-
-
- /**
- * {@inheritDoc}
- * @throws
- */
- @Override
- public boolean has( K key ) throws LdapException
+ public boolean has( PartitionTxn partitionTxn, K key ) throws LdapException
{
try
{
@@ -172,7 +164,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean has( K key, V value ) throws LdapException
+ public boolean has( PartitionTxn transaction, K key, V value ) throws LdapException
{
try
{
@@ -189,7 +181,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasGreaterOrEqual( K key ) throws Exception
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
TupleCursor<K, V> cursor = null;
@@ -199,6 +191,10 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
return cursor.hasNext();
}
+ catch ( IOException ioe )
+ {
+ throw new LdapOtherException( ioe.getMessage() );
+ }
finally
{
if ( cursor != null )
@@ -213,7 +209,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasLessOrEqual( K key ) throws Exception
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
TupleCursor<K, V> cursor = null;
@@ -248,6 +244,10 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
return false;
}
+ catch ( Exception e )
+ {
+ throw new LdapException( e );
+ }
finally
{
if ( cursor != null )
@@ -262,7 +262,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasGreaterOrEqual( K key, V val ) throws LdapException
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -274,7 +274,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
}
- TupleCursor<V, V> cursor = null;
+ ValueCursor<V> valueCursor = null;
try
{
@@ -283,21 +283,21 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
return false;
}
- ValueCursor<V> valueCursor = bt.getValues( key );
+ valueCursor = bt.getValues( key );
int equal = bt.getValueSerializer().compare( val, valueCursor.next() );
return ( equal >= 0 );
}
- catch ( Exception e )
+ catch ( KeyNotFoundException | IOException e )
{
- throw new LdapException( e );
+ throw new LdapException( e.getMessage() );
}
finally
{
- if ( cursor != null )
+ if ( valueCursor != null )
{
- cursor.close();
+ valueCursor.close();
}
}
}
@@ -307,7 +307,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasLessOrEqual( K key, V val ) throws Exception
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -334,7 +334,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public V get( K key ) throws LdapException
+ public V get( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -360,7 +360,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public void put( K key, V value ) throws Exception
+ public void put( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
try
{
@@ -376,10 +376,10 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
count++;
}
}
- catch ( Exception e )
+ catch ( IOException ioe )
{
- LOG.error( I18n.err( I18n.ERR_131, key, name ), e );
- throw e;
+ LOG.error( I18n.err( I18n.ERR_131, key, name ), ioe );
+ throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
@@ -388,7 +388,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public void remove( K key ) throws Exception
+ public void remove( PartitionWriteTxn transaction, K key ) throws LdapException
{
try
{
@@ -424,14 +424,11 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
count--;
}
}
- catch ( Exception e )
+ catch ( IOException | KeyNotFoundException e )
{
LOG.error( I18n.err( I18n.ERR_133, key, name ), e );
- if ( e instanceof IOException )
- {
- throw ( IOException ) e;
- }
+ throw new LdapOtherException( e.getMessage(), e );
}
}
@@ -440,7 +437,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public void remove( K key, V value ) throws Exception
+ public void remove( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
try
{
@@ -468,9 +465,9 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public Cursor<Tuple<K, V>> cursor() throws LdapException
+ public Cursor<Tuple<K, V>> cursor( PartitionTxn transaction ) throws LdapException
{
- return new MavibotCursor<K, V>( this );
+ return new MavibotCursor<>( this );
}
@@ -478,11 +475,11 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public Cursor<Tuple<K, V>> cursor( K key ) throws LdapException
+ public Cursor<Tuple<K, V>> cursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
- return new EmptyCursor<Tuple<K, V>>();
+ return new EmptyCursor<>();
}
try
@@ -491,19 +488,18 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
{
V val = bt.get( key );
- return new SingletonCursor<Tuple<K, V>>(
- new Tuple<K, V>( key, val ) );
+ return new SingletonCursor<>( new Tuple<K, V>( key, val ) );
}
else
{
ValueCursor<V> dupHolder = bt.getValues( key );
- return new KeyTupleValueCursor<K, V>( dupHolder, key );
+ return new KeyTupleValueCursor<>( dupHolder, key );
}
}
catch ( KeyNotFoundException knfe )
{
- return new EmptyCursor<Tuple<K, V>>();
+ return new EmptyCursor<>();
}
catch ( Exception e )
{
@@ -516,7 +512,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public Cursor<V> valueCursor( K key ) throws Exception
+ public Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -529,18 +525,18 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
{
V val = bt.get( key );
- return new SingletonCursor<V>( val );
+ return new SingletonCursor<>( val );
}
else
{
ValueCursor<V> dupCursor = bt.getValues( key );
- return new ValueTreeCursor<V>( dupCursor );
+ return new ValueTreeCursor<>( dupCursor );
}
}
catch ( KeyNotFoundException knfe )
{
- return new EmptyCursor<V>();
+ return new EmptyCursor<>();
}
catch ( Exception e )
{
@@ -553,16 +549,16 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public long count( K key ) throws Exception
+ public long count( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
return 0;
}
- if ( bt.isAllowDuplicates() )
+ try
{
- try
+ if ( bt.isAllowDuplicates() )
{
ValueCursor<V> dupHolder = bt.getValues( key );
int size = dupHolder.size();
@@ -570,58 +566,27 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
return size;
}
- catch ( KeyNotFoundException knfe )
+ else
{
- // No key
- return 0;
+ if ( bt.hasKey( key ) )
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
}
}
- else
+ catch ( KeyNotFoundException knfe )
{
- if ( bt.hasKey( key ) )
- {
- return 1;
- }
- else
- {
- return 0;
- }
+ // No key
+ return 0;
+ }
+ catch ( IOException ioe )
+ {
+ throw new LdapOtherException( ioe.getMessage() );
}
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long greaterThanCount( K key ) throws Exception
- {
- // take a best guess
- return Math.min( count, 10L );
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long lessThanCount( K key ) throws Exception
- {
- // take a best guess
- return Math.min( count, 10L );
- }
-
-
- /**
- * {@inheritDoc}
- */
- /**
- * {@inheritDoc}
- */
- @Override
- public void close() throws Exception
- {
- // do nothing here, the RecordManager will be closed in MavibotMasterTable.close()
}
diff --git a/mavibotv2-partition/pom.xml b/mavibotv2-partition/pom.xml
new file mode 100644
index 0000000..8419302
--- /dev/null
+++ b/mavibotv2-partition/pom.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0"?>
+<!--
+ 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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.directory.server</groupId>
+ <artifactId>apacheds-parent</artifactId>
+ <version>2.0.0-M25-SNAPSHOT</version>
+ </parent>
+ <artifactId>apacheds-mavibotv2-partition</artifactId>
+ <name>ApacheDS Mavibot v2 Partition</name>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.directory.junit</groupId>
+ <artifactId>junit-addons</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.directory.mavibot</groupId>
+ <artifactId>mavibot</artifactId>
+ <version>1.0.0-M9-SNAPSHOT</version>
+ </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-shared</artifactId>
+ </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>
+ </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</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>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <extensions>true</extensions>
+ <configuration>
+ <manifestLocation>META-INF</manifestLocation>
+ <instructions>
+ <Bundle-SymbolicName>${project.groupId}.mavibotv2.partition</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.directory.server.core.partition.impl.btree.mavibotv2;version=${project.version};-noimport:=true
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/AbstractMavibotTxn.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/AbstractMavibotTxn.java
new file mode 100644
index 0000000..71d0639
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/AbstractMavibotTxn.java
@@ -0,0 +1,96 @@
+package org.apache.directory.server.core.partition.impl.btree.mavibot;
+
+import java.io.IOException;
+
+import org.apache.directory.mavibot.btree.BTree;
+import org.apache.directory.mavibot.btree.BTreeInfo;
+import org.apache.directory.mavibot.btree.Page;
+import org.apache.directory.mavibot.btree.RecordManager;
+import org.apache.directory.mavibot.btree.RecordManagerHeader;
+import org.apache.directory.mavibot.btree.Transaction;
+
+public abstract class AbstractMavibotTxn implements MavibotTxn
+{
+ protected Transaction transaction;
+
+ protected AbstractMavibotTxn( Transaction transaction )
+ {
+ this.transaction = transaction;
+ }
+
+
+ @Override
+ public void commit() throws IOException
+ {
+ transaction.commit();
+ }
+
+
+ @Override
+ public void abort() throws IOException
+ {
+ transaction.abort();
+ }
+
+
+ @Override
+ public boolean isClosed()
+ {
+ return transaction.isClosed();
+ }
+
+
+ @Override
+ public void close() throws IOException
+ {
+ transaction.close();
+ }
+
+
+ @Override
+ public long getRevision()
+ {
+ return transaction.getRevision();
+ }
+
+
+ @Override
+ public <K, V> Page<K, V> getPage( BTreeInfo<K, V> btreeInfo, long offset ) throws IOException
+ {
+ return transaction.getPage( btreeInfo, offset );
+ }
+
+
+ @Override
+ public long getCreationDate()
+ {
+ return transaction.getCreationDate();
+ }
+
+
+ @Override
+ public RecordManager getRecordManager()
+ {
+ return transaction.getRecordManager();
+ }
+
+
+ @Override
+ public RecordManagerHeader getRecordManagerHeader()
+ {
+ return transaction.getRecordManagerHeader();
+ }
+
+
+ @Override
+ public <K, V> BTree<K, V> getBTree( String name )
+ {
+ return transaction.getBTree( name );
+ }
+
+
+ public Transaction getTransaction()
+ {
+ return transaction;
+ }
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/DnSerializer.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/DnSerializer.java
new file mode 100644
index 0000000..2f60099
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/DnSerializer.java
@@ -0,0 +1,206 @@
+/*
+ * 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.mavibot;
+
+
+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 java.nio.ByteBuffer;
+import java.util.Comparator;
+
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.mavibot.btree.serializer.AbstractElementSerializer;
+import org.apache.directory.mavibot.btree.serializer.BufferHandler;
+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 extends AbstractElementSerializer<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();
+
+ private static Comparator<Dn> comp = new Comparator<Dn>()
+ {
+ @Override
+ public int compare( Dn dn1, Dn dn2 )
+ {
+ if ( dn1 == null )
+ {
+ if ( dn2 == null )
+ {
+ return 0;
+ }
+
+ return -1;
+ }
+
+ if ( dn2 == null )
+ {
+ return 1;
+ }
+
+ return dn1.getNormName().compareTo( dn2.getNormName() );
+ }
+ };
+
+
+ /**
+ * Creates a new instance of DnSerializer.
+ *
+ * @param schemaManager The reference to the global schemaManager
+ */
+ public DnSerializer()
+ {
+ super( comp );
+ }
+
+
+ /**
+ * <p>
+ *
+ * This is the place where we serialize Dn
+ * <p>
+ */
+ public byte[] serialize( Dn dn )
+ {
+ 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();
+ }
+ catch ( IOException e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+
+ /**
+ * 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( ByteBuffer buffer ) throws IOException
+ {
+ return deserialize( new BufferHandler( buffer.array() ) );
+ }
+
+
+ @Override
+ public Dn deserialize( BufferHandler bufferHandler ) throws IOException
+ {
+ ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( bufferHandler.getBuffer() ) );
+
+ 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() );
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Dn fromBytes( byte[] buffer ) throws IOException
+ {
+ return fromBytes( buffer, 0 );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Dn fromBytes( byte[] buffer, int pos ) throws IOException
+ {
+ int length = buffer.length - pos;
+ ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( buffer, pos, length ) );
+
+ 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() );
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<?> getType()
+ {
+ return Dn.class;
+ }
+}
diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/KeyTupleValueCursor.java
similarity index 50%
copy from jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java
copy to mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/KeyTupleValueCursor.java
index be164b2..20b5c06 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/KeyTupleValueCursor.java
@@ -16,14 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.directory.server.core.partition.impl.btree.jdbm;
+package org.apache.directory.server.core.partition.impl.btree.mavibot;
import java.io.IOException;
-import java.util.Comparator;
-
-import jdbm.btree.BTree;
-import jdbm.helper.TupleBrowser;
import org.apache.directory.api.ldap.model.constants.Loggers;
import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
@@ -31,19 +27,20 @@ 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.mavibot.btree.ValueCursor;
import org.apache.directory.server.i18n.I18n;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Cursor over a set of values for the same key which are store in another
- * BTree. This Cursor is limited to the same key and it's tuples will always
- * return the same key.
+ * Cursor over a set of values for the same key which are store in an in
+ * memory ArrayTree. This Cursor is limited to the same key and it's tuples
+ * will always return the same key.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
-public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
+public class KeyTupleValueCursor<K, V> extends AbstractCursor<Tuple<K, V>>
{
/** A dedicated log for cursors */
private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
@@ -51,35 +48,29 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
/** Speedup for logs */
private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
- private final Comparator<V> comparator;
- private final BTree btree;
+ private final ValueCursor<V> wrapped;
private final K key;
- private jdbm.helper.Tuple<K, V> valueTuple = new jdbm.helper.Tuple<K, V>();
private Tuple<K, V> returnedTuple = new Tuple<K, V>();
- private TupleBrowser<K, V> browser;
private boolean valueAvailable;
/**
- * Creates a Cursor over the tuples of a JDBM BTree.
+ * Creates a Cursor over the tuples of an ArrayTree.
*
- * @param btree the JDBM BTree to build a Cursor over
+ * @param arrayTree the ArrayTree to build a Tuple returning Cursor over
* @param key the constant key for which values are returned
- * @param comparator the Comparator used to determine <b>key</b> ordering
- * @throws Exception of there are problems accessing the BTree
*/
- public KeyTupleBTreeCursor( BTree btree, K key, Comparator<V> comparator ) throws Exception
+ public KeyTupleValueCursor( ValueCursor<V> cursor, K key )
{
+ this.key = key;
+
+ this.wrapped = cursor;
+
if ( IS_DEBUG )
{
- LOG_CURSOR.debug( "Creating KeyTupleBTreeCursor {}", this );
+ LOG_CURSOR.debug( "Creating KeyTupleArrayCursor {}", this );
}
-
- this.key = key;
- this.btree = btree;
- this.comparator = comparator;
- this.browser = btree.browse();
}
@@ -91,9 +82,6 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
}
- /**
- * {@inheritDoc}
- */
public boolean available()
{
return valueAvailable;
@@ -106,109 +94,35 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
}
- /**
- * {@inheritDoc}
- */
public void afterKey( K key ) throws Exception
{
throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
}
- /**
- * {@inheritDoc}
- */
public void beforeValue( K key, V value ) throws Exception
{
- checkNotClosed( "beforeValue()" );
- if ( key != null && !key.equals( this.key ) )
- {
- throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
- }
-
- browser = btree.browse( value );
- clearValue();
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
}
- /**
- * {@inheritDoc}
- */
- @SuppressWarnings("unchecked")
- public void afterValue( K key, V value ) throws LdapException, CursorException
+ public void afterValue( K key, V value ) throws Exception
{
- if ( key != null && !key.equals( this.key ) )
- {
- throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
- }
-
- try
- {
- browser = btree.browse( value );
-
- /*
- * While the next value is less than or equal to the element keep
- * advancing forward to the next item. If we cannot advance any
- * further then stop and return. If we find a value greater than
- * the element then we stop, backup, and return so subsequent calls
- * to getNext() will return a value greater than the element.
- */
- while ( browser.getNext( valueTuple ) )
- {
- checkNotClosed( "afterValue" );
-
- V next = ( V ) valueTuple.getKey();
-
- int nextCompared = comparator.compare( next, value );
-
- if ( nextCompared > 0 )
- {
- /*
- * If we just have values greater than the element argument
- * then we are before the first element and cannot backup, and
- * the call below to getPrevious() will fail. In this special
- * case we just reset the Cursor's browser and return.
- */
- if ( !browser.getPrevious( valueTuple ) )
- {
- browser = btree.browse( this.key );
- }
-
- clearValue();
-
- return;
- }
- }
-
- clearValue();
- }
- catch ( IOException e )
- {
- throw new CursorException( e );
- }
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
}
/**
* Positions this Cursor over the same keys before the value of the
- * supplied valueTuple. The supplied element Tuple's key is not considered at
- * all.
+ * supplied element Tuple. The supplied element Tuple's key is not
+ * considered at all.
*
* @param element the valueTuple who's value is used to position this Cursor
* @throws Exception if there are failures to position the Cursor
*/
public void before( Tuple<K, V> element ) throws LdapException, CursorException
{
- checkNotClosed( "before()" );
- try
- {
- browser = btree.browse( element.getValue() );
- clearValue();
- }
- catch ( IOException e )
- {
- throw new CursorException( e );
- }
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
}
@@ -217,7 +131,7 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
*/
public void after( Tuple<K, V> element ) throws LdapException, CursorException
{
- afterValue( key, element.getValue() );
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
}
@@ -226,16 +140,6 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
*/
public void beforeFirst() throws LdapException, CursorException
{
- checkNotClosed( "beforeFirst()" );
- try
- {
- browser = btree.browse();
- clearValue();
- }
- catch ( IOException e )
- {
- throw new CursorException( e );
- }
}
@@ -244,15 +148,6 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
*/
public void afterLast() throws LdapException, CursorException
{
- checkNotClosed( "afterLast()" );
- try
- {
- browser = btree.browse( null );
- }
- catch ( IOException e )
- {
- throw new CursorException( e );
- }
}
@@ -261,9 +156,7 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
*/
public boolean first() throws LdapException, CursorException
{
- beforeFirst();
-
- return next();
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
}
@@ -272,40 +165,29 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
*/
public boolean last() throws LdapException, CursorException
{
- afterLast();
-
- return previous();
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
}
/**
* {@inheritDoc}
*/
- @SuppressWarnings("unchecked")
public boolean previous() throws LdapException, CursorException
{
checkNotClosed( "previous()" );
try
{
- if ( browser.getPrevious( valueTuple ) )
+ if ( wrapped.hasPrev() )
{
- // work around to fix direction change problem with jdbm browser
- if ( ( returnedTuple.getValue() != null )
- && ( comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 ) )
- {
- browser.getPrevious( valueTuple );
- }
returnedTuple.setKey( key );
- returnedTuple.setValue( ( V ) valueTuple.getKey() );
-
+ returnedTuple.setValue( wrapped.prev() );
valueAvailable = true;
return true;
}
else
{
clearValue();
-
return false;
}
}
@@ -319,24 +201,16 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
/**
* {@inheritDoc}
*/
- @SuppressWarnings("unchecked")
public boolean next() throws LdapException, CursorException
{
checkNotClosed( "next()" );
try
{
- if ( browser.getNext( valueTuple ) )
+ if ( wrapped.hasNext() )
{
- // work around to fix direction change problem with jdbm browser
- if ( returnedTuple.getValue() != null
- && comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 )
- {
- browser.getNext( valueTuple );
- }
-
returnedTuple.setKey( key );
- returnedTuple.setValue( ( V ) valueTuple.getKey() );
+ returnedTuple.setValue( wrapped.next() );
valueAvailable = true;
return true;
@@ -374,12 +248,16 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
/**
* {@inheritDoc}
*/
- @Override
public void close() throws IOException
{
if ( IS_DEBUG )
{
- LOG_CURSOR.debug( "Closing KeyTupleBTreeCursor {}", this );
+ LOG_CURSOR.debug( "Closing KeyTupleArrayCursor {}", this );
+ }
+
+ if ( wrapped != null )
+ {
+ wrapped.close();
}
super.close();
@@ -389,14 +267,55 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
/**
* {@inheritDoc}
*/
- @Override
- public void close( Exception cause ) throws IOException
+ public void close( Exception reason ) throws IOException
{
if ( IS_DEBUG )
{
- LOG_CURSOR.debug( "Closing KeyTupleBTreeCursor {}", this );
+ LOG_CURSOR.debug( "Closing KeyTupleArrayCursor {}", this );
}
- super.close( cause );
+ if ( wrapped != null )
+ {
+ wrapped.close();
+ }
+
+ super.close( reason );
+ }
+
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString( String tabs )
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( tabs ).append( "KeyTupleArrayCursor (" );
+
+ if ( available() )
+ {
+ sb.append( "available)" );
+ }
+ else
+ {
+ sb.append( "absent)" );
+ }
+
+ sb.append( "#" ).append( key );
+
+ sb.append( " :\n" );
+
+ sb.append( wrapped.toString() );
+
+ return sb.toString();
+ }
+
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return toString( "" );
}
}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/LdifTupleComparator.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/LdifTupleComparator.java
new file mode 100644
index 0000000..6779c66
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/LdifTupleComparator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.mavibot;
+
+import java.util.Comparator;
+
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.mavibot.btree.Tuple;
+
+/**
+ * TODO LdifTupleComparator.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdifTupleComparator implements Comparator<Tuple<Dn, String>>
+{
+
+ @Override
+ public int compare( Tuple<Dn, String> t1, Tuple<Dn, String> t2 )
+ {
+ Dn dn1 = t1.getKey();
+ Dn dn2 = t2.getKey();
+
+ if ( dn1.isAncestorOf( dn2 ) )
+ {
+ return -1;
+ }
+ else if ( dn2.isAncestorOf( dn1 ) )
+ {
+ return 1;
+ }
+ else if ( dn1.equals( dn2 ) )
+ {
+ return 0;
+ }
+
+ return dn1.getNormName().compareTo( dn2.getNormName() );
+ }
+
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/LdifTupleReaderWriter.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/LdifTupleReaderWriter.java
new file mode 100644
index 0000000..1f16448
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/LdifTupleReaderWriter.java
@@ -0,0 +1,162 @@
+/*
+ * 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.mavibot;
+
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+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.ldif.LdifEntry;
+import org.apache.directory.api.ldap.model.ldif.LdifReader;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.mavibot.btree.Tuple;
+import org.apache.directory.mavibot.btree.util.TupleReaderWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * TODO LdifTupleReaderWriter.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdifTupleReaderWriter<E> implements TupleReaderWriter<Dn, E>
+{
+
+ private LdifReader reader = null;
+
+ private static final Logger LOG = LoggerFactory.getLogger( LdifTupleReaderWriter.class );
+
+ private String ldifFile;
+
+ private RandomAccessFile raf;
+
+ private SchemaManager schemaManager;
+
+ public LdifTupleReaderWriter( String ldifFile, SchemaManager schemaManager )
+ {
+ this.ldifFile = ldifFile;
+ this.schemaManager = schemaManager;
+
+ try
+ {
+ raf = new RandomAccessFile( ldifFile, "r" );
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+
+ @Override
+ public Tuple<Dn, E> readSortedTuple( DataInputStream in )
+ {
+ try
+ {
+ if ( in.available() > 0 )
+ {
+ Tuple<Dn, E> t = new Tuple<Dn, E>();
+ t.setKey( new Dn( in.readUTF() ) );
+
+ String[] tokens = in.readUTF().split( ":" );
+
+ long offset = Long.valueOf( tokens[0] );
+
+ int length = Integer.valueOf( tokens[1] );
+
+ raf.seek( offset );
+
+ byte[] data = new byte[length];
+
+ raf.read( data, 0, length );
+
+ LdifReader reader = new LdifReader();
+
+ LdifEntry ldifEntry = reader.parseLdif( new String( data ) ).get( 0 );
+ Entry entry = new DefaultEntry( schemaManager, ldifEntry.getEntry() );
+
+ t.setValue( ( E ) entry );
+
+ return t;
+ }
+ }
+ catch ( Exception e )
+ {
+ LOG.error( "Failed to read a sorted tuple", e );
+ }
+
+ return null;
+ }
+
+
+ @Override
+ public Tuple<Dn, E> readUnsortedTuple( DataInputStream in )
+ {
+ try
+ {
+ if ( reader == null )
+ {
+ reader = new LdifReader( in );
+ }
+ }
+ catch ( Exception e )
+ {
+ String msg = "Failed to open the LDIF input stream";
+ LOG.error( msg, e );
+
+ throw new RuntimeException( msg, e );
+ }
+
+ Tuple<Dn, E> t = null;
+
+ if ( reader.hasNext() )
+ {
+ LdifEntry ldifEntry = reader.next();
+
+ if ( ldifEntry == null )
+ {
+ throw new IllegalStateException(
+ "Received null entry while parsing, check the LDIF file for possible incorrect/corrupted entries" );
+ }
+
+ t = new Tuple<Dn, E>();
+
+ t.setKey( ldifEntry.getDn() );
+ t.setValue( ( E ) ( ldifEntry.getOffset() + ":" + ldifEntry.getLengthBeforeParsing() ) );
+ }
+
+ return t;
+ }
+
+
+ @Override
+ public void storeSortedTuple( Tuple<Dn, E> t, DataOutputStream out ) throws IOException
+ {
+ out.writeUTF( t.getKey().getName() );
+ out.writeUTF( ( String ) t.getValue() );
+ }
+
+}
diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotCursor.java
similarity index 50%
copy from jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java
copy to mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotCursor.java
index be164b2..32910b1 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/KeyTupleBTreeCursor.java
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotCursor.java
@@ -16,76 +16,65 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.directory.server.core.partition.impl.btree.jdbm;
+package org.apache.directory.server.core.partition.impl.btree.mavibot;
import java.io.IOException;
-import java.util.Comparator;
-import jdbm.btree.BTree;
-import jdbm.helper.TupleBrowser;
-
-import org.apache.directory.api.ldap.model.constants.Loggers;
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.mavibot.btree.TupleCursor;
+import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
import org.apache.directory.server.i18n.I18n;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Cursor over a set of values for the same key which are store in another
- * BTree. This Cursor is limited to the same key and it's tuples will always
- * return the same key.
+ * Cursor over the Tuples of a Mavibot BTree. If the BTree allows duplicate values,
+ * we will browse each value and return a Tuple for each one of them.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
-public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
+class MavibotCursor<K, V> extends AbstractCursor<Tuple<K, V>>
{
/** A dedicated log for cursors */
- private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
-
- /** Speedup for logs */
- private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
+ private static final Logger LOG_CURSOR = LoggerFactory.getLogger( "CURSOR" );
- private final Comparator<V> comparator;
- private final BTree btree;
- private final K key;
+ /** The table we are building a cursor over */
+ private final MavibotTable<K, V> table;
- private jdbm.helper.Tuple<K, V> valueTuple = new jdbm.helper.Tuple<K, V>();
+ /** The tuple which will be returned */
private Tuple<K, V> returnedTuple = new Tuple<K, V>();
- private TupleBrowser<K, V> browser;
- private boolean valueAvailable;
+
+ /** A flag set when there is a Tuple available */
+ private boolean valueAvailable = false;
+
+ /** The Tuple browser */
+ private TupleCursor<K, V> browser;
/**
- * Creates a Cursor over the tuples of a JDBM BTree.
+ * Creates a Cursor over the tuples of a Mavibot table.
*
- * @param btree the JDBM BTree to build a Cursor over
- * @param key the constant key for which values are returned
- * @param comparator the Comparator used to determine <b>key</b> ordering
- * @throws Exception of there are problems accessing the BTree
+ * @param table the JDBM Table to build a Cursor over
*/
- public KeyTupleBTreeCursor( BTree btree, K key, Comparator<V> comparator ) throws Exception
+ MavibotCursor( MavibotTable<K, V> table )
{
- if ( IS_DEBUG )
- {
- LOG_CURSOR.debug( "Creating KeyTupleBTreeCursor {}", this );
- }
-
- this.key = key;
- this.btree = btree;
- this.comparator = comparator;
- this.browser = btree.browse();
+ LOG_CURSOR.debug( "Creating MavibotCursor {}", this );
+ this.table = table;
}
+ /**
+ * Cleanup the returned tuple before reusing it.
+ */
private void clearValue()
{
- returnedTuple.setKey( key );
+ returnedTuple.setKey( null );
returnedTuple.setValue( null );
valueAvailable = false;
}
@@ -100,83 +89,58 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
}
- public void beforeKey( K key ) throws Exception
- {
- throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
- }
-
-
/**
- * {@inheritDoc}
+ * Sets the position before a given key
+ * @param key The key we want to start with
+ * @throws LdapException
+ * @throws CursorException
*/
- public void afterKey( K key ) throws Exception
+ public void beforeKey( K key ) throws LdapException, CursorException
{
- throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
- }
+ checkNotClosed( "beforeKey()" );
+ closeBrowser( browser );
-
- /**
- * {@inheritDoc}
- */
- public void beforeValue( K key, V value ) throws Exception
- {
- checkNotClosed( "beforeValue()" );
- if ( key != null && !key.equals( this.key ) )
+ try
+ {
+ browser = table.getBTree().browseFrom( key );
+ }
+ catch ( IOException e )
{
- throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
+ throw new CursorException( e );
}
- browser = btree.browse( value );
clearValue();
}
/**
- * {@inheritDoc}
+ * Sets the position before a given key
+ * @param key The key we want to start with
+ * @throws LdapException
+ * @throws CursorException
*/
- @SuppressWarnings("unchecked")
- public void afterValue( K key, V value ) throws LdapException, CursorException
+ public void afterKey( K key ) throws LdapException, CursorException
{
- if ( key != null && !key.equals( this.key ) )
- {
- throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
- }
+ checkNotClosed( "afterKey()" );
+ closeBrowser( browser );
try
{
- browser = btree.browse( value );
-
- /*
- * While the next value is less than or equal to the element keep
- * advancing forward to the next item. If we cannot advance any
- * further then stop and return. If we find a value greater than
- * the element then we stop, backup, and return so subsequent calls
- * to getNext() will return a value greater than the element.
- */
- while ( browser.getNext( valueTuple ) )
- {
- checkNotClosed( "afterValue" );
+ browser = table.getBTree().browseFrom( key );
- V next = ( V ) valueTuple.getKey();
-
- int nextCompared = comparator.compare( next, value );
-
- if ( nextCompared > 0 )
+ if ( table.isDupsEnabled() )
+ {
+ browser.nextKey();
+ }
+ else
+ {
+ if ( browser.hasNextKey() )
{
- /*
- * If we just have values greater than the element argument
- * then we are before the first element and cannot backup, and
- * the call below to getPrevious() will fail. In this special
- * case we just reset the Cursor's browser and return.
- */
- if ( !browser.getPrevious( valueTuple ) )
- {
- browser = btree.browse( this.key );
- }
-
- clearValue();
-
- return;
+ browser.nextKey();
+ }
+ else
+ {
+ browser.afterLast();
}
}
@@ -184,31 +148,44 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
}
catch ( IOException e )
{
+ clearValue();
throw new CursorException( e );
}
}
/**
- * Positions this Cursor over the same keys before the value of the
- * supplied valueTuple. The supplied element Tuple's key is not considered at
- * all.
- *
- * @param element the valueTuple who's value is used to position this Cursor
- * @throws Exception if there are failures to position the Cursor
+ * Sets the position before a given key and a given value for this key
+ * @param key The key we want to start with
+ * @param value The value we want to start with
+ * @throws LdapException
+ * @throws CursorException
+ */
+ public void beforeValue( K key, V value ) throws LdapException, CursorException
+ {
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_596 ) );
+ }
+
+
+ /**
+ * Sets the position after a given key and a given value for this key
+ * @param key The key we want to start with
+ * @param value The value we want to start with
+ * @throws LdapException
+ * @throws CursorException
+ */
+ public void afterValue( K key, V value ) throws LdapException, CursorException
+ {
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_596 ) );
+ }
+
+
+ /**
+ * {@inheritDoc}
*/
public void before( Tuple<K, V> element ) throws LdapException, CursorException
{
- checkNotClosed( "before()" );
- try
- {
- browser = btree.browse( element.getValue() );
- clearValue();
- }
- catch ( IOException e )
- {
- throw new CursorException( e );
- }
+ beforeKey( element.getKey() );
}
@@ -217,7 +194,7 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
*/
public void after( Tuple<K, V> element ) throws LdapException, CursorException
{
- afterValue( key, element.getValue() );
+ afterKey( element.getKey() );
}
@@ -227,15 +204,25 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
public void beforeFirst() throws LdapException, CursorException
{
checkNotClosed( "beforeFirst()" );
+
try
{
- browser = btree.browse();
+ if ( browser == null )
+ {
+ browser = table.getBTree().browse();
+ }
+
+ browser.beforeFirst();
clearValue();
}
catch ( IOException e )
{
throw new CursorException( e );
}
+ catch ( KeyNotFoundException knfe )
+ {
+ throw new CursorException( knfe );
+ }
}
@@ -245,14 +232,25 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
public void afterLast() throws LdapException, CursorException
{
checkNotClosed( "afterLast()" );
+
try
{
- browser = btree.browse( null );
+ if ( browser == null )
+ {
+ browser = table.getBTree().browse();
+ }
+
+ browser.afterLast();
+ clearValue();
}
catch ( IOException e )
{
throw new CursorException( e );
}
+ catch ( KeyNotFoundException knfe )
+ {
+ throw new CursorException( knfe );
+ }
}
@@ -273,7 +271,6 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
public boolean last() throws LdapException, CursorException
{
afterLast();
-
return previous();
}
@@ -281,31 +278,28 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
/**
* {@inheritDoc}
*/
- @SuppressWarnings("unchecked")
public boolean previous() throws LdapException, CursorException
{
checkNotClosed( "previous()" );
+ if ( browser == null )
+ {
+ afterLast();
+ }
try
{
- if ( browser.getPrevious( valueTuple ) )
+ if ( browser.hasPrev() )
{
- // work around to fix direction change problem with jdbm browser
- if ( ( returnedTuple.getValue() != null )
- && ( comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 ) )
- {
- browser.getPrevious( valueTuple );
- }
- returnedTuple.setKey( key );
- returnedTuple.setValue( ( V ) valueTuple.getKey() );
+ org.apache.directory.mavibot.btree.Tuple<K, V> tuple = browser.prev();
+ returnedTuple.setKey( tuple.getKey() );
+ returnedTuple.setValue( tuple.getValue() );
valueAvailable = true;
return true;
}
else
{
clearValue();
-
return false;
}
}
@@ -319,32 +313,29 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
/**
* {@inheritDoc}
*/
- @SuppressWarnings("unchecked")
public boolean next() throws LdapException, CursorException
{
- checkNotClosed( "next()" );
+ checkNotClosed( "previous()" );
+
+ if ( browser == null )
+ {
+ beforeFirst();
+ }
try
{
- if ( browser.getNext( valueTuple ) )
+ if ( browser.hasNext() )
{
- // work around to fix direction change problem with jdbm browser
- if ( returnedTuple.getValue() != null
- && comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 )
- {
- browser.getNext( valueTuple );
- }
-
- returnedTuple.setKey( key );
- returnedTuple.setValue( ( V ) valueTuple.getKey() );
+ org.apache.directory.mavibot.btree.Tuple<K, V> tuple = browser.next();
+ returnedTuple.setKey( tuple.getKey() );
+ returnedTuple.setValue( tuple.getValue() );
valueAvailable = true;
return true;
}
else
{
clearValue();
-
return false;
}
}
@@ -361,7 +352,6 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
public Tuple<K, V> get() throws CursorException
{
checkNotClosed( "get()" );
-
if ( valueAvailable )
{
return returnedTuple;
@@ -377,12 +367,9 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
@Override
public void close() throws IOException
{
- if ( IS_DEBUG )
- {
- LOG_CURSOR.debug( "Closing KeyTupleBTreeCursor {}", this );
- }
-
+ LOG_CURSOR.debug( "Closing MavibotCursor {}", this );
super.close();
+ closeBrowser( browser );
}
@@ -392,11 +379,20 @@ public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
@Override
public void close( Exception cause ) throws IOException
{
- if ( IS_DEBUG )
+ LOG_CURSOR.debug( "Closing MavibotCursor {}", this );
+ super.close( cause );
+ closeBrowser( browser );
+ }
+
+
+ /**
+ * Close the browser
+ */
+ private void closeBrowser( TupleCursor<K, V> browser )
+ {
+ if ( browser != null )
{
- LOG_CURSOR.debug( "Closing KeyTupleBTreeCursor {}", this );
+ browser.close();
}
-
- super.close( cause );
}
}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotDnIndex.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotDnIndex.java
new file mode 100644
index 0000000..49be1b5
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotDnIndex.java
@@ -0,0 +1,123 @@
+/*
+ * 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.mavibot;
+
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+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.MatchingRule;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator;
+import org.apache.directory.mavibot.btree.serializer.StringSerializer;
+import org.apache.directory.server.i18n.I18n;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A special index which stores DN objects.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class MavibotDnIndex extends MavibotIndex<Dn>
+{
+
+ /** A logger for this class */
+ private static final Logger LOG = LoggerFactory.getLogger( MavibotDnIndex.class );
+
+
+ public MavibotDnIndex( String oid )
+ {
+ super( oid, true );
+ initialized = false;
+ }
+
+
+ public void init( SchemaManager schemaManager, AttributeType attributeType ) throws IOException
+ {
+ LOG.debug( "Initializing an Index for attribute '{}'", attributeType.getName() );
+
+ this.attributeType = attributeType;
+
+ if ( attributeId == null )
+ {
+ setAttributeId( attributeType.getName() );
+ }
+
+ if ( this.wkDirPath == null )
+ {
+ NullPointerException e = new NullPointerException( "The index working directory has not be set" );
+ throw e;
+ }
+
+ try
+ {
+ initTables( schemaManager );
+ }
+ catch ( IOException e )
+ {
+ // clean up
+ close();
+ throw e;
+ }
+
+ initialized = true;
+ }
+
+
+ private void initTables( SchemaManager schemaManager ) throws IOException
+ {
+ MatchingRule mr = attributeType.getEquality();
+
+ if ( mr == null )
+ {
+ throw new IOException( I18n.err( I18n.ERR_574, attributeType.getName() ) );
+ }
+
+ /*
+ * The forward key/value map stores attribute values to master table
+ * primary keys. A value for an attribute can occur several times in
+ * different entries so the forward map can have more than one value.
+ */
+ UuidComparator.INSTANCE.setSchemaManager( schemaManager );
+
+ DnSerializer dnSerializer = new DnSerializer();
+
+ String forwardTableName = attributeType.getOid() + FORWARD_BTREE;
+ forward = new MavibotTable<Dn, String>( recordMan, schemaManager, forwardTableName, dnSerializer,
+ StringSerializer.INSTANCE, true );
+
+ String reverseTableName = attributeType.getOid() + REVERSE_BTREE;
+ reverse = new MavibotTable<String, Dn>( recordMan, schemaManager, reverseTableName, StringSerializer.INSTANCE,
+ dnSerializer, !attributeType.isSingleValued() );
+
+ String path = new File( this.wkDirPath, attributeType.getOid() ).getAbsolutePath();
+ // finally write a text file in the format <OID>-<attribute-name>.txt
+ FileWriter fw = new FileWriter( new File( path + "-" + attributeType.getName() + ".txt" ) );
+ // write the AttributeType description
+ fw.write( attributeType.toString() );
+ fw.close();
+ }
+
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotEntrySerializer.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotEntrySerializer.java
new file mode 100644
index 0000000..96fdf00
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotEntrySerializer.java
@@ -0,0 +1,382 @@
+/*
+ * 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.mavibot;
+
+
+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 java.nio.ByteBuffer;
+import java.util.Comparator;
+
+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.mavibot.btree.serializer.AbstractElementSerializer;
+import org.apache.directory.mavibot.btree.serializer.BufferHandler;
+import org.apache.directory.server.i18n.I18n;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Serialize and deserialize a ServerEntry. There is a big difference with the standard
+ * Entry serialization : we don't serialize the entry's Dn, we just serialize it's Rdn.
+ * </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 MavibotEntrySerializer extends AbstractElementSerializer<Entry>
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ /** the logger for this class */
+ private static final Logger LOG = LoggerFactory.getLogger( MavibotEntrySerializer.class );
+
+ /**
+ * Speedup for logs
+ */
+ private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+ /** The schemaManager reference */
+ private static SchemaManager schemaManager;
+
+ private static class EntryComparator implements Comparator<Entry>
+ {
+
+ @Override
+ public int compare( Entry entry1, Entry entry2 )
+ {
+ return entry1.getDn().getName().compareTo( entry1.getDn().getName() );
+ }
+
+ }
+
+ private static Comparator<Entry> comparator = new EntryComparator();
+
+
+ /**
+ * Creates a new instance of ServerEntrySerializer.
+ * The schemaManager MUST be set explicitly using the static {@link #setSchemaManager(SchemaManager)}
+ */
+ public MavibotEntrySerializer()
+ {
+ super( comparator );
+ }
+
+
+ @Override
+ public Comparator<Entry> getComparator()
+ {
+ return comparator;
+ }
+
+
+ /**
+ * <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 )
+ {
+ try
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ ObjectOutput 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() )
+ {
+ AttributeType attributeType = attribute.getAttributeType();
+
+ // Write the oid to be able to restore the AttributeType when deserializing
+ // the attribute
+ String oid = attributeType.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 );
+ }
+
+ byte[] bytes = baos.toByteArray();
+
+ return bytes;
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+
+ /**
+ * 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( ByteBuffer buffer ) throws IOException
+ {
+ // read the length
+ int len = buffer.limit();
+
+ ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( buffer.array(), buffer.position(), len ) );
+
+ 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 );
+ }
+ }
+
+ buffer.position( buffer.position() + len ); // previous position + length
+
+ return entry;
+ }
+ catch ( ClassNotFoundException cnfe )
+ {
+ LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
+ throw new IOException( cnfe.getLocalizedMessage() );
+ }
+ }
+
+
+ @Override
+ public Entry deserialize( BufferHandler bufferHandler ) throws IOException
+ {
+ return deserialize( ByteBuffer.wrap( bufferHandler.getBuffer() ) );
+ }
+
+
+ public static void setSchemaManager( SchemaManager schemaManager )
+ {
+ MavibotEntrySerializer.schemaManager = schemaManager;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Entry fromBytes( byte[] buffer ) throws IOException
+ {
+ return fromBytes( buffer, 0 );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Entry fromBytes( byte[] buffer, int pos ) throws IOException
+ {
+ // read the length
+ int len = buffer.length - pos;
+
+ ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( buffer, pos, len ) );
+
+ 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() );
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<?> getType()
+ {
+ return Entry.class;
+ }
+}
diff --git a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotIndex.java
similarity index 65%
copy from jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
copy to mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotIndex.java
index 90f84e4..e295bad 100644
--- a/jdbm-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotIndex.java
@@ -6,32 +6,27 @@
* 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.jdbm;
+package org.apache.directory.server.core.partition.impl.btree.mavibot;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
+import java.util.Comparator;
-import jdbm.RecordManager;
-import jdbm.helper.ByteArraySerializer;
-import jdbm.helper.MRU;
-import jdbm.recman.BaseRecordManager;
-import jdbm.recman.CacheRecordManager;
-import jdbm.recman.TransactionManager;
-
+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.EmptyCursor;
import org.apache.directory.api.ldap.model.cursor.Tuple;
@@ -40,7 +35,11 @@ import org.apache.directory.api.ldap.model.schema.AttributeType;
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.SerializableComparator;
-import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator;
+import org.apache.directory.mavibot.btree.RecordManager;
+import org.apache.directory.mavibot.btree.serializer.ByteArraySerializer;
+import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
+import org.apache.directory.mavibot.btree.serializer.StringSerializer;
+import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
import org.apache.directory.server.core.partition.impl.btree.IndexCursorAdaptor;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.xdbm.AbstractIndex;
@@ -51,18 +50,16 @@ import org.slf4j.LoggerFactory;
/**
- * A Jdbm based index implementation. It creates an Index for a give AttributeType.
+ * A Mavibot based index implementation. It creates an Index for a give AttributeType.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
-public class JdbmIndex<K> extends AbstractIndex<K, String>
+public class MavibotIndex<K> extends AbstractIndex<K, String>
{
/** A logger for this class */
- private static final Logger LOG = LoggerFactory.getLogger( JdbmIndex.class );
+ private static final Logger LOG = LoggerFactory.getLogger( MavibotIndex.class.getSimpleName() );
- /**
- * default duplicate limit before duplicate keys switch to using a btree for values
- */
+ /** default duplicate limit before duplicate keys switch to using a btree for values */
public static final int DEFAULT_DUPLICATE_LIMIT = 512;
/** the key used for the forward btree name */
@@ -76,46 +73,21 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
* the value of the btree is the entry id of the entry containing an attribute with
* that value
*/
- protected JdbmTable<K, String> forward;
+ protected MavibotTable<K, String> forward;
/**
* the reverse btree where the btree key is the entry id of the entry containing a
* value for the indexed attribute, and the btree value is the value of the indexed
* attribute
*/
- protected JdbmTable<String, K> reverse;
-
- /**
- * the JDBM record manager for the file containing this index
- */
- protected RecordManager recMan;
-
- /**
- * duplicate limit before duplicate keys switch to using a btree for values
- */
- protected int numDupLimit = DEFAULT_DUPLICATE_LIMIT;
+ protected MavibotTable<String, K> reverse;
/** a custom working directory path when specified in configuration */
protected File wkDirPath;
+ /** The recordManager */
+ protected RecordManager recordMan;
- /*
- * NOTE: Duplicate Key Limit
- *
- * Jdbm cannot store duplicate keys: meaning it cannot have more than one value
- * for the same key in the btree. Thus as a workaround we stuff values for the
- * same key into a TreeSet. This is only effective up to some threshold after
- * which we run into problems with serialization on and off disk. A threshold
- * is used to determine when to switch from using a TreeSet to start using another
- * btree in the same index file just for the values. This value only btree just
- * has keys populated without a value for it's btree entries. When the switch
- * occurs the value for the key in the index btree contains a pointer to the
- * btree containing it's values.
- *
- * This numDupLimit is the threshold at which we switch from using in memory
- * containers for values of the same key to using a btree for those values
- * instead with indirection.
- */
// ------------------------------------------------------------------------
// C O N S T R U C T O R S
@@ -123,7 +95,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
/**
* Creates a JdbmIndex instance for a give AttributeId
*/
- public JdbmIndex( String attributeId, boolean withReverse )
+ public MavibotIndex( String attributeId, boolean withReverse )
{
super( attributeId, withReverse );
@@ -133,7 +105,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
/**
* Initialize the index for an Attribute, with a specific working directory (may be null).
- *
+ *
* @param schemaManager The schemaManager to use to get back the Attribute
* @param attributeType The attributeType this index is created for
* @throws IOException If the initialization failed
@@ -142,6 +114,12 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
{
LOG.debug( "Initializing an Index for attribute '{}'", attributeType.getName() );
+ // check if the RecordManager reference is null, if yes, then throw an IllegalStateException
+ if ( recordMan == null )
+ {
+ throw new IllegalStateException( "No RecordManager reference was set in the index " + getAttributeId() );
+ }
+
this.attributeType = attributeType;
if ( attributeId == null )
@@ -151,28 +129,9 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
if ( this.wkDirPath == null )
{
- NullPointerException e = new NullPointerException( "The index working directory has not be set" );
-
- throw e;
+ throw new NullPointerException( "The index working directory has not be set" );
}
- String path = new File( this.wkDirPath, attributeType.getOid() ).getAbsolutePath();
-
- BaseRecordManager base = new BaseRecordManager( path );
- TransactionManager transactionManager = base.getTransactionManager();
- transactionManager.setMaximumTransactionsInLog( 2000 );
-
- // see DIRSERVER-2002
- // prevent the OOM when more than 50k users are loaded at a stretch
- // adding this system property to make it configurable till JDBM gets replaced by Mavibot
- String cacheSizeVal = System.getProperty( "jdbm.recman.cache.size", "100" );
-
- int recCacheSize = Integer.parseInt( cacheSizeVal );
-
- LOG.info( "Setting CacheRecondManager's cache size to {}", recCacheSize );
-
- recMan = new CacheRecordManager( base, new MRU( recCacheSize ) );
-
try
{
initTables( schemaManager );
@@ -184,27 +143,19 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
throw e;
}
- // finally write a text file in the format <OID>-<attribute-name>.txt
- FileWriter fw = new FileWriter( new File( path + "-" + attributeType.getName() + ".txt" ) );
- // write the AttributeType description
- fw.write( attributeType.toString() );
- fw.close();
-
initialized = true;
}
/**
* Initializes the forward and reverse tables used by this Index.
- *
+ *
* @param schemaManager The server schemaManager
* @throws IOException if we cannot initialize the forward and reverse
* tables
*/
private void initTables( SchemaManager schemaManager ) throws IOException
{
- SerializableComparator<K> comp;
-
MatchingRule mr = attributeType.getEquality();
if ( mr == null )
@@ -212,29 +163,39 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
throw new IOException( I18n.err( I18n.ERR_574, attributeType.getName() ) );
}
- comp = new SerializableComparator<K>( mr.getOid() );
+ SerializableComparator<K> comp = new SerializableComparator<>( mr.getOid() );
+ comp.setSchemaManager( schemaManager );
/*
* The forward key/value map stores attribute values to master table
* primary keys. A value for an attribute can occur several times in
* different entries so the forward map can have more than one value.
*/
- UuidComparator.INSTANCE.setSchemaManager( schemaManager );
- comp.setSchemaManager( schemaManager );
- if ( mr.getSyntax().isHumanReadable() )
+ ElementSerializer<K> forwardKeySerializer = null;
+
+ if ( !attributeType.getSyntax().isHumanReadable() )
{
- forward = new JdbmTable<K, String>( schemaManager, attributeType.getOid() + FORWARD_BTREE, numDupLimit,
- recMan,
- comp, UuidComparator.INSTANCE, StringSerializer.INSTANCE, UuidSerializer.INSTANCE );
+ forwardKeySerializer = ( ElementSerializer<K> ) new ByteArraySerializer( ( Comparator<byte[]> ) comp );
}
else
{
- forward = new JdbmTable<K, String>( schemaManager, attributeType.getOid() + FORWARD_BTREE, numDupLimit,
- recMan,
- comp, UuidComparator.INSTANCE, new ByteArraySerializer(), UuidSerializer.INSTANCE );
+ forwardKeySerializer = ( ElementSerializer<K> ) new StringSerializer( ( Comparator<String> ) comp );
+ }
+
+ boolean forwardDups = true;
+
+ String oid = attributeType.getOid();
+ // disable duplicates for entryCSN and entryUUID attribute indices
+ if ( oid.equals( SchemaConstants.ENTRY_CSN_AT_OID ) || oid.equals( SchemaConstants.ENTRY_UUID_AT_OID ) )
+ {
+ forwardDups = false;
}
+ String forwardTableName = attributeType.getOid() + FORWARD_BTREE;
+ forward = new MavibotTable<>( recordMan, schemaManager, forwardTableName, forwardKeySerializer,
+ StringSerializer.INSTANCE, forwardDups, AbstractBTreePartition.DEFAULT_CACHE_SIZE );
+
/*
* Now the reverse map stores the primary key into the master table as
* the key and the values of attributes as the value. If an attribute
@@ -243,49 +204,37 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
*/
if ( withReverse )
{
- if ( attributeType.isSingleValued() )
- {
- reverse = new JdbmTable<String, K>( schemaManager, attributeType.getOid() + REVERSE_BTREE, recMan,
- UuidComparator.INSTANCE, UuidSerializer.INSTANCE, null );
- }
- else
- {
- reverse = new JdbmTable<String, K>( schemaManager, attributeType.getOid() + REVERSE_BTREE, numDupLimit,
- recMan,
- UuidComparator.INSTANCE, comp, UuidSerializer.INSTANCE, null );
- }
+ String reverseTableName = attributeType.getOid() + REVERSE_BTREE;
+ reverse = new MavibotTable<>( recordMan, schemaManager, reverseTableName, StringSerializer.INSTANCE,
+ forwardKeySerializer, !attributeType.isSingleValued() );
}
- }
-
- // ------------------------------------------------------------------------
- // C O N F I G U R A T I O N M E T H O D S
- // ------------------------------------------------------------------------
- /**
- * Gets the threshold at which point duplicate keys use btree indirection to store
- * their values.
- *
- * @return the threshold for storing a keys values in another btree
- */
- public int getNumDupLimit()
- {
- return numDupLimit;
+ String path = new File( this.wkDirPath, attributeType.getOid() ).getAbsolutePath();
+
+ // finally write a text file in the format <OID>-<attribute-name>.txt
+ try ( FileWriter fw = new FileWriter( new File( path + "-" + attributeType.getName() + ".txt" ) ) )
+ {
+ // write the AttributeType description
+ fw.write( attributeType.toString() );
+ }
}
/**
- * Sets the threshold at which point duplicate keys use btree indirection to store
- * their values.
+ * Sets the RecordManager
*
- * @param numDupLimit the threshold for storing a keys values in another btree
+ * @param rm the RecordManager instance
*/
- public void setNumDupLimit( int numDupLimit )
+ public void setRecordManager( RecordManager rm )
{
- protect( "numDupLimit" );
- this.numDupLimit = numDupLimit;
+ this.recordMan = rm;
}
+ // ------------------------------------------------------------------------
+ // C O N F I G U R A T I O N M E T H O D S
+ // ------------------------------------------------------------------------
+
/**
* Sets the working directory path to something other than the default. Sometimes more
* performance is gained by locating indices on separate disk spindles.
@@ -294,7 +243,6 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
*/
public void setWkDirPath( URI wkDirPath )
{
- //.out.println( "IDX Defining a WorkingDir : " + wkDirPath );
protect( "wkDirPath" );
this.wkDirPath = new File( wkDirPath );
}
@@ -386,7 +334,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
*/
public synchronized void add( K attrVal, String id ) throws Exception
{
- // The pair to be added must exists
+ // The pair to be removed must exists
forward.put( attrVal, id );
if ( withReverse )
@@ -456,11 +404,11 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
{
if ( withReverse )
{
- return new IndexCursorAdaptor<K>( ( Cursor ) reverse.cursor(), false );
+ return new IndexCursorAdaptor<>( ( Cursor ) reverse.cursor(), false );
}
else
{
- return new EmptyIndexCursor<K>();
+ return new EmptyIndexCursor<>();
}
}
@@ -477,11 +425,11 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
{
if ( withReverse )
{
- return new IndexCursorAdaptor<K>( ( Cursor ) reverse.cursor( id ), false );
+ return new IndexCursorAdaptor<>( ( Cursor ) reverse.cursor( id ), false );
}
else
{
- return new EmptyIndexCursor<K>();
+ return new EmptyIndexCursor<>();
}
}
@@ -489,7 +437,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
@SuppressWarnings("unchecked")
public Cursor<IndexEntry<K, String>> forwardCursor( K key ) throws Exception
{
- return new IndexCursorAdaptor<K>( ( Cursor ) forward.cursor( key ), true );
+ return new IndexCursorAdaptor<>( ( Cursor ) forward.cursor( key ), true );
}
@@ -501,7 +449,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
}
else
{
- return new EmptyCursor<K>();
+ return new EmptyCursor<>();
}
}
@@ -536,7 +484,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
/**
* {@inheritDoc}
*/
- public boolean reverse( String id ) throws Exception
+ public boolean reverse( String id ) throws LdapException
{
if ( withReverse )
{
@@ -552,7 +500,7 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
/**
* {@inheritDoc}
*/
- public boolean reverse( String id, K attrVal ) throws Exception
+ public boolean reverse( String id, K attrVal ) throws LdapException
{
return forward.has( attrVal, id );
}
@@ -566,18 +514,22 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
*/
public synchronized void close() throws IOException
{
- if ( forward != null )
+ try
{
- forward.close();
- }
+ if ( forward != null )
+ {
+ forward.close();
+ }
- if ( reverse != null )
+ if ( reverse != null )
+ {
+ reverse.close();
+ }
+ }
+ catch ( Exception e )
{
- reverse.close();
+ throw new IOException( e );
}
-
- commit( recMan );
- recMan.close();
}
@@ -586,31 +538,14 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
*/
public synchronized void sync() throws IOException
{
- // Commit
- recMan.commit();
-
- // And flush the journal
- if ( ( commitNumber.get() % 4000 ) == 0 )
- {
- BaseRecordManager baseRecordManager = null;
-
- if ( recMan instanceof CacheRecordManager )
- {
- baseRecordManager = ( ( BaseRecordManager ) ( ( CacheRecordManager ) recMan ).getRecordManager() );
- }
- else
- {
- baseRecordManager = ( ( BaseRecordManager ) recMan );
- }
-
- baseRecordManager.getTransactionManager().synchronizeLog();
- }
+ // Nothing to do
}
/**
* {@inheritDoc}
*/
+ @Override
public boolean isDupsEnabled()
{
if ( withReverse )
@@ -625,20 +560,6 @@ public class JdbmIndex<K> extends AbstractIndex<K, String>
/**
- * Commit the modification on disk
- *
- * @param recordManager The recordManager used for the commit
- */
- private void commit( RecordManager recordManager ) throws IOException
- {
- if ( commitNumber.incrementAndGet() % 2000 == 0 )
- {
- sync();
- }
- }
-
-
- /**
* @see Object#toString()
*/
public String toString()
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotMasterTable.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotMasterTable.java
new file mode 100644
index 0000000..67bfb61
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotMasterTable.java
@@ -0,0 +1,58 @@
+/*
+ * 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.mavibot;
+
+
+import java.io.IOException;
+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.mavibot.btree.RecordManager;
+import org.apache.directory.mavibot.btree.serializer.StringSerializer;
+import org.apache.directory.server.xdbm.MasterTable;
+
+
+/**
+ * TODO MavibotMasterTable.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class MavibotMasterTable extends MavibotTable<String, Entry> implements MasterTable
+{
+ public MavibotMasterTable( RecordManager recordMan, SchemaManager schemaManager, String name, int cacheSize )
+ throws IOException
+ {
+ super( recordMan, schemaManager, name, StringSerializer.INSTANCE, new MavibotEntrySerializer(), false, cacheSize );
+ }
+
+ public MavibotMasterTable( RecordManager recordMan, SchemaManager schemaManager, String name )
+ throws IOException
+ {
+ super( recordMan, schemaManager, name, StringSerializer.INSTANCE, new MavibotEntrySerializer(), false );
+ }
+
+
+ @Override
+ public String getNextId( Entry entry ) throws Exception
+ {
+ return UUID.randomUUID().toString();
+ }
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotParentIdAndRdnSerializer.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotParentIdAndRdnSerializer.java
new file mode 100644
index 0000000..dd18529
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotParentIdAndRdnSerializer.java
@@ -0,0 +1,302 @@
+/*
+ * 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.mavibot;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Comparator;
+
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.name.Rdn;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.util.Serialize;
+import org.apache.directory.mavibot.btree.serializer.AbstractElementSerializer;
+import org.apache.directory.mavibot.btree.serializer.BufferHandler;
+import org.apache.directory.mavibot.btree.util.Strings;
+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 MavibotParentIdAndRdnSerializer extends AbstractElementSerializer<ParentIdAndRdn>
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 1L;
+
+ /** the logger for this class */
+ private static final Logger LOG = LoggerFactory.getLogger( MavibotParentIdAndRdnSerializer.class );
+
+ /**
+ * Speedup for logs
+ */
+ private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+ /** The schemaManager reference */
+ private static SchemaManager schemaManager;
+
+ private static Comparator<ParentIdAndRdn> comparator = new Comparator<ParentIdAndRdn>()
+ {
+
+ @Override
+ public int compare( ParentIdAndRdn rdn1, ParentIdAndRdn rdn2 )
+ {
+ return rdn1.compareTo( rdn2 );
+ }
+
+ };
+
+
+ /**
+ * Creates a new instance of ParentIdAndRdnSerializer.
+ * The schemaManager MUST be set explicitly using the static {@link #setSchemaManager(SchemaManager)}
+ */
+ public MavibotParentIdAndRdnSerializer()
+ {
+ super( comparator );
+ }
+
+
+ /**
+ * This is the place where we serialize ParentIdAndRdn. The format is the following :<br/>
+ * <ul>
+ * <li>length</li>
+ * <li>the RDN</li>
+ * <li>the parent ID</li>
+ * <li>Number of children</li>
+ * <li>Number of descendant</li>
+ * <li></li>
+ * <li></li>
+ * <li></li>
+ * </ul>
+ */
+ public byte[] serialize( ParentIdAndRdn parentIdAndRdn )
+ {
+ try
+ {
+ int bufferSize = 1024;
+
+ while ( bufferSize < Integer.MAX_VALUE )
+ {
+ // allocate a big enough buffer for most of the cases
+ byte[] buffer = new byte[bufferSize];
+
+ try
+ {
+ // The current position.
+ int pos = 0;
+
+ // First, the Dn
+ Rdn[] rdns;
+
+ try
+ {
+ rdns = parentIdAndRdn.getRdns();
+ }
+ catch ( NullPointerException npe )
+ {
+ throw npe;
+ }
+
+ // Write the Rdn of the Dn
+ // The number of RDN (we may have more than one)
+ if ( ( rdns == null ) || ( rdns.length == 0 ) )
+ {
+ pos = Serialize.serialize( 0, buffer, pos );
+ }
+ else
+ {
+ pos = Serialize.serialize( rdns.length, buffer, pos );
+
+ for ( Rdn rdn : rdns )
+ {
+ pos = rdn.serialize( buffer, pos );
+ }
+ }
+
+ // Then the parentId.
+ String parentId = parentIdAndRdn.getParentId();
+ byte[] parentIdBytes = Strings.getBytesUtf8( parentId );
+ pos = Serialize.serialize( parentIdBytes, buffer, pos );
+
+ // The number of children
+ pos = Serialize.serialize( parentIdAndRdn.getNbChildren(), buffer, pos );
+
+ // The number of descendants
+ pos = Serialize.serialize( parentIdAndRdn.getNbDescendants(), buffer, pos );
+
+ if ( IS_DEBUG )
+ {
+ LOG.debug( ">------------------------------------------------" );
+ LOG.debug( "Serialize " + parentIdAndRdn );
+ }
+
+
+ // Copy the serialized data
+ byte[] result = new byte[pos];
+ System.arraycopy( buffer, 0, result, 0, pos );
+
+ return result;
+ }
+ catch ( ArrayIndexOutOfBoundsException aioobe )
+ {
+ // Bad luck, try with a bigger buffer
+ bufferSize += bufferSize;
+ }
+ }
+
+ // No reason we should reach this point
+ throw new RuntimeException();
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+
+ /**
+ * 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( BufferHandler bufferHandler ) throws IOException
+ {
+ return deserialize( ByteBuffer.wrap( bufferHandler.getBuffer() ) );
+ }
+
+
+ @Override
+ public ParentIdAndRdn deserialize( ByteBuffer buffer ) throws IOException
+ {
+ ParentIdAndRdn parentIdAndRdn = fromBytes( buffer.array(), buffer.position() );
+
+ return parentIdAndRdn;
+ }
+
+
+ @Override
+ public int compare( ParentIdAndRdn type1, ParentIdAndRdn type2 )
+ {
+ return type1.compareTo( type2 );
+ }
+
+
+ @Override
+ public Comparator<ParentIdAndRdn> getComparator()
+ {
+ return comparator;
+ }
+
+
+ public static void setSchemaManager( SchemaManager schemaManager )
+ {
+ MavibotParentIdAndRdnSerializer.schemaManager = schemaManager;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ParentIdAndRdn fromBytes( byte[] buffer ) throws IOException
+ {
+ return fromBytes( buffer, 0 );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ParentIdAndRdn fromBytes( byte[] buffer, int pos ) throws IOException
+ {
+ try
+ {
+ ParentIdAndRdn parentIdAndRdn = new ParentIdAndRdn();
+
+ // Read the number of rdns, if any
+ int nbRdns = Serialize.deserializeInt( buffer, pos );
+ pos += 4;
+
+ 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 );
+ pos = rdn.deserialize( buffer, pos );
+ rdns[i] = rdn;
+ }
+
+ parentIdAndRdn.setRdns( rdns );
+ }
+
+ // Read the parent ID
+ byte[] uuidBytes = Serialize.deserializeBytes( buffer, pos );
+ pos += 4 + uuidBytes.length;
+ String uuid = Strings.utf8ToString( uuidBytes );
+
+ parentIdAndRdn.setParentId( uuid );
+
+ // Read the number of children and descendants
+ int nbChildren = Serialize.deserializeInt( buffer, pos );
+ pos += 4;
+
+ int nbDescendants = Serialize.deserializeInt( buffer, pos );
+ pos += 4;
+
+ parentIdAndRdn.setNbChildren( nbChildren );
+ parentIdAndRdn.setNbDescendants( nbDescendants );
+
+ return parentIdAndRdn;
+ }
+ catch ( LdapInvalidAttributeValueException cnfe )
+ {
+ LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
+ throw new IOException( cnfe.getLocalizedMessage() );
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<?> getType()
+ {
+ return ParentIdAndRdn.class;
+ }
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotPartition.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotPartition.java
new file mode 100644
index 0000000..c9c83be
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotPartition.java
@@ -0,0 +1,540 @@
+/*
+ * 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.mavibot;
+
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.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.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.util.exception.MultiException;
+import org.apache.directory.mavibot.btree.RecordManager;
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.api.DnFactory;
+import org.apache.directory.server.core.api.entry.ClonedServerEntry;
+import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.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.partition.impl.btree.AbstractBTreePartition;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.server.xdbm.Index;
+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;
+
+
+/**
+ * The Mavibot backed partition.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class MavibotPartition extends AbstractBTreePartition
+{
+ /** static logger */
+ private static final Logger LOG = LoggerFactory.getLogger( MavibotPartition.class );
+
+ private static final String MAVIBOT_DB_FILE_EXTN = ".data";
+
+ private static final FilenameFilter DB_FILTER = ( dir, name ) -> name.endsWith( MAVIBOT_DB_FILE_EXTN ) && !name.startsWith( "master." );
+
+ private RecordManager recordMan;
+
+ /** the entry cache */
+ private Cache entryCache;
+
+
+ public MavibotPartition( SchemaManager schemaManager, DnFactory dnFactory )
+ {
+ super( schemaManager, dnFactory );
+
+ MavibotEntrySerializer.setSchemaManager( schemaManager );
+
+ // Initialize the cache size
+ 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 );
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void doRepair() throws Exception
+ {
+ // Nothing to do
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void doInit() throws Exception
+ {
+ if ( !initialized )
+ {
+ // setup optimizer and registries for parent
+ if ( !isOptimizerEnabled() )
+ {
+ setOptimizer( new NoOpOptimizer() );
+ }
+ else
+ {
+ setOptimizer( new DefaultOptimizer<Entry>( this ) );
+ }
+
+ EvaluatorBuilder evaluatorBuilder = new EvaluatorBuilder( this, schemaManager );
+ CursorBuilder cursorBuilder = new CursorBuilder( this, evaluatorBuilder );
+
+ setSearchEngine( new DefaultSearchEngine( this, cursorBuilder, evaluatorBuilder, getOptimizer() ) );
+
+ // Create the underlying directories (only if needed)
+ File partitionDir = new File( getPartitionPath() );
+
+ if ( !partitionDir.exists() && !partitionDir.mkdirs() )
+ {
+ throw new IOException( I18n.err( I18n.ERR_112_COULD_NOT_CREATE_DIRECORY, partitionDir ) );
+ }
+
+ 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 );
+ }
+
+ recordMan = new RecordManager( partitionDir.getPath() );
+
+ // Initialize the indexes
+ super.doInit();
+
+ // First, check if the file storing the data exists
+
+ // Create the master table (the table containing all the entries)
+ Cache masterTableCache = cacheService.getCache( suffixDn.getName() );
+ master = new MavibotMasterTable( recordMan, schemaManager, "master", masterTableCache.getCacheConfiguration().getMaxElementsInMemory() );
+
+ // get all index db files first
+ File[] allIndexDbFiles = partitionDir.listFiles( DB_FILTER );
+
+ // get the names of the db files also
+ List<String> indexDbFileNameList = Arrays.asList( partitionDir.list( DB_FILTER ) );
+
+ // then add all index objects to a list
+ List<String> allIndices = new ArrayList<String>();
+
+ for ( Index<?, String> index : systemIndices.values() )
+ {
+ allIndices.add( index.getAttribute().getOid() );
+ }
+
+ List<Index<?, String>> indexToBuild = new ArrayList<Index<?, String>>();
+
+ // this loop is used for two purposes
+ // one for collecting all user indices
+ // two for finding a new index to be built
+ // just to avoid another iteration for determining which is the new index
+ /* FIXME the below code needs to be modified to suit Mavibot
+ for ( Index<?, Entry, String> index : userIndices.values() )
+ {
+ String indexOid = index.getAttribute().getOid();
+ allIndices.add( indexOid );
+
+ // take the part after removing .db from the
+ String name = indexOid + MAVIBOT_DB_FILE_EXTN;
+
+ // if the name doesn't exist in the list of index DB files
+ // this is a new index and we need to build it
+ if ( !indexDbFileNameList.contains( name ) )
+ {
+ indexToBuild.add( index );
+ }
+ }
+
+ if ( indexToBuild.size() > 0 )
+ {
+ buildUserIndex( indexToBuild );
+ }
+
+ deleteUnusedIndexFiles( allIndices, allIndexDbFiles );
+ */
+
+ if ( cacheService != null )
+ {
+ entryCache = cacheService.getCache( getId() );
+
+ int cacheSizeConfig = entryCache.getCacheConfiguration().getMaxElementsInMemory();
+
+ if ( cacheSizeConfig < cacheSize )
+ {
+ entryCache.getCacheConfiguration().setMaxElementsInMemory( cacheSize );
+ }
+ }
+
+ // We are done !
+ initialized = true;
+ }
+ }
+
+
+ @Override
+ protected Index<?, String> convertAndInit( Index<?, String> index ) throws Exception
+ {
+ MavibotIndex<?> mavibotIndex;
+
+ if ( index instanceof MavibotRdnIndex )
+ {
+ mavibotIndex = ( MavibotRdnIndex ) index;
+ }
+ else if ( index instanceof MavibotDnIndex )
+ {
+ mavibotIndex = ( MavibotDnIndex ) index;
+ }
+ else if ( index instanceof MavibotIndex<?> )
+ {
+ mavibotIndex = ( MavibotIndex<?> ) index;
+
+ if ( mavibotIndex.getWkDirPath() == null )
+ {
+ mavibotIndex.setWkDirPath( partitionPath );
+ }
+ }
+ else
+ {
+ LOG.debug( "Supplied index {} is not a MavibotIndex. "
+ + "Will create new MavibotIndex using copied configuration parameters.", index );
+ mavibotIndex = new MavibotIndex( index.getAttributeId(), true );
+ mavibotIndex.setCacheSize( index.getCacheSize() );
+ mavibotIndex.setWkDirPath( index.getWkDirPath() );
+ }
+
+ mavibotIndex.setRecordManager( recordMan );
+
+ mavibotIndex.init( schemaManager, schemaManager.lookupAttributeTypeRegistry( index.getAttributeId() ) );
+
+ return mavibotIndex;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ protected synchronized void doDestroy() throws Exception
+ {
+ MultiException errors = new MultiException( I18n.err( I18n.ERR_577 ) );
+
+ if ( !initialized )
+ {
+ return;
+ }
+
+ try
+ {
+ super.doDestroy();
+ }
+ catch ( Exception e )
+ {
+ errors.addThrowable( e );
+ }
+
+ // This is specific to the MAVIBOT store : close the record manager
+ try
+ {
+ recordMan.close();
+ LOG.debug( "Closed record manager for {} partition.", suffixDn );
+ }
+ catch ( Throwable t )
+ {
+ LOG.error( I18n.err( I18n.ERR_127 ), t );
+ errors.addThrowable( t );
+ }
+ finally
+ {
+ if ( entryCache != null )
+ {
+ entryCache.removeAll();
+ }
+ }
+
+ if ( errors.size() > 0 )
+ {
+ throw errors;
+ }
+ }
+
+
+ @Override
+ protected Index createSystemIndex( String indexOid, URI path, boolean withReverse ) throws Exception
+ {
+ LOG.debug( "Supplied index {} is not a MavibotIndex. "
+ + "Will create new MavibotIndex using copied configuration parameters." );
+ MavibotIndex<?> mavibotIndex;
+
+ if ( indexOid.equals( ApacheSchemaConstants.APACHE_RDN_AT_OID ) )
+ {
+ mavibotIndex = new MavibotRdnIndex();
+ mavibotIndex.setAttributeId( ApacheSchemaConstants.APACHE_RDN_AT_OID );
+ }
+ else if ( indexOid.equals( ApacheSchemaConstants.APACHE_ALIAS_AT_OID ) )
+ {
+ mavibotIndex = new MavibotDnIndex( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
+ mavibotIndex.setAttributeId( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
+ }
+ else
+ {
+ mavibotIndex = new MavibotIndex( indexOid, withReverse );
+ }
+
+ mavibotIndex.setWkDirPath( path );
+
+ return mavibotIndex;
+ }
+
+
+ @Override
+ public void sync() throws Exception
+ {
+ if ( !initialized )
+ {
+ return;
+ }
+
+ // Sync all system indices
+ for ( Index<?, String> idx : systemIndices.values() )
+ {
+ idx.sync();
+ }
+
+ // Sync all user defined userIndices
+ for ( Index<?, String> idx : userIndices.values() )
+ {
+ idx.sync();
+ }
+
+ ( ( MavibotMasterTable ) master ).sync();
+ }
+
+
+ /**jdbm
+ * removes any unused/removed attribute index files present under the partition's
+ * working directory
+ */
+ private void deleteUnusedIndexFiles( List<String> allIndices, File[] dbFiles )
+ {
+
+ }
+
+
+ /**
+ * Builds user defined indexes on a attributes by browsing all the entries present in master db
+ *
+ * @param userIndexes then user defined indexes to create
+ * @throws Exception in case of any problems while building the index
+ */
+ private void buildUserIndex( List<Index<?, String>> userIndexes ) throws Exception
+ {
+ Cursor<Tuple<String, Entry>> cursor = master.cursor();
+ cursor.beforeFirst();
+
+ while ( cursor.next() )
+ {
+ for ( Index index : userIndexes )
+ {
+ AttributeType atType = index.getAttribute();
+
+ String attributeOid = index.getAttribute().getOid();
+
+ LOG.info( "building the index for attribute type {}", atType );
+
+ Tuple<String, Entry> tuple = cursor.get();
+
+ String id = tuple.getKey();
+ Entry entry = tuple.getValue();
+
+ Attribute entryAttr = entry.get( atType );
+
+ if ( entryAttr != null )
+ {
+ for ( Value<?> value : entryAttr )
+ {
+ index.add( value.getValue(), id );
+ }
+
+ // Adds only those attributes that are indexed
+ presenceIdx.add( attributeOid, id );
+ }
+ }
+ }
+
+ cursor.close();
+ }
+
+
+ /**
+ * {@inheritDoc}}
+ */
+ public String getDefaultId()
+ {
+ return Partition.DEFAULT_ID;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getRootId()
+ {
+ return Partition.ROOT_ID;
+ }
+
+
+ public RecordManager getRecordMan()
+ {
+ return recordMan;
+ }
+
+
+ public Entry lookupCache( String id )
+ {
+ if ( entryCache == null )
+ {
+ return null;
+ }
+
+ Element el = entryCache.get( id );
+
+ if ( el != null )
+ {
+ return ( Entry ) el.getValue();
+ }
+
+ return null;
+ }
+
+
+ @Override
+ public void addToCache( String id, Entry entry )
+ {
+ if ( entryCache == null )
+ {
+ return;
+ }
+
+ if ( entry instanceof ClonedServerEntry )
+ {
+ entry = ( ( ClonedServerEntry ) entry ).getOriginalEntry();
+ }
+
+ entryCache.put( new Element( id, entry ) );
+ }
+
+
+ @Override
+ public void updateCache( OperationContext opCtx )
+ {
+ if ( entryCache == null )
+ {
+ return;
+ }
+
+ try
+ {
+ if ( opCtx instanceof ModifyOperationContext )
+ {
+ // replace the entry
+ ModifyOperationContext modCtx = ( ModifyOperationContext ) opCtx;
+ Entry entry = modCtx.getAlteredEntry();
+ String id = entry.get( SchemaConstants.ENTRY_UUID_AT ).getString();
+
+ if ( entry instanceof ClonedServerEntry )
+ {
+ entry = ( ( ClonedServerEntry ) entry ).getOriginalEntry();
+ }
+
+ entryCache.replace( new Element( id, entry ) );
+ }
+ else if ( ( opCtx instanceof MoveOperationContext ) || ( opCtx instanceof MoveAndRenameOperationContext )
+ || ( opCtx instanceof RenameOperationContext ) )
+ {
+ // clear the cache it is not worth updating all the children
+ entryCache.removeAll();
+ }
+ else if ( opCtx instanceof DeleteOperationContext )
+ {
+ // delete the entry
+ DeleteOperationContext delCtx = ( DeleteOperationContext ) opCtx;
+ entryCache.remove( delCtx.getEntry().get( SchemaConstants.ENTRY_UUID_AT ).getString() );
+ }
+ }
+ catch ( LdapException e )
+ {
+ LOG.warn( "Failed to update entry cache", e );
+ }
+ }
+
+
+ /**
+ * @return The set of system and user indexes
+ */
+ public Set<Index<?, String>> getAllIndices()
+ {
+ Set<Index<?, String>> all = new HashSet<Index<?, String>>( systemIndices.values() );
+ all.addAll( userIndices.values() );
+
+ return all;
+ }
+
+}
\ No newline at end of file
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotRdnIndex.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotRdnIndex.java
new file mode 100644
index 0000000..4e8220e
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotRdnIndex.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.mavibot;
+
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.MatchingRule;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.mavibot.btree.serializer.StringSerializer;
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.server.xdbm.ParentIdAndRdn;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A special index which stores Rdn objects.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class MavibotRdnIndex extends MavibotIndex<ParentIdAndRdn>
+{
+
+ /** A logger for this class */
+ private static final Logger LOG = LoggerFactory.getLogger( MavibotRdnIndex.class );
+
+
+ public MavibotRdnIndex()
+ {
+ super( ApacheSchemaConstants.APACHE_RDN_AT_OID, true );
+ initialized = false;
+ }
+
+
+ public void init( SchemaManager schemaManager, AttributeType attributeType ) throws IOException
+ {
+ LOG.debug( "Initializing an Index for attribute '{}'", attributeType.getName() );
+
+ this.attributeType = attributeType;
+
+ if ( attributeId == null )
+ {
+ setAttributeId( attributeType.getName() );
+ }
+
+ if ( this.wkDirPath == null )
+ {
+ NullPointerException e = new NullPointerException( "The index working directory has not be set" );
+ throw e;
+ }
+
+ String path = new File( this.wkDirPath, attributeType.getOid() ).getAbsolutePath();
+
+ try
+ {
+ initTables( schemaManager );
+ }
+ catch ( IOException e )
+ {
+ // clean up
+ close();
+ throw e;
+ }
+
+ // finally write a text file in the format <OID>-<attribute-name>.txt
+ FileWriter fw = new FileWriter( new File( path + "-" + attributeType.getName() + ".txt" ) );
+
+ // write the AttributeType description
+ fw.write( attributeType.toString() );
+ fw.close();
+
+ initialized = true;
+ }
+
+
+ /**
+ * Initializes the forward and reverse tables used by this Index.
+ *
+ * @param schemaManager The server schemaManager
+ * @throws IOException if we cannot initialize the forward and reverse
+ * tables
+ * @throws NamingException
+ */
+ private void initTables( SchemaManager schemaManager ) throws IOException
+ {
+ MatchingRule mr = attributeType.getEquality();
+
+ if ( mr == null )
+ {
+ throw new IOException( I18n.err( I18n.ERR_574, attributeType.getName() ) );
+ }
+
+ MavibotParentIdAndRdnSerializer.setSchemaManager( schemaManager );
+ MavibotParentIdAndRdnSerializer parentIdAndSerializer = new MavibotParentIdAndRdnSerializer();
+
+ String forwardTableName = attributeType.getOid() + FORWARD_BTREE;
+ forward = new MavibotTable<ParentIdAndRdn, String>( recordMan, schemaManager, forwardTableName,
+ parentIdAndSerializer, StringSerializer.INSTANCE, false );
+
+ String reverseTableName = attributeType.getOid() + REVERSE_BTREE;
+ reverse = new MavibotTable<String, ParentIdAndRdn>( recordMan, schemaManager, reverseTableName,
+ StringSerializer.INSTANCE, parentIdAndSerializer, false );
+ }
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotReadTxn.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotReadTxn.java
new file mode 100644
index 0000000..764cfa0
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotReadTxn.java
@@ -0,0 +1,11 @@
+package org.apache.directory.server.core.partition.impl.btree.mavibot;
+
+import org.apache.directory.mavibot.btree.Transaction;
+
+public class MavibotReadTxn extends AbstractMavibotTxn
+{
+ public MavibotReadTxn( Transaction transaction )
+ {
+ super( transaction );
+ }
+}
diff --git a/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
similarity index 64%
copy from mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
copy to mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
index 124d865..ff03213 100644
--- a/mavibot-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTable.java
@@ -21,29 +21,41 @@ package org.apache.directory.server.core.partition.impl.btree.mavibot;
import java.io.IOException;
+import java.util.Map;
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.mavibot.btree.BTree;
import org.apache.directory.mavibot.btree.BTreeFactory;
+import org.apache.directory.mavibot.btree.InsertResult;
import org.apache.directory.mavibot.btree.RecordManager;
+import org.apache.directory.mavibot.btree.Transaction;
import org.apache.directory.mavibot.btree.TupleCursor;
import org.apache.directory.mavibot.btree.ValueCursor;
+import org.apache.directory.mavibot.btree.WriteTransaction;
import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
+import org.apache.directory.mavibot.btree.serializer.LongSerializer;
+import org.apache.directory.mavibot.btree.serializer.StringSerializer;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
import org.apache.directory.server.core.avltree.ArrayMarshaller;
import org.apache.directory.server.core.avltree.ArrayTree;
import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
+import org.apache.directory.server.core.partition.impl.btree.mavibot.MavibotIndex;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.xdbm.AbstractTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import jdbm.helper.Serializer;
+
/**
* A Mavibot Table. It extends the default Apache DS Table, when Mavibot is the
@@ -64,6 +76,15 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
/** The used recordManager */
protected RecordManager recordMan;
+ /** the limit at which we start using btree redirection for duplicates */
+ private int numDupLimit = MavibotIndex.DEFAULT_DUPLICATE_LIMIT;
+
+ /** a cache of duplicate BTrees */
+ private final Map<Long, BTree<K, V>> duplicateBtrees;
+
+ /** A value serializer */
+ private final ElementSerializer valueSerializer;
+
/**
* Creates a new instance of MavibotTable.
@@ -103,20 +124,27 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
{
super( schemaManager, name, keySerializer.getComparator(), valueSerializer.getComparator() );
this.recordMan = recordMan;
+ this.valueSerializer = valueSerializer;
+ duplicateBtrees = null;
- bt = recordMan.getManagedTree( name );
+ try ( Transaction transaction = recordMan.beginReadTransaction() )
+ {
+ bt = transaction.getBTree( name );
+ }
if ( bt == null )
{
- bt = BTreeFactory.createPersistedBTree( name, keySerializer, valueSerializer, allowDuplicates, cacheSize );
-
+ // Create a new BTree
+ WriteTransaction writeTransaction = recordMan.beginWriteTransaction();
+
try
{
- recordMan.manage( bt );
+ bt = recordMan.addBTree( writeTransaction, name, keySerializer, valueSerializer );
+ writeTransaction.commit();
}
- catch ( BTreeAlreadyManagedException e )
+ catch ( Exception e )
{
- // should never happen
+ writeTransaction.abort();
throw new RuntimeException( e );
}
}
@@ -129,7 +157,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
}
this.allowsDuplicates = allowDuplicates;
- arrayMarshaller = new ArrayMarshaller<V>( valueComparator );
+ arrayMarshaller = new ArrayMarshaller<>( valueComparator );
// Initialize the count
count = bt.getNbElems();
@@ -140,31 +168,16 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean isDupsEnabled()
- {
- return allowsDuplicates;
- }
-
-
- /**
- * {@inheritDoc}
- * @throws
- */
- @Override
- public boolean has( K key ) throws LdapException
+ public boolean has( PartitionTxn transaction, K key ) throws LdapException
{
try
{
- return bt.hasKey( key );
+ return bt.hasKey( ( ( MavibotTxn ) transaction ).getTransaction(), key );
}
catch ( IOException ioe )
{
throw new LdapException( ioe );
}
- catch ( KeyNotFoundException knfe )
- {
- throw new LdapException( knfe );
- }
}
@@ -172,11 +185,11 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean has( K key, V value ) throws LdapException
+ public boolean has( PartitionTxn transaction, K key, V value ) throws LdapException
{
try
{
- return bt.contains( key, value );
+ return bt.contains( ( ( MavibotTxn ) transaction ).getTransaction(), key, value );
}
catch ( IOException e )
{
@@ -189,16 +202,20 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasGreaterOrEqual( K key ) throws Exception
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
TupleCursor<K, V> cursor = null;
try
{
- cursor = bt.browseFrom( key );
+ cursor = bt.browseFrom( ( ( MavibotTxn ) transaction ).getTransaction(), key );
return cursor.hasNext();
}
+ catch ( IOException ioe )
+ {
+ throw new LdapOtherException( ioe.getMessage() );
+ }
finally
{
if ( cursor != null )
@@ -213,13 +230,13 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasLessOrEqual( K key ) throws Exception
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
TupleCursor<K, V> cursor = null;
try
{
- cursor = bt.browseFrom( key );
+ cursor = bt.browseFrom( ( ( MavibotTxn ) transaction ).getTransaction(), key );
org.apache.directory.mavibot.btree.Tuple<K, V> tuple = null;
@@ -248,6 +265,10 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
return false;
}
+ catch ( IOException ioe )
+ {
+ throw new LdapOtherException( ioe.getMessage() );
+ }
finally
{
if ( cursor != null )
@@ -262,7 +283,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasGreaterOrEqual( K key, V val ) throws LdapException
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -274,16 +295,16 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
}
- TupleCursor<V, V> cursor = null;
+ ValueCursor<V> valueCursor = null;
try
{
- if ( !bt.hasKey( key ) )
+ if ( !bt.hasKey( ( ( MavibotTxn ) transaction ).getTransaction(), key ) )
{
return false;
}
- ValueCursor<V> valueCursor = bt.getValues( key );
+ valueCursor = bt.getValues( ( ( MavibotTxn ) transaction ).getTransaction(), key );
int equal = bt.getValueSerializer().compare( val, valueCursor.next() );
@@ -295,9 +316,9 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
}
finally
{
- if ( cursor != null )
+ if ( valueCursor != null )
{
- cursor.close();
+ valueCursor.close();
}
}
}
@@ -307,7 +328,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public boolean hasLessOrEqual( K key, V val ) throws Exception
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -319,7 +340,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
}
- if ( !bt.hasKey( key ) )
+ if ( !bt.hasKey( ( ( MavibotTxn ) transaction ).getTransaction(), key ) )
{
return false;
}
@@ -334,7 +355,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public V get( K key ) throws LdapException
+ public V get( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -343,7 +364,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
try
{
- return bt.get( key );
+ return bt.get( ( ( MavibotTxn ) transaction ).getTransaction(), key );
}
catch ( KeyNotFoundException knfe )
{
@@ -360,7 +381,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public void put( K key, V value ) throws Exception
+ public void put( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
try
{
@@ -369,17 +390,17 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
throw new IllegalArgumentException( I18n.err( I18n.ERR_594 ) );
}
- V existingVal = bt.insert( key, value );
+ InsertResult<K, V> existingVal = bt.insert( ( ( MavibotWriteTxn ) transaction ).getWriteTransaction(), key, value );
if ( existingVal == null )
{
count++;
}
}
- catch ( Exception e )
+ catch ( IOException ioe )
{
- LOG.error( I18n.err( I18n.ERR_131, key, name ), e );
- throw e;
+ LOG.error( I18n.err( I18n.ERR_131, key, name ), ioe );
+ throw new LdapOtherException( ioe.getMessage(), ioe);
}
}
@@ -388,7 +409,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public void remove( K key ) throws Exception
+ public void remove( PartitionWriteTxn transaction, K key ) throws LdapException
{
try
{
@@ -398,12 +419,13 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
}
// Get the associated valueHolder
- if ( bt.isAllowDuplicates() )
+ if ( allowsDuplicates )
{
- ValueCursor<V> valueCursor = bt.getValues( key );
+ ValueCursor<V> valueCursor = bt.getValues( ( ( MavibotWriteTxn ) transaction ).getWriteTransaction(), key );
int size = valueCursor.size();
valueCursor.close();
- org.apache.directory.mavibot.btree.Tuple<K, V> returned = bt.delete( key );
+ org.apache.directory.mavibot.btree.Tuple<K, V> returned =
+ bt.delete( ( ( MavibotWriteTxn ) transaction ).getWriteTransaction(), key );
if ( null == returned )
{
@@ -414,7 +436,8 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
}
else
{
- org.apache.directory.mavibot.btree.Tuple<K, V> returned = bt.delete( key );
+ org.apache.directory.mavibot.btree.Tuple<K, V> returned =
+ bt.delete( ( ( MavibotWriteTxn ) transaction ).getWriteTransaction(), key );
if ( null == returned )
{
@@ -424,14 +447,11 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
count--;
}
}
- catch ( Exception e )
+ catch ( IOException ioe )
{
- LOG.error( I18n.err( I18n.ERR_133, key, name ), e );
+ LOG.error( I18n.err( I18n.ERR_133, key, name ), ioe );
- if ( e instanceof IOException )
- {
- throw ( IOException ) e;
- }
+ throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
@@ -440,7 +460,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public void remove( K key, V value ) throws Exception
+ public void remove( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
try
{
@@ -449,7 +469,8 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
return;
}
- org.apache.directory.mavibot.btree.Tuple<K, V> tuple = bt.delete( key, value );
+ org.apache.directory.mavibot.btree.Tuple<K, V> tuple =
+ bt.delete( ( ( MavibotWriteTxn ) transaction ).getWriteTransaction(), key, value );
// We decrement the counter only when the key was found
if ( tuple != null )
@@ -457,9 +478,11 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
count--;
}
}
- catch ( Exception e )
+ catch ( IOException ioe )
{
- LOG.error( I18n.err( I18n.ERR_132, key, value, name ), e );
+ LOG.error( I18n.err( I18n.ERR_132, key, value, name ), ioe );
+
+ throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
@@ -468,9 +491,9 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public Cursor<Tuple<K, V>> cursor() throws LdapException
+ public Cursor<Tuple<K, V>> cursor( PartitionTxn transaction ) throws LdapException
{
- return new MavibotCursor<K, V>( this );
+ return new MavibotCursor<>( this );
}
@@ -478,11 +501,11 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public Cursor<Tuple<K, V>> cursor( K key ) throws LdapException
+ public Cursor<Tuple<K, V>> cursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
- return new EmptyCursor<Tuple<K, V>>();
+ return new EmptyCursor<>();
}
try
@@ -491,19 +514,18 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
{
V val = bt.get( key );
- return new SingletonCursor<Tuple<K, V>>(
- new Tuple<K, V>( key, val ) );
+ return new SingletonCursor<>( new Tuple<K, V>( key, val ) );
}
else
{
ValueCursor<V> dupHolder = bt.getValues( key );
- return new KeyTupleValueCursor<K, V>( dupHolder, key );
+ return new KeyTupleValueCursor<>( dupHolder, key );
}
}
catch ( KeyNotFoundException knfe )
{
- return new EmptyCursor<Tuple<K, V>>();
+ return new EmptyCursor<>();
}
catch ( Exception e )
{
@@ -516,31 +538,31 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public Cursor<V> valueCursor( K key ) throws Exception
+ public Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
- return new EmptyCursor<V>();
+ return new EmptyCursor<>();
}
try
{
if ( !allowsDuplicates )
{
- V val = bt.get( key );
+ V val = bt.get( ( ( MavibotTxn ) transaction ).getTransaction(), key );
- return new SingletonCursor<V>( val );
+ return new SingletonCursor<>( val );
}
else
{
- ValueCursor<V> dupCursor = bt.getValues( key );
+ ValueCursor<V> dupCursor = bt.getValues( ( ( MavibotTxn ) transaction ).getTransaction(), key );
- return new ValueTreeCursor<V>( dupCursor );
+ return new ValueTreeCursor<>( dupCursor );
}
}
catch ( KeyNotFoundException knfe )
{
- return new EmptyCursor<V>();
+ return new EmptyCursor<>();
}
catch ( Exception e )
{
@@ -553,39 +575,46 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
* {@inheritDoc}
*/
@Override
- public long count( K key ) throws Exception
+ public long count( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
return 0;
}
- if ( bt.isAllowDuplicates() )
+ try
{
- try
+ if ( allowsDuplicates )
{
- ValueCursor<V> dupHolder = bt.getValues( key );
- int size = dupHolder.size();
- dupHolder.close();
-
- return size;
+ try
+ {
+ ValueCursor dupHolder = (ValueCursor) bt.get( ( ( MavibotTxn ) transaction ).getTransaction(), key );
+ int size = dupHolder.size();
+ dupHolder.close();
+
+ return size;
+ }
+ catch ( KeyNotFoundException knfe )
+ {
+ // No key
+ return 0;
+ }
}
- catch ( KeyNotFoundException knfe )
+ else
{
- // No key
- return 0;
+ if ( bt.hasKey( ( ( MavibotTxn ) transaction ).getTransaction(), key ) )
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
}
}
- else
+ catch ( IOException ioe )
{
- if ( bt.hasKey( key ) )
- {
- return 1;
- }
- else
- {
- return 0;
- }
+ throw new LdapOtherException( ioe.getMessage() );
}
}
@@ -593,41 +622,6 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- @Override
- public long greaterThanCount( K key ) throws Exception
- {
- // take a best guess
- return Math.min( count, 10L );
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long lessThanCount( K key ) throws Exception
- {
- // take a best guess
- return Math.min( count, 10L );
- }
-
-
- /**
- * {@inheritDoc}
- */
- /**
- * {@inheritDoc}
- */
- @Override
- public void close() throws Exception
- {
- // do nothing here, the RecordManager will be closed in MavibotMasterTable.close()
- }
-
-
- /**
- * {@inheritDoc}
- */
public ArrayTree<V> getDupsContainer( byte[] serialized ) throws IOException
{
if ( serialized == null )
@@ -661,6 +655,7 @@ public class MavibotTable<K, V> extends AbstractTable<K, V>
/**
* @see Object#toString()
*/
+ @Override
public String toString()
{
StringBuilder sb = new StringBuilder();
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTxn.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTxn.java
new file mode 100644
index 0000000..5205040
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotTxn.java
@@ -0,0 +1,9 @@
+package org.apache.directory.server.core.partition.impl.btree.mavibot;
+
+import org.apache.directory.mavibot.btree.Transaction;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+
+public interface MavibotTxn extends PartitionTxn, Transaction
+{
+ Transaction getTransaction();
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotWriteTxn.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotWriteTxn.java
new file mode 100644
index 0000000..bc13160
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/MavibotWriteTxn.java
@@ -0,0 +1,18 @@
+package org.apache.directory.server.core.partition.impl.btree.mavibot;
+
+import org.apache.directory.mavibot.btree.Transaction;
+import org.apache.directory.mavibot.btree.WriteTransaction;
+
+public class MavibotWriteTxn extends AbstractMavibotTxn
+{
+ public MavibotWriteTxn( Transaction transaction )
+ {
+ super( transaction );
+ }
+
+
+ public WriteTransaction getWriteTransaction()
+ {
+ return ( WriteTransaction ) transaction;
+ }
+}
diff --git a/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/ValueTreeCursor.java b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/ValueTreeCursor.java
new file mode 100644
index 0000000..1ff6069
--- /dev/null
+++ b/mavibotv2-partition/src/main/java/org/apache/directory/server/core/partition/impl/btree/mavibot/ValueTreeCursor.java
@@ -0,0 +1,155 @@
+/*
+ * 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.mavibot;
+
+
+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.exception.LdapException;
+import org.apache.directory.mavibot.btree.ValueCursor;
+import org.apache.directory.server.i18n.I18n;
+
+
+/**
+ * TODO ValueTreeCursor.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ValueTreeCursor<V> extends AbstractCursor<V>
+{
+
+ private ValueCursor<V> wrapped;
+
+ private V available;
+
+ // marker to detect the availability (cause Mavibot supports null values also)
+ private V notAvailable = ( V ) new Object();
+
+
+ public ValueTreeCursor( ValueCursor<V> cursor )
+ {
+ this.wrapped = cursor;
+ }
+
+
+ @Override
+ public boolean available()
+ {
+ return ( available != notAvailable );
+ }
+
+
+ @Override
+ public void before( V element ) throws LdapException, CursorException
+ {
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
+ }
+
+
+ @Override
+ public void after( V element ) throws LdapException, CursorException
+ {
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
+ }
+
+
+ @Override
+ public void beforeFirst() throws LdapException, CursorException
+ {
+ }
+
+
+ @Override
+ public void afterLast() throws LdapException, CursorException
+ {
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
+ }
+
+
+ @Override
+ public boolean first() throws LdapException, CursorException
+ {
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
+ }
+
+
+ @Override
+ public boolean last() throws LdapException, CursorException
+ {
+ throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
+ }
+
+
+ @Override
+ public boolean previous() throws LdapException, CursorException
+ {
+ try
+ {
+ if ( wrapped.hasPrev() )
+ {
+ available = wrapped.prev();
+ return true;
+ }
+ else
+ {
+ available = notAvailable;
+ return false;
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new CursorException( e );
+ }
+ }
+
+
+ @Override
+ public boolean next() throws LdapException, CursorException
+ {
+ try
+ {
+ if ( wrapped.hasNext() )
+ {
+ available = wrapped.next();
+ return true;
+ }
+ else
+ {
+ available = notAvailable;
+ return false;
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new CursorException( e );
+ }
+ }
+
+
+ @Override
+ public V get() throws CursorException
+ {
+ return available;
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 3ecbb0a..149ca12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,6 +103,7 @@
<module>all</module>
<module>jdbm-partition</module>
<module>mavibot-partition</module>
+ <!--module>mavibotv2-partition</module-->
<module>xdbm-partition</module>
<module>core-shared</module>
<module>core-api</module>
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java
index 6d6d503..4a97063 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java
@@ -302,6 +302,7 @@ public class PwdModifyHandler implements ExtendedOperationHandler<PasswordModify
// The user is not authenticated : we have to use the provided userIdentity
// and the oldPassword to check if the user is present
BindOperationContext bindContext = new BindOperationContext( adminSession );
+ bindContext.setTransaction( requestor.getCoreSession().getDirectoryService().getPartitionNexus().beginReadTransaction() );
bindContext.setDn( userDn );
bindContext.setCredentials( oldPassword );
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/request/BindRequestHandler.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/request/BindRequestHandler.java
index e35328d..7192302 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/request/BindRequestHandler.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/request/BindRequestHandler.java
@@ -108,6 +108,7 @@ public class BindRequestHandler extends LdapRequestHandler<BindRequest>
// create a new Bind context, with a null session, as we don't have
// any context yet.
BindOperationContext bindContext = new BindOperationContext( null );
+ bindContext.setTransaction( ldapServer.getDirectoryService().getPartitionNexus().beginReadTransaction() );
// Stores the Dn of the user to check, and its password
bindContext.setDn( bindRequest.getDn() );
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/SimpleMechanismHandler.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/SimpleMechanismHandler.java
index 76c77d0..ae22efa 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/SimpleMechanismHandler.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/SimpleMechanismHandler.java
@@ -57,6 +57,7 @@ public class SimpleMechanismHandler implements MechanismHandler
// create a new Bind context, with a null session, as we don't have
// any context yet.
BindOperationContext bindContext = new BindOperationContext( null );
+ bindContext.setTransaction( ldapSession.getLdapServer().getDirectoryService().getPartitionNexus().beginReadTransaction() );
// Stores the Dn of the user to check, and its password
bindContext.setDn( bindRequest.getDn() );
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/ntlm/NtlmSaslServer.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/ntlm/NtlmSaslServer.java
index ca82431..d72a6d1 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/ntlm/NtlmSaslServer.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/ntlm/NtlmSaslServer.java
@@ -205,6 +205,7 @@ public class NtlmSaslServer extends AbstractSaslServer
private CoreSession authenticate( String user, String password ) throws InvalidNameException, Exception
{
BindOperationContext bindContext = new BindOperationContext( getLdapSession().getCoreSession() );
+ bindContext.setTransaction( getLdapSession().getCoreSession().getDirectoryService().getPartitionNexus().beginReadTransaction() );
bindContext.setDn( new Dn( user ) );
bindContext.setCredentials( Strings.getBytesUtf8( password ) );
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/plain/PlainSaslServer.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/plain/PlainSaslServer.java
index 54343bc..91757cb 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/plain/PlainSaslServer.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/plain/PlainSaslServer.java
@@ -281,6 +281,7 @@ public class PlainSaslServer extends AbstractSaslServer
try
{
BindOperationContext bindContext = new BindOperationContext( ldapSession.getCoreSession() );
+ bindContext.setTransaction( ldapSession.getCoreSession().getDirectoryService().getPartitionNexus().beginReadTransaction() );
bindContext.setDn( entry.getDn() );
bindContext.setCredentials( Strings.getBytesUtf8( password ) );
bindContext.setIoSession( ldapSession.getIoSession() );
diff --git a/server-integ/src/test/java/org/apache/directory/server/operations/modify/ModifyReferralIT.java b/server-integ/src/test/java/org/apache/directory/server/operations/modify/ModifyReferralIT.java
index 1fd1705..08998b7 100644
--- a/server-integ/src/test/java/org/apache/directory/server/operations/modify/ModifyReferralIT.java
+++ b/server-integ/src/test/java/org/apache/directory/server/operations/modify/ModifyReferralIT.java
@@ -32,6 +32,8 @@ import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.ldap.LdapContext;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.message.Control;
import org.apache.directory.api.ldap.model.message.ModifyRequest;
import org.apache.directory.api.ldap.model.message.ModifyRequestImpl;
import org.apache.directory.api.ldap.model.message.ModifyResponse;
@@ -117,19 +119,29 @@ public class ModifyReferralIT extends AbstractLdapTestUnit
public void testOnReferralWithManageDsaITControl() throws Exception
{
LdapConnection conn = getWiredConnection( getLdapServer() );
+
+ Dn target = new Dn( "uid=akarasuluref,ou=users,ou=system" );
ManageDsaIT manageDSAIT = new ManageDsaITImpl();
manageDSAIT.setCritical( true );
// modify success
ModifyRequest modifyRequest = new ModifyRequestImpl();
- modifyRequest.setName( new Dn( "uid=akarasuluref,ou=users,ou=system" ) );
+ modifyRequest.setName( target );
modifyRequest.add( "description", "referral to akarasulu" );
modifyRequest.addControl( manageDSAIT );
conn.modify( modifyRequest );
assertTrue( conn.compare( "uid=akarasuluref,ou=users,ou=system", "description", "referral to akarasulu" ) );
+
+ Entry result = conn.lookup( target, new Control[] {manageDSAIT}, "*" );
+
+ System.out.println( result );
+
+ result = conn.lookup( target, new Control[] {}, "*" );
+
+ System.out.println( result );
conn.close();
}
diff --git a/server-integ/src/test/java/org/apache/directory/server/operations/search/PagedSearchApiIT.java b/server-integ/src/test/java/org/apache/directory/server/operations/search/PagedSearchApiIT.java
index 94e0a0f..09ebd90 100644
--- a/server-integ/src/test/java/org/apache/directory/server/operations/search/PagedSearchApiIT.java
+++ b/server-integ/src/test/java/org/apache/directory/server/operations/search/PagedSearchApiIT.java
@@ -49,7 +49,6 @@ import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.api.ldap.model.message.controls.PagedResults;
import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
import org.apache.directory.api.ldap.model.name.Dn;
-import org.apache.directory.api.ldap.util.JndiUtils;
import org.apache.directory.api.util.Network;
import org.apache.directory.api.util.Strings;
import org.apache.directory.ldap.client.api.EntryCursorImpl;
@@ -269,9 +268,6 @@ public class PagedSearchApiIT extends AbstractLdapTestUnit
results.add( result );
}
- // Now read the next ones
- Map<String, Control> controls = cursor.getSearchResultDone().getControls();
-
if ( cursor.getSearchResultDone().getLdapResult().getResultCode() == ResultCodeEnum.SIZE_LIMIT_EXCEEDED )
{
hasSizeLimitException = true;
@@ -279,6 +275,9 @@ public class PagedSearchApiIT extends AbstractLdapTestUnit
break;
}
+ // Now read the next ones
+ Map<String, Control> controls = cursor.getSearchResultDone().getControls();
+
PagedResults responseControl = ( PagedResults ) controls.get( PagedResults.OID );
assertEquals( 0, responseControl.getSize() );
diff --git a/server-integ/src/test/java/org/apache/directory/server/operations/search/PersistentSearchApiIT.java b/server-integ/src/test/java/org/apache/directory/server/operations/search/PersistentSearchApiIT.java
new file mode 100644
index 0000000..75919ab
--- /dev/null
+++ b/server-integ/src/test/java/org/apache/directory/server/operations/search/PersistentSearchApiIT.java
@@ -0,0 +1,740 @@
+/*
+ * 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.operations.search;
+
+
+import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.List;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+import javax.naming.event.EventDirContext;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.ObjectChangeListener;
+import javax.naming.ldap.HasControls;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.api.ldap.codec.api.CodecControl;
+import org.apache.directory.api.ldap.codec.api.LdapApiService;
+import org.apache.directory.api.ldap.codec.controls.search.entryChange.EntryChangeDecorator;
+import org.apache.directory.api.ldap.codec.controls.search.persistentSearch.PersistentSearchDecorator;
+import org.apache.directory.api.ldap.model.cursor.SearchCursor;
+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.ldif.LdifUtils;
+import org.apache.directory.api.ldap.model.message.Control;
+import org.apache.directory.api.ldap.model.message.SearchRequest;
+import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.api.ldap.model.message.controls.ChangeType;
+import org.apache.directory.api.ldap.model.message.controls.EntryChange;
+import org.apache.directory.api.ldap.model.message.controls.PersistentSearch;
+import org.apache.directory.api.ldap.model.message.controls.PersistentSearchImpl;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.util.JndiUtils;
+import org.apache.directory.api.util.Network;
+import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.ApplyLdifs;
+import org.apache.directory.server.core.api.event.EventService;
+import org.apache.directory.server.core.api.event.RegistrationEntry;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Test case which tests the correct operation of the persistent search decorator.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+@RunWith(FrameworkRunner.class)
+@CreateLdapServer(
+ transports =
+ {
+ @CreateTransport(protocol = "LDAP")
+ })
+@ApplyLdifs(
+ {
+ // Entry # 2
+ "dn: cn=Tori Amos,ou=system",
+ "objectClass: person",
+ "objectClass: top",
+ "cn: Tori Amos",
+ "description: an American singer-songwriter",
+ "sn: Amos" })
+public class PersistentSearchApiIT extends AbstractLdapTestUnit
+{
+ private static final Logger LOG = LoggerFactory.getLogger( PersistentSearchApiIT.class );
+
+ private static final String BASE = "ou=system";
+ private static final String PERSON_DESCRIPTION = "an American singer-songwriter";
+ private static final String RDN = "cn=Tori Amos";
+
+
+ /**
+ * Creation of required attributes of a person entry.
+ */
+ private Attributes getPersonAttributes( String sn, String cn ) throws LdapException
+ {
+ Attributes attributes = LdifUtils.createJndiAttributes(
+ "objectClass: top",
+ "objectClass: person",
+ "cn", cn,
+ "sn", sn );
+
+ return attributes;
+ }
+
+ EventDirContext ctx;
+ EventService eventService;
+ PSearchListener listener;
+ Thread t;
+
+
+ private void setUpListenerReturnECs() throws Exception
+ {
+ setUpListener( true, new PersistentSearchImpl(), false );
+ }
+
+
+ private void setUpListener( boolean returnECs, PersistentSearch persistentSearch, boolean ignoreEmptyRegistryCheck )
+ throws Exception
+ {
+ ctx = ( EventDirContext ) getWiredContext( getLdapServer() ).lookup( BASE );
+ eventService = getLdapServer().getDirectoryService().getEventService();
+ List<RegistrationEntry> registrationEntryList = eventService.getRegistrationEntries();
+
+ if ( !ignoreEmptyRegistryCheck )
+ {
+ assertTrue( registrationEntryList.isEmpty() );
+ }
+
+ persistentSearch.setReturnECs( returnECs );
+ listener = new PSearchListener( persistentSearch );
+ t = new Thread( listener, "PSearchListener" );
+ t.start();
+
+ // let's wait until the listener thread started
+ while ( eventService.getRegistrationEntries().isEmpty() )
+ {
+ Thread.sleep( 100 );
+ }
+
+ // Now we wait until the listener is registered (timing dependent crap)
+ Thread.sleep( 250 );
+ }
+
+
+ private void setUpListener() throws Exception
+ {
+ ctx = ( EventDirContext ) getWiredContext( getLdapServer() ).lookup( BASE );
+ eventService = getLdapServer().getDirectoryService().getEventService();
+ List<RegistrationEntry> registrationEntryList = eventService.getRegistrationEntries();
+ assertTrue( registrationEntryList.isEmpty() );
+
+ listener = new PSearchListener();
+ t = new Thread( listener, "PSearchListener" );
+ t.start();
+
+ // let's wait until the listener thread started
+ while ( eventService.getRegistrationEntries().isEmpty() )
+ {
+ Thread.sleep( 100 );
+ }
+
+ // Now we wait until the listener is registered (timing dependent crap)
+ Thread.sleep( 250 );
+ }
+
+
+ @After
+ public void tearDownListener() throws Exception
+ {
+ if( listener == null )
+ {
+ return;
+ }
+
+ listener.close();
+ ctx.close();
+
+ while ( !eventService.getRegistrationEntries().isEmpty() )
+ {
+ Thread.sleep( 100 );
+ }
+ }
+
+
+ private void waitForThreadToDie( Thread t ) throws Exception
+ {
+ long start = System.currentTimeMillis();
+
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 1000 )
+ {
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Shows correct notifications for modify(4) changes.
+ */
+ @Test
+ public void testPsearchModify() throws Exception
+ {
+ setUpListener();
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( RDN, listener.result.getName() );
+ }
+
+
+ /**
+ * Shows correct notifications for moddn(8) changes.
+ */
+ @Test
+ public void testPsearchModifyDn() throws Exception
+ {
+ setUpListener();
+ ctx.rename( RDN, "cn=Jack Black" );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( "cn=Jack Black", listener.result.getName() );
+ }
+
+
+ /**
+ * Shows correct notifications for delete(2) changes.
+ */
+ @Test
+ public void testPsearchDelete() throws Exception
+ {
+ setUpListener();
+ ctx.destroySubcontext( RDN );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( RDN, listener.result.getName() );
+ }
+
+
+ /**
+ * Shows correct notifications for add(1) changes.
+ */
+ @Test
+ public void testPsearchAdd() throws Exception
+ {
+ setUpListener();
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( "cn=Jack Black", listener.result.getName() );
+ }
+
+
+ /**
+ * Shows correct notifications for modify(4) changes with returned
+ * EntryChangeControl.
+ */
+ @Test
+ public void testPsearchModifyWithEC() throws Exception
+ {
+ setUpListenerReturnECs();
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, new BasicAttributes( "description", PERSON_DESCRIPTION,
+ true ) );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( RDN, listener.result.getName() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+ }
+
+
+ /**
+ * Shows correct notifications for moddn(8) changes with returned
+ * EntryChangeControl.
+ */
+ @Test
+ public void testPsearchModifyDnWithEC() throws Exception
+ {
+ setUpListenerReturnECs();
+ ctx.rename( RDN, "cn=Jack Black" );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( "cn=Jack Black", listener.result.getName() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODDN );
+ assertEquals( ( RDN + ",ou=system" ), listener.result.control.getPreviousDn().getName() );
+ }
+
+
+ /**
+ * Shows correct notifications for delete(2) changes with returned
+ * EntryChangeControl.
+ */
+ @Test
+ public void testPsearchDeleteWithEC() throws Exception
+ {
+ setUpListenerReturnECs();
+ ctx.destroySubcontext( RDN );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( RDN, listener.result.getName() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.DELETE );
+ }
+
+
+ /**
+ * Shows correct notifications for add(1) changes with returned
+ * EntryChangeControl.
+ */
+ @Test
+ public void testPsearchAddWithEC() throws Exception
+ {
+ setUpListenerReturnECs();
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+ waitForThreadToDie( t );
+ assertNotNull( listener.result );
+ assertEquals( "cn=Jack Black", listener.result.getName() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+ }
+
+
+ /**
+ * Shows correct notifications for only add(1) and modify(4) registered changes with returned
+ * EntryChangeControl but not deletes.
+ */
+ @Test
+ public void testPsearchAddModifyEnabledWithEC() throws Exception
+ {
+ PersistentSearch ctrl = new PersistentSearchImpl();
+ ctrl.setReturnECs( true );
+ ctrl.setChangeTypes( ChangeType.ADD.getValue() );
+ ctrl.enableNotification( ChangeType.MODIFY );
+ setUpListener( true, ctrl, false );
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+ waitForThreadToDie( t );
+
+ assertNotNull( listener.result );
+ assertEquals( "cn=Jack Black", listener.result.getName() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+ tearDownListener();
+
+ setUpListener( true, ctrl, true );
+ ctx.destroySubcontext( "cn=Jack Black" );
+ waitForThreadToDie( t );
+ assertNull( listener.result );
+
+ // thread is still waiting for notifications try a modify
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, new BasicAttributes( "description", PERSON_DESCRIPTION,
+ true ) );
+ waitForThreadToDie( t );
+
+ assertNotNull( listener.result );
+ assertEquals( RDN, listener.result.getName() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1908
+ */
+ @Test
+ public void testPsearchMove() throws Exception
+ {
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, ldapServer.getPort() );
+ connection.bind( "uid=admin,ou=system", "secret" );
+
+ Entry newOu = new DefaultEntry( "uid=persist, ou=users,ou=system" );
+ newOu.add( "objectClass", "inetOrgPerson" );
+ newOu.add( "cn", "persist_cn" );
+ newOu.add( "sn", "persist_sn" );
+
+ connection.add( newOu );
+
+ SearchRequest sr = new SearchRequestImpl();
+ sr.setBase( new Dn( BASE ) );
+ sr.setFilter( "(objectClass=*)" );
+ sr.setScope( SearchScope.SUBTREE );
+
+ PersistentSearch ps = new PersistentSearchImpl();
+ ps.setChangesOnly( true );
+ ps.setReturnECs( true );
+ ps.setCritical( true );
+
+ sr.addControl( ps );
+
+ final SearchCursor cursor = connection.search( sr );
+
+ final List<Entry> entryList = new ArrayList<Entry>();
+
+ Runnable r = new Runnable()
+ {
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ while( cursor.next() )
+ {
+ entryList.add( cursor.getEntry() );
+ }
+ }
+ catch( Exception e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+ };
+
+ new Thread( r ).start();
+
+ connection.move( newOu.getDn(), newOu.getDn().getParent().getParent() );
+ Thread.sleep( 1000 );
+ assertFalse( entryList.isEmpty() );
+ assertEquals( 1, entryList.size() );
+ assertEquals( "uid=persist,ou=system", entryList.get( 0 ).getDn().getName() );
+
+ connection.close();
+ }
+
+
+ /**
+ * Shows correct notifications for add(1) changes with returned
+ * EntryChangeControl and changesOnly set to false so we return
+ * the first set of entries.
+ *
+ * This test is commented out because it exhibits some producer
+ * consumer lockups (server and client being in same process)
+ *
+ * PLUS ALL THIS GARBAGE IS TIME DEPENDENT!!!!!
+ */
+ // public void testPsearchAddWithECAndFalseChangesOnly() throws Exception
+ // {
+ // PersistentSearchDecorator decorator = new PersistentSearchDecorator();
+ // decorator.setReturnECs( true );
+ // decorator.setChangesOnly( false );
+ // PSearchListener listener = new PSearchListener( decorator );
+ // Thread t = new Thread( listener );
+ // t.start();
+ //
+ // Thread.sleep( 3000 );
+ //
+ // assertEquals( 5, listener.count );
+ // ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+ //
+ // long start = System.currentTimeMillis();
+ // while ( t.isAlive() )
+ // {
+ // Thread.sleep( 100 );
+ // if ( System.currentTimeMillis() - start > 3000 )
+ // {
+ // break;
+ // }
+ // }
+ //
+ // assertEquals( 6, listener.count );
+ // assertNotNull( listener.result );
+ // assertEquals( "cn=Jack Black", listener.result.getName() );
+ // assertEquals( listener.result.decorator.getChangeType(), ChangeType.ADD );
+ // }
+
+ /**
+ * Shows notifications functioning with the JNDI notification API of the SUN
+ * provider.
+ *
+ @Test
+ public void testPsearchAbandon() throws Exception
+ {
+ PersistentSearchDecorator decorator = new PersistentSearchDecorator();
+ decorator.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( decorator );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while ( !listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ assertEquals( "cn=Jack Black", listener.result.getName() );
+ assertEquals( listener.result.decorator.getChangeType(), ChangeType.ADD );
+
+ listener = new PSearchListener( decorator );
+
+ t = new Thread( listener );
+ t.start();
+
+ ctx.destroySubcontext( "cn=Jack Black" );
+
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ // there seems to be a race condition here
+ // assertNull( listener.result );
+ assertNotNull( listener.result );
+ assertEquals( "cn=Jack Black", listener.result.getName() );
+ assertEquals( ChangeType.DELETE, listener.result.decorator.getChangeType() );
+ listener.result = null;
+
+ // thread is still waiting for notifications try a modify
+ ctx.modifyAttributes( Rdn, DirContext.REMOVE_ATTRIBUTE, new AttributesImpl( "description", PERSON_DESCRIPTION,
+ true ) );
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ break;
+ }
+ }
+
+ assertNull( listener.result );
+ //assertEquals( Rdn, listener.result.getName() );
+ //assertEquals( listener.result.decorator.getChangeType(), ChangeType.MODIFY );
+ }*/
+
+ class JndiNotificationListener implements NamespaceChangeListener, ObjectChangeListener
+ {
+ boolean hasError = false;
+ ArrayList<EventObject> list = new ArrayList<EventObject>();
+ NamingExceptionEvent exceptionEvent = null;
+
+
+ public void objectAdded( NamingEvent evt )
+ {
+ list.add( 0, evt );
+ }
+
+
+ public void objectRemoved( NamingEvent evt )
+ {
+ list.add( 0, evt );
+ }
+
+
+ public void objectRenamed( NamingEvent evt )
+ {
+ list.add( 0, evt );
+ }
+
+
+ public void namingExceptionThrown( NamingExceptionEvent evt )
+ {
+ hasError = true;
+ exceptionEvent = evt;
+ list.add( 0, evt );
+ }
+
+
+ public void objectChanged( NamingEvent evt )
+ {
+ list.add( 0, evt );
+ }
+ }
+
+ class PSearchListener implements Runnable
+ {
+ boolean isReady = false;
+ PSearchNotification result;
+ final PersistentSearchDecorator persistentSearch;
+ LdapContext ctx;
+ NamingEnumeration<SearchResult> list;
+
+
+ PSearchListener()
+ {
+ persistentSearch = new PersistentSearchDecorator( getLdapServer().getDirectoryService()
+ .getLdapCodecService() );
+ }
+
+
+ PSearchListener( PersistentSearch persistentSearch )
+ {
+ CodecControl<? extends Control> wrapped =
+ getLdapServer().getDirectoryService().getLdapCodecService().newControl( persistentSearch );
+ this.persistentSearch = ( PersistentSearchDecorator ) wrapped;
+ }
+
+
+ void close()
+ {
+ if ( list != null )
+ {
+ try
+ {
+ list.close();
+ LOG.debug( "PSearchListener: search naming enumeration closed()" );
+ }
+ catch ( Exception e )
+ {
+ LOG.error( "Error closing NamingEnumeration on PSearchListener", e );
+ }
+ }
+
+ if ( ctx != null )
+ {
+ try
+ {
+ ctx.close();
+ LOG.debug( "PSearchListener: search context closed()" );
+ }
+ catch ( Exception e )
+ {
+ LOG.error( "Error closing connection on PSearchListener", e );
+ }
+ }
+ }
+
+
+ public void run()
+ {
+ LOG.debug( "PSearchListener.run() called." );
+ LdapApiService codec = getLdapServer().getDirectoryService().getLdapCodecService();
+ persistentSearch.setCritical( true );
+ persistentSearch.setValue( persistentSearch.getValue() );
+
+ Control[] ctxCtls = new Control[]
+ { persistentSearch };
+
+ try
+ {
+ ctx = ( LdapContext ) getWiredContext( getLdapServer() ).lookup( BASE );
+ ctx.setRequestControls( JndiUtils.toJndiControls( codec, ctxCtls ) );
+ isReady = true;
+ LOG.debug( "PSearchListener is ready and about to issue persistent search request." );
+ list = ctx.search( "", "objectClass=*", null );
+ LOG.debug( "PSearchListener search request returned." );
+ EntryChange ecControl = null;
+
+ while ( list.hasMore() )
+ {
+ LOG.debug( "PSearchListener search request got an item." );
+ javax.naming.ldap.Control[] controls;
+ SearchResult sresult = list.next();
+
+ if ( sresult instanceof HasControls )
+ {
+ controls = ( ( HasControls ) sresult ).getControls();
+
+ if ( controls != null )
+ {
+ for ( javax.naming.ldap.Control jndiControl : controls )
+ {
+ if ( jndiControl.getID().equals(
+ EntryChange.OID ) )
+ {
+ ecControl = ( EntryChange ) JndiUtils.fromJndiControl( codec, jndiControl );
+ ( ( EntryChangeDecorator ) ecControl ).decode( jndiControl.getEncodedValue() );
+ }
+ }
+ }
+ }
+
+ result = new PSearchNotification( sresult, ecControl );
+ break;
+ }
+
+ LOG.debug( "PSearchListener broke out of while loop." );
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace();
+ LOG.error( "PSearchListener encountered error", e );
+ }
+ finally
+ {
+ }
+ }
+ }
+
+ class PSearchNotification extends SearchResult
+ {
+ private static final long serialVersionUID = 1L;
+ final EntryChange control;
+
+
+ public PSearchNotification( SearchResult result, EntryChange control )
+ {
+ super( result.getName(), result.getClassName(), result.getObject(), result.getAttributes(), result
+ .isRelative() );
+ this.control = control;
+ }
+
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "Dn: " ).append( getName() ).append( "\n" );
+
+ if ( control != null )
+ {
+ buf.append( " EntryChangeControl =\n" );
+ buf.append( " changeType : " ).append( control.getChangeType() ).append( "\n" );
+ buf.append( " previousDN : " ).append( control.getPreviousDn() ).append( "\n" );
+ buf.append( " changeNumber : " ).append( control.getChangeNumber() ).append( "\n" );
+ }
+
+ return buf.toString();
+ }
+ }
+}
diff --git a/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/AbstractTable.java b/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/AbstractTable.java
index 4173d42..9050fed 100644
--- a/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/AbstractTable.java
+++ b/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/AbstractTable.java
@@ -24,7 +24,9 @@ import java.io.IOException;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
import org.apache.directory.server.i18n.I18n;
@@ -119,13 +121,45 @@ public abstract class AbstractTable<K, V> implements Table<K, V>
/**
* {@inheritDoc}
*/
- public long count() throws IOException
+ public long count( PartitionTxn transaction ) throws LdapException
{
return count;
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public long greaterThanCount( PartitionTxn transaction, K key ) throws LdapException
+ {
+ // take a best guess
+ return Math.min( count, 10L );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long lessThanCount( PartitionTxn transaction, K key ) throws LdapException
+ {
+ // take a best guess
+ return Math.min( count, 10L );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDupsEnabled()
+ {
+ return allowsDuplicates;
+ }
+
+
+ /**
* @see Object#toString()
*/
public String toString()
diff --git a/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/Table.java b/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/Table.java
index 1f723e2..d13819c 100644
--- a/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/Table.java
+++ b/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/Table.java
@@ -24,6 +24,9 @@ import java.util.Comparator;
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.exception.LdapException;
+import org.apache.directory.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
/**
@@ -80,22 +83,24 @@ public interface Table<K, V>
* this is exactly the same as a get call with a check to see if the
* returned value is null or not.
*
+ * @param transaction The transaction we are running in
* @param key the Object of the key to check for
* @return true if the key exists, false otherwise
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
*/
- boolean has( K key ) throws Exception;
+ boolean has( PartitionTxn transaction, K key ) throws LdapException;
/**
* Checks to see if this table has a key with a specific value.
*
+ * @param transaction The transaction we are running in
* @param key the key to check for
* @param value the value to check for
* @return true if a record with the key and value exists, false otherwise
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
*/
- boolean has( K key, V value ) throws Exception;
+ boolean has( PartitionTxn transaction, K key, V value ) throws LdapException;
/**
@@ -104,12 +109,13 @@ public interface Table<K, V>
* call to return true. The underlying database must sort keys based on a
* key comparator because this method depends on key ordering.
*
+ * @param transaction The transaction we are running in
* @param key the key to compare keys to
* @return true if a Tuple with a key greater than or equal to the key
* argument exists, false otherwise
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
*/
- boolean hasGreaterOrEqual( K key ) throws Exception;
+ boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException;
/**
@@ -118,12 +124,13 @@ public interface Table<K, V>
* call to return true. The underlying database must sort keys based on a
* key comparator because this method depends on key ordering.
*
+ * @param transaction The transaction we are running in
* @param key the key to compare keys to
* @return true if a Tuple with a key less than or equal to the key
* argument exists, false otherwise
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
*/
- boolean hasLessOrEqual( K key ) throws Exception;
+ boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException;
/**
@@ -138,15 +145,16 @@ public interface Table<K, V>
* If the table does not support duplicates then an
* UnsupportedOperationException is thrown.
*
+ * @param transaction The transaction we are running in
* @param key the key
* @param val the value to compare values to
* @return true if a Tuple with a key equal to the key argument and a
* value greater than the value argument exists, false otherwise
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
* or if the underlying Db is not of the Btree type that allows sorted
* duplicate values.
*/
- boolean hasGreaterOrEqual( K key, V val ) throws Exception;
+ boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException;
/**
@@ -161,15 +169,16 @@ public interface Table<K, V>
* If the table does not support duplicates then an
* UnsupportedOperationException is thrown.
*
+ * @param transaction The transaction we are running in
* @param key the key
* @param val the value to compare values to
* @return true if a Tuple with a key equal to the key argument and a
* value less than the value argument exists, false otherwise
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
* or if the underlying Db is not of the Btree type that allows sorted
* duplicate values.
*/
- boolean hasLessOrEqual( K key, V val ) throws Exception;
+ boolean hasLessOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException;
// ------------------------------------------------------------------------
@@ -180,59 +189,64 @@ public interface Table<K, V>
* Gets the value of a record by key if the key exists. If this Table
* allows duplicate keys then the first key will be returned. If this
* Table sorts keys then the key will be the smallest key in the Table as
- * specificed by this Table's comparator or the default bytewise lexical
+ * specified by this Table's comparator or the default byte-wise lexical
* comparator.
*
+ * @param transaction The transaction we are running in
* @param key the key of the record
* @return the value of the record with the specified key if key exists or
* null if no such key exists.
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
*/
- V get( K key ) throws Exception;
+ V get( PartitionTxn transaction, K key ) throws LdapException;
/**
* Puts a record into this Table. Null is not allowed for keys or values
* and should result in an IllegalArgumentException.
*
+ * @param writeTransaction The transaction we are running in
* @param key the key of the record
* @param value the value of the record.
- * @throws Exception if there is a failure to read or write to the
+ * @throws LdapException if there is a failure to read or write to the
* underlying Db
* @throws IllegalArgumentException if a null key or value is used
*/
- void put( K key, V value ) throws Exception;
+ void put( PartitionWriteTxn writeTransaction, K key, V value ) throws LdapException;
/**
* Removes all records with a specified key from this Table.
*
+ * @param writeTransaction The transaction we are running in
* @param key the key of the records to remove
- * @throws Exception if there is a failure to read or write to
+ * @throws LdapException if there is a failure to read or write to
* the underlying Db
*/
- void remove( K key ) throws Exception;
+ void remove( PartitionWriteTxn writeTransaction, K key ) throws LdapException;
/**
* Removes a single key value pair with a specified key and value from
* this Table.
*
+ * @param writeTransaction The transaction we are running in
* @param key the key of the record to remove
* @param value the value of the record to remove
- * @throws Exception if there is a failure to read or write to
+ * @throws LdapException if there is a failure to read or write to
* the underlying Db
*/
- void remove( K key, V value ) throws Exception;
+ void remove( PartitionWriteTxn writeTransaction, K key, V value ) throws LdapException;
/**
* Creates a Cursor that traverses Tuples in a Table.
*
+ * @param transaction The transaction we are running in
* @return a Cursor over Tuples containing the key value pairs
- * @throws Exception if there are failures accessing underlying stores
+ * @throws LdapException if there are failures accessing underlying stores
*/
- Cursor<Tuple<K, V>> cursor() throws Exception;
+ Cursor<Tuple<K, V>> cursor( PartitionTxn transaction ) throws LdapException;
/**
@@ -244,11 +258,12 @@ public interface Table<K, V>
* to a specific key. This Cursor is naturally limited to return only
* the tuples for the same key.
*
+ * @param transaction The transaction we are running in
* @param key the duplicate key to return the Tuples of
* @return a Cursor over Tuples containing the same key
- * @throws Exception if there are failures accessing underlying stores
+ * @throws LdapException if there are failures accessing underlying stores
*/
- Cursor<Tuple<K, V>> cursor( K key ) throws Exception;
+ Cursor<Tuple<K, V>> cursor( PartitionTxn transaction, K key ) throws LdapException;
/**
@@ -261,11 +276,12 @@ public interface Table<K, V>
* reused to return key value pairs. This Cursor is naturally limited to
* return only the values for the same key.
*
+ * @param transaction The transaction we are running in
* @param key the duplicate key to return the values of
* @return a Cursor over values of a key
- * @throws Exception if there are failures accessing underlying stores
+ * @throws LdapException if there are failures accessing underlying stores
*/
- Cursor<V> valueCursor( K key ) throws Exception;
+ Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException;
// ------------------------------------------------------------------------
@@ -275,21 +291,23 @@ public interface Table<K, V>
/**
* Gets the count of the number of Tuples in this Table.
*
+ * @param transaction The transaction we are running in
* @return the number of records
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
*/
- long count() throws Exception;
+ long count( PartitionTxn transaction ) throws LdapException;
/**
* Gets the count of the number of records in this Table with a specific
* key: returns the number of duplicates for a key.
*
+ * @param transaction The transaction we are running in
* @param key the Object key to count.
* @return the number of duplicate records for a key.
- * @throws Exception if there is a failure to read the underlying Db
+ * @throws LdapException if there is a failure to read the underlying Db
*/
- long count( K key ) throws Exception;
+ long count( PartitionTxn transaction, K key ) throws LdapException;
/**
@@ -297,11 +315,12 @@ public interface Table<K, V>
* specific key argument provided need not exist for this call to return
* a non-zero value.
*
+ * @param transaction The transaction we are running in
* @param key the key to use in comparisons
* @return the number of keys greater than or equal to the key
- * @throws Exception if there is a failure to read the underlying db
+ * @throws LdapException if there is a failure to read the underlying db
*/
- long greaterThanCount( K key ) throws Exception;
+ long greaterThanCount( PartitionTxn transaction, K key ) throws LdapException;
/**
@@ -309,17 +328,19 @@ public interface Table<K, V>
* specific key argument provided need not exist for this call to return
* a non-zero value.
*
+ * @param transaction The transaction we are running in
* @param key the key to use in comparisons
* @return the number of keys less than or equal to the key
- * @throws Exception if there is a failure to read the underlying db
+ * @throws LdapException if there is a failure to read the underlying db
*/
- long lessThanCount( K key ) throws Exception;
+ long lessThanCount( PartitionTxn transaction, K key ) throws LdapException;
/**
* Closes the underlying Db of this Table.
*
- * @throws Exception on any failures
+ * @param transaction The transaction we are running in
+ * @throws LdapException on any failures
*/
- void close() throws Exception;
+ void close( PartitionTxn transaction ) throws LdapException;
}
diff --git a/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/impl/avl/AvlTable.java b/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/impl/avl/AvlTable.java
index 7d53c3b..d398c4c 100644
--- a/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/impl/avl/AvlTable.java
+++ b/xdbm-partition/src/main/java/org/apache/directory/server/xdbm/impl/avl/AvlTable.java
@@ -27,6 +27,8 @@ 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.server.core.api.partition.PartitionTxn;
+import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
import org.apache.directory.server.core.avltree.AvlSingletonOrOrderedSetCursor;
import org.apache.directory.server.core.avltree.AvlTree;
import org.apache.directory.server.core.avltree.AvlTreeCursor;
@@ -69,7 +71,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public void close() throws Exception
+ @Override
+ public void close( PartitionTxn transaction ) throws LdapException
{
( ( AvlTreeMapImpl<K, V> ) avl ).removeAll();
}
@@ -78,7 +81,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public long count( K key ) throws Exception
+ @Override
+ public long count( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -106,7 +110,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public V get( K key ) throws LdapException
+ @Override
+ public V get( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -134,7 +139,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public long greaterThanCount( K key ) throws Exception
+ @Override
+ public long greaterThanCount( PartitionTxn transaction, K key ) throws LdapException
{
return avl.getSize();
}
@@ -143,7 +149,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public boolean has( K key ) throws Exception
+ @Override
+ public boolean has( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -157,7 +164,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public boolean has( K key, V value ) throws LdapException
+ @Override
+ public boolean has( PartitionTxn transaction, K key, V value ) throws LdapException
{
if ( key == null )
{
@@ -171,7 +179,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public boolean hasGreaterOrEqual( K key ) throws Exception
+ @Override
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -185,7 +194,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public boolean hasGreaterOrEqual( K key, V val ) throws LdapException
+ @Override
+ public boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -212,7 +222,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public boolean hasLessOrEqual( K key ) throws Exception
+ @Override
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -226,7 +237,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public boolean hasLessOrEqual( K key, V val ) throws Exception
+ @Override
+ public boolean hasLessOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
{
if ( key == null )
{
@@ -253,16 +265,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public boolean isDupsEnabled()
- {
- return allowsDuplicates;
- }
-
-
- /**
- * {@inheritDoc}
- */
- public long lessThanCount( K key ) throws Exception
+ @Override
+ public long lessThanCount( PartitionTxn transaction, K key ) throws LdapException
{
return count;
}
@@ -271,7 +275,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public void put( K key, V value ) throws Exception
+ @Override
+ public void put( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
if ( ( key == null ) || ( value == null ) )
{
@@ -288,7 +293,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public void remove( K key ) throws Exception
+ @Override
+ public void remove( PartitionWriteTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -316,7 +322,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public void remove( K key, V value ) throws Exception
+ @Override
+ public void remove( PartitionWriteTxn transaction, K key, V value ) throws LdapException
{
if ( avl.remove( key, value ) != null )
{
@@ -328,7 +335,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public Cursor<Tuple<K, V>> cursor() throws LdapException
+ @Override
+ public Cursor<Tuple<K, V>> cursor( PartitionTxn transaction ) throws LdapException
{
if ( !allowsDuplicates )
{
@@ -343,7 +351,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public Cursor<Tuple<K, V>> cursor( K key ) throws Exception
+ @Override
+ public Cursor<Tuple<K, V>> cursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
@@ -370,7 +379,8 @@ public class AvlTable<K, V> extends AbstractTable<K, V>
/**
* {@inheritDoc}
*/
- public Cursor<V> valueCursor( K key ) throws Exception
+ @Override
+ public Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException
{
if ( key == null )
{
--
To stop receiving notification emails like this one, please contact
elecharny@apache.org.