Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java	(revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java	(revision 0)
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+
+public class CompositeNodeStateDiff implements NodeStateDiff {
+
+    private final List<NodeStateDiff> providers;
+
+    public CompositeNodeStateDiff(List<NodeStateDiff> providers) {
+        this.providers = providers;
+    }
+
+    public CompositeNodeStateDiff(NodeStateDiff... providers) {
+        this(Arrays.asList(providers));
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after) {
+        for (NodeStateDiff d : providers) {
+            d.propertyAdded(after);
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) {
+        for (NodeStateDiff d : providers) {
+            d.propertyChanged(before, after);
+        }
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) {
+        for (NodeStateDiff d : providers) {
+            d.propertyDeleted(before);
+        }
+    }
+
+    @Override
+    public void childNodeAdded(String name, NodeState after) {
+        for (NodeStateDiff d : providers) {
+            d.childNodeAdded(name, after);
+        }
+    }
+
+    @Override
+    public void childNodeChanged(String name, NodeState before, NodeState after) {
+        for (NodeStateDiff d : providers) {
+            d.childNodeChanged(name, before, after);
+        }
+    }
+
+    @Override
+    public void childNodeDeleted(String name, NodeState before) {
+        for (NodeStateDiff d : providers) {
+            d.childNodeDeleted(name, before);
+        }
+    }
+}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java	(revision 1402537)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java	(working copy)
@@ -24,6 +24,8 @@
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_UNKNOWN;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
 
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -64,9 +66,8 @@
 
         // <path>, <builder>
         Map<String, NodeBuilder> allDefs = new HashMap<String, NodeBuilder>();
-
-        IndexDefDiff diff = new IndexDefDiff(builder, allDefs);
-        after.compareAgainstBaseState(before, diff);
+        after.compareAgainstBaseState(before,
+                new IndexDefDiff(builder, allDefs));
 
         // <type, <<path>, <builder>>
         Map<String, Map<String, NodeBuilder>> updates = new HashMap<String, Map<String, NodeBuilder>>();
@@ -86,26 +87,54 @@
         }
 
         // commit
+        List<IndexHook> hooks = new ArrayList<IndexHook>();
+        List<IndexHook> reindexHooks = new ArrayList<IndexHook>();
+
         for (String type : updates.keySet()) {
             Map<String, NodeBuilder> update = updates.get(type);
             for (String path : update.keySet()) {
                 NodeBuilder updateBuiler = update.get(path);
                 boolean reindex = getAndResetReindex(updateBuiler);
-                List<? extends IndexHook> hooks = provider.getIndexHooks(type,
-                        updateBuiler);
-                for (IndexHook hook : hooks) {
-                    if (reindex) {
-                        hook.processCommit(MemoryNodeState.EMPTY_NODE, after);
-                    } else {
-                        hook.processCommit(before, after);
-                    }
+                if (reindex) {
+                    reindexHooks.addAll(provider.getIndexHooks(type,
+                            updateBuiler));
+                } else {
+                    hooks.addAll(provider.getIndexHooks(type, updateBuiler));
                 }
-                
             }
         }
+        processIndexHooks(reindexHooks, MemoryNodeState.EMPTY_NODE, after);
+        processIndexHooks(hooks, before, after);
         return builder.getNodeState();
     }
 
+    private void processIndexHooks(List<IndexHook> hooks, NodeState before,
+            NodeState after) throws CommitFailedException {
+        try {
+
+            List<NodeStateDiff> diffs = new ArrayList<NodeStateDiff>();
+            for (IndexHook hook : hooks) {
+                diffs.add(hook.preProcess());
+            }
+            after.compareAgainstBaseState(before, new CompositeNodeStateDiff(
+                    diffs));
+            for (IndexHook hook : hooks) {
+                hook.postProcess(after);
+            }
+
+        } finally {
+            for (IndexHook hook : hooks) {
+                try {
+                    hook.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    throw new CommitFailedException(
+                            "Failed to close the index hook", e);
+                }
+            }
+        }
+    }
+
     protected static boolean getAndResetReindex(NodeBuilder builder) {
         boolean isReindex = false;
         PropertyState ps = builder.getProperty(REINDEX_PROPERTY_NAME);
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java	(revision 1402537)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java	(working copy)
@@ -16,10 +16,16 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.plugins.index.IndexHook;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 /**
  * {@link IndexHook} implementation that is responsible for keeping the
@@ -28,19 +34,61 @@
  * @see LuceneIndex
  * 
  */
-public class LuceneHook implements IndexHook {
+public class LuceneHook implements IndexHook, CommitHook {
 
     private final NodeBuilder builder;
 
+    private IndexHook luceneEditor;
+
     public LuceneHook(NodeBuilder builder) {
         this.builder = builder;
     }
 
+    // -----------------------------------------------------< CommitHook >--
+
     @Override
+    @Nonnull
     public NodeState processCommit(NodeState before, NodeState after)
             throws CommitFailedException {
-        new LuceneEditor(builder).processCommit(before, after);
+        LuceneEditor editor = new LuceneEditor(builder);
+        try {
+            NodeStateDiff diff = editor.preProcess();
+            after.compareAgainstBaseState(before, diff);
+            editor.postProcess(after);
+        } finally {
+            try {
+                editor.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+                throw new CommitFailedException(
+                        "Failed to close the index hook", e);
+            }
+        }
         return after;
     }
 
+    // -----------------------------------------------------< IndexHook >--
+
+    @Override
+    @Nonnull
+    public NodeStateDiff preProcess() throws CommitFailedException {
+        luceneEditor = new LuceneEditor(builder);
+        return luceneEditor.preProcess();
+    }
+
+    @Override
+    public void postProcess(NodeState after) throws CommitFailedException {
+        luceneEditor.postProcess(after);
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            if (luceneEditor != null) {
+                luceneEditor.close();
+            }
+        } finally {
+            luceneEditor = null;
+        }
+    }
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java	(revision 1402537)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java	(working copy)
@@ -21,15 +21,17 @@
 import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newPropertyField;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newPathTerm;
 
+import java.io.Closeable;
 import java.io.IOException;
 
+import javax.annotation.Nonnull;
 import javax.jcr.PropertyType;
 
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.plugins.index.IndexHook;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -40,7 +42,6 @@
 import org.apache.lucene.document.Document;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.Version;
 import org.apache.tika.Tika;
 import org.apache.tika.exception.TikaException;
@@ -48,7 +49,7 @@
 /**
  * This class updates a Lucene index when node content is changed.
  */
-class LuceneEditor implements CommitHook, LuceneIndexConstants {
+class LuceneEditor implements IndexHook, LuceneIndexConstants {
 
     private static final Tika TIKA = new Tika();
 
@@ -76,21 +77,28 @@
         this.root = root;
     }
 
+    // -----------------------------------------------------< IndexHook >--
+
+    private LuceneDiff diff;
+
     @Override
-    public NodeState processCommit(NodeState before, NodeState after)
-            throws CommitFailedException {
-        Directory directory = new ReadWriteOakDirectory(
-                root.child(INDEX_DATA_CHILD_NAME));
+    public NodeStateDiff preProcess() throws CommitFailedException {
         try {
-            IndexWriter writer = new IndexWriter(directory, config);
-            try {
-                LuceneDiff diff = new LuceneDiff(writer, "/");
-                after.compareAgainstBaseState(before, diff);
-                diff.postProcess(after);
-            } finally {
-                writer.close();
-            }
-            return after;
+            IndexWriter writer = new IndexWriter(new ReadWriteOakDirectory(
+                    root.child(INDEX_DATA_CHILD_NAME)), config);
+            diff = new LuceneDiff(writer, "/");
+            return diff;
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new CommitFailedException(
+                    "Failed to create writer for the full text search index", e);
+        }
+    }
+
+    @Override
+    public void postProcess(NodeState after) throws CommitFailedException {
+        try {
+            diff.postProcess(after);
         } catch (IOException e) {
             e.printStackTrace();
             throw new CommitFailedException(
@@ -98,7 +106,12 @@
         }
     }
 
-    private static class LuceneDiff implements NodeStateDiff {
+    @Override
+    public void close() throws IOException {
+        diff.close();
+    }
+
+    private static class LuceneDiff implements NodeStateDiff, Closeable {
 
         private final IndexWriter writer;
 
@@ -124,6 +137,11 @@
         }
 
         @Override
+        public void close() throws IOException {
+            writer.close();
+        }
+
+        @Override
         public void propertyAdded(PropertyState after) {
             modified = true;
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java	(revision 1402537)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java	(working copy)
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.property;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -23,8 +24,10 @@
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.plugins.index.IndexHook;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 import com.google.common.collect.Maps;
 
@@ -36,14 +39,18 @@
  * @see PropertyIndexLookup
  * 
  */
-public class PropertyIndexHook implements IndexHook {
-    
+public class PropertyIndexHook implements IndexHook, CommitHook {
+
     private final NodeBuilder builder;
 
+    private Map<String, List<PropertyIndexUpdate>> indexes;
+
     public PropertyIndexHook(NodeBuilder builder) {
         this.builder = builder;
     }
 
+    // -----------------------------------------------------< CommitHook >--
+
     @Override @Nonnull
     public NodeState processCommit(NodeState before, NodeState after)
             throws CommitFailedException {
@@ -60,5 +67,25 @@
         return after;
     }
 
+    // -----------------------------------------------------< IndexHook >--
+
+    @Override @Nonnull
+    public NodeStateDiff preProcess() {
+        indexes = Maps.newHashMap();
+        return new PropertyIndexDiff(builder, indexes);
+    }
 
+    @Override
+    public void postProcess(NodeState after) throws CommitFailedException {
+        for (List<PropertyIndexUpdate> updates : indexes.values()) {
+            for (PropertyIndexUpdate update : updates) {
+                update.apply();
+            }
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        indexes = null;
+    }
 }
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java	(revision 1402537)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java	(working copy)
@@ -16,13 +16,21 @@
  */
 package org.apache.jackrabbit.oak.plugins.index;
 
-import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import java.io.Closeable;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 /**
  * Represents the content of a QueryIndex as well as a mechanism for keeping
  * this content up to date.
  * 
  */
-public interface IndexHook extends CommitHook {
+public interface IndexHook extends Closeable {
+
+    NodeStateDiff preProcess() throws CommitFailedException;
+
+    void postProcess(NodeState after) throws CommitFailedException;
 
 }
