From 77131174b396433451592ca82362a3534ce74c80 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Tue, 16 Dec 2014 14:49:33 +0000
Subject: [PATCH] OPENDJ-1602 (CR-5566) New pluggable storage based backend

---
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DN2URI.java            |    8 
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DatabaseContainer.java |   31 --
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/AttributeIndex.java    |  117 +++++-----
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NullIndex.java         |    2 
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java     |   26 +
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/VLVIndex.java          |   30 +-
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/Index.java             |   11 
 opendj3-server-dev/src/server/org/opends/server/types/ConfigChangeResult.java             |   71 ++----
 opendj3-server-dev/src/server/org/opends/server/backends/pluggable/EntryContainer.java    |  307 +++++++++++++--------------
 9 files changed, 285 insertions(+), 318 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/AttributeIndex.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/AttributeIndex.java
index 849475b..2183b19 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/AttributeIndex.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/AttributeIndex.java
@@ -29,7 +29,6 @@
 
 import java.io.Closeable;
 import java.util.*;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
@@ -38,7 +37,6 @@
 import org.forgerock.opendj.ldap.Assertion;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.DecodeException;
-import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.schema.MatchingRule;
 import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
 import org.forgerock.opendj.ldap.spi.IndexingOptions;
@@ -48,6 +46,8 @@
 import org.opends.server.admin.std.server.LocalDBIndexCfg;
 import org.opends.server.backends.pluggable.BackendImpl.StorageRuntimeException;
 import org.opends.server.backends.pluggable.BackendImpl.TreeName;
+import org.opends.server.backends.pluggable.BackendImpl.WriteOperation;
+import org.opends.server.backends.pluggable.BackendImpl.WriteableStorage;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.monitors.DatabaseEnvironmentMonitor;
 import org.opends.server.types.*;
@@ -133,42 +133,42 @@
    * @param entryContainer The entryContainer of this attribute index.
    * @throws ConfigException if a configuration related error occurs.
    */
-  public AttributeIndex(LocalDBIndexCfg indexConfig, EntryContainer entryContainer) throws ConfigException
+  public AttributeIndex(LocalDBIndexCfg indexConfig, EntryContainer entryContainer, WriteableStorage txn) throws ConfigException
   {
     this.entryContainer = entryContainer;
     this.indexConfig = indexConfig;
 
-    buildPresenceIndex();
-    buildIndexes(IndexType.EQUALITY);
-    buildIndexes(IndexType.SUBSTRING);
-    buildIndexes(IndexType.ORDERING);
-    buildIndexes(IndexType.APPROXIMATE);
-    buildExtensibleIndexes();
+    buildPresenceIndex(txn);
+    buildIndexes(txn, IndexType.EQUALITY);
+    buildIndexes(txn, IndexType.SUBSTRING);
+    buildIndexes(txn, IndexType.ORDERING);
+    buildIndexes(txn, IndexType.APPROXIMATE);
+    buildExtensibleIndexes(txn);
 
     final JEIndexConfig config = new JEIndexConfig(indexConfig.getSubstringLength());
     indexQueryFactory = new IndexQueryFactoryImpl(nameToIndexes, config);
     extensibleIndexesMapping = computeExtensibleIndexesMapping();
   }
 
-  private void buildPresenceIndex()
+  private void buildPresenceIndex(WriteableStorage txn)
   {
     final IndexType indexType = IndexType.PRESENCE;
     if (indexConfig.getIndexType().contains(indexType))
     {
       String indexID = indexType.toString();
-      nameToIndexes.put(indexID, newPresenceIndex(indexConfig));
+      nameToIndexes.put(indexID, newPresenceIndex(txn, indexConfig));
     }
   }
 
-  private Index newPresenceIndex(LocalDBIndexCfg cfg)
+  private Index newPresenceIndex(WriteableStorage txn, LocalDBIndexCfg cfg)
   {
     final AttributeType attrType = cfg.getAttribute();
     final TreeName indexName = getIndexName(attrType, IndexType.PRESENCE.toString());
     final PresenceIndexer indexer = new PresenceIndexer(attrType);
-    return entryContainer.newIndexForAttribute(indexName, indexer, cfg.getIndexEntryLimit());
+    return entryContainer.newIndexForAttribute(txn, indexName, indexer, cfg.getIndexEntryLimit());
   }
 
-  private void buildExtensibleIndexes() throws ConfigException
+  private void buildExtensibleIndexes(WriteableStorage txn) throws ConfigException
   {
     final IndexType indexType = IndexType.EXTENSIBLE;
     if (indexConfig.getIndexType().contains(indexType))
@@ -198,14 +198,14 @@
           if (!nameToIndexes.containsKey(indexId))
           {
             // There is no index available for this index id. Create a new index
-            nameToIndexes.put(indexId, newAttributeIndex(indexConfig, indexer));
+            nameToIndexes.put(indexId, newAttributeIndex(txn, indexConfig, indexer));
           }
         }
       }
     }
   }
 
-  private void buildIndexes(IndexType indexType) throws ConfigException
+  private void buildIndexes(WriteableStorage txn, IndexType indexType) throws ConfigException
   {
     if (indexConfig.getIndexType().contains(indexType))
     {
@@ -219,7 +219,7 @@
 
       for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
       {
-        nameToIndexes.put(indexID, newAttributeIndex(indexConfig, indexer));
+        nameToIndexes.put(indexID, newAttributeIndex(txn, indexConfig, indexer));
       }
     }
   }
@@ -241,12 +241,13 @@
     }
   }
 
-  private Index newAttributeIndex(LocalDBIndexCfg indexConfig, org.forgerock.opendj.ldap.spi.Indexer indexer)
+  private Index newAttributeIndex(WriteableStorage txn, LocalDBIndexCfg indexConfig,
+      org.forgerock.opendj.ldap.spi.Indexer indexer)
   {
     final AttributeType attrType = indexConfig.getAttribute();
     final TreeName indexName = getIndexName(attrType, indexer.getIndexID());
     final AttributeIndexer attrIndexer = new AttributeIndexer(attrType, indexer);
-    return entryContainer.newIndexForAttribute(indexName, attrIndexer, indexConfig.getIndexEntryLimit());
+    return entryContainer.newIndexForAttribute(txn, indexName, attrIndexer, indexConfig.getIndexEntryLimit());
   }
 
   private TreeName getIndexName(AttributeType attrType, String indexID)
@@ -260,11 +261,11 @@
    * @throws StorageRuntimeException if a JE database error occurs while
    * opening the index.
    */
-  public void open() throws StorageRuntimeException
+  public void open(WriteableStorage txn) throws StorageRuntimeException
   {
     for (Index index : nameToIndexes.values())
     {
-      index.open();
+      index.open(txn);
     }
     indexConfig.addChangeListener(this);
   }
@@ -635,35 +636,37 @@
 
   /** {@inheritDoc} */
   @Override
-  public synchronized ConfigChangeResult applyConfigurationChange(LocalDBIndexCfg cfg)
+  public synchronized ConfigChangeResult applyConfigurationChange(final LocalDBIndexCfg cfg)
   {
-    // this method is not perf sensitive, using an AtomicBoolean will not hurt
-    AtomicBoolean adminActionRequired = new AtomicBoolean(false);
-    ArrayList<LocalizableMessage> messages = new ArrayList<LocalizableMessage>();
+    final ConfigChangeResult ccr = new ConfigChangeResult();
     try
     {
-      applyChangeToPresenceIndex(cfg, adminActionRequired, messages);
-      applyChangeToIndex(IndexType.EQUALITY, cfg, adminActionRequired, messages);
-      applyChangeToIndex(IndexType.SUBSTRING, cfg, adminActionRequired, messages);
-      applyChangeToIndex(IndexType.ORDERING, cfg, adminActionRequired, messages);
-      applyChangeToIndex(IndexType.APPROXIMATE, cfg, adminActionRequired, messages);
-      applyChangeToExtensibleIndexes(cfg, adminActionRequired, messages);
+      entryContainer.getStorage().write(new WriteOperation()
+      {
+        @Override
+        public void run(WriteableStorage txn) throws Exception
+        {
+          applyChangeToPresenceIndex(txn, cfg, ccr);
+          applyChangeToIndex(txn, IndexType.EQUALITY, cfg, ccr);
+          applyChangeToIndex(txn, IndexType.SUBSTRING, cfg, ccr);
+          applyChangeToIndex(txn, IndexType.ORDERING, cfg, ccr);
+          applyChangeToIndex(txn, IndexType.APPROXIMATE, cfg, ccr);
+          applyChangeToExtensibleIndexes(txn, cfg, ccr);
+        }
+      });
 
       extensibleIndexesMapping = computeExtensibleIndexesMapping();
       indexConfig = cfg;
-
-      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired.get(), messages);
     }
     catch(Exception e)
     {
-      messages.add(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
-      return new ConfigChangeResult(
-          DirectoryServer.getServerErrorResultCode(), adminActionRequired.get(), messages);
+      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
+      ccr.addMessage(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
     }
+    return ccr;
   }
 
-  private void applyChangeToExtensibleIndexes(LocalDBIndexCfg cfg,
-      AtomicBoolean adminActionRequired, ArrayList<LocalizableMessage> messages)
+  private void applyChangeToExtensibleIndexes(WriteableStorage txn, LocalDBIndexCfg cfg, ConfigChangeResult ccr)
   {
     final AttributeType attrType = cfg.getAttribute();
     if (!cfg.getIndexType().contains(IndexType.EXTENSIBLE))
@@ -694,8 +697,8 @@
         validIndexIds.add(indexId);
         if (!nameToIndexes.containsKey(indexId))
         {
-          Index index = newAttributeIndex(cfg, indexer);
-          openIndex(index, adminActionRequired, messages);
+          Index index = newAttributeIndex(txn, cfg, indexer);
+          openIndex(txn, index, ccr);
           nameToIndexes.put(indexId, index);
         }
         else
@@ -703,8 +706,8 @@
           Index index = nameToIndexes.get(indexId);
           if (index.setIndexEntryLimit(indexEntryLimit))
           {
-            adminActionRequired.set(true);
-            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
+            ccr.setAdminActionRequired(true);
+            ccr.addMessage(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
           }
           if (indexConfig.getSubstringLength() != cfg.getSubstringLength())
           {
@@ -770,8 +773,7 @@
     return rules;
   }
 
-  private void applyChangeToIndex(IndexType indexType, LocalDBIndexCfg cfg,
-      AtomicBoolean adminActionRequired, ArrayList<LocalizableMessage> messages)
+  private void applyChangeToIndex(WriteableStorage txn, IndexType indexType, LocalDBIndexCfg cfg, ConfigChangeResult ccr)
   {
     String indexId = indexType.toString();
     Index index = nameToIndexes.get(indexId);
@@ -786,8 +788,8 @@
       final MatchingRule matchingRule = getMatchingRule(indexType, cfg.getAttribute());
       for (org.forgerock.opendj.ldap.spi.Indexer indexer : matchingRule.getIndexers())
       {
-        index = newAttributeIndex(cfg, indexer);
-        openIndex(index, adminActionRequired, messages);
+        index = newAttributeIndex(txn, cfg, indexer);
+        openIndex(txn, index, ccr);
         nameToIndexes.put(indexId, index);
       }
     }
@@ -796,14 +798,13 @@
       // already exists. Just update index entry limit.
       if (index.setIndexEntryLimit(cfg.getIndexEntryLimit()))
       {
-        adminActionRequired.set(true);
-        messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
+        ccr.setAdminActionRequired(true);
+        ccr.addMessage(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
       }
     }
   }
 
-  private void applyChangeToPresenceIndex(LocalDBIndexCfg cfg, AtomicBoolean adminActionRequired,
-      ArrayList<LocalizableMessage> messages)
+  private void applyChangeToPresenceIndex(WriteableStorage txn, LocalDBIndexCfg cfg, ConfigChangeResult ccr)
   {
     final IndexType indexType = IndexType.PRESENCE;
     final String indexID = indexType.toString();
@@ -816,8 +817,8 @@
 
     if (index == null)
     {
-      index = newPresenceIndex(cfg);
-      openIndex(index, adminActionRequired, messages);
+      index = newPresenceIndex(txn, cfg);
+      openIndex(txn, index, ccr);
       nameToIndexes.put(indexID, index);
     }
     else
@@ -825,8 +826,8 @@
       // already exists. Just update index entry limit.
       if (index.setIndexEntryLimit(cfg.getIndexEntryLimit()))
       {
-        adminActionRequired.set(true);
-        messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
+        ccr.setAdminActionRequired(true);
+        ccr.addMessage(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
       }
     }
   }
@@ -848,14 +849,14 @@
     }
   }
 
-  private void openIndex(Index index, AtomicBoolean adminActionRequired, ArrayList<LocalizableMessage> messages)
+  private void openIndex(WriteableStorage txn, Index index, ConfigChangeResult ccr)
   {
-    index.open();
+    index.open(txn);
 
     if (!index.isTrusted())
     {
-      adminActionRequired.set(true);
-      messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(index.getName()));
+      ccr.setAdminActionRequired(true);
+      ccr.addMessage(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(index.getName()));
     }
   }
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DN2URI.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DN2URI.java
index d4c4d94..d3d7f78 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DN2URI.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DN2URI.java
@@ -497,7 +497,7 @@
    * DN.  The referral URLs will be set appropriately for the references found
    * in the referral entry.
    */
-  public void targetEntryReferrals(DN targetDN, SearchScope searchScope)
+  public void targetEntryReferrals(ReadableStorage txn, DN targetDN, SearchScope searchScope)
        throws DirectoryException
   {
     if (containsReferrals == ConditionResult.UNDEFINED)
@@ -512,7 +512,7 @@
 
     try
     {
-      Cursor cursor = storage.openCursor(treeName);
+      final Cursor cursor = txn.openCursor(treeName);
       try
       {
         // Go up through the DIT hierarchy until we find a referral.
@@ -550,7 +550,7 @@
    *          has been reached or the search has been abandoned).
    * @throws DirectoryException If a Directory Server error occurs.
    */
-  public boolean returnSearchReferences(SearchOperation searchOp)
+  public boolean returnSearchReferences(ReadableStorage txn, SearchOperation searchOp)
        throws DirectoryException
   {
     if (containsReferrals == ConditionResult.UNDEFINED)
@@ -584,7 +584,7 @@
     ByteSequence startKey = suffix;
     try
     {
-      Cursor cursor = storage.openCursor(treeName);
+      final Cursor cursor = txn.openCursor(treeName);
       try
       {
         // Initialize the cursor very close to the starting value then
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DatabaseContainer.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DatabaseContainer.java
index f9832b6..4da4491 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DatabaseContainer.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/DatabaseContainer.java
@@ -78,34 +78,12 @@
    * @throws StorageRuntimeException if a JE database error occurs while
    * opening the index.
    */
-  public void open() throws StorageRuntimeException
+  public void open(WriteableStorage txn) throws StorageRuntimeException
   {
-    if (dbConfig.getTransactional())
+    storage.openTree(treeName);
+    if (logger.isTraceEnabled())
     {
-      // Open the database under a transaction.
-      Transaction txn = entryContainer.beginTransaction();
-      try
-      {
-        treeName = storage.openDatabase(txn, treeName, dbConfig);
-        if (logger.isTraceEnabled())
-        {
-          logger.trace("JE database %s opened. txnid=%d", treeName, txn.getId());
-        }
-        EntryContainer.transactionCommit(txn);
-      }
-      catch (StorageRuntimeException e)
-      {
-        EntryContainer.transactionAbort(txn);
-        throw e;
-      }
-    }
-    else
-    {
-      treeName = storage.openDatabase(null, treeName, dbConfig);
-      if (logger.isTraceEnabled())
-      {
-        logger.trace("JE database %s opened. txnid=none", treeName);
-      }
+      logger.trace("JE database %s opened. txnid=%d", treeName, txn.getId());
     }
   }
 
@@ -131,7 +109,6 @@
     {
       treeName.sync();
     }
-    storage.openTree(treeName)
     treeName.close();
     treeName = null;
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/EntryContainer.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/EntryContainer.java
index a9ebc68..35716bd 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/EntryContainer.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/EntryContainer.java
@@ -152,14 +152,19 @@
   {
     /** {@inheritDoc} */
     @Override
-    public boolean isConfigurationAddAcceptable(
-        LocalDBIndexCfg cfg,
-        List<LocalizableMessage> unacceptableReasons)
+    public boolean isConfigurationAddAcceptable(final LocalDBIndexCfg cfg, List<LocalizableMessage> unacceptableReasons)
     {
       try
       {
-        //Try creating all the indexes before confirming they are valid ones.
-        new AttributeIndex(cfg, EntryContainer.this);
+        storage.write(new WriteOperation()
+        {
+          @Override
+          public void run(WriteableStorage txn) throws Exception
+          {
+            //Try creating all the indexes before confirming they are valid ones.
+            new AttributeIndex(cfg, EntryContainer.this, txn);
+          }
+        });
         return true;
       }
       catch(Exception e)
@@ -171,31 +176,33 @@
 
     /** {@inheritDoc} */
     @Override
-    public ConfigChangeResult applyConfigurationAdd(LocalDBIndexCfg cfg)
+    public ConfigChangeResult applyConfigurationAdd(final LocalDBIndexCfg cfg)
     {
-      boolean adminActionRequired = false;
-      List<LocalizableMessage> messages = new ArrayList<LocalizableMessage>();
-
+      final ConfigChangeResult ccr = new ConfigChangeResult();
       try
       {
-        AttributeIndex index = new AttributeIndex(cfg, EntryContainer.this);
-        index.open();
-        if(!index.isTrusted())
+        storage.write(new WriteOperation()
         {
-          adminActionRequired = true;
-          messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
-              cfg.getAttribute().getNameOrOID()));
-        }
-        attrIndexMap.put(cfg.getAttribute(), index);
+          @Override
+          public void run(WriteableStorage txn) throws Exception
+          {
+            final AttributeIndex index = new AttributeIndex(cfg, EntryContainer.this, txn);
+            index.open(txn);
+            if (!index.isTrusted())
+            {
+              ccr.setAdminActionRequired(true);
+              ccr.addMessage(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(cfg.getAttribute().getNameOrOID()));
+            }
+            attrIndexMap.put(cfg.getAttribute(), index);
+          }
+        });
       }
       catch(Exception e)
       {
-        messages.add(LocalizableMessage.raw(e.getLocalizedMessage()));
-        return new ConfigChangeResult(
-            DirectoryServer.getServerErrorResultCode(), adminActionRequired, messages);
+        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
+        ccr.addMessage(LocalizableMessage.raw(e.getLocalizedMessage()));
       }
-
-      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired, messages);
+      return ccr;
     }
 
     /** {@inheritDoc} */
@@ -308,38 +315,38 @@
 
     /** {@inheritDoc} */
     @Override
-    public ConfigChangeResult applyConfigurationAdd(LocalDBVLVIndexCfg cfg)
+    public ConfigChangeResult applyConfigurationAdd(final LocalDBVLVIndexCfg cfg)
     {
-      boolean adminActionRequired = false;
-      ArrayList<LocalizableMessage> messages = new ArrayList<LocalizableMessage>();
-
+      final ConfigChangeResult ccr = new ConfigChangeResult();
       try
       {
-        VLVIndex vlvIndex = new VLVIndex(cfg, state, storage, EntryContainer.this);
-        vlvIndex.open();
-        if(!vlvIndex.isTrusted())
+        storage.write(new WriteOperation()
         {
-          adminActionRequired = true;
-          messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
-              cfg.getName()));
-        }
-        vlvIndexMap.put(cfg.getName().toLowerCase(), vlvIndex);
+          @Override
+          public void run(WriteableStorage txn) throws Exception
+          {
+            VLVIndex vlvIndex = new VLVIndex(cfg, state, storage, EntryContainer.this, txn);
+            vlvIndex.open(txn);
+            if(!vlvIndex.isTrusted())
+            {
+              ccr.setAdminActionRequired(true);
+              ccr.addMessage(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(cfg.getName()));
+            }
+            vlvIndexMap.put(cfg.getName().toLowerCase(), vlvIndex);
+          }
+        });
       }
       catch(Exception e)
       {
-        messages.add(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
-        return new ConfigChangeResult(
-            DirectoryServer.getServerErrorResultCode(), adminActionRequired, messages);
+        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
+        ccr.addMessage(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
       }
-
-      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired, messages);
+      return ccr;
     }
 
     /** {@inheritDoc} */
     @Override
-    public boolean isConfigurationDeleteAcceptable(
-        LocalDBVLVIndexCfg cfg,
-        List<LocalizableMessage> unacceptableReasons)
+    public boolean isConfigurationDeleteAcceptable(LocalDBVLVIndexCfg cfg, List<LocalizableMessage> unacceptableReasons)
     {
       // TODO: validate more before returning true?
       return true;
@@ -425,8 +432,7 @@
    * @throws StorageRuntimeException If an error occurs in the JE database.
    * @throws ConfigException if a configuration related error occurs.
    */
-  public void open()
-  throws StorageRuntimeException, ConfigException
+  public void open(WriteableStorage txn) throws StorageRuntimeException, ConfigException
   {
     try
     {
@@ -437,17 +443,17 @@
 
       id2entry = new ID2Entry(databasePrefix.child(ID2ENTRY_DATABASE_NAME),
           entryDataConfig, storage, this);
-      id2entry.open();
+      id2entry.open(txn);
 
       dn2id = new DN2ID(databasePrefix.child(DN2ID_DATABASE_NAME), storage, this);
-      dn2id.open();
+      dn2id.open(txn);
 
       state = new State(databasePrefix.child(STATE_DATABASE_NAME), storage, this);
-      state.open();
+      state.open(txn);
 
       if (config.isSubordinateIndexesEnabled())
       {
-        openSubordinateIndexes();
+        openSubordinateIndexes(txn);
       }
       else
       {
@@ -459,7 +465,7 @@
         {
           state.putIndexTrustState(null, id2children, false);
         }
-        id2children.open(); // No-op
+        id2children.open(txn); // No-op
 
         id2subtree = new NullIndex(databasePrefix.child(ID2SUBTREE_DATABASE_NAME),
             new ID2SIndexer(), state, storage, this);
@@ -467,20 +473,20 @@
         {
           state.putIndexTrustState(null, id2subtree, false);
         }
-        id2subtree.open(); // No-op
+        id2subtree.open(txn); // No-op
 
         logger.info(NOTE_JEB_SUBORDINATE_INDEXES_DISABLED, backend.getBackendID());
       }
 
       dn2uri = new DN2URI(databasePrefix.child(REFERRAL_DATABASE_NAME), storage, this);
-      dn2uri.open();
+      dn2uri.open(txn);
 
       for (String idx : config.listLocalDBIndexes())
       {
         LocalDBIndexCfg indexCfg = config.getLocalDBIndex(idx);
 
-        AttributeIndex index = new AttributeIndex(indexCfg, this);
-        index.open();
+        AttributeIndex index = new AttributeIndex(indexCfg, this, txn);
+        index.open(txn);
         if(!index.isTrusted())
         {
           logger.info(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD, index.getName());
@@ -492,8 +498,8 @@
       {
         LocalDBVLVIndexCfg vlvIndexCfg = config.getLocalDBVLVIndex(idx);
 
-        VLVIndex vlvIndex = new VLVIndex(vlvIndexCfg, state, storage, this);
-        vlvIndex.open();
+        VLVIndex vlvIndex = new VLVIndex(vlvIndexCfg, state, storage, this, txn);
+        vlvIndex.open(txn);
 
         if(!vlvIndex.isTrusted())
         {
@@ -683,9 +689,9 @@
    * @return The highest entry ID.
    * @throws StorageRuntimeException If an error occurs in the JE database.
    */
-  public EntryID getHighestEntryID() throws StorageRuntimeException
+  public EntryID getHighestEntryID(ReadableStorage txn) throws StorageRuntimeException
   {
-    Cursor cursor = storage.openCursor(id2entry.getName());
+    Cursor cursor = txn.openCursor(id2entry.getName());
     try
     {
       // Position a cursor on the last data item, and the key should give the highest ID.
@@ -821,9 +827,7 @@
           // Handle base-object search first.
           if (searchScope == SearchScope.BASE_OBJECT)
           {
-            // Fetch the base entry.
-            Entry baseEntry = fetchBaseEntry(aBaseDN, searchScope);
-
+            final Entry baseEntry = fetchBaseEntry(txn, aBaseDN, searchScope);
             if (!isManageDsaITOperation(searchOperation))
             {
               dn2uri.checkTargetForReferral(baseEntry, searchOperation.getScope());
@@ -1002,7 +1006,7 @@
             {
               rootContainer.getMonitorProvider().updateIndexedSearchCount();
             }
-            searchIndexed(entryIDList, candidatesAreInScope, searchOperation, pageRequest);
+            searchIndexed(txn, entryIDList, candidatesAreInScope, searchOperation, pageRequest);
           }
           else
           {
@@ -1045,7 +1049,7 @@
               }
             }
 
-            searchNotIndexed(searchOperation, pageRequest);
+            searchNotIndexed(txn, searchOperation, pageRequest);
           }
           return null;
         }
@@ -1075,7 +1079,7 @@
    * @throws DirectoryException If an error prevented the search from being
    * processed.
    */
-  private void searchNotIndexed(SearchOperation searchOperation, PagedResultsControl pageRequest)
+  private void searchNotIndexed(ReadableStorage txn, SearchOperation searchOperation, PagedResultsControl pageRequest)
       throws DirectoryException, CanceledOperationException
   {
     DN aBaseDN = searchOperation.getBaseDN();
@@ -1087,9 +1091,7 @@
     // the base entry processing if the cookie is set.
     if (pageRequest == null || pageRequest.getCookie().length() == 0)
     {
-      // Fetch the base entry.
-      Entry baseEntry = fetchBaseEntry(aBaseDN, searchScope);
-
+      final Entry baseEntry = fetchBaseEntry(txn, aBaseDN, searchScope);
       if (!manageDsaIT)
       {
         dn2uri.checkTargetForReferral(baseEntry, searchScope);
@@ -1105,7 +1107,7 @@
       }
 
       if (!manageDsaIT
-          && !dn2uri.returnSearchReferences(searchOperation)
+          && !dn2uri.returnSearchReferences(txn, searchOperation)
           && pageRequest != null)
       {
         // Indicate no more pages.
@@ -1165,7 +1167,7 @@
 
     try
     {
-      Cursor cursor = storage.openCursor(dn2id.getName());
+      final Cursor cursor = txn.openCursor(dn2id.getName());
       try
       {
         // Initialize the cursor very close to the starting value.
@@ -1305,11 +1307,9 @@
    * @throws DirectoryException If an error prevented the search from being
    * processed.
    */
-  private void searchIndexed(EntryIDSet entryIDList,
-      boolean candidatesAreInScope,
-      SearchOperation searchOperation,
-      PagedResultsControl pageRequest)
-  throws DirectoryException, CanceledOperationException
+  private void searchIndexed(ReadableStorage txn, EntryIDSet entryIDList, boolean candidatesAreInScope,
+      SearchOperation searchOperation, PagedResultsControl pageRequest) throws DirectoryException,
+      CanceledOperationException
   {
     SearchScope searchScope = searchOperation.getScope();
     DN aBaseDN = searchOperation.getBaseDN();
@@ -1336,8 +1336,7 @@
     }
     else if (!manageDsaIT)
     {
-      // Return any search result references.
-      continueSearch = dn2uri.returnSearchReferences(searchOperation);
+      continueSearch = dn2uri.returnSearchReferences(txn, searchOperation);
     }
 
     // Make sure the candidate list is smaller than the lookthrough limit
@@ -1408,9 +1407,7 @@
     if (searchOperation.getEntriesSent() == 0
         && searchOperation.getReferencesSent() == 0)
     {
-      // Fetch the base entry if it exists.
-      Entry baseEntry = fetchBaseEntry(aBaseDN, searchScope);
-
+      final Entry baseEntry = fetchBaseEntry(txn, aBaseDN, searchScope);
       if (!manageDsaIT)
       {
         dn2uri.checkTargetForReferral(baseEntry, searchScope);
@@ -1499,7 +1496,7 @@
             if (parentDN != null)
             {
               // Check for referral entries above the target.
-              dn2uri.targetEntryReferrals(entry.getName(), null);
+              dn2uri.targetEntryReferrals(txn, entry.getName(), null);
 
               // Read the parent ID from dn2id.
               parentID = dn2id.get(txn, parentDN, false);
@@ -1651,7 +1648,7 @@
           try
           {
             // Check for referral entries above the target entry.
-            dn2uri.targetEntryReferrals(entryDN, null);
+            dn2uri.targetEntryReferrals(txn, entryDN, null);
 
             // Determine whether this is a subtree delete.
             boolean isSubtreeDelete =
@@ -1677,8 +1674,6 @@
 
             ByteSequence startKey = suffix;
 
-            CursorConfig cursorConfig = new CursorConfig();
-            cursorConfig.setReadCommitted(true);
             Cursor cursor = dn2id.openCursor(txn);
             try
             {
@@ -1989,7 +1984,7 @@
             {
               // The entryDN does not exist.
               // Check for referral entries above the target entry.
-              dn2uri.targetEntryReferrals(entryDN, null);
+              dn2uri.targetEntryReferrals(txn, entryDN, null);
               return null;
             }
 
@@ -2212,7 +2207,7 @@
             if (oldApexID == null)
             {
               // Check for referral entries above the target entry.
-              dn2uri.targetEntryReferrals(currentDN, null);
+              dn2uri.targetEntryReferrals(txn, currentDN, null);
 
               LocalizableMessage message = ERR_JEB_MODIFYDN_NO_SUCH_OBJECT.get(currentDN);
               DN matchedDN = getMatchedDN(baseDN);
@@ -3109,7 +3104,7 @@
         {
           TreeName oldName = db.getName();
           String newName = oldName.replace(databasePrefix, newDbPrefix);
-          storage.renameDatabase(null, oldName, newName);
+          storage.renameDatabase(txn, oldName, newName);
           db.setName(newName);
         }
 
@@ -3122,7 +3117,7 @@
       // Open the containers backup.
       for(DatabaseContainer db : databases)
       {
-        db.open();
+        db.open(txn);
       }
     }
   }
@@ -3161,74 +3156,79 @@
 
   /** {@inheritDoc} */
   @Override
-  public ConfigChangeResult applyConfigurationChange(LocalDBBackendCfg cfg)
+  public ConfigChangeResult applyConfigurationChange(final LocalDBBackendCfg cfg)
   {
-    boolean adminActionRequired = false;
-    ArrayList<LocalizableMessage> messages = new ArrayList<LocalizableMessage>();
+    final ConfigChangeResult ccr = new ConfigChangeResult();
 
     exclusiveLock.lock();
     try
     {
-      if (config.isSubordinateIndexesEnabled() != cfg.isSubordinateIndexesEnabled())
+      storage.write(new WriteOperation()
       {
-        if (cfg.isSubordinateIndexesEnabled())
+        @Override
+        public void run(WriteableStorage txn) throws Exception
         {
-          // Re-enabling subordinate indexes.
-          openSubordinateIndexes();
+          if (config.isSubordinateIndexesEnabled() != cfg.isSubordinateIndexesEnabled())
+          {
+            if (cfg.isSubordinateIndexesEnabled())
+            {
+              // Re-enabling subordinate indexes.
+              openSubordinateIndexes(txn);
+            }
+            else
+            {
+              // Disabling subordinate indexes. Use a null index and ensure that
+              // future attempts to use the real indexes will fail.
+              id2children.close();
+              id2children = new NullIndex(databasePrefix.child(ID2CHILDREN_DATABASE_NAME),
+                  new ID2CIndexer(), state, storage, EntryContainer.this);
+              state.putIndexTrustState(null, id2children, false);
+              id2children.open(txn); // No-op
+
+              id2subtree.close();
+              id2subtree = new NullIndex(databasePrefix.child(ID2SUBTREE_DATABASE_NAME),
+                  new ID2SIndexer(), state, storage, EntryContainer.this);
+              state.putIndexTrustState(null, id2subtree, false);
+              id2subtree.open(txn); // No-op
+
+              logger.info(NOTE_JEB_SUBORDINATE_INDEXES_DISABLED, cfg.getBackendId());
+            }
+          }
+
+          if (config.getIndexEntryLimit() != cfg.getIndexEntryLimit())
+          {
+            if (id2children.setIndexEntryLimit(cfg.getIndexEntryLimit()))
+            {
+              ccr.setAdminActionRequired(true);
+              ccr.addMessage(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(id2children.getName()));
+            }
+
+            if (id2subtree.setIndexEntryLimit(cfg.getIndexEntryLimit()))
+            {
+              ccr.setAdminActionRequired(true);
+              ccr.addMessage(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(id2subtree.getName()));
+            }
+          }
+
+          DataConfig entryDataConfig = new DataConfig(cfg.isEntriesCompressed(),
+              cfg.isCompactEncoding(), rootContainer.getCompressedSchema());
+          id2entry.setDataConfig(entryDataConfig);
+
+          EntryContainer.this.config = cfg;
         }
-        else
-        {
-          // Disabling subordinate indexes. Use a null index and ensure that
-          // future attempts to use the real indexes will fail.
-          id2children.close();
-          id2children = new NullIndex(databasePrefix.child(ID2CHILDREN_DATABASE_NAME),
-              new ID2CIndexer(), state, storage, this);
-          state.putIndexTrustState(null, id2children, false);
-          id2children.open(); // No-op
-
-          id2subtree.close();
-          id2subtree = new NullIndex(databasePrefix.child(ID2SUBTREE_DATABASE_NAME),
-              new ID2SIndexer(), state, storage, this);
-          state.putIndexTrustState(null, id2subtree, false);
-          id2subtree.open(); // No-op
-
-          logger.info(NOTE_JEB_SUBORDINATE_INDEXES_DISABLED, cfg.getBackendId());
-        }
-      }
-
-      if (config.getIndexEntryLimit() != cfg.getIndexEntryLimit())
-      {
-        if (id2children.setIndexEntryLimit(cfg.getIndexEntryLimit()))
-        {
-          adminActionRequired = true;
-          messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(id2children.getName()));
-        }
-
-        if (id2subtree.setIndexEntryLimit(cfg.getIndexEntryLimit()))
-        {
-          adminActionRequired = true;
-          messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(id2subtree.getName()));
-        }
-      }
-
-      DataConfig entryDataConfig = new DataConfig(cfg.isEntriesCompressed(),
-          cfg.isCompactEncoding(), rootContainer.getCompressedSchema());
-      id2entry.setDataConfig(entryDataConfig);
-
-      this.config = cfg;
+      });
     }
-    catch (StorageRuntimeException e)
+    catch (Exception e)
     {
-      messages.add(LocalizableMessage.raw(stackTraceToSingleLineString(e)));
-      return new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
-          false, messages);
+      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
+      ccr.addMessage(LocalizableMessage.raw(stackTraceToSingleLineString(e)));
     }
     finally
     {
       exclusiveLock.unlock();
     }
 
-    return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired, messages);
+    return ccr;
   }
 
   /**
@@ -3294,7 +3294,7 @@
     {
       for(DatabaseContainer db : databases)
       {
-        db.open();
+        db.open(txn);
       }
 
       Transaction txn = null;
@@ -3370,7 +3370,7 @@
     }
     finally
     {
-      database.open();
+      database.open(txn);
     }
     if(logger.isTraceEnabled())
     {
@@ -3401,20 +3401,18 @@
     return null;
   }
 
-  /**
-   * Opens the id2children and id2subtree indexes.
-   */
-  private void openSubordinateIndexes()
+  /** Opens the id2children and id2subtree indexes. */
+  private void openSubordinateIndexes(WriteableStorage txn)
   {
-    id2children = newIndex(ID2CHILDREN_DATABASE_NAME, new ID2CIndexer());
-    id2subtree = newIndex(ID2SUBTREE_DATABASE_NAME, new ID2SIndexer());
+    id2children = newIndex(txn, ID2CHILDREN_DATABASE_NAME, new ID2CIndexer());
+    id2subtree = newIndex(txn, ID2SUBTREE_DATABASE_NAME, new ID2SIndexer());
   }
 
-  private Index newIndex(String name, Indexer indexer)
+  private Index newIndex(WriteableStorage txn, String name, Indexer indexer)
   {
     final Index index = new Index(databasePrefix.child(name),
-        indexer, state, config.getIndexEntryLimit(), 0, true, storage, this);
-    index.open();
+        indexer, state, config.getIndexEntryLimit(), 0, true, storage, txn, this);
+    index.open(txn);
     if (!index.isTrusted())
     {
       logger.info(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD, index.getName());
@@ -3430,10 +3428,10 @@
    * @param indexEntryLimit the index entry limit
    * @return a new index
    */
-  Index newIndexForAttribute(TreeName indexName, Indexer indexer, int indexEntryLimit)
+  Index newIndexForAttribute(WriteableStorage txn, TreeName indexName, Indexer indexer, int indexEntryLimit)
   {
     final int cursorEntryLimit = 100000;
-    return new Index(indexName, indexer, state, indexEntryLimit, cursorEntryLimit, false, storage, this);
+    return new Index(indexName, indexer, state, indexEntryLimit, cursorEntryLimit, false, storage, txn, this);
   }
 
 
@@ -3481,10 +3479,9 @@
    * @return the Entry matching the baseDN.
    * @throws DirectoryException if the baseDN doesn't exist.
    */
-  private Entry fetchBaseEntry(DN baseDN, SearchScope searchScope)
-          throws DirectoryException
+  private Entry fetchBaseEntry(ReadableStorage txn, DN baseDN, SearchScope searchScope)
+      throws DirectoryException
   {
-    // Fetch the base entry.
     Entry baseEntry = null;
     try
     {
@@ -3499,7 +3496,7 @@
     if (baseEntry == null)
     {
       // Check for referral entries above the base entry.
-      dn2uri.targetEntryReferrals(baseDN, searchScope);
+      dn2uri.targetEntryReferrals(txn, baseDN, searchScope);
 
       LocalizableMessage message = ERR_JEB_SEARCH_NO_SUCH_OBJECT.get(baseDN);
       DN matchedDN = getMatchedDN(baseDN);
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/Index.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/Index.java
index 18c9221..257ff78 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/Index.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/Index.java
@@ -37,13 +37,13 @@
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ConditionResult;
 import org.forgerock.opendj.ldap.spi.IndexingOptions;
-import org.opends.server.backends.pluggable.IndexBuffer.BufferedIndexValues;
 import org.opends.server.backends.pluggable.BackendImpl.Cursor;
 import org.opends.server.backends.pluggable.BackendImpl.ReadableStorage;
 import org.opends.server.backends.pluggable.BackendImpl.Storage;
 import org.opends.server.backends.pluggable.BackendImpl.StorageRuntimeException;
 import org.opends.server.backends.pluggable.BackendImpl.TreeName;
 import org.opends.server.backends.pluggable.BackendImpl.WriteableStorage;
+import org.opends.server.backends.pluggable.IndexBuffer.BufferedIndexValues;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
 import org.opends.server.types.Modification;
@@ -77,9 +77,6 @@
    */
   private int entryLimitExceededCount;
 
-  /** The max number of tries to rewrite phantom records. */
-  private final int phantomWriteRetries = 3;
-
   /**
    * Whether to maintain a count of IDs for a key once the entry limit
    * has exceeded.
@@ -129,7 +126,7 @@
    */
   public Index(TreeName name, Indexer indexer, State state,
         int indexEntryLimit, int cursorEntryLimit, boolean maintainCount,
-        Storage storage, EntryContainer entryContainer)
+        Storage storage, WriteableStorage txn, EntryContainer entryContainer)
       throws StorageRuntimeException
   {
     super(name, storage, entryContainer);
@@ -140,11 +137,11 @@
 
     this.state = state;
     this.trusted = state.getIndexTrustState(null, this);
-    if (!trusted && entryContainer.getHighestEntryID().longValue() == 0)
+    if (!trusted && entryContainer.getHighestEntryID(txn).longValue() == 0)
     {
       // If there are no entries in the entry container then there
       // is no reason why this index can't be upgraded to trusted.
-      setTrusted(null, true);
+      setTrusted(txn, true);
     }
   }
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NullIndex.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NullIndex.java
index f10b99b..d4b5a13 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NullIndex.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/NullIndex.java
@@ -208,7 +208,7 @@
 
   /** {@inheritDoc} */
   @Override
-  public void open() throws StorageRuntimeException
+  public void open(WriteableStorage txn) throws StorageRuntimeException
   {
     // Do nothing.
   }
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
index 0fe1368..efc4b12 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
@@ -40,6 +40,8 @@
 import org.opends.server.api.Backend;
 import org.opends.server.backends.pluggable.BackendImpl.Storage;
 import org.opends.server.backends.pluggable.BackendImpl.StorageRuntimeException;
+import org.opends.server.backends.pluggable.BackendImpl.WriteOperation;
+import org.opends.server.backends.pluggable.BackendImpl.WriteableStorage;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.monitors.DatabaseEnvironmentMonitor;
 import org.opends.server.types.ConfigChangeResult;
@@ -47,10 +49,6 @@
 import org.opends.server.types.FilePermission;
 import org.opends.server.types.InitializationException;
 
-
-
-
-
 import static org.opends.messages.ConfigMessages.*;
 import static org.opends.messages.JebMessages.*;
 import static org.opends.server.util.StaticUtils.*;
@@ -209,7 +207,15 @@
     }
 
     compressedSchema = new JECompressedSchema(storage);
-    openAndRegisterEntryContainers(config.getBaseDN());
+
+    storage.write(new WriteOperation()
+    {
+      @Override
+      public void run(WriteableStorage txn) throws Exception
+      {
+        openAndRegisterEntryContainers(txn, config.getBaseDN());
+      }
+    });
   }
 
   /**
@@ -229,7 +235,7 @@
    * @throws ConfigException If an configuration error occurs while opening
    *                         the entry container.
    */
-  public EntryContainer openEntryContainer(DN baseDN, String name)
+  public EntryContainer openEntryContainer(DN baseDN, String name, WriteableStorage txn)
       throws StorageRuntimeException, ConfigException
   {
     String databasePrefix;
@@ -244,7 +250,7 @@
 
     EntryContainer ec = new EntryContainer(baseDN, databasePrefix,
                                            backend, config, storage, this);
-    ec.open();
+    ec.open(txn);
     return ec;
   }
 
@@ -283,15 +289,15 @@
    * @throws ConfigException         If a configuration error occurs while
    *                                 opening the entry container.
    */
-  private void openAndRegisterEntryContainers(Set<DN> baseDNs)
+  private void openAndRegisterEntryContainers(WriteableStorage txn, Set<DN> baseDNs)
       throws StorageRuntimeException, InitializationException, ConfigException
   {
     EntryID id;
     EntryID highestID = null;
     for(DN baseDN : baseDNs)
     {
-      EntryContainer ec = openEntryContainer(baseDN, null);
-      id = ec.getHighestEntryID();
+      EntryContainer ec = openEntryContainer(baseDN, null, txn);
+      id = ec.getHighestEntryID(txn);
       registerEntryContainer(baseDN, ec);
       if(highestID == null || id.compareTo(highestID) > 0)
       {
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/VLVIndex.java b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/VLVIndex.java
index 37001ee..8325d22 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/VLVIndex.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/VLVIndex.java
@@ -51,6 +51,7 @@
 import org.opends.server.backends.pluggable.BackendImpl.ReadableStorage;
 import org.opends.server.backends.pluggable.BackendImpl.Storage;
 import org.opends.server.backends.pluggable.BackendImpl.StorageRuntimeException;
+import org.opends.server.backends.pluggable.BackendImpl.WriteOperation;
 import org.opends.server.backends.pluggable.BackendImpl.WriteableStorage;
 import org.opends.server.controls.ServerSideSortRequestControl;
 import org.opends.server.controls.VLVRequestControl;
@@ -131,7 +132,7 @@
    * configuration
    */
   public VLVIndex(LocalDBVLVIndexCfg config, State state, Storage env,
-                  EntryContainer entryContainer)
+                  EntryContainer entryContainer, ReadableStorage txn)
       throws StorageRuntimeException, ConfigException
   {
     super(entryContainer.getDatabasePrefix().child("vlv."+config.getName()),
@@ -197,7 +198,7 @@
 
     this.state = state;
     this.trusted = state.getIndexTrustState(null, this);
-    if (!trusted && entryContainer.getHighestEntryID().longValue() == 0)
+    if (!trusted && entryContainer.getHighestEntryID(txn).longValue() == 0)
     {
       // If there are no entries in the entry container then there
       // is no reason why this vlvIndex can't be upgraded to trusted.
@@ -223,11 +224,11 @@
 
   /** {@inheritDoc} */
   @Override
-  public void open() throws StorageRuntimeException
+  public void open(WriteableStorage txn) throws StorageRuntimeException
   {
-    super.open();
+    super.open(txn);
 
-    Cursor cursor = storage.openCursor(treeName);
+    final Cursor cursor = txn.openCursor(treeName);
     try
     {
       while (cursor.next())
@@ -1304,8 +1305,8 @@
     {
       this.sortedSetCapacity = cfg.getMaxBlockSize();
 
-      // Require admin action only if the new capacity is larger. Otherwise,
-      // we will lazyly update the sorted sets.
+      // Require admin action only if the new capacity is larger.
+      // Otherwise, we will lazily update the sorted sets.
       if (config.getMaxBlockSize() < cfg.getMaxBlockSize())
       {
         adminActionRequired = true;
@@ -1391,12 +1392,19 @@
       entryContainer.exclusiveLock.lock();
       try
       {
-        close();
-        open();
+        storage.write(new WriteOperation()
+        {
+          @Override
+          public void run(WriteableStorage txn) throws Exception
+          {
+            close();
+            open(txn);
+          }
+        });
       }
-      catch(StorageRuntimeException de)
+      catch (Exception e)
       {
-        messages.add(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(de)));
+        messages.add(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
         if(resultCode == ResultCode.SUCCESS)
         {
           resultCode = DirectoryServer.getServerErrorResultCode();
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/ConfigChangeResult.java b/opendj3-server-dev/src/server/org/opends/server/types/ConfigChangeResult.java
index 80de10b..abb1543 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/ConfigChangeResult.java
+++ b/opendj3-server-dev/src/server/org/opends/server/types/ConfigChangeResult.java
@@ -44,27 +44,31 @@
      mayInvoke=true)
 public final class ConfigChangeResult
 {
-  // A set of messages describing the changes that were made, any
-  // action that may be required, or any problems that were
-  // encountered.
-  private List<LocalizableMessage> messages;
-
-  // Indicates whether one or more of the changes requires
-  // administrative action in order to take effect.
-  private boolean adminActionRequired;
-
-  // The result code to return to the client from this configuration
-  // change.
-  private ResultCode resultCode;
-
-
+  /**
+   * A set of messages describing the changes that were made, any action that
+   * may be required, or any problems that were encountered.
+   */
+  private final List<LocalizableMessage> messages;
 
   /**
-   * Creates a new config change result object with the provided
-   * information.
+   * Indicates whether one or more of the changes requires administrative action
+   * in order to take effect.
+   */
+  private boolean adminActionRequired;
+
+  /** The result code to return to the client from this configuration change. */
+  private ResultCode resultCode;
+
+  /** Creates a new mutable config change result object. */
+  public ConfigChangeResult()
+  {
+    this(ResultCode.SUCCESS, false, new ArrayList<LocalizableMessage>());
+  }
+
+  /**
+   * Creates a new config change result object with the provided information.
    *
-   * @param  resultCode           The result code for this config
-   *                              change result.
+   * @param  resultCode           The result code for this config change result.
    * @param  adminActionRequired  Indicates whether administrative
    *                              action is required for one or more
    *                              of the changes to take effect.
@@ -72,13 +76,9 @@
   public ConfigChangeResult(ResultCode resultCode,
                             boolean adminActionRequired)
   {
-    this.resultCode          = resultCode;
-    this.adminActionRequired = adminActionRequired;
-    this.messages            = new ArrayList<LocalizableMessage>();
+    this(resultCode, adminActionRequired, new ArrayList<LocalizableMessage>());
   }
 
-
-
   /**
    * Creates a new config change result object with the provided
    * information.
@@ -101,8 +101,6 @@
     this.messages            = messages;
   }
 
-
-
   /**
    * Retrieves the result code for this config change result.
    *
@@ -126,8 +124,6 @@
     this.resultCode = resultCode;
   }
 
-
-
   /**
    * Indicates whether administrative action is required before one or
    * more of the changes will take effect.
@@ -141,8 +137,6 @@
     return adminActionRequired;
   }
 
-
-
   /**
    * Specifies whether administrative action is required before one or
    * more of the changes will take effect.
@@ -157,8 +151,6 @@
     this.adminActionRequired = adminActionRequired;
   }
 
-
-
   /**
    * Retrieves the set of messages that provide explanation for the
    * processing of the configuration changes.  This list may be
@@ -172,8 +164,6 @@
     return messages;
   }
 
-
-
   /**
    * Adds the provided message to the set of messages for this config
    * change result.
@@ -186,8 +176,6 @@
     messages.add(message);
   }
 
-
-
   /**
    * Retrieves a string representation of this config change result.
    *
@@ -201,8 +189,6 @@
     return buffer.toString();
   }
 
-
-
   /**
    * Appends a string representation of this config change result to
    * the provided buffer.
@@ -213,18 +199,15 @@
   public void toString(StringBuilder buffer)
   {
     buffer.append("ConfigChangeResult(result=");
-    buffer.append(resultCode.toString());
+    buffer.append(resultCode);
     buffer.append(", adminActionRequired=");
     buffer.append(adminActionRequired);
     buffer.append(", messages={");
 
     if (! messages.isEmpty())
     {
-      Iterator<LocalizableMessage> iterator = messages.iterator();
-
-      LocalizableMessage firstMessage = iterator.next();
-      buffer.append(firstMessage);
-
+      final Iterator<LocalizableMessage> iterator = messages.iterator();
+      buffer.append(iterator.next());
       while (iterator.hasNext())
       {
         buffer.append(",");
@@ -232,8 +215,6 @@
       }
     }
 
-
     buffer.append("})");
   }
 }
-

--
Gitblit v1.10.0