jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From alexparvule...@apache.org
Subject svn commit: r1407425 [1/2] - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/index/ main/java/org/apache/jackrabbit/oak/plugins/index/lucene/ main/java/org/apache/jackrabbit/oak/plugins/index/property/ test/java/org/a...
Date Fri, 09 Nov 2012 11:26:35 GMT
Author: alexparvulescu
Date: Fri Nov  9 11:26:30 2012
New Revision: 1407425

URL: http://svn.apache.org/viewvc?rev=1407425&view=rev
Log:
OAK-394 IndexManagerHook to manage existing indexes

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/HierarchicalNodeStateDiff.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiff.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiffTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexHookProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHookProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexQueryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java Fri Nov  9 11:26:30 2012
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-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);
-        }
-    }
-}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/HierarchicalNodeStateDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/HierarchicalNodeStateDiff.java?rev=1407425&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/HierarchicalNodeStateDiff.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/HierarchicalNodeStateDiff.java Fri Nov  9 11:26:30 2012
@@ -0,0 +1,32 @@
+/*
+ * 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 org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+
+public interface HierarchicalNodeStateDiff<T extends HierarchicalNodeStateDiff<T>>
+        extends NodeStateDiff {
+
+    /**
+     * Generates a child HierarchicalNodeStateDiff, based on the current
+     * NodeStateDiff
+     */
+    T child(String name);
+
+    String getPath();
+
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/HierarchicalNodeStateDiff.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/HierarchicalNodeStateDiff.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHook.java Fri Nov  9 11:26:30 2012
@@ -19,17 +19,21 @@ package org.apache.jackrabbit.oak.plugin
 import java.io.Closeable;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 
 /**
  * Represents the content of a QueryIndex as well as a mechanism for keeping
  * this content up to date.
  * 
  */
-public interface IndexHook extends Closeable {
+public interface IndexHook extends HierarchicalNodeStateDiff<IndexHook>,
+        Closeable {
 
-    NodeStateDiff preProcess() throws CommitFailedException;
+    /**
+     * Applies the changes to the index content
+     */
+    void apply() throws CommitFailedException;
 
-    void postProcess() throws CommitFailedException;
+    void reindex(NodeBuilder state) throws CommitFailedException;
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManager.java Fri Nov  9 11:26:30 2012
@@ -16,29 +16,15 @@
  */
 package org.apache.jackrabbit.oak.plugins.index;
 
-import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
-import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
-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.TYPE_PROPERTY_NAME;
-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;
 
 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.memory.MemoryNodeState;
 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 org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
 
 /**
  * Keeps existing IndexHooks updated.
@@ -64,186 +50,38 @@ public class IndexHookManager implements
             throws CommitFailedException {
         NodeBuilder builder = after.builder();
 
-        // <path>, <builder>
-        Map<String, NodeBuilder> allDefs = new HashMap<String, NodeBuilder>();
-        after.compareAgainstBaseState(before,
-                new IndexDefDiff(builder, allDefs));
-
-        // <type, <<path>, <builder>>
-        Map<String, Map<String, NodeBuilder>> updates = new HashMap<String, Map<String, NodeBuilder>>();
-        for (String def : allDefs.keySet()) {
-            NodeBuilder cb = allDefs.get(def);
-            String type = TYPE_UNKNOWN;
-            PropertyState typePS = cb.getProperty(TYPE_PROPERTY_NAME);
-            if (typePS != null && !typePS.isArray()) {
-                type = typePS.getValue(Type.STRING);
-            }
-            Map<String, NodeBuilder> defs = updates.get(type);
-            if (defs == null) {
-                defs = new HashMap<String, NodeBuilder>();
-                updates.put(type, defs);
-            }
-            defs.put(def, cb);
-        }
-
-        // 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);
-                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);
+        // <type, <path, indexhook>>
+        Map<String, Map<String, List<IndexHook>>> updates = new HashMap<String, Map<String, List<IndexHook>>>();
+        after.compareAgainstBaseState(before, new IndexHookManagerDiff(
+                provider, builder, updates));
+        apply(updates);
         return builder.getNodeState();
     }
 
-    private void processIndexHooks(List<IndexHook> hooks, NodeState before,
-            NodeState after) throws CommitFailedException {
+    private void apply(Map<String, Map<String, List<IndexHook>>> updates)
+            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();
-            }
-
-        } finally {
-            for (IndexHook hook : hooks) {
-                try {
-                    hook.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    throw new CommitFailedException(
-                            "Failed to close the index hook", e);
+            for (String type : updates.keySet()) {
+                for (List<IndexHook> hooks : updates.get(type).values()) {
+                    for (IndexHook hook : hooks) {
+                        hook.apply();
+                    }
                 }
             }
-        }
-    }
-
-    protected static boolean getAndResetReindex(NodeBuilder builder) {
-        boolean isReindex = false;
-        PropertyState ps = builder.getProperty(REINDEX_PROPERTY_NAME);
-        if (ps != null) {
-            isReindex = ps.getValue(Type.BOOLEAN);
-            builder.setProperty(REINDEX_PROPERTY_NAME, false);
-        }
-        return isReindex;
-    }
-
-    protected static class IndexDefDiff implements NodeStateDiff {
-
-        private final IndexDefDiff parent;
-
-        private final NodeBuilder node;
-
-        private final String name;
-
-        private String path;
-
-        private final Map<String, NodeBuilder> updates;
-
-        public IndexDefDiff(IndexDefDiff parent, NodeBuilder node, String name,
-                String path, Map<String, NodeBuilder> updates) {
-            this.parent = parent;
-            this.node = node;
-            this.name = name;
-            this.path = path;
-            this.updates = updates;
-
-            if (node != null
-                    && isIndexNodeType(node.getProperty(JCR_PRIMARYTYPE))) {
-                node.setProperty(REINDEX_PROPERTY_NAME, true);
-                this.updates.put(getPath(), node);
-            }
-
-            if (node != null && node.hasChildNode(INDEX_DEFINITIONS_NAME)) {
-                NodeBuilder index = node.child(INDEX_DEFINITIONS_NAME);
-                for (String indexName : index.getChildNodeNames()) {
-                    String indexPath = concat(getPath(),
-                            INDEX_DEFINITIONS_NAME, indexName);
-                    NodeBuilder indexChild = index.child(indexName);
-                    if (isIndexNodeType(indexChild.getProperty(JCR_PRIMARYTYPE))) {
-                        this.updates.put(indexPath, indexChild);
+        } finally {
+            for (String type : updates.keySet()) {
+                for (List<IndexHook> hooks : updates.get(type).values()) {
+                    for (IndexHook hook : hooks) {
+                        try {
+                            hook.close();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                            throw new CommitFailedException(
+                                    "Failed to close the index hook", e);
+                        }
                     }
                 }
             }
         }
-
-        public IndexDefDiff(NodeBuilder root, Map<String, NodeBuilder> updates) {
-            this(null, root, null, "/", updates);
-        }
-
-        public IndexDefDiff(IndexDefDiff parent, String name) {
-            this(parent, getChildNode(parent.node, name), name, null,
-                    parent.updates);
-        }
-
-        private static NodeBuilder getChildNode(NodeBuilder node, String name) {
-            if (node != null && node.hasChildNode(name)) {
-                return node.child(name);
-            } else {
-                return null;
-            }
-        }
-
-        private String getPath() {
-            if (path == null) { // => parent != null
-                path = concat(parent.getPath(), name);
-            }
-            return path;
-        }
-
-        @Override
-        public void propertyAdded(PropertyState after) {
-        }
-
-        @Override
-        public void propertyChanged(PropertyState before, PropertyState after) {
-        }
-
-        @Override
-        public void propertyDeleted(PropertyState before) {
-        }
-
-        @Override
-        public void childNodeAdded(String name, NodeState after) {
-            childNodeChanged(name, MemoryNodeState.EMPTY_NODE, after);
-        }
-
-        @Override
-        public void childNodeChanged(String name, NodeState before,
-                NodeState after) {
-            if (NodeStateUtils.isHidden(name)) {
-                return;
-            }
-            after.compareAgainstBaseState(before, new IndexDefDiff(this, name));
-        }
-
-        @Override
-        public void childNodeDeleted(String name, NodeState before) {
-        }
-
-        private boolean isIndexNodeType(PropertyState ps) {
-            return ps != null
-                    && !ps.isArray()
-                    && ps.getValue(Type.STRING).equals(
-                            INDEX_DEFINITIONS_NODE_TYPE);
-        }
-
     }
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiff.java?rev=1407425&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiff.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiff.java Fri Nov  9 11:26:30 2012
@@ -0,0 +1,324 @@
+/*
+ * 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 static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+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.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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+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.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
+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 org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Acts as a composite NodeStateDiff, it delegates all the diff's events to the
+ * existing IndexHooks.
+ * 
+ * This allows for a simultaneous update of all the indexes via a single
+ * traversal of the changes.
+ * 
+ * 
+ */
+class IndexHookManagerDiff implements NodeStateDiff {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(IndexHookManagerDiff.class);
+
+    private final IndexHookProvider provider;
+
+    private final IndexHookManagerDiff parent;
+
+    private final NodeBuilder node;
+
+    private final String name;
+
+    private String path;
+
+    /**
+     * <type, <path, indexhook>>
+     */
+    private final Map<String, Map<String, List<IndexHook>>> updates;
+
+    private IndexHookManagerDiff(IndexHookProvider provider,
+            IndexHookManagerDiff parent, NodeBuilder node, String name,
+            String path, Map<String, Map<String, List<IndexHook>>> updates)
+            throws CommitFailedException {
+        this.provider = provider;
+        this.parent = parent;
+        this.node = node;
+        this.name = name;
+        this.path = path;
+        this.updates = updates;
+
+        if (node != null && isIndexNodeType(node.getProperty(JCR_PRIMARYTYPE))) {
+            // to prevent double-reindex we only call reindex if:
+            // - the flag exists and is set to true
+            // OR
+            // - the flag does not exist
+            boolean reindex = node.getProperty(REINDEX_PROPERTY_NAME) == null
+                    || node.getProperty(REINDEX_PROPERTY_NAME).getValue(
+                            Type.BOOLEAN);
+            if (reindex) {
+                node.setProperty(REINDEX_PROPERTY_NAME, true);
+                String type = TYPE_UNKNOWN;
+                PropertyState typePS = node.getProperty(TYPE_PROPERTY_NAME);
+                if (typePS != null && !typePS.isArray()) {
+                    type = typePS.getValue(Type.STRING);
+                }
+                // TODO this is kinda fragile
+                NodeBuilder rebuildBuilder = parent.parent.node;
+                String relativePath = PathUtils.getAncestorPath(getPath(), 2);
+                // find parent index by type and trigger a full reindex
+                List<IndexHook> indexes = getIndexesWithRelativePaths(
+                        relativePath, getIndexes(type));
+                for (IndexHook ih : indexes) {
+                    ih.reindex(rebuildBuilder);
+                }
+            }
+        }
+
+        if (node != null && node.hasChildNode(INDEX_DEFINITIONS_NAME)) {
+            Set<String> existingTypes = new HashSet<String>();
+            Set<String> reindexTypes = new HashSet<String>();
+
+            NodeBuilder index = node.child(INDEX_DEFINITIONS_NAME);
+            for (String indexName : index.getChildNodeNames()) {
+                NodeBuilder indexChild = index.child(indexName);
+                if (isIndexNodeType(indexChild.getProperty(JCR_PRIMARYTYPE))) {
+                    // this reindex should only happen when the flag is set
+                    // before the index impl is online
+                    boolean reindex = indexChild
+                            .getProperty(REINDEX_PROPERTY_NAME) != null
+                            && indexChild.getProperty(REINDEX_PROPERTY_NAME)
+                                    .getValue(Type.BOOLEAN);
+                    String type = TYPE_UNKNOWN;
+                    PropertyState typePS = indexChild
+                            .getProperty(TYPE_PROPERTY_NAME);
+                    if (typePS != null && !typePS.isArray()) {
+                        type = typePS.getValue(Type.STRING);
+                    }
+                    if (reindex) {
+                        reindexTypes.add(type);
+                    }
+                    existingTypes.add(type);
+                }
+            }
+            existingTypes.remove(TYPE_UNKNOWN);
+            reindexTypes.remove(TYPE_UNKNOWN);
+            for (String type : existingTypes) {
+                Map<String, List<IndexHook>> byType = this.updates.get(type);
+                if (byType == null) {
+                    byType = new TreeMap<String, List<IndexHook>>();
+                    this.updates.put(type, byType);
+                }
+                List<IndexHook> hooks = byType.get(getPath());
+                if (hooks == null) {
+                    hooks = Lists.newArrayList();
+                    byType.put(getPath(), hooks);
+                }
+                if (reindexTypes.contains(type)) {
+                    for (IndexHook ih : provider.getIndexHooks(type, node)) {
+                        ih.reindex(node);
+                        // TODO proper cleanup of resources in the case of an
+                        // exception?
+                        hooks.add(ih);
+                    }
+                } else {
+                    hooks.addAll(provider.getIndexHooks(type, node));
+                }
+            }
+        }
+    }
+
+    private IndexHookManagerDiff(IndexHookProvider provider,
+            IndexHookManagerDiff parent, String name)
+            throws CommitFailedException {
+        this(provider, parent, getChildNode(parent.node, name), name, null,
+                parent.updates);
+    }
+
+    public IndexHookManagerDiff(IndexHookProvider provider, NodeBuilder root,
+            Map<String, Map<String, List<IndexHook>>> updates)
+            throws CommitFailedException {
+        this(provider, null, root, null, "/", updates);
+    }
+
+    private static NodeBuilder getChildNode(NodeBuilder node, String name) {
+        if (node != null && node.hasChildNode(name)) {
+            return node.child(name);
+        } else {
+            return null;
+        }
+    }
+
+    private String getPath() {
+        if (path == null) { // => parent != null
+            path = concat(parent.getPath(), name);
+        }
+        return path;
+    }
+
+    /**
+     * 
+     * Returns IndexHooks of all types that have the best match (are situated
+     * the closest on the hierarchy) for the given path.
+     * 
+     */
+    private Map<String, List<IndexHook>> getIndexes() {
+        Map<String, List<IndexHook>> hooks = new HashMap<String, List<IndexHook>>();
+        for (String type : this.updates.keySet()) {
+            Map<String, List<IndexHook>> newIndexes = getIndexes(type);
+            for (String key : newIndexes.keySet()) {
+                if (hooks.containsKey(key)) {
+                    hooks.get(key).addAll(newIndexes.get(key));
+                } else {
+                    hooks.put(key, newIndexes.get(key));
+                }
+            }
+        }
+        return hooks;
+    }
+
+    /**
+     * 
+     * Returns IndexHooks of the given type that have the best match (are
+     * situated the closest on the hierarchy) for the current path.
+     * 
+     */
+    private Map<String, List<IndexHook>> getIndexes(String type) {
+        Map<String, List<IndexHook>> hooks = new HashMap<String, List<IndexHook>>();
+        Map<String, List<IndexHook>> indexes = this.updates.get(type);
+        if (indexes != null && !indexes.isEmpty()) {
+            Iterator<String> iterator = indexes.keySet().iterator();
+            String bestMatch = iterator.next();
+            while (iterator.hasNext()) {
+                String key = iterator.next();
+                if (PathUtils.isAncestor(key, getPath())) {
+                    bestMatch = key;
+                } else {
+                    break;
+                }
+            }
+            List<IndexHook> existing = hooks.get(bestMatch);
+            if (existing == null) {
+                existing = new ArrayList<IndexHook>();
+                hooks.put(bestMatch, existing);
+            }
+            existing.addAll(indexes.get(bestMatch));
+        }
+        return hooks;
+    }
+
+    /**
+     * Fixes the relative paths on the best matching indexes so updates apply
+     * properly
+     */
+    private List<IndexHook> getIndexesWithRelativePaths(String path,
+            Map<String, List<IndexHook>> bestMatches) {
+        List<IndexHook> hooks = new ArrayList<IndexHook>();
+        for (String relativePath : bestMatches.keySet()) {
+            for (IndexHook update : bestMatches.get(relativePath)) {
+                IndexHook u = update;
+                String downPath = path.substring(relativePath.length());
+                for (String p : PathUtils.elements(downPath)) {
+                    u = u.child(p);
+                }
+                hooks.add(u);
+            }
+        }
+        return hooks;
+    }
+
+    private static boolean isIndexNodeType(PropertyState ps) {
+        return ps != null && !ps.isArray()
+                && ps.getValue(Type.STRING).equals(INDEX_DEFINITIONS_NODE_TYPE);
+    }
+
+    // -----------------------------------------------------< NodeStateDiff >--
+
+    @Override
+    public void propertyAdded(PropertyState after) {
+        for (IndexHook update : getIndexesWithRelativePaths(getPath(),
+                getIndexes())) {
+            update.propertyAdded(after);
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) {
+        for (IndexHook update : getIndexesWithRelativePaths(getPath(),
+                getIndexes())) {
+            update.propertyChanged(before, after);
+        }
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) {
+        for (IndexHook update : getIndexesWithRelativePaths(getPath(),
+                getIndexes())) {
+            update.propertyDeleted(before);
+        }
+    }
+
+    @Override
+    public void childNodeAdded(String name, NodeState after) {
+        childNodeChanged(name, MemoryNodeState.EMPTY_NODE, after);
+    }
+
+    @Override
+    public void childNodeChanged(String name, NodeState before, NodeState after) {
+        if (NodeStateUtils.isHidden(name)) {
+            return;
+        }
+        getIndexesWithRelativePaths(concat(getPath(), name), getIndexes());
+
+        try {
+            after.compareAgainstBaseState(before, new IndexHookManagerDiff(
+                    provider, this, name));
+        } catch (CommitFailedException e) {
+            LOG.error(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public void childNodeDeleted(String name, NodeState before) {
+        childNodeChanged(name, before, MemoryNodeState.EMPTY_NODE);
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiff.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookManagerDiff.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java Fri Nov  9 11:26:30 2012
@@ -35,11 +35,19 @@ public interface IndexHookProvider {
      * <code>type</code> param is of an unknown value, the provider is expected
      * to return an empty list.
      * 
+     * <p>
+     * The <code>builder</code> must point to the repository content node, not
+     * the index content node. Each <code>IndexHook</code> implementation will
+     * have to drill down to its specific index content, and possibly deal with
+     * multiple indexes of the same type.
+     * </p>
+     * 
      * @param type
      *            the index type
      * @param builder
-     *            the node state builder that will be used for updates
-     * @return a list of index hooks
+     *            the node state builder of the content node that will be used
+     *            for updates
+     * @return a list of index hooks of the given type
      */
     @Nonnull
     List<? extends IndexHook> getIndexHooks(String type, NodeBuilder builder);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java Fri Nov  9 11:26:30 2012
@@ -1,124 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.jackrabbit.oak.plugins.index.lucene;
-
-import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
-
-import java.io.IOException;
-
-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.IndexHook;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.util.Version;
-
-/**
- * This class updates a Lucene index when node content is changed.
- */
-class LuceneEditor implements IndexHook, LuceneIndexConstants {
-
-    private static final Version VERSION = Version.LUCENE_40;
-
-    private static final Analyzer ANALYZER = new StandardAnalyzer(VERSION);
-
-    private static final IndexWriterConfig config = getIndexWriterConfig();
-
-    private LuceneIndexDiff diff;
-
-    private static IndexWriterConfig getIndexWriterConfig() {
-        // FIXME: Hack needed to make Lucene work in an OSGi environment
-        Thread thread = Thread.currentThread();
-        ClassLoader loader = thread.getContextClassLoader();
-        thread.setContextClassLoader(IndexWriterConfig.class.getClassLoader());
-        try {
-            return new IndexWriterConfig(VERSION, ANALYZER);
-        } finally {
-            thread.setContextClassLoader(loader);
-        }
-    }
-
-    private final NodeBuilder root;
-
-    public LuceneEditor(NodeBuilder root) {
-        this.root = root;
-    }
-
-    // -----------------------------------------------------< IndexHook >--
-
-    @Override
-    public NodeStateDiff preProcess() throws CommitFailedException {
-        try {
-            IndexWriter writer = new IndexWriter(new ReadWriteOakDirectory(
-                    getIndexNode(root).child(INDEX_DATA_CHILD_NAME)), config);
-            diff = new LuceneIndexDiff(writer, root, "/");
-            return diff;
-        } catch (IOException e) {
-            e.printStackTrace();
-            throw new CommitFailedException(
-                    "Failed to create writer for the full text search index", e);
-        }
-    }
-
-    private static NodeBuilder getIndexNode(NodeBuilder node) {
-        if (node != null && node.hasChildNode(INDEX_DEFINITIONS_NAME)) {
-            NodeBuilder index = node.child(INDEX_DEFINITIONS_NAME);
-            for (String indexName : index.getChildNodeNames()) {
-                NodeBuilder child = index.child(indexName);
-                if (isIndexNodeType(child.getProperty(JCR_PRIMARYTYPE))
-                        && isIndexType(child.getProperty(TYPE_PROPERTY_NAME),
-                                TYPE_LUCENE)) {
-                    return child;
-                }
-            }
-        }
-        // did not find a proper child, will use root directly
-        return node;
-    }
-
-    private static boolean isIndexNodeType(PropertyState ps) {
-        return ps != null && !ps.isArray()
-                && ps.getValue(Type.STRING).equals(INDEX_DEFINITIONS_NODE_TYPE);
-    }
-
-    private static boolean isIndexType(PropertyState ps, String type) {
-        return ps != null && !ps.isArray()
-                && ps.getValue(Type.STRING).equals(type);
-    }
-
-    @Override
-    public void postProcess() throws CommitFailedException {
-        try {
-            diff.postProcess();
-        } catch (IOException e) {
-            e.printStackTrace();
-            throw new CommitFailedException(
-                    "Failed to update the full text search index", e);
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        diff.close();
-        diff = null;
-    }
-}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneHook.java Fri Nov  9 11:26:30 2012
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-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.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
-
-/**
- * {@link IndexHook} implementation that is responsible for keeping the
- * {@link LuceneIndex} up to date
- * 
- * @see LuceneIndex
- * 
- */
-public class LuceneHook implements IndexHook {
-
-    private final NodeBuilder builder;
-
-    private IndexHook luceneEditor;
-
-    public LuceneHook(NodeBuilder builder) {
-        this.builder = builder;
-    }
-
-    // -----------------------------------------------------< IndexHook >--
-
-    @Override
-    @Nonnull
-    public NodeStateDiff preProcess() throws CommitFailedException {
-        luceneEditor = new LuceneEditor(builder);
-        return luceneEditor.preProcess();
-    }
-
-    @Override
-    public void postProcess() throws CommitFailedException {
-        luceneEditor.postProcess();
-    }
-
-    @Override
-    public void close() throws IOException {
-        try {
-            if (luceneEditor != null) {
-                luceneEditor.close();
-            }
-        } finally {
-            luceneEditor = null;
-        }
-    }
-}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java Fri Nov  9 11:26:30 2012
@@ -20,7 +20,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import javax.annotation.CheckForNull;
 import javax.jcr.RepositoryException;
@@ -35,11 +34,10 @@ import org.apache.jackrabbit.oak.core.Re
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
 import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
-import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Cursors;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
-import org.apache.jackrabbit.oak.spi.query.IndexRow;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -137,7 +135,7 @@ public class LuceneIndex implements Quer
         }
         if (!builder.hasChildNode(INDEX_DATA_CHILD_NAME)) {
             // index not initialized yet
-            return new PathCursor(Collections.<String> emptySet());
+            return Cursors.newPathCursor(Collections.<String> emptySet());
         }
         builder = builder.child(INDEX_DATA_CHILD_NAME);
 
@@ -167,7 +165,7 @@ public class LuceneIndex implements Quer
                     }
                     LOG.debug("query via {} took {} ms.", this,
                             System.currentTimeMillis() - s);
-                    return new PathCursor(paths);
+                    return Cursors.newPathCursor(paths);
                 } finally {
                     reader.close();
                 }
@@ -176,7 +174,7 @@ public class LuceneIndex implements Quer
             }
         } catch (IOException e) {
             e.printStackTrace();
-            return new PathCursor(Collections.<String> emptySet());
+            return Cursors.newPathCursor(Collections.<String> emptySet());
         }
     }
 
@@ -202,7 +200,7 @@ public class LuceneIndex implements Quer
             qs.add(new PrefixQuery(newPathTerm(path)));
             break;
         case DIRECT_CHILDREN:
-            // FIXME
+            // FIXME OAK-420
             if (!path.endsWith("/")) {
                 path += "/";
             }
@@ -358,38 +356,6 @@ public class LuceneIndex implements Quer
         return new TermQuery(new Term(name, type.getName()));
     }
 
-    /**
-     * A cursor over the resulting paths.
-     */
-    private static class PathCursor implements Cursor {
-
-        private final Iterator<String> iterator;
-
-        private String path;
-
-        public PathCursor(Collection<String> paths) {
-            this.iterator = paths.iterator();
-        }
-
-        @Override
-        public boolean next() {
-            if (iterator.hasNext()) {
-                path = iterator.next();
-                return true;
-            } else {
-                path = null;
-                return false;
-            }
-        }
-
-        @Override
-        public IndexRow currentRow() {
-            // TODO support jcr:score and possibly rep:exceprt
-            return new IndexRowImpl(path);
-        }
-
-    }
-
     @Override
     public String toString() {
         return "LuceneIndex [index=" + index + "]";

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java Fri Nov  9 11:26:30 2012
@@ -17,6 +17,10 @@
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.util.Version;
+import org.apache.tika.Tika;
 
 public interface LuceneIndexConstants extends IndexConstants {
 
@@ -24,4 +28,10 @@ public interface LuceneIndexConstants ex
 
     String INDEX_DATA_CHILD_NAME = ":data";
 
+    Version VERSION = Version.LUCENE_40;
+
+    Analyzer ANALYZER = new StandardAnalyzer(VERSION);
+
+    Tika TIKA = new Tika();
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java Fri Nov  9 11:26:30 2012
@@ -16,74 +16,124 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
-import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newPathField;
-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 java.util.HashMap;
+import java.util.Map;
 
-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.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.plugins.index.IndexHook;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
 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 org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.tika.Tika;
-import org.apache.tika.exception.TikaException;
-
-public class LuceneIndexDiff implements NodeStateDiff, Closeable {
 
-    private static final Tika TIKA = new Tika();
+/**
+ * {@link IndexHook} implementation that is responsible for keeping the
+ * {@link LuceneIndex} up to date
+ * 
+ * @see LuceneIndex
+ * 
+ */
+public class LuceneIndexDiff implements IndexHook, LuceneIndexConstants {
 
-    private final IndexWriter writer;
+    private final LuceneIndexDiff parent;
 
     private final NodeBuilder node;
 
-    private final String path;
+    private final String name;
 
-    private boolean modified;
+    private String path;
 
-    private IOException exception;
+    private final Map<String, LuceneIndexUpdate> updates;
 
-    public LuceneIndexDiff(IndexWriter writer, NodeBuilder node, String path) {
-        this.writer = writer;
+    private LuceneIndexDiff(LuceneIndexDiff parent, NodeBuilder node,
+            String name, String path, Map<String, LuceneIndexUpdate> updates) {
+        this.parent = parent;
         this.node = node;
+        this.name = name;
         this.path = path;
+        this.updates = updates;
+
+        if (node != null && node.hasChildNode(INDEX_DEFINITIONS_NAME)) {
+            NodeBuilder index = node.child(INDEX_DEFINITIONS_NAME);
+            for (String indexName : index.getChildNodeNames()) {
+                NodeBuilder child = index.child(indexName);
+                if (isIndexNode(child) && !this.updates.containsKey(getPath())) {
+                    this.updates.put(getPath(), new LuceneIndexUpdate(
+                            getPath(), child));
+                }
+            }
+        }
+        if (node != null && name != null && !NodeStateUtils.isHidden(name)) {
+            for (LuceneIndexUpdate update : updates.values()) {
+                update.insert(getPath(), node);
+            }
+        }
+    }
+
+    private LuceneIndexDiff(LuceneIndexDiff parent, String name) {
+        this(parent, getChildNode(parent.node, name), name, null,
+                parent.updates);
     }
 
-    public void postProcess() throws IOException {
-        if (exception != null) {
-            throw exception;
-        }
-        if (modified) {
-            writer.updateDocument(newPathTerm(path),
-                    makeDocument(path, node.getNodeState()));
+    public LuceneIndexDiff(NodeBuilder root) {
+        this(null, root, null, "/", new HashMap<String, LuceneIndexUpdate>());
+    }
+
+    private static NodeBuilder getChildNode(NodeBuilder node, String name) {
+        if (node != null && node.hasChildNode(name)) {
+            return node.child(name);
+        } else {
+            return null;
+        }
+    }
+
+    public String getPath() {
+        if (path == null) { // => parent != null
+            path = concat(parent.getPath(), name);
         }
+        return path;
+    }
+
+    private static boolean isIndexNode(NodeBuilder node) {
+        PropertyState ps = node.getProperty(JCR_PRIMARYTYPE);
+        boolean isNodeType = ps != null && !ps.isArray()
+                && ps.getValue(Type.STRING).equals(INDEX_DEFINITIONS_NODE_TYPE);
+        if (!isNodeType) {
+            return false;
+        }
+        PropertyState type = node.getProperty(TYPE_PROPERTY_NAME);
+        boolean isIndexType = type != null && !type.isArray()
+                && type.getValue(Type.STRING).equals(TYPE_LUCENE);
+        return isIndexType;
     }
 
     // -----------------------------------------------------< NodeStateDiff >--
 
     @Override
     public void propertyAdded(PropertyState after) {
-        modified = true;
+        for (LuceneIndexUpdate update : updates.values()) {
+            update.insert(getPath(), node);
+        }
     }
 
     @Override
     public void propertyChanged(PropertyState before, PropertyState after) {
-        modified = true;
+        for (LuceneIndexUpdate update : updates.values()) {
+            update.insert(getPath(), node);
+        }
     }
 
     @Override
     public void propertyDeleted(PropertyState before) {
-        modified = true;
+        for (LuceneIndexUpdate update : updates.values()) {
+            update.insert(getPath(), node);
+        }
     }
 
     @Override
@@ -91,22 +141,8 @@ public class LuceneIndexDiff implements 
         if (NodeStateUtils.isHidden(name)) {
             return;
         }
-        if (exception == null) {
-            try {
-                addSubtree(concat(path, name), after);
-            } catch (IOException e) {
-                exception = e;
-            }
-        }
-    }
-
-    private void addSubtree(String path, NodeState state) throws IOException {
-        writer.updateDocument(newPathTerm(path), makeDocument(path, state));
-        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
-            if (NodeStateUtils.isHidden(entry.getName())) {
-                continue;
-            }
-            addSubtree(concat(path, entry.getName()), entry.getNodeState());
+        for (LuceneIndexUpdate update : updates.values()) {
+            update.insert(concat(getPath(), name), node);
         }
     }
 
@@ -115,16 +151,7 @@ public class LuceneIndexDiff implements 
         if (NodeStateUtils.isHidden(name)) {
             return;
         }
-        if (exception == null && node.hasChildNode(name)) {
-            LuceneIndexDiff diff = new LuceneIndexDiff(writer,
-                    node.child(name), concat(path, name));
-            after.compareAgainstBaseState(before, diff);
-            try {
-                diff.postProcess();
-            } catch (IOException e) {
-                exception = e;
-            }
-        }
+        after.compareAgainstBaseState(before, child(name));
     }
 
     @Override
@@ -132,60 +159,45 @@ public class LuceneIndexDiff implements 
         if (NodeStateUtils.isHidden(name)) {
             return;
         }
-        if (exception == null) {
-            try {
-                deleteSubtree(concat(path, name), before);
-            } catch (IOException e) {
-                exception = e;
-            }
+        for (LuceneIndexUpdate update : updates.values()) {
+            update.remove(concat(getPath(), name));
         }
     }
 
-    private void deleteSubtree(String path, NodeState state) throws IOException {
-        writer.deleteDocuments(newPathTerm(path));
-        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
-            if (NodeStateUtils.isHidden(entry.getName())) {
-                continue;
-            }
-            deleteSubtree(concat(path, entry.getName()), entry.getNodeState());
+    // -----------------------------------------------------< IndexHook >--
+
+    @Override
+    public void apply() throws CommitFailedException {
+        for (LuceneIndexUpdate update : updates.values()) {
+            update.apply();
         }
     }
 
-    private static Document makeDocument(String path, NodeState state) {
-        Document document = new Document();
-        document.add(newPathField(path));
-        for (PropertyState property : state.getProperties()) {
-            String pname = property.getName();
-            switch (property.getType().tag()) {
-            case PropertyType.BINARY:
-                for (Blob v : property.getValue(Type.BINARIES)) {
-                    document.add(newPropertyField(pname, parseStringValue(v)));
-                }
-                break;
-            default:
-                for (String v : property.getValue(Type.STRINGS)) {
-                    document.add(newPropertyField(pname, v));
-                }
-                break;
+    @Override
+    public void reindex(NodeBuilder state) throws CommitFailedException {
+        boolean reindex = false;
+        for (LuceneIndexUpdate update : updates.values()) {
+            if (update.getAndResetReindexFlag()) {
+                reindex = true;
             }
         }
-        return document;
-    }
-
-    private static String parseStringValue(Blob v) {
-        try {
-            return TIKA.parseToString(v.getNewStream());
-        } catch (IOException e) {
-        } catch (TikaException e) {
+        if (reindex) {
+            state.getNodeState().compareAgainstBaseState(
+                    MemoryNodeState.EMPTY_NODE,
+                    new LuceneIndexDiff(null, state, null, "/", updates));
         }
-        return "";
     }
 
-    // -----------------------------------------------------< Closeable >--
+    @Override
+    public IndexHook child(String name) {
+        return new LuceneIndexDiff(this, name);
+    }
 
     @Override
     public void close() throws IOException {
-        writer.close();
+        for (LuceneIndexUpdate update : updates.values()) {
+            update.close();
+        }
+        updates.clear();
     }
-
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexHookProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexHookProvider.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexHookProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexHookProvider.java Fri Nov  9 11:26:30 2012
@@ -29,9 +29,9 @@ import org.apache.jackrabbit.oak.spi.sta
 import com.google.common.collect.ImmutableList;
 
 /**
- * Service that provides Lucene based IndexHooks
+ * Service that provides Lucene based {@link IndexHook}s
  * 
- * @see LuceneHook
+ * @see LuceneIndexDiff
  * @see IndexHookProvider
  * 
  */
@@ -43,7 +43,7 @@ public class LuceneIndexHookProvider imp
     public List<? extends IndexHook> getIndexHooks(String type,
             NodeBuilder builder) {
         if (TYPE_LUCENE.equals(type)) {
-            return ImmutableList.of(new LuceneHook(builder));
+            return ImmutableList.of(new LuceneIndexDiff(builder));
         }
         return ImmutableList.of();
     }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java?rev=1407425&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java Fri Nov  9 11:26:30 2012
@@ -0,0 +1,196 @@
+/*
+ * 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.lucene;
+
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newPathField;
+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 java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+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.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.tika.exception.TikaException;
+
+import com.google.common.base.Preconditions;
+
+class LuceneIndexUpdate implements Closeable, LuceneIndexConstants {
+
+    private static IndexWriterConfig getIndexWriterConfig() {
+        // FIXME: Hack needed to make Lucene work in an OSGi environment
+        Thread thread = Thread.currentThread();
+        ClassLoader loader = thread.getContextClassLoader();
+        thread.setContextClassLoader(IndexWriterConfig.class.getClassLoader());
+        try {
+            return new IndexWriterConfig(VERSION, ANALYZER);
+        } finally {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    private static final IndexWriterConfig config = getIndexWriterConfig();
+
+    private final String path;
+
+    private final NodeBuilder index;
+
+    private final Map<String, NodeState> insert = new TreeMap<String, NodeState>();
+
+    private final Set<String> remove = new TreeSet<String>();
+
+    public LuceneIndexUpdate(String path, NodeBuilder index) {
+        this.path = path;
+        this.index = index;
+    }
+
+    public void insert(String path, NodeBuilder value) {
+        Preconditions.checkArgument(path.startsWith(this.path));
+        if (!insert.containsKey(path)) {
+            String key = path.substring(this.path.length());
+            if ("".equals(key)) {
+                key = "/";
+            }
+            // null value can come from a deleted node, followed by a deleted
+            // property event which would trigger an update on the previously
+            // deleted node
+            if (value != null) {
+                insert.put(key, value.getNodeState());
+            }
+        }
+    }
+
+    public void remove(String path) {
+        Preconditions.checkArgument(path.startsWith(this.path));
+        remove.add(path.substring(this.path.length()));
+    }
+
+    boolean getAndResetReindexFlag() {
+        boolean reindex = index.getProperty(REINDEX_PROPERTY_NAME) != null
+                && index.getProperty(REINDEX_PROPERTY_NAME).getValue(
+                        Type.BOOLEAN);
+        index.setProperty(REINDEX_PROPERTY_NAME, false);
+        return reindex;
+    }
+
+    public void apply() throws CommitFailedException {
+        if(remove.isEmpty() && insert.isEmpty()){
+            return;
+        }
+        IndexWriter writer = null;
+        try {
+            writer = new IndexWriter(new ReadWriteOakDirectory(
+                    index.child(INDEX_DATA_CHILD_NAME)), config);
+            for (String p : remove) {
+                deleteSubtreeWriter(writer, p);
+            }
+            for (String p : insert.keySet()) {
+                NodeState ns = insert.get(p);
+                addSubtreeWriter(writer, p, ns);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new CommitFailedException(
+                    "Failed to update the full text search index", e);
+        } finally {
+            remove.clear();
+            insert.clear();
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (IOException e) {
+                    //
+                }
+            }
+        }
+    }
+
+    private void deleteSubtreeWriter(IndexWriter writer, String path)
+            throws IOException {
+        // TODO verify the removal of the entire sub-hierarchy
+         writer.deleteDocuments(newPathTerm(path));
+        if (!path.endsWith("/")) {
+            path += "/";
+        }
+        writer.deleteDocuments(new PrefixQuery(newPathTerm(path)));
+    }
+
+    private void addSubtreeWriter(IndexWriter writer, String path,
+            NodeState state) throws IOException {
+        writer.updateDocument(newPathTerm(path), makeDocument(path, state));
+        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+            if (NodeStateUtils.isHidden(entry.getName())) {
+                continue;
+            }
+            addSubtreeWriter(writer, concat(path, entry.getName()),
+                    entry.getNodeState());
+        }
+    }
+
+    private static Document makeDocument(String path, NodeState state) {
+        Document document = new Document();
+        document.add(newPathField(path));
+        for (PropertyState property : state.getProperties()) {
+            String pname = property.getName();
+            switch (property.getType().tag()) {
+            case PropertyType.BINARY:
+                for (Blob v : property.getValue(Type.BINARIES)) {
+                    document.add(newPropertyField(pname, parseStringValue(v)));
+                }
+                break;
+            default:
+                for (String v : property.getValue(Type.STRINGS)) {
+                    document.add(newPropertyField(pname, v));
+                }
+                break;
+            }
+        }
+        return document;
+    }
+
+    private static String parseStringValue(Blob v) {
+        try {
+            return TIKA.parseToString(v.getNewStream());
+        } catch (IOException e) {
+        } catch (TikaException e) {
+        }
+        return "";
+    }
+
+    @Override
+    public void close() throws IOException {
+        remove.clear();
+        insert.clear();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexUpdate.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java Fri Nov  9 11:26:30 2012
@@ -39,6 +39,9 @@ public final class TermFactory {
      * @return the path term.
      */
     public static Term newPathTerm(String path) {
+        if (!"/".equals(path) && path.startsWith("/")) {
+            path = path.substring(1);
+        }
         return new Term(FieldNames.PATH, path);
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java Fri Nov  9 11:26:30 2012
@@ -79,6 +79,8 @@ import com.google.common.collect.Sets;
  */
 public class PropertyIndex implements QueryIndex {
 
+    public static final String TYPE = "property";
+
     private static final int MAX_STRING_LENGTH = 100; // TODO: configurable
 
     static List<String> encode(PropertyValue value) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java Fri Nov  9 11:26:30 2012
@@ -17,31 +17,37 @@
 package org.apache.jackrabbit.oak.plugins.index.property;
 
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 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.commons.PathUtils.concat;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.TYPE;
 
+import java.io.IOException;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+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.IndexHook;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
 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 org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
 
 /**
- * {@link NodeStateDiff} implementation that extracts changes for the
- * {@link PropertyIndexHook} to be applied on the {@link PropertyIndex}
+ * {@link IndexHook} implementation that is responsible for keeping the
+ * {@link PropertyIndex} up to date
  * 
- * @see PropertyIndexHook
+ * @see PropertyIndex
+ * @see PropertyIndexLookup
  * 
  */
-class PropertyIndexDiff implements NodeStateDiff {
+class PropertyIndexDiff implements IndexHook {
 
     private final PropertyIndexDiff parent;
 
@@ -63,27 +69,27 @@ class PropertyIndexDiff implements NodeS
         this.path = path;
         this.updates = updates;
 
-        if (node != null && isIndexNodeType(node.getProperty(JCR_PRIMARYTYPE))) {
-            update(node, name);
-        }
         if (node != null && node.hasChildNode(INDEX_DEFINITIONS_NAME)) {
             NodeBuilder index = node.child(INDEX_DEFINITIONS_NAME);
             for (String indexName : index.getChildNodeNames()) {
-                update(index.child(indexName), indexName);
+                NodeBuilder child = index.child(indexName);
+                if (isIndexNode(child)) {
+                    update(child, indexName);
+                }
             }
         }
     }
 
-    public PropertyIndexDiff(
-            NodeBuilder root, Map<String, List<PropertyIndexUpdate>> updates) {
-        this(null, root, null, "/", updates);
-    }
-
-    public PropertyIndexDiff(PropertyIndexDiff parent, String name) {
+    private PropertyIndexDiff(PropertyIndexDiff parent, String name) {
         this(parent, getChildNode(parent.node, name),
                 name, null, parent.updates);
     }
 
+    public PropertyIndexDiff(NodeBuilder root) {
+        this(null, root, null, "/",
+                new HashMap<String, List<PropertyIndexUpdate>>());
+    }
+
     private static NodeBuilder getChildNode(NodeBuilder node, String name) {
         if (node != null && node.hasChildNode(name)) {
             return node.child(name);
@@ -92,7 +98,7 @@ class PropertyIndexDiff implements NodeS
         }
     }
 
-    private String getPath() {
+    public String getPath() {
         if (path == null) { // => parent != null
             path = concat(parent.getPath(), name);
         }
@@ -118,13 +124,30 @@ class PropertyIndexDiff implements NodeS
                 list = Lists.newArrayList();
                 this.updates.put(pname, list);
             }
-            list.add(new PropertyIndexUpdate(getPath(), builder));
+            boolean exists = false;
+            for (PropertyIndexUpdate piu : list) {
+                if (piu.getPath().equals(getPath())) {
+                    exists = true;
+                    break;
+                }
+            }
+            if (!exists) {
+                list.add(new PropertyIndexUpdate(getPath(), builder));
+            }
         }
     }
 
-    private boolean isIndexNodeType(PropertyState ps) {
-        return ps != null && !ps.isArray()
+    private static boolean isIndexNode(NodeBuilder node) {
+        PropertyState ps = node.getProperty(JCR_PRIMARYTYPE);
+        boolean isNodeType = ps != null && !ps.isArray()
                 && ps.getValue(Type.STRING).equals(INDEX_DEFINITIONS_NODE_TYPE);
+        if (!isNodeType) {
+            return false;
+        }
+        PropertyState type = node.getProperty(TYPE_PROPERTY_NAME);
+        boolean isIndexType = type != null && !type.isArray()
+                && type.getValue(Type.STRING).equals(TYPE);
+        return isIndexType;
     }
 
     //-----------------------------------------------------< NodeStateDiff >--
@@ -160,7 +183,7 @@ class PropertyIndexDiff implements NodeS
     public void childNodeChanged(
             String name, NodeState before, NodeState after) {
         if (!NodeStateUtils.isHidden(name)) {
-            after.compareAgainstBaseState(before, new PropertyIndexDiff(this, name));
+            after.compareAgainstBaseState(before, child(name));
         }
     }
 
@@ -169,4 +192,41 @@ class PropertyIndexDiff implements NodeS
         childNodeChanged(name, before, MemoryNodeState.EMPTY_NODE);
     }
 
+    // -----------------------------------------------------< IndexHook >--
+
+    @Override
+    public void apply() throws CommitFailedException {
+        for (List<PropertyIndexUpdate> updateList : updates.values()) {
+            for (PropertyIndexUpdate update : updateList) {
+                update.apply();
+            }
+        }
+    }
+
+    @Override
+    public void reindex(NodeBuilder state) throws CommitFailedException {
+        boolean reindex = false;
+        for (List<PropertyIndexUpdate> updateList : updates.values()) {
+            for (PropertyIndexUpdate update : updateList) {
+                if (update.getAndResetReindexFlag()) {
+                    reindex = true;
+                }
+            }
+        }
+        if (reindex) {
+            state.getNodeState().compareAgainstBaseState(
+                    MemoryNodeState.EMPTY_NODE,
+                    new PropertyIndexDiff(null, state, null, "/", updates));
+        }
+    }
+
+    @Override
+    public IndexHook child(String name) {
+        return new PropertyIndexDiff(this, name);
+    }
+
+    @Override
+    public void close() throws IOException {
+        updates.clear();
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHook.java Fri Nov  9 11:26:30 2012
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.jackrabbit.oak.plugins.index.property;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-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.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
-
-import com.google.common.collect.Maps;
-
-/**
- * {@link IndexHook} implementation that is responsible for keeping the
- * {@link PropertyIndex} up to date
- * 
- * @see PropertyIndex
- * @see PropertyIndexLookup
- * 
- */
-public class PropertyIndexHook implements IndexHook {
-
-    private final NodeBuilder builder;
-
-    private Map<String, List<PropertyIndexUpdate>> indexes;
-
-    public PropertyIndexHook(NodeBuilder builder) {
-        this.builder = builder;
-    }
-
-
-    // -----------------------------------------------------< IndexHook >--
-
-    @Override @Nonnull
-    public NodeStateDiff preProcess() {
-        indexes = Maps.newHashMap();
-        return new PropertyIndexDiff(builder, indexes);
-    }
-
-    @Override
-    public void postProcess() 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

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHookProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHookProvider.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHookProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexHookProvider.java Fri Nov  9 11:26:30 2012
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.property;
 
+import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.TYPE;
+
 import java.util.List;
 
 import org.apache.felix.scr.annotations.Component;
@@ -29,7 +31,7 @@ import com.google.common.collect.Immutab
 /**
  * Service that provides PropertyIndex based IndexHooks
  * 
- * @see PropertyIndexHook
+ * @see PropertyIndexDiff
  * @see IndexHookProvider
  * 
  */
@@ -37,13 +39,11 @@ import com.google.common.collect.Immutab
 @Service(IndexHookProvider.class)
 public class PropertyIndexHookProvider implements IndexHookProvider {
 
-    private static final String TYPE = "property";
-
     @Override
     public List<? extends IndexHook> getIndexHooks(String type,
             NodeBuilder builder) {
         if (TYPE.equals(type)) {
-            return ImmutableList.of(new PropertyIndexHook(builder));
+            return ImmutableList.of(new PropertyIndexDiff(builder));
         }
         return ImmutableList.of();
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java?rev=1407425&r1=1407424&r2=1407425&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java Fri Nov  9 11:26:30 2012
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.property;
 
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -54,6 +56,10 @@ class PropertyIndexUpdate {
         this.remove = Maps.newHashMap();
     }
 
+    String getPath() {
+        return path;
+    }
+
     public void insert(String path, PropertyState value) {
         Preconditions.checkArgument(path.startsWith(this.path));
         putValues(insert, path.substring(this.path.length()), value);
@@ -79,6 +85,14 @@ class PropertyIndexUpdate {
         }
     }
 
+    boolean getAndResetReindexFlag() {
+        boolean reindex = node.getProperty(REINDEX_PROPERTY_NAME) != null
+                && node.getProperty(REINDEX_PROPERTY_NAME).getValue(
+                        Type.BOOLEAN);
+        node.setProperty(REINDEX_PROPERTY_NAME, false);
+        return reindex;
+    }
+
     public void apply() throws CommitFailedException {
         boolean unique = node.getProperty("unique") != null
                 && node.getProperty("unique").getValue(Type.BOOLEAN);



Mime
View raw message