From 5905074f71d4add21a72881816b873e3053d8a37 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Tue, 02 Dec 2014 09:57:47 +0000
Subject: [PATCH] OPENDJ-1602 New pluggable storage based backend

---
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitBackend.java         |  150 ++++++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NotImplementedException.java  |   51 ++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/SuffixContainer.java          |   33 +
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitSuffixContainer.java |  308 ++++++++++++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java                 |   13 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/package-info.java             |   33 +
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitDN2ID.java           |  168 +++++++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitRootContainer.java   |  316 +++++++++++++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/PluggableStorageBackend.java  |   94 +++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java            |    3 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitID2Entry.java        |  160 ++++++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/KeyValueStore.java            |  106 ++++
 12 files changed, 1,410 insertions(+), 25 deletions(-)

diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java
index 6c5df91..33f1ca7 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -75,17 +75,17 @@
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
   /** The name of the entry database. */
-  public static final String ID2ENTRY_DATABASE_NAME = "id2entry";
+  public static final String ID2ENTRY_DATABASE_NAME = ID2ENTRY_INDEX_NAME;
   /** The name of the DN database. */
-  public static final String DN2ID_DATABASE_NAME = "dn2id";
+  public static final String DN2ID_DATABASE_NAME = DN2ID_INDEX_NAME;
   /** The name of the children index database. */
-  private static final String ID2CHILDREN_DATABASE_NAME = "id2children";
+  private static final String ID2CHILDREN_DATABASE_NAME = ID2CHILDREN_INDEX_NAME;
   /** The name of the subtree index database. */
-  private static final String ID2SUBTREE_DATABASE_NAME = "id2subtree";
+  private static final String ID2SUBTREE_DATABASE_NAME = ID2SUBTREE_INDEX_NAME;
   /** The name of the referral database. */
-  private static final String REFERRAL_DATABASE_NAME = "referral";
+  private static final String REFERRAL_DATABASE_NAME = REFERRAL_INDEX_NAME;
   /** The name of the state database. */
-  private static final String STATE_DATABASE_NAME = "state";
+  private static final String STATE_DATABASE_NAME = STATE_INDEX_NAME;
   /** The attribute used to return a search index debug string to the client. */
   public static final String ATTR_DEBUG_SEARCH_INDEX = "debugsearchindex";
 
@@ -510,6 +510,7 @@
    *
    * @throws DatabaseException If an error occurs in the JE database.
    */
+  @Override
   public void close() throws DatabaseException
   {
     // Close core indexes.
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitBackend.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitBackend.java
new file mode 100644
index 0000000..4660da0
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitBackend.java
@@ -0,0 +1,150 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+package org.opends.server.backends.persistit;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.opends.server.admin.std.server.LocalDBBackendCfg;
+import org.opends.server.backends.pluggable.PluggableStorageBackend;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+
+import com.persistit.exception.PersistitException;
+import com.sleepycat.je.DatabaseException;
+
+import static org.opends.messages.BackendMessages.*;
+import static org.opends.messages.JebMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
+/**
+ * This is an implementation of a Directory Server Backend which stores entries
+ * locally in a Persistit database.
+ */
+public class PersistitBackend extends PluggableStorageBackend<LocalDBBackendCfg>
+{
+  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+
+  private PersistitRootContainer rootContainer;
+
+  /**
+   * Returns the root container.
+   *
+   * @return the root container
+   */
+  public PersistitRootContainer getRootContainer()
+  {
+    return rootContainer;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void initializeBackend() throws ConfigException, InitializationException
+  {
+    rootContainer = new PersistitRootContainer(this, cfg);
+    rootContainer.open();
+
+    registerBaseDNs(cfg.getBaseDN());
+
+    cfg.addLocalDBChangeListener(this);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void finalizeBackend()
+  {
+    super.finalizeBackend();
+    cfg.removeLocalDBChangeListener(this);
+
+    deregisterBaseDNs(rootContainer.getSuffixContainers().keySet());
+
+    // Close the database.
+    try
+    {
+      rootContainer.close();
+      rootContainer = null;
+    }
+    catch (DatabaseException e)
+    {
+      logger.traceException(e);
+      logger.error(ERR_JEB_DATABASE_EXCEPTION, e.getMessage());
+    }
+
+    // Log an informational message.
+    logger.info(NOTE_BACKEND_OFFLINE, cfg.getBackendId());
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public long getEntryCount()
+  {
+    if (rootContainer != null)
+    {
+      try
+      {
+        return rootContainer.getEntryCount();
+      }
+      catch (Exception e)
+      {
+        logger.traceException(e);
+      }
+    }
+    return -1;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Entry getEntry(DN entryDN) throws DirectoryException
+  {
+    PersistitSuffixContainer sc = rootContainer.getSuffixContainer(entryDN);
+    try
+    {
+      return sc.getEntry(entryDN);
+    }
+    catch (PersistitException e)
+    {
+      logger.traceException(e);
+      throw createDirectoryException(e);
+    }
+  }
+
+  /**
+   * Creates a customized DirectoryException from the DatabaseException thrown
+   * by JE backend.
+   *
+   * @param e
+   *          The PersistitException to be converted.
+   * @return DirectoryException created from exception.
+   */
+  private DirectoryException createDirectoryException(PersistitException e)
+  {
+    // TODO JNR rename the exception to remove the "JEB"
+    LocalizableMessage message = ERR_JEB_DATABASE_EXCEPTION.get(stackTraceToSingleLineString(e));
+    return new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
+  }
+}
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitDN2ID.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitDN2ID.java
new file mode 100644
index 0000000..3767624
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitDN2ID.java
@@ -0,0 +1,168 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+package org.opends.server.backends.persistit;
+
+import org.opends.server.backends.jeb.EntryID;
+import org.opends.server.backends.pluggable.KeyValueStore;
+import org.opends.server.backends.pluggable.NotImplementedException;
+import org.opends.server.backends.pluggable.SuffixContainer;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+
+import com.persistit.Exchange;
+import com.persistit.Key;
+import com.persistit.Transaction;
+import com.persistit.Value;
+import com.persistit.exception.PersistitException;
+
+import static org.opends.server.backends.jeb.JebFormat.*;
+
+/**
+ * Persistit implementation of the DN2ID index.
+ */
+class PersistitDN2ID implements KeyValueStore<DN, EntryID, Transaction, Void>
+{
+
+  private static final String INDEX_NAME = SuffixContainer.ID2ENTRY_INDEX_NAME;
+  private final String fullyQualifiedIndexName;
+  private final PersistitSuffixContainer suffixContainer;
+  private final int prefixRDNComponents;
+
+  /**
+   * Creates a new dn2id index.
+   *
+   * @param suffixContainer
+   *          the suffix container holding this dn2id index
+   */
+  PersistitDN2ID(PersistitSuffixContainer suffixContainer)
+  {
+    this.suffixContainer = suffixContainer;
+    this.fullyQualifiedIndexName = suffixContainer.getFullyQualifiedIndexName(INDEX_NAME);
+    this.prefixRDNComponents = suffixContainer.getBaseDN().size();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void open() throws DirectoryException
+  {
+    try
+    {
+      this.suffixContainer.createTree(INDEX_NAME);
+    }
+    catch (PersistitException e)
+    {
+      throw new NotImplementedException(e);
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean insert(Transaction txn, DN dn, EntryID entryID) throws DirectoryException
+  {
+    Exchange ex = null;
+    try
+    {
+      ex = suffixContainer.getExchange(INDEX_NAME);
+      appendAll(ex.getKey(), dnToDNKey(dn, prefixRDNComponents));
+      ex.getValue().put(entryID.longValue());
+      ex.store();
+      return true;
+    }
+    catch (PersistitException e)
+    {
+      throw new NotImplementedException(e);
+    }
+    finally
+    {
+      suffixContainer.releaseExchange(ex);
+    }
+  }
+
+  private void appendAll(Key key, byte[] bytes)
+  {
+    if (bytes.length == 0)
+    {
+      // TODO JNR which is best here?
+      // key.append(null);
+      key.append((byte) ',');
+    }
+    else
+    {
+      // FIXME JNR this way to append is really not efficient
+      for (byte b : bytes)
+      {
+        key.append(b);
+      }
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean put(Transaction txn, DN dn, EntryID entryID) throws DirectoryException
+  {
+    throw new NotImplementedException();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public EntryID get(Transaction txn, DN dn, Void mode) throws DirectoryException
+  {
+    Exchange ex = null;
+    try
+    {
+      ex = suffixContainer.getExchange(INDEX_NAME);
+      appendAll(ex.getKey(), dnToDNKey(dn, prefixRDNComponents));
+      ex.fetch();
+      final Value value = ex.getValue();
+      if (value.isDefined())
+      {
+        return new EntryID(value.getLong());
+      }
+      return null;
+    }
+    catch (PersistitException e)
+    {
+      throw new NotImplementedException(e);
+    }
+    finally
+    {
+      suffixContainer.releaseExchange(ex);
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean remove(Transaction txn, DN dn) throws DirectoryException
+  {
+    throw new NotImplementedException();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void close()
+  {
+    // nothing to do
+  }
+}
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitID2Entry.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitID2Entry.java
new file mode 100644
index 0000000..5c0bc54
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitID2Entry.java
@@ -0,0 +1,160 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+package org.opends.server.backends.persistit;
+
+import org.forgerock.opendj.ldap.ByteString;
+import org.opends.server.api.CompressedSchema;
+import org.opends.server.backends.jeb.DataConfig;
+import org.opends.server.backends.jeb.EntryID;
+import org.opends.server.backends.jeb.ID2Entry;
+import org.opends.server.backends.pluggable.KeyValueStore;
+import org.opends.server.backends.pluggable.NotImplementedException;
+import org.opends.server.backends.pluggable.SuffixContainer;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+
+import com.persistit.Exchange;
+import com.persistit.Value;
+import com.persistit.exception.PersistitException;
+
+/**
+ * Persistit implementation of the ID2entry index.
+ */
+class PersistitID2Entry implements KeyValueStore<EntryID, Entry, Void, Void>
+{
+
+  // TODO JNR use com.persistit.encoding.ObjectCache when decoding attributes?
+
+  private static final String INDEX_NAME = SuffixContainer.ID2ENTRY_INDEX_NAME;
+  private final String fullyQualifiedIndexName;
+  private final PersistitSuffixContainer suffixContainer;
+  /** TODO JNR remove. */
+  private final DataConfig dataConfig = new DataConfig(false, false, new CompressedSchema());
+
+  /**
+   * Creates a new id2entry index.
+   *
+   * @param suffixContainer
+   *          the suffix container holding this id2entry index
+   */
+  PersistitID2Entry(PersistitSuffixContainer suffixContainer)
+  {
+    this.suffixContainer = suffixContainer;
+    this.fullyQualifiedIndexName = suffixContainer.getFullyQualifiedIndexName(INDEX_NAME);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void open() throws DirectoryException
+  {
+    try
+    {
+      this.suffixContainer.createTree(INDEX_NAME);
+    }
+    catch (PersistitException e)
+    {
+      throw new NotImplementedException(e);
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean insert(Void txn, EntryID entryID, Entry entry) throws DirectoryException
+  {
+    Exchange ex = null;
+    try
+    {
+      ex = suffixContainer.getExchange(INDEX_NAME);
+      ex.getKey().append(entryID.longValue());
+      ex.getValue().putByteArray(toByteArray(entry));
+      ex.store();
+      return true;
+    }
+    catch (PersistitException e)
+    {
+      throw new NotImplementedException(e);
+    }
+    finally
+    {
+      suffixContainer.releaseExchange(ex);
+    }
+  }
+
+  private byte[] toByteArray(Entry entry) throws DirectoryException
+  {
+    ByteString bs = ID2Entry.entryToDatabase(entry, dataConfig);
+    return bs.toByteArray();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean put(Void txn, EntryID entryID, Entry entry) throws DirectoryException
+  {
+    throw new NotImplementedException();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Entry get(Void txn, EntryID entryID, Void mode) throws DirectoryException
+  {
+    Exchange ex = null;
+    try
+    {
+      ex = suffixContainer.getExchange(INDEX_NAME);
+      ex.getKey().append(entryID.longValue());
+      ex.fetch();
+      final Value value = ex.getValue();
+      if (value.isDefined())
+      {
+        ByteString bytes = ByteString.wrap(value.getByteArray());
+        CompressedSchema compressedSchema = dataConfig.getEntryEncodeConfig().getCompressedSchema();
+        return ID2Entry.entryFromDatabase(bytes, compressedSchema);
+      }
+      return null;
+    }
+    catch (Exception e)
+    {
+      throw new NotImplementedException(e);
+    }
+    finally
+    {
+      suffixContainer.releaseExchange(ex);
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean remove(Void txn, EntryID entryID) throws DirectoryException
+  {
+    throw new NotImplementedException();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void close()
+  {
+    // nothing to do
+  }
+}
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitRootContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitRootContainer.java
new file mode 100644
index 0000000..e7d0450
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitRootContainer.java
@@ -0,0 +1,316 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+package org.opends.server.backends.persistit;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.opends.server.admin.std.server.LocalDBBackendCfg;
+import org.opends.server.backends.pluggable.NotImplementedException;
+import org.opends.server.backends.pluggable.RootContainer;
+import org.opends.server.backends.pluggable.SuffixContainer;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.FilePermission;
+import org.opends.server.types.InitializationException;
+
+import com.persistit.Persistit;
+import com.persistit.Volume;
+import com.persistit.exception.PersistitException;
+
+import static org.opends.messages.ConfigMessages.*;
+import static org.opends.messages.JebMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
+/**
+ * Persistit implementation of a {@link RootContainer}.
+ */
+class PersistitRootContainer implements RootContainer<PersistitSuffixContainer>
+{
+  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+
+  private final PersistitBackend backend;
+  private final LocalDBBackendCfg cfg;
+  private Persistit db;
+
+  /** The base DNs contained in this root container. */
+  private final ConcurrentHashMap<DN, PersistitSuffixContainer> suffixContainers =
+      new ConcurrentHashMap<DN, PersistitSuffixContainer>();
+
+  /**
+   * Constructor for this class.
+   *
+   * @param backend
+   *          the persistit backend
+   * @param config
+   *          the configuration object
+   */
+  PersistitRootContainer(PersistitBackend backend, LocalDBBackendCfg config)
+  {
+    this.backend = backend;
+    this.cfg = config;
+  }
+
+  /**
+   * Returns the persistit backend.
+   *
+   * @return the persistit backend
+   */
+  public final PersistitBackend getBackend()
+  {
+    return backend;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Map<DN, PersistitSuffixContainer> getSuffixContainers()
+  {
+    return suffixContainers;
+  }
+
+  /**
+   * Code copied from
+   * {@link org.opends.server.backends.jeb. RootContainer#open(com.sleepycat.je.EnvironmentConfig)}
+   *
+   * @throws ConfigException
+   *           If an configuration error occurs while creating the environment.
+   */
+  void open() throws ConfigException
+  {
+    // Determine the backend database directory.
+    File parentDirectory = getFileForPath(cfg.getDBDirectory());
+    File backendDirectory = new File(parentDirectory, cfg.getBackendId());
+
+    // Create the directory if it doesn't exist.
+    if (!backendDirectory.exists())
+    {
+      if (!backendDirectory.mkdirs())
+      {
+        throw new ConfigException(ERR_JEB_CREATE_FAIL.get(backendDirectory.getPath()));
+      }
+    }
+    // Make sure the directory is valid.
+    else if (!backendDirectory.isDirectory())
+    {
+      throw new ConfigException(ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath()));
+    }
+
+    FilePermission backendPermission;
+    try
+    {
+      backendPermission = FilePermission.decodeUNIXMode(cfg.getDBDirectoryPermissions());
+    }
+    catch (Exception e)
+    {
+      throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(cfg.dn()));
+    }
+
+    // Make sure the mode will allow the server itself access to the database
+    if (!backendPermission.isOwnerWritable()
+        || !backendPermission.isOwnerReadable()
+        || !backendPermission.isOwnerExecutable())
+    {
+      throw new ConfigException(ERR_CONFIG_BACKEND_INSANE_MODE.get(cfg.getDBDirectoryPermissions()));
+    }
+
+    // Get the backend database backendDirectory permissions and apply
+    if (FilePermission.canSetPermissions())
+    {
+      try
+      {
+        if (!FilePermission.setPermissions(backendDirectory, backendPermission))
+        {
+          logger.warn(WARN_JEB_UNABLE_SET_PERMISSIONS, backendPermission, backendDirectory);
+        }
+      }
+      catch (Exception e)
+      {
+        // Log an warning that the permissions were not set.
+        logger.warn(WARN_JEB_SET_PERMISSIONS_FAILED, backendDirectory, e);
+      }
+    }
+
+    openAndRegisterSuffixContainers(backendDirectory);
+  }
+
+  private void openAndRegisterSuffixContainers(File backendDirectory)
+  {
+    DN[] baseDNs = backend.getBaseDNs();
+    final Properties properties = new Properties();
+    properties.setProperty("datapath", backendDirectory.toString());
+    properties.setProperty("logpath", backendDirectory + "/log");
+    properties.setProperty("logfile", "${logpath}/dj_${timestamp}.log");
+    properties.setProperty("buffer.count.16384", "64K");
+    for (int i = 0; i < baseDNs.length; i++)
+    {
+      // TODO JNR in the replace() down below,
+      // persistit does not like commas and does not know how to escape them
+      final String baseDN = toVolumeName(baseDNs[i]);
+      properties.setProperty("volume." + (i + 1), "${datapath}/" + baseDN + ",create,pageSize:16K,"
+          + "initialSize:50M,extensionSize:1M,maximumSize:10G");
+    }
+    properties.setProperty("journalpath", "${datapath}/dj_journal");
+    try
+    {
+      db = new Persistit(properties);
+      db.initialize();
+
+      openAndRegisterSuffixContainers(baseDNs);
+
+      if (logger.isTraceEnabled())
+      {
+        logger.trace("Persistit (%s) environment opened with the following config: %n%s",
+            Persistit.VERSION, properties);
+
+        // Get current size of heap in bytes
+        long heapSize = Runtime.getRuntime().totalMemory();
+
+        // Get maximum size of heap in bytes. The heap cannot grow beyond this size.
+        // Any attempt will result in an OutOfMemoryException.
+        long heapMaxSize = Runtime.getRuntime().maxMemory();
+
+        // Get amount of free memory within the heap in bytes. This size will increase
+        // after garbage collection and decrease as new objects are created.
+        long heapFreeSize = Runtime.getRuntime().freeMemory();
+
+        logger.trace("Current size of heap: %d bytes", heapSize);
+        logger.trace("Max size of heap: %d bytes", heapMaxSize);
+        logger.trace("Free memory in heap: %d bytes", heapFreeSize);
+      }
+    }
+    catch (Exception e)
+    {
+      throw new NotImplementedException(e);
+    }
+  }
+
+  private void openAndRegisterSuffixContainers(DN[] baseDNs) throws PersistitException, InitializationException,
+      ConfigException, DirectoryException
+  {
+    for (DN baseDN : baseDNs)
+    {
+      PersistitSuffixContainer sc = openSuffixContainer(baseDN, null);
+      registerSuffixContainer(baseDN, sc);
+    }
+  }
+
+  private PersistitSuffixContainer openSuffixContainer(DN baseDN, String name) throws PersistitException,
+      DirectoryException
+  {
+    String databasePrefix;
+    if (name == null || name.equals(""))
+    {
+      databasePrefix = baseDN.toNormalizedString();
+    }
+    else
+    {
+      databasePrefix = name;
+    }
+
+    final Volume volume = db.loadVolume(toVolumeName(baseDN));
+    final PersistitSuffixContainer suffixContainer =
+        new PersistitSuffixContainer(baseDN, databasePrefix, this, db, volume);
+    suffixContainer.open();
+    return suffixContainer;
+  }
+
+  private String toVolumeName(DN dn)
+  {
+    return dn.toString().replace(",", "_");
+  }
+
+  private void registerSuffixContainer(DN baseDN, PersistitSuffixContainer suffixContainer)
+      throws InitializationException
+  {
+    PersistitSuffixContainer sc = suffixContainers.get(baseDN);
+    if (sc != null)
+    {
+      // If an entry container for this baseDN is already opened we do not allow
+      // another to be opened.
+      throw new InitializationException(ERR_JEB_ENTRY_CONTAINER_ALREADY_REGISTERED.get(sc.getIndexPrefix(), baseDN));
+    }
+
+    suffixContainers.put(baseDN, suffixContainer);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void close()
+  {
+    for (PersistitSuffixContainer sc : suffixContainers.values())
+    {
+      sc.close();
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public long getEntryCount()
+  {
+    boolean couldDetermineAllCounts = true;
+    long result = 0;
+    for (SuffixContainer suffixContainer : getSuffixContainers().values())
+    {
+      final long suffixCount = suffixContainer.getEntryCount();
+      if (suffixCount != -1)
+      {
+        result += suffixCount;
+      }
+      else
+      {
+        couldDetermineAllCounts = false;
+      }
+    }
+    return couldDetermineAllCounts ? result : -1;
+  }
+
+  /**
+   * Return the suffix container holding a specific DN.
+   *
+   * @param entryDN
+   *          The DN for which to return the suffix container.
+   * @return The suffix container holding the DN
+   */
+  PersistitSuffixContainer getSuffixContainer(DN entryDN)
+  {
+    PersistitSuffixContainer sc = null;
+    DN nodeDN = entryDN;
+
+    while (sc == null && nodeDN != null)
+    {
+      sc = suffixContainers.get(nodeDN);
+      if (sc == null)
+      {
+        nodeDN = nodeDN.getParentDNInSuffix();
+      }
+    }
+
+    return sc;
+  }
+}
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitSuffixContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitSuffixContainer.java
new file mode 100644
index 0000000..31dd3d7
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistitSuffixContainer.java
@@ -0,0 +1,308 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+package org.opends.server.backends.persistit;
+
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.opends.server.api.EntryCache;
+import org.opends.server.backends.jeb.EntryID;
+import org.opends.server.backends.pluggable.SuffixContainer;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+
+import com.persistit.Exchange;
+import com.persistit.Persistit;
+import com.persistit.Volume;
+import com.persistit.exception.PersistitException;
+import com.sleepycat.je.DatabaseException;
+
+import static org.opends.messages.JebMessages.*;
+
+/**
+ * Persistit implementation of a {@link SuffixContainer}.
+ */
+class PersistitSuffixContainer implements SuffixContainer
+{
+  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+
+  /** The baseDN handled by this suffix container. */
+  private final DN baseDN;
+  /**
+   * Prevents name clashes for common indexes (like id2entry) across multiple
+   * suffixes. For example when a root container contains multiple suffixes.
+   */
+  private final String indexPrefix;
+  private final PersistitRootContainer rootContainer;
+  private final Persistit db;
+  private final Volume volume;
+
+  private PersistitID2Entry id2entry;
+  private PersistitDN2ID dn2id;
+
+  /**
+   * Constructor for this class.
+   *
+   * @param baseDN
+   *          The baseDN handled by this suffix container
+   * @param indexPrefix
+   *          the prefix to use for indexes name
+   * @param rootContainer
+   *          the persisit root container
+   * @param db
+   *          the persisit database
+   * @param volume
+   *          the volume where indexes will be created
+   */
+  PersistitSuffixContainer(DN baseDN, String indexPrefix, PersistitRootContainer rootContainer,
+      Persistit db, Volume volume)
+  {
+    this.baseDN = baseDN;
+    this.indexPrefix = indexPrefix;
+    this.rootContainer = rootContainer;
+    this.db = db;
+    this.volume = volume;
+  }
+
+  /**
+   * Opens the entryContainer for reading and writing.
+   *
+   * @throws DirectoryException
+   *           If an error occurs while opening the suffix container.
+   */
+  void open() throws DirectoryException
+  {
+    id2entry = new PersistitID2Entry(this);
+    id2entry.open();
+    dn2id = new PersistitDN2ID(this);
+    dn2id.open();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void close()
+  {
+    dn2id.close();
+    id2entry.close();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public DN getBaseDN()
+  {
+    return baseDN;
+  }
+
+  /**
+   * Returns the index name prefix.
+   *
+   * @return the index name prefix
+   */
+  public String getIndexPrefix()
+  {
+    return indexPrefix;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public long getEntryCount()
+  {
+    // FIXME To be implemented
+    // JNR: I have not found a suitable API to return this value
+    return -1;
+  }
+
+  /**
+   * Returns the id2entry index.
+   *
+   * @return the id2entry index
+   */
+  public PersistitID2Entry getID2Entry()
+  {
+    return id2entry;
+  }
+
+  /**
+   * Creates a {@link Tree} for the fully qualified index name.
+   *
+   * @param fullyQualifiedIndexName
+   *          The fully qualified index name
+   * @throws PersistitException
+   *           if a database exception happens
+   */
+  void createTree(String fullyQualifiedIndexName) throws PersistitException
+  {
+    volume.getTree(fullyQualifiedIndexName, true);
+  }
+
+  /**
+   * Returns a new {@link Exchange} working on the index provided via its fully
+   * qualified name.
+   * <p>
+   * Note: exchanges obtained with this method must be released by calling
+   * {@link #releaseExchange(Exchange)}.
+   *
+   * @param fullyQualifiedIndexName
+   *          The fully qualified index name
+   * @return the exchange
+   * @throws PersistitException
+   *           if a database exception happens
+   */
+  final Exchange getExchange(String fullyQualifiedIndexName) throws PersistitException
+  {
+    return db.getExchange(volume, fullyQualifiedIndexName, false);
+  }
+
+  /**
+   * Returns the fully qualified index name for this simple index name.
+   * <p>
+   * e.g.
+   *
+   * <pre>
+   * PersistitSuffixContainer sc = ...; // initialize the suffix container
+   * assertEquals(sc.getIndexPrefix(), "dccom");
+   * assertEquals(sc.getFullyQualifiedIndexName("id2entry"), "dccom_id2entry");
+   * </pre>
+   *
+   * @param simpleIndexName
+   *          the simple index name to convert to a fully qualified index name
+   * @return the fully qualified index name
+   */
+  public String getFullyQualifiedIndexName(String simpleIndexName)
+  {
+    return indexPrefix + "_" + simpleIndexName;
+  }
+
+  /**
+   * Releases the provided exchange.
+   *
+   * @param exchange
+   *          the exchange to release
+   */
+  final void releaseExchange(Exchange exchange)
+  {
+    if (exchange != null)
+    {
+      db.releaseExchange(exchange);
+    }
+  }
+
+  /**
+   * Indicates whether an entry with the specified DN exists.
+   *
+   * @param entryDN
+   *          The DN of the entry for which to determine existence.
+   * @return <CODE>true</CODE> if the specified entry exists, or
+   *         <CODE>false</CODE> if it does not.
+   * @throws DirectoryException
+   *           If a problem occurs while trying to make the determination.
+   */
+  public boolean entryExists(DN entryDN) throws DirectoryException
+  {
+    // Try the entry cache first.
+    EntryCache<?> entryCache = DirectoryServer.getEntryCache();
+    if (entryCache != null && entryCache.containsEntry(entryDN))
+    {
+      return true;
+    }
+
+    try
+    {
+      // Read the ID from dn2id.
+      EntryID id = dn2id.get(null, entryDN, null);
+      return id != null;
+    }
+    catch (DatabaseException e)
+    {
+      logger.traceException(e);
+      return false;
+    }
+  }
+
+  /**
+   * Retrieves and returns the entry associated to the provided DN.
+   *
+   * @param entryDN
+   *          the DN of the entry to return
+   * @return the entry associated to the provided DN
+   * @throws DirectoryException
+   *           if a directory exception happens
+   * @throws PersistitException
+   *           if a database exception happens
+   */
+  Entry getEntry(DN entryDN) throws DirectoryException, PersistitException
+  {
+    EntryCache<?> entryCache = DirectoryServer.getEntryCache();
+
+    // Try the entry cache first.
+    Entry entry = null;
+    if (entryCache != null)
+    {
+      entry = entryCache.getEntry(entryDN);
+    }
+
+    if (entry == null)
+    {
+      // Read dn2id.
+      EntryID entryID = dn2id.get(null, entryDN, null);
+      if (entryID == null)
+      {
+        // The entryDN does not exist.
+
+        // Check for referral entries above the target entry.
+        // TODO JNR uncomment next line
+        // dn2uri.targetEntryReferrals(entryDN, null);
+
+        return null;
+      }
+
+      // Read id2entry.
+      entry = id2entry.get(null, entryID, null);
+      if (entry == null)
+      {
+        // The entryID does not exist.
+        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), ERR_JEB_MISSING_ID2ENTRY_RECORD
+            .get(entryID));
+      }
+
+      // Put the entry in the cache making sure not to overwrite
+      // a newer copy that may have been inserted since the time
+      // we read the cache.
+      if (entryCache != null)
+      {
+        entryCache.putEntryIfAbsent(entry, rootContainer.getBackend(), entryID.longValue());
+      }
+    }
+
+    return entry;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public String toString()
+  {
+    return getClass().getSimpleName() + " baseDN=" + baseDN;
+  }
+}
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/package-info.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/package-info.java
new file mode 100644
index 0000000..00d74b5
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/package-info.java
@@ -0,0 +1,33 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+
+/**
+ * Contains the code for the Directory Server backend that uses Persistit
+ * as the repository for storing entry and index information.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.backends.persistit;
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/KeyValueStore.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/KeyValueStore.java
new file mode 100644
index 0000000..c83d4e9
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/KeyValueStore.java
@@ -0,0 +1,106 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+package org.opends.server.backends.pluggable;
+
+import java.io.Closeable;
+
+import org.opends.server.types.DirectoryException;
+
+/**
+ * TODO JNR.
+ * @param <K> TODO JNR
+ * @param <V> TODO JNR
+ * @param <T> TODO JNR
+ * @param <M> TODO JNR
+ */
+public interface KeyValueStore<K, V, T, M> extends Closeable
+{
+
+  /**
+   * TODO JNR.
+   *
+   * @throws DirectoryException
+   *           TODO JNR
+   */
+  void open() throws DirectoryException;
+
+  /**
+   * TODO JNR.
+   *
+   * @param txn
+   *          TODO JNR
+   * @param key
+   *          TODO JNR
+   * @param value
+   *          TODO JNR
+   * @return TODO JNR
+   * @throws DirectoryException
+   *           TODO JNR
+   */
+  boolean insert(T txn, K key, V value) throws DirectoryException;
+
+  /**
+   * TODO JNR.
+   *
+   * @param txn
+   *          TODO JNR
+   * @param key
+   *          TODO JNR
+   * @param value
+   *          TODO JNR
+   * @return TODO JNR
+   * @throws DirectoryException
+   *           TODO JNR
+   */
+  boolean put(T txn, K key, V value) throws DirectoryException;
+
+  /**
+   * TODO JNR.
+   *
+   * @param txn
+   *          TODO JNR
+   * @param key
+   *          TODO JNR
+   * @param mode
+   *          TODO JNR
+   * @return TODO JNR
+   * @throws DirectoryException
+   *           TODO JNR
+   */
+  V get(T txn, K key, M mode) throws DirectoryException;
+
+  /**
+   * TODO JNR.
+   *
+   * @param txn
+   *          TODO JNR
+   * @param key
+   *          TODO JNR
+   * @return TODO JNR
+   * @throws DirectoryException
+   *           TODO JNR
+   */
+  boolean remove(T txn, K key) throws DirectoryException;
+}
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NotImplementedException.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NotImplementedException.java
new file mode 100644
index 0000000..cb18210
--- /dev/null
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NotImplementedException.java
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2014 ForgeRock AS
+ */
+package org.opends.server.backends.pluggable;
+
+/**
+ * Class that helps figuring out where code is not implemented. This class
+ * should only be used during the implementation of pluggable storage backends.
+ */
+public class NotImplementedException extends RuntimeException
+{
+  private static final long serialVersionUID = 6647576563755499711L;
+
+  /** Default constructor. */
+  public NotImplementedException()
+  {
+    super("Code is not implemented");
+  }
+
+  /**
+   * Default constructor.
+   *
+   * @param cause
+   *          the cause
+   */
+  public NotImplementedException(Throwable cause)
+  {
+    super("Code is not implemented", cause);
+  }
+}
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/PluggableStorageBackend.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/PluggableStorageBackend.java
index d523b40..70a3e56 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/PluggableStorageBackend.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/PluggableStorageBackend.java
@@ -25,15 +25,20 @@
  */
 package org.opends.server.backends.pluggable;
 
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
+import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.config.server.ConfigException;
 import org.forgerock.opendj.ldap.ConditionResult;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.util.Reject;
-import org.opends.server.admin.Configuration;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.server.BackendCfg;
 import org.opends.server.api.Backend;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DeleteOperation;
@@ -45,6 +50,7 @@
 import org.opends.server.types.BackupConfig;
 import org.opends.server.types.BackupDirectory;
 import org.opends.server.types.CanceledOperationException;
+import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
@@ -64,29 +70,65 @@
  * @param <C>
  *          the type of the BackendCfg for the current backend
  */
-public abstract class PluggableStorageBackend<C extends Configuration> extends Backend<C>
+public abstract class PluggableStorageBackend<C extends BackendCfg>
+    extends Backend<C>
+    implements ConfigurationChangeListener<C>
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
+  /** The configuration object. */
+  protected C cfg;
   /** The set of base DNs for this backend. */
   private DN[] baseDNs;
 
   /** {@inheritDoc} */
   @Override
-  public void configureBackend(final Configuration cfg) throws ConfigException
+  public void configureBackend(final C cfg) throws ConfigException
   {
     Reject.ifNull(cfg);
+
+    this.cfg = cfg;
+    baseDNs = this.cfg.getBaseDN().toArray(new DN[0]);
   }
 
   /** {@inheritDoc} */
   @Override
-  public void initializeBackend() throws InitializationException
+  public boolean isConfigurationChangeAcceptable(C cfg, List<LocalizableMessage> unacceptableReasons)
   {
-    for (DN baseDN : this.baseDNs)
+    return false;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public ConfigChangeResult applyConfigurationChange(C cfg)
+  {
+    return null;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void initializeBackend() throws ConfigException, InitializationException
+  {
+    registerBaseDNs(cfg.getBaseDN());
+  }
+
+  /**
+   * Associates the current backend with the provided baseDNs in the directory
+   * server.
+   *
+   * @param baseDNs
+   *          the base DNs to be associated with this backend
+   * @throws InitializationException
+   *           If a problem occurs during initialization that is not related to
+   *           the server configuration.
+   */
+  public void registerBaseDNs(Collection<DN> baseDNs) throws InitializationException
+  {
+    for (DN baseDN : baseDNs)
     {
       try
       {
-        DirectoryServer.registerBaseDN(baseDN, this, true);
+        DirectoryServer.registerBaseDN(baseDN, this, false);
       }
       catch (final Exception e)
       {
@@ -101,7 +143,19 @@
   {
     super.finalizeBackend();
 
-    for (DN baseDN : this.baseDNs)
+    deregisterBaseDNs(cfg.getBaseDN());
+  }
+
+  /**
+   * Dissociates the current backend from the provided baseDNs in the directory
+   * server.
+   *
+   * @param baseDNs
+   *          the base DNs to dissociate from this backend
+   */
+  public void deregisterBaseDNs(Collection<DN> baseDNs)
+  {
+    for (DN baseDN : baseDNs)
     {
       try
       {
@@ -125,7 +179,7 @@
   @Override
   public void preloadEntryCache() throws UnsupportedOperationException
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
@@ -139,7 +193,7 @@
   @Override
   public boolean isIndexed(final AttributeType attributeType, final IndexType indexType)
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
@@ -151,7 +205,7 @@
       throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
           ERR_BACKEND_GET_ENTRY_NULL.get(getBackendID()));
     }
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
@@ -170,7 +224,7 @@
   @Override
   public long numSubordinates(final DN entryDN, final boolean subtree) throws DirectoryException
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
@@ -210,9 +264,9 @@
 
   /** {@inheritDoc} */
   @Override
-  public void search(final SearchOperation searchOperation) throws DirectoryException
+  public void search(final SearchOperation searchOperation) throws DirectoryException, CanceledOperationException
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
@@ -263,14 +317,14 @@
   @Override
   public boolean supportsBackup()
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
   @Override
   public boolean supportsBackup(BackupConfig backupConfig, StringBuilder unsupportedReason)
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
@@ -293,7 +347,7 @@
   @Override
   public boolean supportsRestore()
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
   /** {@inheritDoc} */
@@ -308,7 +362,13 @@
   @Override
   public long getEntryCount()
   {
-    throw new RuntimeException("Not implemented");
+    throw new NotImplementedException();
   }
 
+  /** {@inheritDoc} */
+  @Override
+  public String toString()
+  {
+    return getClass().getSimpleName() + " baseDNs=" + Arrays.toString(baseDNs);
+  }
 }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
index f5c96a9..e351e95 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
@@ -24,6 +24,7 @@
  */
 package org.opends.server.backends.pluggable;
 
+import java.io.Closeable;
 import java.util.Map;
 
 import org.opends.server.types.DN;
@@ -43,7 +44,7 @@
  * @param <T>
  *          the type of the suffix containers
  */
-public interface RootContainer<T extends SuffixContainer>
+public interface RootContainer<T extends SuffixContainer> extends Closeable
 {
 
   /**
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/SuffixContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/SuffixContainer.java
index a7a1726..a90682a 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/SuffixContainer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/SuffixContainer.java
@@ -24,6 +24,8 @@
  */
 package org.opends.server.backends.pluggable;
 
+import java.io.Closeable;
+
 import org.opends.server.types.DN;
 
 /**
@@ -32,10 +34,39 @@
  * stores a.k.a indexes. It stores entries in these key-values stores and
  * maintain the indexes all in sync on updates.
  */
-public interface SuffixContainer
+public interface SuffixContainer extends Closeable
 {
 
   /**
+   * The name of the index associating normalized DNs to ids. LDAP DNs uniquely
+   * identify entries.
+   */
+  String DN2ID_INDEX_NAME = "dn2id";
+  /**
+   * The name of the index associating entry ids to entries. Entry ids are
+   * monotonically increasing unique longs and entries are serialized versions
+   * of LDAP entries.
+   */
+  String ID2ENTRY_INDEX_NAME = "id2entry";
+  /**
+   * The name of the index associating an entry id to the entry id set of all
+   * its children.
+   */
+  String ID2CHILDREN_INDEX_NAME = "id2children";
+  /**
+   * The name of the index associating an entry id to the entry id set of all
+   * its subordinates.
+   */
+  String ID2SUBTREE_INDEX_NAME = "id2subtree";
+  /** The name of the index associating normalized DNs to normalized URIs. */
+  String REFERRAL_INDEX_NAME = "referral";
+  /**
+   * The name of the index which associates indexes with their trust state, i.e.
+   * does the index needs to be rebuilt ?
+   */
+  String STATE_INDEX_NAME = "state";
+
+  /**
    * Returns the baseDN that this suffix container is responsible for.
    *
    * @return the baseDN that this suffix container is responsible for

--
Gitblit v1.10.0