You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2013/06/11 15:00:06 UTC

svn commit: r1491780 [1/3] - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/ oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/ oak-core/src/main/java/org/a...

Author: jukka
Date: Tue Jun 11 13:00:04 2013
New Revision: 1491780

URL: http://svn.apache.org/r1491780
Log:
OAK-860: Streamline the IndexEditor

Added:
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexEditor.java   (with props)
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexEditorProvider.java   (contents, props changed)
      - copied, changed from r1491447, jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexHookProvider.java
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexDefinition.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexDefinitionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrCommitHook.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexDiff.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexHook.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexHookProvider.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexUpdate.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrNodeStateDiff.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrObserver.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrCommitHookTest.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexDiffIT.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiIndexEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeIndexEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdate.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategyTest.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProvider.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/aggregation/NodeAggregator.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/LuceneIndexHelper.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/LuceneInitializerHelper.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/OakSolrUtils.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndexProvider.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/SolrBaseTest.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/TestUtils.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrCommitHookIT.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexHookIT.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTest.java
    jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryEngineIT.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java Tue Jun 11 13:00:04 2013
@@ -23,6 +23,7 @@ import static java.util.concurrent.Execu
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nonnull;
 import javax.jcr.NoSuchWorkspaceException;
@@ -32,8 +33,6 @@ import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.core.MicroKernelImpl;
-import org.apache.jackrabbit.mongomk.MongoMK;
-import org.apache.jackrabbit.mongomk.util.LogWrapper;
 import org.apache.jackrabbit.oak.api.ContentRepository;
 import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.api.Root;
@@ -279,7 +278,9 @@ public class Oak {
                 .compose(editorProviders)));
 
         if (asyncIndexing) {
-            new AsyncIndexUpdate(store, executor, indexEditors);
+            executor.scheduleWithFixedDelay(
+                    new AsyncIndexUpdate("async", store, indexEditors),
+                    1, 5, TimeUnit.SECONDS);
         }
 
         // FIXME: OAK-810 move to proper workspace initialization

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiIndexEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiIndexEditorProvider.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiIndexEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiIndexEditorProvider.java Tue Jun 11 13:00:04 2013
@@ -18,10 +18,12 @@
  */
 package org.apache.jackrabbit.oak.osgi;
 
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.plugins.index.CompositeIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 /**
  * This IndexEditor provider combines all index editors of all available OSGi
@@ -35,13 +37,15 @@ public class OsgiIndexEditorProvider ext
     }
 
     @Override
-    public Editor getIndexEditor(String type, NodeBuilder builder) {
+    public Editor getIndexEditor(
+            String type, NodeBuilder builder, NodeState root)
+            throws CommitFailedException {
         IndexEditorProvider composite = CompositeIndexEditorProvider
                 .compose(getServices());
         if (composite == null) {
             return null;
         }
-        return composite.getIndexEditor(type, builder);
+        return composite.getIndexEditor(type, builder, root);
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java Tue Jun 11 13:00:04 2013
@@ -19,33 +19,14 @@
 package org.apache.jackrabbit.oak.plugins.index;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ASYNC_PROPERTY_NAME;
-import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
-import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
-import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.getBoolean;
-import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.getString;
-import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.isIndexNodeType;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
-import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
-import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.EditorHook;
-import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
-import org.apache.jackrabbit.oak.spi.commit.VisibleEditor;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
@@ -57,243 +38,36 @@ public class AsyncIndexUpdate implements
     private static final Logger log = LoggerFactory
             .getLogger(AsyncIndexUpdate.class);
 
-    private final static int CONFIG_WATCH_DELAY_MS = 30000;
-
-    // TODO index impl run frequency could be picked up from the index config
-    // directly
-    private final static int INDEX_TASK_DELAY_MS = 5000;
+    private final String name;
 
     private final NodeStore store;
 
-    private final ScheduledExecutorService executor;
-
-    private final IndexEditorProvider provider;
-
+    private final CommitHook hook;
+    
     private NodeState current = EmptyNodeState.EMPTY_NODE;
 
-    final Map<String, IndexTask> active = new ConcurrentHashMap<String, IndexTask>();
-
-    private boolean started;
-
-    public AsyncIndexUpdate(@Nonnull NodeStore store,
-            @Nonnull ScheduledExecutorService executor,
+    public AsyncIndexUpdate(
+            @Nonnull String name,
+            @Nonnull NodeStore store,
             @Nonnull IndexEditorProvider provider) {
+        this.name = checkNotNull(name);
         this.store = checkNotNull(store);
-        this.executor = checkNotNull(executor);
-        this.provider = checkNotNull(provider);
-    }
-
-    public synchronized void start() {
-        if (started) {
-            log.error("Background index config watcher task already started");
-            return;
-        }
-        started = true;
-        executor.scheduleWithFixedDelay(this, 100, CONFIG_WATCH_DELAY_MS,
-                TimeUnit.MILLISECONDS);
+        this.hook = new EditorHook(
+                new IndexUpdateProvider(checkNotNull(provider), name));
     }
 
     @Override
     public void run() {
-        log.debug("Running background index config watcher task");
-        NodeState after = store.getRoot();
+        log.debug("Running background index task {}", name);
+        NodeStoreBranch branch = store.branch();
+        NodeState after = branch.getHead();
         try {
-            EditorHook hook = new EditorHook(new EditorProvider() {
-                @Override
-                public Editor getRootEditor(NodeState before, NodeState after,
-                        NodeBuilder builder) {
-                    return VisibleEditor.wrap(new IndexConfigWatcher());
-                }
-            });
-            hook.processCommit(current, after);
+            NodeState processed = hook.processCommit(current, after);
+            branch.setRoot(processed);
+            branch.merge(EmptyHook.INSTANCE);
             current = after;
         } catch (CommitFailedException e) {
-            log.warn("IndexTask update failed", e);
-        }
-    }
-
-    public synchronized void replace(Map<String, Set<String>> async) {
-        Set<String> in = new HashSet<String>(async.keySet());
-        Set<String> existing = active.keySet();
-        for (String type : existing) {
-            if (in.remove(type)) {
-                Set<String> defs = async.get(type);
-                if (defs.isEmpty()) {
-                    remove(type);
-                } else {
-                    addOrUpdate(type, defs);
-                }
-            } else {
-                remove(type);
-            }
-        }
-        for (String type : in) {
-            addOrUpdate(type, async.get(type));
-        }
-    }
-
-    void addOrUpdate(String type, Set<String> defs) {
-        IndexTask task = active.get(type);
-        if (task == null) {
-            task = new IndexTask(store, provider, type, defs);
-            active.put(type, task);
-            task.start(executor);
-        } else {
-            task.update(defs);
-        }
-    }
-
-    void remove(String type) {
-        IndexTask task = active.remove(type);
-        if (task != null) {
-            task.stop();
-        }
-    }
-
-    /**
-     * This Editor is responsible for watching over changes on async index defs:
-     * added index defs and deleted index defs are pushed forward to the
-     * #replace method
-     * 
-     */
-    class IndexConfigWatcher extends DefaultEditor {
-
-        private final Map<String, Set<String>> async = new HashMap<String, Set<String>>();
-
-        @Override
-        public void enter(NodeState before, NodeState after)
-                throws CommitFailedException {
-            if (!after.hasChildNode(INDEX_DEFINITIONS_NAME)) {
-                return;
-            }
-            NodeState index = after.getChildNode(INDEX_DEFINITIONS_NAME);
-            for (String indexName : index.getChildNodeNames()) {
-                NodeState indexChild = index.getChildNode(indexName);
-                if (isIndexNodeType(indexChild)) {
-                    boolean isasync = getBoolean(indexChild,
-                            ASYNC_PROPERTY_NAME);
-                    String type = getString(indexChild, TYPE_PROPERTY_NAME);
-                    if (type == null || !isasync) {
-                        // skip null and non-async types
-                        continue;
-                    }
-                    Set<String> defs = async.get(type);
-                    if (defs == null) {
-                        defs = new HashSet<String>();
-                        async.put(type, defs);
-                    }
-                    defs.add(type);
-                }
-            }
-        }
-
-        @Override
-        public void leave(NodeState before, NodeState after)
-                throws CommitFailedException {
-            replace(async);
-            async.clear();
-        }
-
-    }
-
-    static class IndexTask implements Runnable {
-
-        private static final Logger log = LoggerFactory
-                .getLogger(IndexTask.class);
-
-        private final NodeStore store;
-
-        private final String type;
-        private Set<String> defs;
-
-        private final IndexEditorProvider provider;
-
-        private ScheduledFuture<?> future;
-
-        private NodeState before;
-
-        public IndexTask(NodeStore store, IndexEditorProvider provider,
-                String type, Set<String> defs) {
-            this.store = store;
-            this.provider = provider;
-            this.type = type;
-            this.defs = defs;
-            this.before = EmptyNodeState.EMPTY_NODE;
-        }
-
-        public void update(Set<String> defs) {
-            // check of there are any changes
-            // TODO what happens when I move a def? (rm + add appears as a no-op
-            // in the set)
-            if (this.defs.equals(defs)) {
-                // no-op
-                return;
-            }
-
-            log.debug("Updated index def for type {}, reindexing", type);
-            this.defs = defs;
-            this.before = EmptyNodeState.EMPTY_NODE;
-        }
-
-        public synchronized void start(ScheduledExecutorService executor) {
-            if (future != null) {
-                throw new IllegalStateException("IndexTask has already started");
-            }
-            future = executor.scheduleWithFixedDelay(this, 100,
-                    INDEX_TASK_DELAY_MS, TimeUnit.MILLISECONDS);
-        }
-
-        public synchronized void stop() {
-            if (future == null) {
-                log.warn("IndexTask has already stopped.");
-                return;
-            }
-            future.cancel(true);
-        }
-
-        @Override
-        public void run() {
-            log.debug("Running background index task for type {}.", type);
-            NodeStoreBranch branch = store.branch();
-            NodeState after = branch.getHead();
-            try {
-                EditorHook hook = new EditorHook(new TypedEditorProvider(
-                        provider, type));
-                NodeState processed = hook.processCommit(before, after);
-                branch.setRoot(processed);
-                branch.merge(EmptyHook.INSTANCE);
-                before = after;
-            } catch (CommitFailedException e) {
-                log.warn("IndexTask update failed", e);
-            }
-        }
-    }
-
-    /**
-     * This creates a composite editor from a type-filtered index provider.
-     * 
-     */
-    private static class TypedEditorProvider implements EditorProvider {
-
-        private final IndexEditorProvider provider;
-
-        private final String type;
-
-        public TypedEditorProvider(IndexEditorProvider provider, String type) {
-            this.type = type;
-            this.provider = provider;
-        }
-
-        /**
-         * This does not make any effort to filter async definitions. The
-         * assumption is that given an index type, all of the returned index
-         * hooks inherit the same async assumption.
-         * 
-         */
-        @Override
-        public Editor getRootEditor(NodeState before, NodeState after,
-                NodeBuilder builder) {
-            return VisibleEditor.wrap(provider.getIndexEditor(type, builder));
+            log.warn("Background index update " + name + " failed", e);
         }
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeIndexEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeIndexEditorProvider.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeIndexEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeIndexEditorProvider.java Tue Jun 11 13:00:04 2013
@@ -22,10 +22,12 @@ import java.util.List;
 
 import javax.annotation.Nonnull;
 
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.spi.commit.CompositeEditor;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.commit.VisibleEditor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -41,7 +43,8 @@ public class CompositeIndexEditorProvide
         if (providers.isEmpty()) {
             return new IndexEditorProvider() {
                 @Override
-                public Editor getIndexEditor(String type, NodeBuilder builder) {
+                public Editor getIndexEditor(
+                        String type, NodeBuilder builder, NodeState root) {
                     return null;
                 }
             };
@@ -64,10 +67,12 @@ public class CompositeIndexEditorProvide
     }
 
     @Override
-    public Editor getIndexEditor(String type, NodeBuilder builder) {
+    public Editor getIndexEditor(
+            String type, NodeBuilder builder, NodeState root)
+            throws CommitFailedException {
         List<Editor> indexes = Lists.newArrayList();
         for (IndexEditorProvider provider : providers) {
-            Editor e = provider.getIndexEditor(type, builder);
+            Editor e = provider.getIndexEditor(type, builder, root);
             if (e != null) {
                 indexes.add(e);
             }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java Tue Jun 11 13:00:04 2013
@@ -17,9 +17,12 @@
 package org.apache.jackrabbit.oak.plugins.index;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
 
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 /**
  * Extension point for plugging in different kinds of IndexEditor providers.
@@ -29,23 +32,22 @@ import org.apache.jackrabbit.oak.spi.sta
 public interface IndexEditorProvider {
 
     /**
-     * 
      * Each provider knows how to produce a certain type of index. If the
      * <code>type</code> param is of an unknown value, the provider is expected
      * to return {@code null}.
      * 
      * <p>
-     * The <code>builder</code> must points to the index definition node
-     * under which the indexer is expected to store the index content.
+     * The <code>definition</code> builder must points to the index definition
+     * node under which the indexer is expected to store the index content.
      * </p>
      * 
-     * @param type
-     *            the index type
-     * @param builder
-     *            the node state builder of the index definition node that
-     *            will be used for updates
+     * @param type  index type
+     * @param definition index definition node builder, used for updates
+     * @param root root node state, used for things like node type information
      * @return index update editor, or {@code null} if type is unknown
      */
     @CheckForNull
-    Editor getIndexEditor(String type, NodeBuilder builder);
+    Editor getIndexEditor(
+            @Nonnull String type, @Nonnull NodeBuilder definition,
+            @Nonnull NodeState root) throws CommitFailedException;
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdate.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdate.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdate.java Tue Jun 11 13:00:04 2013
@@ -27,9 +27,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.getString;
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
@@ -43,20 +41,30 @@ import org.apache.jackrabbit.oak.spi.com
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
+import com.google.common.base.Objects;
+
 class IndexUpdate implements Editor {
 
     private final IndexEditorProvider provider;
 
-    private final boolean async;
+    private final String async;
 
     private final NodeState root;
 
     private final NodeBuilder builder;
 
+    /**
+     * Editors for indexes that will be normally updated.
+     */
     private final List<Editor> editors = newArrayList();
 
+    /**
+     * Editors for indexes that need to be re-indexed.
+     */
+    private final List<Editor> reindex = newArrayList();
+
     IndexUpdate(
-            IndexEditorProvider provider, boolean async,
+            IndexEditorProvider provider, String async,
             NodeState root, NodeBuilder builder) {
         this.provider = checkNotNull(provider);
         this.async = async;
@@ -75,43 +83,11 @@ class IndexUpdate implements Editor {
     @Override
     public void enter(NodeState before, NodeState after)
             throws CommitFailedException {
-        List<Editor> reindex = newArrayList();
-        if (builder.hasChildNode(INDEX_DEFINITIONS_NAME)) {
-            Map<String, Editor> tempEditors = new HashMap<String, Editor>();
-            NodeBuilder definitions = builder.child(INDEX_DEFINITIONS_NAME);
-            for (String name : definitions.getChildNodeNames()) {
-                NodeBuilder definition = definitions.child(name);
-                if (async == getBoolean(definition, ASYNC_PROPERTY_NAME)) {
-                    String type = getString(definition, TYPE_PROPERTY_NAME);
-                    Editor editor = null;
-                    if (tempEditors.containsKey(type)) {
-                        editor = tempEditors.get(type);
-                    } else {
-                        editor = provider.getIndexEditor(type, builder);
-                        tempEditors.put(type, editor);
-                    }
-
-                    if (editor == null) {
-                        // trigger reindexing when an indexer becomes available
-                        definition.setProperty(REINDEX_PROPERTY_NAME, true);
-                    } else if (getBoolean(definition, REINDEX_PROPERTY_NAME)) {
-                        definition.setProperty(REINDEX_PROPERTY_NAME, false);
-                        // as we don't know the index content node name
-                        // beforehand, we'll remove all child nodes
-                        for (String rm : definition.getChildNodeNames()) {
-                            definition.getChildNode(rm).remove();
-                        }
-                        reindex.add(editor);
-                    } else {
-                        editors.add(VisibleEditor.wrap(editor));
-                    }
-                }
-            }
-        }
+        collectIndexEditors(builder.getChildNode(INDEX_DEFINITIONS_NAME));
 
         // no-op when reindex is empty
         CommitFailedException exception = EditorDiff.process(
-                VisibleEditor.wrap(CompositeEditor.compose(reindex)), MISSING_NODE, after);
+                CompositeEditor.compose(reindex), MISSING_NODE, after);
         if (exception != null) {
             throw exception;
         }
@@ -121,6 +97,31 @@ class IndexUpdate implements Editor {
         }
     }
 
+    private void collectIndexEditors(NodeBuilder definitions)
+            throws CommitFailedException {
+        for (String name : definitions.getChildNodeNames()) {
+            NodeBuilder definition = definitions.getChildNode(name);
+            if (Objects.equal(async, getString(definition, ASYNC_PROPERTY_NAME))) {
+                String type = getString(definition, TYPE_PROPERTY_NAME);
+                Editor editor = provider.getIndexEditor(type, definition, root);
+                if (editor == null) {
+                    // trigger reindexing when an indexer becomes available
+                    definition.setProperty(REINDEX_PROPERTY_NAME, true);
+                } else if (getBoolean(definition, REINDEX_PROPERTY_NAME)) {
+                    definition.setProperty(REINDEX_PROPERTY_NAME, false);
+                    // as we don't know the index content node name
+                    // beforehand, we'll remove all child nodes
+                    for (String rm : definition.getChildNodeNames()) {
+                        definition.getChildNode(rm).remove();
+                    }
+                    reindex.add(VisibleEditor.wrap(editor));
+                } else {
+                    editors.add(VisibleEditor.wrap(editor));
+                }
+            }
+        }
+    }
+
     @Override
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateProvider.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateProvider.java Tue Jun 11 13:00:04 2013
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.plugins.index;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
@@ -27,13 +28,14 @@ public class IndexUpdateProvider impleme
 
     private final IndexEditorProvider provider;
 
-    private final boolean async;
+    private final String async;
 
     public IndexUpdateProvider(IndexEditorProvider provider) {
-        this(provider, false);
+        this(provider, null);
     }
 
-    public IndexUpdateProvider(IndexEditorProvider provider, boolean async) {
+    public IndexUpdateProvider(
+            @Nonnull IndexEditorProvider provider, @CheckForNull String async) {
         this.provider = provider;
         this.async = async;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java Tue Jun 11 13:00:04 2013
@@ -16,38 +16,33 @@
  */
 package org.apache.jackrabbit.oak.plugins.index;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.util.NodeUtil;
-
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
 import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
 import static org.apache.jackrabbit.oak.api.Type.NAME;
 import static org.apache.jackrabbit.oak.api.Type.NAMES;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
-import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.PROPERTY_NAMES;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
-import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_UNKNOWN;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.UNIQUE_PROPERTY_NAME;
 
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.util.NodeUtil;
+
 /**
  * TODO document
  */
@@ -123,59 +118,6 @@ public class IndexUtils {
         entry.setNames(PROPERTY_NAMES, propertyNames);
     }
 
-    /**
-     * Builds a list of the existing index definitions.
-     * <p/>
-     * Checks only children of the provided state for an index definitions
-     * container node, aka a node named {@link IndexConstants#INDEX_DEFINITIONS_NAME}
-     *
-     * @param state
-     * @param indexConfigPath
-     * @param typeFilter
-     * @return A list of index definitions.
-     */
-    public static List<IndexDefinition> buildIndexDefinitions(NodeState state,
-                                                              String indexConfigPath, String typeFilter) {
-        NodeState definitions = state.getChildNode(INDEX_DEFINITIONS_NAME);
-        if (!definitions.exists()) {
-            return Collections.emptyList();
-        }
-        indexConfigPath = concat(indexConfigPath, INDEX_DEFINITIONS_NAME);
-
-        List<IndexDefinition> defs = new ArrayList<IndexDefinition>();
-        for (ChildNodeEntry c : definitions.getChildNodeEntries()) {
-            IndexDefinition def = getDefinition(indexConfigPath, c, typeFilter);
-            if (def == null) {
-                continue;
-            }
-            defs.add(def);
-        }
-        return defs;
-    }
-
-    /**
-     * Builds an {@link IndexDefinition} out of a {@link ChildNodeEntry}
-     *
-     * @param path
-     * @param def {@code ChildNodeEntry} storing the index definition.
-     * @param typeFilter
-     * @return a new {@code IndexDefinition}
-     */
-    private static IndexDefinition getDefinition(String path,
-                                                 ChildNodeEntry def, String typeFilter) {
-        String name = def.getName();
-        NodeState ns = def.getNodeState();
-        PropertyState typeProp = ns.getProperty(TYPE_PROPERTY_NAME);
-        String type = TYPE_UNKNOWN;
-        if (typeProp != null && !typeProp.isArray()) {
-            type = typeProp.getValue(STRING);
-        }
-        if (typeFilter != null && !typeFilter.equals(type)) {
-            return null;
-        }
-        return new IndexDefinitionImpl(name, type, concat(path, name));
-    }
-
     public static boolean isIndexNodeType(NodeState state) {
         PropertyState ps = state.getProperty(JCR_PRIMARYTYPE);
         return ps != null

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java Tue Jun 11 13:00:04 2013
@@ -16,197 +16,168 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.property;
 
+import static com.google.common.base.Predicates.in;
 import static com.google.common.collect.Iterables.addAll;
-import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Iterables.any;
 import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Collections.singleton;
 import static org.apache.jackrabbit.JcrConstants.JCR_ISMIXIN;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.CONSTRAINT;
 import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
-import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.PROPERTY_NAMES;
-import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider.TYPE;
-import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.encode;
 import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
 import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_MIXIN_SUBTYPES;
 import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_PRIMARY_SUBTYPES;
-import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.isIndexNodeType;
-import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.getChildOrNull;
 
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
+import javax.jcr.PropertyType;
+
 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.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
-import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
-import com.google.common.collect.ImmutableList;
-
 /**
- * {@link IndexEditor} implementation that is responsible for keeping the
- * {@link PropertyIndex} up to date.
- * <br>
- * There is a tree of PropertyIndexDiff objects, each object represents the
- * changes at a given node.
+ * Index editor for keeping a property index up to date.
  * 
  * @see PropertyIndex
  * @see PropertyIndexLookup
  */
-class PropertyIndexEditor implements IndexEditor, Closeable {
+class PropertyIndexEditor implements IndexEditor {
 
-    private final IndexStoreStrategy store = new ContentMirrorStoreStrategy();
+    /** Index storage strategy */
+    private static final IndexStoreStrategy STORE =
+            new ContentMirrorStoreStrategy();
 
-    /**
-     * The parent (null if this is the root node).
-     */
+    /** Parent editor, or {@code null} if this is the root editor. */
     private final PropertyIndexEditor parent;
 
-    /**
-     * The node (can be null in the case of a deleted node).
-     */
-    private final NodeBuilder node;
+    /** Name of this node, or {@code null} for the root node. */
+    private final String name;
 
-    /**
-     * The node name (the path element). Null for the root node.
-     */
-    private final String nodeName;
+    /** Path of this editor, built lazily in {@link #getPath()}. */
+    private String path;
+
+    /** Index definition node builder */
+    private final NodeBuilder definition;
+
+    private final Set<String> propertyNames;
+
+    private final Set<String> primaryTypes;
+
+    private final Set<String> mixinTypes;
+
+    private final Set<String> keysToCheckForUniqueness;
 
     /**
-     * The path of the changed node (built lazily).
+     * Flag to indicate whether individual property changes should
+     * be tracked for this node.
      */
-    private String path;
-
-    private final boolean isRoot;
+    private boolean trackChanges = false;
 
     /**
-     * The map of known indexes. Key: the property name. Value: the list of
-     * indexes (it is possible to have multiple indexes for the same property
-     * name).
+     * Matching property value keys from the before state,
+     * or {@code null} if this node is not indexed.
      */
-    private final Map<String, List<PropertyIndexUpdate>> indexMap;
+    private Set<String> beforeKeys = null;
 
     /**
-     * The {@code /jcr:system/jcr:nodeTypes} subtree.
+     * Matching property value keys from the after state,
+     * or {@code null} if this node is not indexed.
      */
-    private final NodeState types;
+    private Set<String> afterKeys = null;
 
-    public PropertyIndexEditor(NodeBuilder builder) {
-        this(null, builder, null, "/", true);
-    }
+    public PropertyIndexEditor(NodeBuilder definition, NodeState root) {
+        this.parent = null;
+        this.name = null;
+        this.path = "/";
+        this.definition = definition;
+
+        // get property names
+        this.propertyNames = newHashSet(definition.getNames(PROPERTY_NAMES));
+
+        // get declaring types, and all their subtypes
+        // TODO: should we reindex when type definitions change?
+        if (definition.hasProperty(DECLARING_NODE_TYPES)) {
+            this.primaryTypes = newHashSet();
+            this.mixinTypes = newHashSet();
+            NodeState types =
+                    root.getChildNode(JCR_SYSTEM).getChildNode(JCR_NODE_TYPES);
+            for (String name : definition.getNames(DECLARING_NODE_TYPES)) {
+                NodeState type = types.getChildNode(name);
+                if (type.getBoolean(JCR_ISMIXIN)) {
+                    mixinTypes.add(name);
+                } else {
+                    primaryTypes.add(name);
+                }
+                addAll(mixinTypes, type.getNames(OAK_MIXIN_SUBTYPES));
+                addAll(primaryTypes, type.getNames(OAK_PRIMARY_SUBTYPES));
+            }
+        } else {
+            this.primaryTypes = null;
+            this.mixinTypes = null;
+        }
 
-    private PropertyIndexEditor(PropertyIndexEditor parent, String nodeName) {
-        this(parent, getChildOrNull(parent.node, nodeName), nodeName, null, false);
+        // keep track of modified keys for uniqueness checks
+        if (definition.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
+            this.keysToCheckForUniqueness = newHashSet();
+        } else {
+            this.keysToCheckForUniqueness = null;
+        }
     }
 
-    private PropertyIndexEditor(PropertyIndexEditor parent, NodeBuilder node,
-            String nodeName, String path, boolean root) {
+    private PropertyIndexEditor(PropertyIndexEditor parent, String name) {
         this.parent = parent;
-        this.node = node;
-        this.nodeName = nodeName;
-        this.path = path;
-        this.isRoot = root;
-
-        if (parent == null) {
-            this.indexMap = new HashMap<String, List<PropertyIndexUpdate>>();
-            if (node.hasChildNode(JCR_SYSTEM)) {
-                NodeBuilder typeNB = node.getChildNode(JCR_SYSTEM)
-                        .getChildNode(JCR_NODE_TYPES);
-                this.types = typeNB.getNodeState();
-            } else {
-                this.types = EmptyNodeState.MISSING_NODE;
-            }
-        } else {
-            this.indexMap = parent.indexMap;
-            this.types = parent.types;
-        }
+        this.name = name;
+        this.path = null;
+        this.definition = parent.definition;
+        this.propertyNames = parent.propertyNames;
+        this.primaryTypes = parent.primaryTypes;
+        this.mixinTypes = parent.mixinTypes;
+        this.keysToCheckForUniqueness = parent.keysToCheckForUniqueness;
     }
 
-    public String getPath() {
-        // build the path lazily
+    /**
+     * Returns the path of this node, building it lazily when first requested.
+     */
+    private String getPath() {
         if (path == null) {
-            path = concat(parent.getPath(), nodeName);
+            path = concat(parent.getPath(), name);
         }
         return path;
     }
 
-    /**
-     * Get all the indexes for the given property name.
-     * 
-     * @param propertyName
-     *            the property name
-     * @return the indexes
-     */
-    private Iterable<PropertyIndexUpdate> getIndexes(String propertyName) {
-        List<PropertyIndexUpdate> indexes = indexMap.get(propertyName);
-        if (indexes == null) {
-            return ImmutableList.of();
-        }
-        List<PropertyIndexUpdate> filtered = new ArrayList<PropertyIndexUpdate>();
-        for (PropertyIndexUpdate pi : indexes) {
-            if (node == null || pi.matchesNodeType(node, getPath())) {
-                filtered.add(pi);
-            }
-        }
-        return filtered;
+    private boolean isOfMatchingType(NodeState state) {
+        return primaryTypes == null // no type limitations
+                || primaryTypes.contains(state.getName(JCR_PRIMARYTYPE))
+                || any(state.getNames(JCR_MIXINTYPES), in(mixinTypes));
     }
 
-    /**
-     * Add the index definitions to the in-memory set of known index
-     * definitions.
-     * 
-     * @param state
-     *            the node state that contains the index definition
-     * @param indexName
-     *            the name of the index
-     */
-    private void addIndexes(NodeState state, String indexName) {
-        Set<String> primaryTypes = newHashSet();
-        Set<String> mixinTypes = newHashSet();
-        for (String typeName : state.getNames(DECLARING_NODE_TYPES)) {
-            NodeState type = types.getChildNode(typeName);
-            if (type.getBoolean(JCR_ISMIXIN)) {
-                mixinTypes.add(typeName);
-            } else {
-                primaryTypes.add(typeName);
-            }
-            addAll(primaryTypes, type.getNames(OAK_PRIMARY_SUBTYPES));
-            addAll(mixinTypes, type.getNames(OAK_MIXIN_SUBTYPES));
+    private static void addValueKeys(Set<String> keys, PropertyState property) {
+        if (property.getType().tag() != PropertyType.BINARY) {
+            keys.addAll(encode(PropertyValues.create(property)));
         }
+    }
 
-        PropertyState ps = state.getProperty(PROPERTY_NAMES);
-        Iterable<String> propertyNames = ps != null ? ps.getValue(Type.NAMES)
-                : ImmutableList.of(indexName);
-        for (String pname : propertyNames) {
-            List<PropertyIndexUpdate> list = this.indexMap.get(pname);
-            if (list == null) {
-                list = newArrayList();
-                this.indexMap.put(pname, list);
-            }
-            boolean exists = false;
-            String localPath = getPath();
-            for (PropertyIndexUpdate piu : list) {
-                if (piu.matches(localPath, primaryTypes, mixinTypes)) {
-                    exists = true;
-                    break;
-                }
-            }
-            if (!exists) {
-                PropertyIndexUpdate update = new PropertyIndexUpdate(
-                        getPath(), node.child(INDEX_DEFINITIONS_NAME).child(indexName),
-                        store, primaryTypes, mixinTypes);
-                list.add(update);
+    private static void addMatchingKeys(
+            Set<String> keys, NodeState state, Iterable<String> propertyNames) {
+        for (String propertyName : propertyNames) {
+            PropertyState property = state.getProperty(propertyName);
+            if (property != null) {
+                addValueKeys(keys, property);
             }
         }
     }
@@ -214,75 +185,100 @@ class PropertyIndexEditor implements Ind
     @Override
     public void enter(NodeState before, NodeState after)
             throws CommitFailedException {
-        if (after != null && after.hasChildNode(INDEX_DEFINITIONS_NAME)) {
-            NodeState index = after.getChildNode(INDEX_DEFINITIONS_NAME);
-            for (String indexName : index.getChildNodeNames()) {
-                NodeState child = index.getChildNode(indexName);
-                if (isIndexNodeType(child, TYPE)) {
-                    addIndexes(child, indexName);
-                }
-            }
+        boolean beforeMatches = before.exists() && isOfMatchingType(before);
+        boolean afterMatches  = after.exists()  && isOfMatchingType(after);
+
+        if (beforeMatches || afterMatches) {
+            beforeKeys = newHashSet();
+            afterKeys = newHashSet();
+        }
+ 
+        if (beforeMatches && afterMatches) {
+            trackChanges = true;
+        } else if (beforeMatches) {
+            // all matching values should be removed from the index
+            addMatchingKeys(beforeKeys, before, propertyNames);
+        } else if (afterMatches) {
+            // all matching values should be added to the index
+            addMatchingKeys(afterKeys, after, propertyNames);
         }
     }
 
     @Override
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {
-        if (isRoot) {
-            for (List<PropertyIndexUpdate> updates : indexMap.values()) {
-                for (PropertyIndexUpdate update : updates) {
-                    update.checkUniqueKeys();
+        if (beforeKeys != null) {
+            Set<String> sharedKeys = newHashSet(beforeKeys);
+            sharedKeys.retainAll(afterKeys);
+
+            beforeKeys.removeAll(sharedKeys);
+            afterKeys.removeAll(sharedKeys);
+
+            if (!beforeKeys.isEmpty() || !afterKeys.isEmpty()) {
+                NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME);
+                STORE.update(index, getPath(), beforeKeys, afterKeys);
+
+                if (keysToCheckForUniqueness != null) {
+                    keysToCheckForUniqueness.addAll(afterKeys);
                 }
             }
         }
-    }
 
-    @Override
-    public void propertyAdded(PropertyState after) throws CommitFailedException {
-        for (PropertyIndexUpdate update : getIndexes(after.getName())) {
-            update.insert(getPath(), after);
+        if (parent == null) {
+            // make sure that the index node exist, even with no content
+            NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME);
+
+            // check uniqueness constraints when leaving the root
+            if (keysToCheckForUniqueness != null
+                    && !keysToCheckForUniqueness.isEmpty()) {
+                NodeState state = index.getNodeState();
+                for (String key : keysToCheckForUniqueness) {
+                    if (STORE.count(state, singleton(key), 2) > 1) {
+                        throw new CommitFailedException(
+                                CONSTRAINT, 30,
+                                "Uniqueness constraint violated for key " + key);
+                    }
+                }
+            }
         }
     }
 
     @Override
-    public void propertyChanged(PropertyState before, PropertyState after)
-            throws CommitFailedException {
-        for (PropertyIndexUpdate update : getIndexes(after.getName())) {
-            update.remove(getPath(), before);
-            update.insert(getPath(), after);
+    public void propertyAdded(PropertyState after) {
+        if (trackChanges && propertyNames.contains(after.getName())) {
+            addValueKeys(afterKeys, after);
         }
     }
 
     @Override
-    public void propertyDeleted(PropertyState before)
-            throws CommitFailedException {
-        for (PropertyIndexUpdate update : getIndexes(before.getName())) {
-            update.remove(getPath(), before);
+    public void propertyChanged(PropertyState before, PropertyState after) {
+        if (trackChanges && propertyNames.contains(after.getName())) {
+            addValueKeys(beforeKeys, before);
+            addValueKeys(afterKeys, after);
         }
     }
 
     @Override
-    public Editor childNodeAdded(String name, NodeState after)
-            throws CommitFailedException {
-        return childNodeChanged(name, EMPTY_NODE, after);
+    public void propertyDeleted(PropertyState before) {
+        if (trackChanges && propertyNames.contains(before.getName())) {
+            addValueKeys(beforeKeys, before);
+        }
     }
 
     @Override
-    public Editor childNodeChanged(String name, NodeState before,
-            NodeState after) throws CommitFailedException {
+    public Editor childNodeAdded(String name, NodeState after) {
         return new PropertyIndexEditor(this, name);
     }
 
     @Override
-    public Editor childNodeDeleted(String name, NodeState before)
-            throws CommitFailedException {
-        return childNodeChanged(name, before, EMPTY_NODE);
+    public Editor childNodeChanged(
+            String name, NodeState before, NodeState after) {
+        return new PropertyIndexEditor(this, name);
     }
 
-    // -----------------------------------------------------< Closeable >--
-
     @Override
-    public void close() throws IOException {
-        indexMap.clear();
+    public Editor childNodeDeleted(String name, NodeState before) {
+        return new PropertyIndexEditor(this, name);
     }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java Tue Jun 11 13:00:04 2013
@@ -21,6 +21,7 @@ import org.apache.felix.scr.annotations.
 import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 /**
  * Service that provides PropertyIndex based editors.
@@ -36,9 +37,10 @@ public class PropertyIndexEditorProvider
     public static final String TYPE = "property";
 
     @Override
-    public Editor getIndexEditor(String type, NodeBuilder builder) {
+    public Editor getIndexEditor(
+            String type, NodeBuilder definition, NodeState root) {
         if (TYPE.equals(type)) {
-            return new PropertyIndexEditor(builder);
+            return new PropertyIndexEditor(definition, root);
         }
         return null;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java Tue Jun 11 13:00:04 2013
@@ -24,6 +24,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider.TYPE;
 import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.encode;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
 
 import java.util.Iterator;
 import java.util.Set;
@@ -85,13 +86,13 @@ public class PropertyIndexLookup {
         }
 
         if (PathUtils.denotesRoot(path)) {
-            return getIndexDataNode(root, propertyName, supertypes) != null;
+            return getIndexDataNode(root, propertyName, supertypes).exists();
         }
 
         NodeState node = root;
         Iterator<String> it = PathUtils.elements(path).iterator();
         while (it.hasNext()) {
-            if (getIndexDataNode(node, propertyName, supertypes) != null) {
+            if (getIndexDataNode(node, propertyName, supertypes).exists()) {
                 return true;
             }
             node = node.getChildNode(it.next());
@@ -106,10 +107,11 @@ public class PropertyIndexLookup {
         }
 
         NodeState state = getIndexDataNode(root, propertyName, supertypes);
-        if (state == null) {
+        if (state.exists()) {
+            return store.query(filter, propertyName, state, encode(value));
+        } else {
             throw new IllegalArgumentException("No index for " + propertyName);
         }
-        return store.query(filter, propertyName, state, encode(value));
     }
 
     public double getCost(Filter filter, String name, PropertyValue value) {
@@ -119,10 +121,11 @@ public class PropertyIndexLookup {
         }
 
         NodeState state = getIndexDataNode(root, name, supertypes);
-        if (state == null) {
+        if (state.exists()) {
+            return store.count(state, encode(value), MAX_COST);
+        } else {
             return Double.POSITIVE_INFINITY;
         }
-        return store.count(state, encode(value), MAX_COST);
     }
 
     /**
@@ -139,7 +142,7 @@ public class PropertyIndexLookup {
     private NodeState getIndexDataNode(
             NodeState node, String propertyName, Set<String> supertypes) {
         //keep a fallback to a matching index def that has *no* node type constraints
-        NodeState fallback = null;
+        NodeState fallback = MISSING_NODE;
 
         NodeState state = node.getChildNode(INDEX_DEFINITIONS_NAME);
         for (ChildNodeEntry entry : state.getChildNodeEntries()) {
@@ -161,7 +164,7 @@ public class PropertyIndexLookup {
                     }
                 } else if (supertypes == null) {
                     return index;
-                } else if (fallback == null) {
+                } else if (index.exists() && !fallback.exists()) {
                     fallback = index;
                 }
             }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java Tue Jun 11 13:00:04 2013
@@ -16,14 +16,12 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.property.strategy;
 
-import java.util.Collections;
+import static com.google.common.collect.Queues.newArrayDeque;
+
 import java.util.Deque;
 import java.util.Iterator;
-import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
 
-import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.query.Filter;
@@ -65,82 +63,52 @@ public class ContentMirrorStoreStrategy 
     static final Logger LOG = LoggerFactory.getLogger(ContentMirrorStoreStrategy.class);
 
     @Override
-    public void remove(NodeBuilder index, String key, Iterable<String> values) {
-        if (!index.hasChildNode(key)) {
-            return;
-        }
-        NodeBuilder child = index.child(key);
-        Map<String, NodeBuilder> parents = new TreeMap<String, NodeBuilder>(Collections.reverseOrder());
-
-        for (String rm : values) {
-            if (PathUtils.denotesRoot(rm)) {
-                child.removeProperty("match");
-            } else {
-                String parentPath = PathUtils.getParentPath(rm);
-                String name = PathUtils.getName(rm);
-                NodeBuilder indexEntry = parents.get(parentPath);
-                if (indexEntry == null) {
-                    indexEntry = child;
-                    String segmentPath = "";
-                    Iterator<String> segments = PathUtils.elements(parentPath)
-                            .iterator();
-                    while (segments.hasNext()) {
-                        String segment = segments.next();
-                        segmentPath = PathUtils.concat(segmentPath, segment);
-                        indexEntry = indexEntry.child(segment);
-                        parents.put(segmentPath, indexEntry);
-                    }
-                }
-                if (indexEntry.hasChildNode(name)) {
-                    NodeBuilder childEntry = indexEntry.child(name);
-                    childEntry.removeProperty("match");
-                    if (childEntry.getChildNodeCount() == 0) {
-                        indexEntry.getChildNode(name).remove();
-                    }
-                }
-            }
+    public void update(
+            NodeBuilder index, String path,
+            Set<String> beforeKeys, Set<String> afterKeys) {
+        for (String key : beforeKeys) {
+            remove(index, key, path);
         }
-        // prune the index: remove all children that have no children
-        // and no "match" property progressing bottom up
-        Iterator<String> it = parents.keySet().iterator();
-        while (it.hasNext()) {
-            String path = it.next();
-            NodeBuilder parent = parents.get(path);
-            pruneNode(parent);
-        }
-
-        // finally prune the index node
-        pruneNode(child);
-        if (child.getChildNodeCount() == 0
-                && !child.hasProperty("match")) {
-            index.getChildNode(key).remove();
+        for (String key : afterKeys) {
+            insert(index, key, path);
         }
     }
 
-    private static void pruneNode(NodeBuilder parent) {
-        if (!parent.exists()) {
-            return;
-        }
-        for (String name : parent.getChildNodeNames()) {
-            NodeBuilder segment = parent.child(name);
-            if (segment.getChildNodeCount() == 0
-                    && !segment.hasProperty("match")) {
-                parent.getChildNode(name).remove();
+    private void remove(NodeBuilder index, String key, String value) {
+        NodeBuilder builder = index.getChildNode(key);
+        if (builder.exists()) {
+            // Collect all builders along the given path
+            Deque<NodeBuilder> builders = newArrayDeque();
+            builders.addFirst(builder);
+
+            // Descend to the correct location in the index tree
+            for (String name : PathUtils.elements(value)) {
+                builder = builder.getChildNode(name);
+                builders.addFirst(builder);
+            }
+
+            // Drop the match value,  if present
+            if (builder.exists()) {
+                builder.removeProperty("match");
+            }
+
+            // Prune all index nodes that are no longer needed
+            for (NodeBuilder node : builders) {
+                if (node.getBoolean("match") || node.getChildNodeCount() > 0) {
+                    return;
+                } else if (node.exists()) {
+                    node.remove();
+                }
             }
         }
     }
 
-    @Override
-    public void insert(NodeBuilder index, String key, Iterable<String> values)
-            throws CommitFailedException {
-        NodeBuilder child = index.child(key);
-        for (String add : values) {
-            NodeBuilder indexEntry = child;
-            for (String segment : PathUtils.elements(add)) {
-                indexEntry = indexEntry.child(segment);
-            }
-            indexEntry.setProperty("match", true);
+    private void insert(NodeBuilder index, String key, String value) {
+        NodeBuilder builder = index.child(key);
+        for (String name : PathUtils.elements(value)) {
+            builder = builder.child(name);
         }
+        builder.setProperty("match", true);
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java Tue Jun 11 13:00:04 2013
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.util.Set;
 
-import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -30,26 +29,16 @@ import org.apache.jackrabbit.oak.spi.sta
 public interface IndexStoreStrategy {
 
     /**
-     * Removes a set of values from the index
+     * Updates the index for the given path.
      * 
      * @param index the index node
-     * @param key the index key
-     * @param values the values to be removed from the given key
-     * @throws CommitFailedException
+     * @param path path stored in the index
+     * @param beforeKeys keys that no longer apply to the path
+     * @param afterKeys keys that now do apply to the path
      */
-    void remove(NodeBuilder index, String key, Iterable<String> values)
-            throws CommitFailedException;
-
-    /**
-     * Inserts a set of values in the index
-     * 
-     * @param index the index node
-     * @param key the index key
-     * @param values the values to be added to the given key
-     * @throws CommitFailedException
-     */
-    void insert(NodeBuilder index, String key, Iterable<String> values)
-            throws CommitFailedException;
+    void update(
+        NodeBuilder index, String path,
+        Set<String> beforeKeys, Set<String> afterKeys);
     
     /**
      * Search for a given set of values.

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java Tue Jun 11 13:00:04 2013
@@ -260,7 +260,7 @@ public class MemoryNodeBuilder implement
 
     @Override
     public NodeBuilder setChildNode(String name, NodeState state) {
-        checkState(exists(), "This builder does not exist: " + name);
+        checkState(exists(), "This builder does not exist: " + this.name);
         head().getMutableNodeState().setChildNode(checkNotNull(name), checkNotNull(state));
         MemoryNodeBuilder builder = createChildBuilder(name);
         updated();

Modified: jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd Tue Jun 11 13:00:04 2013
@@ -511,7 +511,8 @@
  * @since oak 0.6
  */
 [oak:queryIndexDefinition] > nt:unstructured
-  - type (STRING)
+  - type (STRING) mandatory
+  - async (STRING)
   - reindex (BOOLEAN) mandatory IGNORE
 
 //------------------------------------------------------------------------------

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java Tue Jun 11 13:00:04 2013
@@ -23,12 +23,8 @@ import static org.apache.jackrabbit.oak.
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
 
-import org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate.IndexTask;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexLookup;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
@@ -74,7 +70,6 @@ public class AsyncIndexUpdateTest {
     @Test
     public void testAsync() throws Exception {
         NodeStore store = new MemoryNodeStore();
-        ScheduledExecutorService executor = Executors.newScheduledThreadPool(0);
         IndexEditorProvider provider = new PropertyIndexEditorProvider();
 
         NodeStoreBranch branch = store.branch();
@@ -82,15 +77,15 @@ public class AsyncIndexUpdateTest {
         NodeBuilder builder = root.builder();
         createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
                 "rootIndex", true, false, ImmutableSet.of("foo"), null)
-                .setProperty(ASYNC_PROPERTY_NAME, true);
+                .setProperty(ASYNC_PROPERTY_NAME, "async");
         builder.child("testRoot").setProperty("foo", "abc");
 
         // merge it back in
         branch.setRoot(builder.getNodeState());
         branch.merge(EmptyHook.INSTANCE);
 
-        AsyncIndexUpdate async = new AsyncIndexUpdate(store, executor, provider);
-        runIndexing(async, 1);
+        AsyncIndexUpdate async = new AsyncIndexUpdate("async", store, provider);
+        async.run();
         root = store.getRoot();
 
         // first check that the index content nodes exist
@@ -101,15 +96,6 @@ public class AsyncIndexUpdateTest {
         assertEquals(ImmutableSet.of("testRoot"), find(lookup, "foo", "abc"));
     }
 
-    private static void runIndexing(AsyncIndexUpdate async, int expectedActive) {
-        async.run();
-        Map<String, IndexTask> active = async.active;
-        assertEquals(expectedActive, active.size());
-        for (IndexTask task : active.values()) {
-            task.run();
-        }
-    }
-
     /**
      * Async Index Test with 2 index defs at the same location
      * <ul>
@@ -122,7 +108,6 @@ public class AsyncIndexUpdateTest {
     @Test
     public void testAsyncDouble() throws Exception {
         NodeStore store = new MemoryNodeStore();
-        ScheduledExecutorService executor = Executors.newScheduledThreadPool(0);
         IndexEditorProvider provider = new PropertyIndexEditorProvider();
 
         NodeStoreBranch branch = store.branch();
@@ -130,10 +115,10 @@ public class AsyncIndexUpdateTest {
         NodeBuilder builder = root.builder();
         createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
                 "rootIndex", true, false, ImmutableSet.of("foo"), null)
-                .setProperty(ASYNC_PROPERTY_NAME, true);
+                .setProperty(ASYNC_PROPERTY_NAME, "async");
         createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
                 "rootIndexSecond", true, false, ImmutableSet.of("bar"), null)
-                .setProperty(ASYNC_PROPERTY_NAME, true);
+                .setProperty(ASYNC_PROPERTY_NAME, "async");
 
         builder.child("testRoot").setProperty("foo", "abc")
                 .setProperty("bar", "def");
@@ -143,8 +128,8 @@ public class AsyncIndexUpdateTest {
         branch.setRoot(builder.getNodeState());
         branch.merge(EmptyHook.INSTANCE);
 
-        AsyncIndexUpdate async = new AsyncIndexUpdate(store, executor, provider);
-        runIndexing(async, 1);
+        AsyncIndexUpdate async = new AsyncIndexUpdate("async", store, provider);
+        async.run();
         root = store.getRoot();
 
         // first check that the index content nodes exist
@@ -176,7 +161,6 @@ public class AsyncIndexUpdateTest {
     @Test
     public void testAsyncDoubleSubtree() throws Exception {
         NodeStore store = new MemoryNodeStore();
-        ScheduledExecutorService executor = Executors.newScheduledThreadPool(0);
         IndexEditorProvider provider = new PropertyIndexEditorProvider();
 
         NodeStoreBranch branch = store.branch();
@@ -184,12 +168,12 @@ public class AsyncIndexUpdateTest {
         NodeBuilder builder = root.builder();
         createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
                 "rootIndex", true, false, ImmutableSet.of("foo"), null)
-                .setProperty(ASYNC_PROPERTY_NAME, true);
+                .setProperty(ASYNC_PROPERTY_NAME, "async");
         createIndexDefinition(
                 builder.child("newchild").child("other")
                         .child(INDEX_DEFINITIONS_NAME), "subIndex", true,
-                false, ImmutableSet.of("foo"), null).setProperty(
-                ASYNC_PROPERTY_NAME, true);
+                false, ImmutableSet.of("foo"), null)
+                .setProperty(ASYNC_PROPERTY_NAME, "async");
 
         builder.child("testRoot").setProperty("foo", "abc");
         builder.child("newchild").child("other").child("testChild")
@@ -199,8 +183,8 @@ public class AsyncIndexUpdateTest {
         branch.setRoot(builder.getNodeState());
         branch.merge(EmptyHook.INSTANCE);
 
-        AsyncIndexUpdate async = new AsyncIndexUpdate(store, executor, provider);
-        runIndexing(async, 1);
+        AsyncIndexUpdate async = new AsyncIndexUpdate("async", store, provider);
+        async.run();
         root = store.getRoot();
 
         // first check that the index content nodes exist

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java?rev=1491780&r1=1491779&r2=1491780&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java Tue Jun 11 13:00:04 2013
@@ -50,6 +50,9 @@ import com.google.common.collect.Sets;
 
 public class IndexUpdateTest {
 
+    private static final EditorHook HOOK = new EditorHook(
+            new IndexUpdateProvider(new PropertyIndexEditorProvider()));
+
     private NodeState root = new InitialContent().initialize(EMPTY_NODE);
 
     private NodeBuilder builder = root.builder();
@@ -81,10 +84,7 @@ public class IndexUpdateTest {
 
         NodeState after = builder.getNodeState();
 
-        IndexUpdateProvider p = new IndexUpdateProvider(
-                new PropertyIndexEditorProvider());
-        EditorHook hook = new EditorHook(p);
-        NodeState indexed = hook.processCommit(before, after);
+        NodeState indexed = HOOK.processCommit(before, after);
 
         // first check that the index content nodes exist
         checkPathExists(indexed, INDEX_DEFINITIONS_NAME, "rootIndex",
@@ -120,10 +120,7 @@ public class IndexUpdateTest {
 
         NodeState after = builder.getNodeState();
 
-        IndexUpdateProvider p = new IndexUpdateProvider(
-                new PropertyIndexEditorProvider());
-        EditorHook hook = new EditorHook(p);
-        NodeState indexed = hook.processCommit(before, after);
+        NodeState indexed = HOOK.processCommit(before, after);
 
         // first check that the index content nodes exist
         NodeState ns = checkPathExists(indexed, INDEX_DEFINITIONS_NAME,
@@ -159,10 +156,7 @@ public class IndexUpdateTest {
                 .setProperty(REINDEX_PROPERTY_NAME, true);
         NodeState after = builder.getNodeState();
 
-        IndexUpdateProvider p = new IndexUpdateProvider(
-                new PropertyIndexEditorProvider());
-        EditorHook hook = new EditorHook(p);
-        NodeState indexed = hook.processCommit(before, after);
+        NodeState indexed = HOOK.processCommit(before, after);
 
         // first check that the index content nodes exist
         NodeState ns = checkPathExists(indexed, INDEX_DEFINITIONS_NAME,
@@ -183,19 +177,16 @@ public class IndexUpdateTest {
                 "existing", true, false, ImmutableSet.of("foo"), null);
 
         NodeState before = builder.getNodeState();
+        NodeBuilder other = builder.child("test").child("other");
         // Add index definition
         createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo",
                 true, false, ImmutableSet.of("foo"), null);
         createIndexDefinition(
-                builder.child("test").child("other")
-                        .child(INDEX_DEFINITIONS_NAME), "index2", true, false,
+                other.child(INDEX_DEFINITIONS_NAME), "index2", true, false,
                 ImmutableSet.of("foo"), null);
         NodeState after = builder.getNodeState();
 
-        IndexUpdateProvider p = new IndexUpdateProvider(
-                new PropertyIndexEditorProvider());
-        EditorHook hook = new EditorHook(p);
-        NodeState indexed = hook.processCommit(before, after);
+        NodeState indexed = HOOK.processCommit(before, after);
 
         // check that the index content nodes exist
         checkPathExists(indexed, INDEX_DEFINITIONS_NAME, "existing",