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 al...@apache.org on 2012/10/31 11:21:38 UTC

svn commit: r1404071 - 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/apache/...

Author: alexparvulescu
Date: Wed Oct 31 10:21:37 2012
New Revision: 1404071

URL: http://svn.apache.org/viewvc?rev=1404071&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/CompositeNodeStateDiff.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java   (with props)
Modified:
    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/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/property/PropertyIndexHook.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

Added: 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=1404071&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java Wed Oct 31 10:21:37 2012
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+
+public class CompositeNodeStateDiff implements NodeStateDiff {
+
+    private final List<NodeStateDiff> providers;
+
+    public CompositeNodeStateDiff(List<NodeStateDiff> providers) {
+        this.providers = providers;
+    }
+
+    public CompositeNodeStateDiff(NodeStateDiff... providers) {
+        this(Arrays.asList(providers));
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after) {
+        for (NodeStateDiff d : providers) {
+            d.propertyAdded(after);
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) {
+        for (NodeStateDiff d : providers) {
+            d.propertyChanged(before, after);
+        }
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) {
+        for (NodeStateDiff d : providers) {
+            d.propertyDeleted(before);
+        }
+    }
+
+    @Override
+    public void childNodeAdded(String name, NodeState after) {
+        for (NodeStateDiff d : providers) {
+            d.childNodeAdded(name, after);
+        }
+    }
+
+    @Override
+    public void childNodeChanged(String name, NodeState before, NodeState after) {
+        for (NodeStateDiff d : providers) {
+            d.childNodeChanged(name, before, after);
+        }
+    }
+
+    @Override
+    public void childNodeDeleted(String name, NodeState before) {
+        for (NodeStateDiff d : providers) {
+            d.childNodeDeleted(name, before);
+        }
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/CompositeNodeStateDiff.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=1404071&r1=1404070&r2=1404071&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 Wed Oct 31 10:21:37 2012
@@ -16,13 +16,20 @@
  */
 package org.apache.jackrabbit.oak.plugins.index;
 
-import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import java.io.Closeable;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 /**
  * Represents the content of a QueryIndex as well as a mechanism for keeping
  * this content up to date.
  * 
  */
-public interface IndexHook extends CommitHook {
+public interface IndexHook extends Closeable {
+
+    NodeStateDiff preProcess() throws CommitFailedException;
+
+    void postProcess() 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=1404071&r1=1404070&r2=1404071&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 Wed Oct 31 10:21:37 2012
@@ -24,6 +24,8 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_UNKNOWN;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
 
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -64,9 +66,8 @@ public class IndexHookManager implements
 
         // <path>, <builder>
         Map<String, NodeBuilder> allDefs = new HashMap<String, NodeBuilder>();
-
-        IndexDefDiff diff = new IndexDefDiff(builder, allDefs);
-        after.compareAgainstBaseState(before, diff);
+        after.compareAgainstBaseState(before,
+                new IndexDefDiff(builder, allDefs));
 
         // <type, <<path>, <builder>>
         Map<String, Map<String, NodeBuilder>> updates = new HashMap<String, Map<String, NodeBuilder>>();
@@ -86,26 +87,54 @@ public class IndexHookManager implements
         }
 
         // commit
+        List<IndexHook> hooks = new ArrayList<IndexHook>();
+        List<IndexHook> reindexHooks = new ArrayList<IndexHook>();
+
         for (String type : updates.keySet()) {
             Map<String, NodeBuilder> update = updates.get(type);
             for (String path : update.keySet()) {
                 NodeBuilder updateBuiler = update.get(path);
                 boolean reindex = getAndResetReindex(updateBuiler);
-                List<? extends IndexHook> hooks = provider.getIndexHooks(type,
-                        updateBuiler);
-                for (IndexHook hook : hooks) {
-                    if (reindex) {
-                        hook.processCommit(MemoryNodeState.EMPTY_NODE, after);
-                    } else {
-                        hook.processCommit(before, after);
-                    }
+                if (reindex) {
+                    reindexHooks.addAll(provider.getIndexHooks(type,
+                            updateBuiler));
+                } else {
+                    hooks.addAll(provider.getIndexHooks(type, updateBuiler));
                 }
-                
             }
         }
+        processIndexHooks(reindexHooks, MemoryNodeState.EMPTY_NODE, after);
+        processIndexHooks(hooks, before, after);
         return builder.getNodeState();
     }
 
+    private void processIndexHooks(List<IndexHook> hooks, NodeState before,
+            NodeState after) throws CommitFailedException {
+        try {
+
+            List<NodeStateDiff> diffs = new ArrayList<NodeStateDiff>();
+            for (IndexHook hook : hooks) {
+                diffs.add(hook.preProcess());
+            }
+            after.compareAgainstBaseState(before, new CompositeNodeStateDiff(
+                    diffs));
+            for (IndexHook hook : hooks) {
+                hook.postProcess();
+            }
+
+        } finally {
+            for (IndexHook hook : hooks) {
+                try {
+                    hook.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    throw new CommitFailedException(
+                            "Failed to close the index hook", e);
+                }
+            }
+        }
+    }
+
     protected static boolean getAndResetReindex(NodeBuilder builder) {
         boolean isReindex = false;
         PropertyState ps = builder.getProperty(REINDEX_PROPERTY_NAME);

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=1404071&r1=1404070&r2=1404071&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 Wed Oct 31 10:21:37 2012
@@ -16,41 +16,26 @@
  */
 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 static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 
 import java.io.IOException;
 
-import javax.jcr.PropertyType;
-
-import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.spi.commit.CommitHook;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.plugins.index.IndexHook;
 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.analysis.Analyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.document.Document;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.Version;
-import org.apache.tika.Tika;
-import org.apache.tika.exception.TikaException;
 
 /**
  * This class updates a Lucene index when node content is changed.
  */
-class LuceneEditor implements CommitHook, LuceneIndexConstants {
-
-    private static final Tika TIKA = new Tika();
+class LuceneEditor implements IndexHook, LuceneIndexConstants {
 
     private static final Version VERSION = Version.LUCENE_40;
 
@@ -58,6 +43,8 @@ class LuceneEditor implements CommitHook
 
     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();
@@ -76,167 +63,62 @@ class LuceneEditor implements CommitHook
         this.root = root;
     }
 
+    // -----------------------------------------------------< IndexHook >--
+
     @Override
-    public NodeState processCommit(NodeState before, NodeState after)
-            throws CommitFailedException {
-        Directory directory = new ReadWriteOakDirectory(
-                root.child(INDEX_DATA_CHILD_NAME));
+    public NodeStateDiff preProcess() throws CommitFailedException {
         try {
-            IndexWriter writer = new IndexWriter(directory, config);
-            try {
-                LuceneDiff diff = new LuceneDiff(writer, "/");
-                after.compareAgainstBaseState(before, diff);
-                diff.postProcess(after);
-            } finally {
-                writer.close();
-            }
-            return after;
+            IndexWriter writer = new IndexWriter(new ReadWriteOakDirectory(
+                    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 update the full text search index", e);
+                    "Failed to create writer for the full text search index", e);
         }
     }
 
-    private static class LuceneDiff implements NodeStateDiff {
-
-        private final IndexWriter writer;
-
-        private final String path;
-
-        private boolean modified;
-
-        private IOException exception;
-
-        public LuceneDiff(IndexWriter writer, String path) {
-            this.writer = writer;
-            this.path = path;
-        }
-
-        public void postProcess(NodeState state) throws IOException {
-            if (exception != null) {
-                throw exception;
-            }
-            if (modified) {
-                writer.updateDocument(newPathTerm(path),
-                        makeDocument(path, state));
-            }
-        }
-
-        @Override
-        public void propertyAdded(PropertyState after) {
-            modified = true;
-        }
-
-        @Override
-        public void propertyChanged(PropertyState before, PropertyState after) {
-            modified = true;
-        }
-
-        @Override
-        public void propertyDeleted(PropertyState before) {
-            modified = true;
-        }
-
-        @Override
-        public void childNodeAdded(String name, NodeState after) {
-            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());
-            }
-        }
-
-        @Override
-        public void childNodeChanged(String name, NodeState before,
-                NodeState after) {
-            if (NodeStateUtils.isHidden(name)) {
-                return;
-            }
-            if (exception == null) {
-                try {
-                    LuceneDiff diff = new LuceneDiff(writer, concat(path, name));
-                    after.compareAgainstBaseState(before, diff);
-                    diff.postProcess(after);
-                } catch (IOException e) {
-                    exception = e;
-                }
-            }
-        }
-
-        @Override
-        public void childNodeDeleted(String name, NodeState before) {
-            if (NodeStateUtils.isHidden(name)) {
-                return;
-            }
-            if (exception == null) {
-                try {
-                    deleteSubtree(concat(path, name), before);
-                } catch (IOException e) {
-                    exception = 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 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());
-            }
-        }
+    private static boolean isIndexNodeType(PropertyState ps) {
+        return ps != null && !ps.isArray()
+                && ps.getValue(Type.STRING).equals(INDEX_DEFINITIONS_NODE_TYPE);
+    }
 
-        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 boolean isIndexType(PropertyState ps, String type) {
+        return ps != null && !ps.isArray()
+                && ps.getValue(Type.STRING).equals(type);
+    }
 
-        private static String parseStringValue(Blob v) {
-            try {
-                return TIKA.parseToString(v.getNewStream());
-            } catch (IOException e) {
-            } catch (TikaException e) {
-            }
-            return "";
+    @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=1404071&r1=1404070&r2=1404071&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 Wed Oct 31 10:21:37 2012
@@ -16,10 +16,14 @@
  */
 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.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 /**
  * {@link IndexHook} implementation that is responsible for keeping the
@@ -32,15 +36,34 @@ public class LuceneHook implements Index
 
     private final NodeBuilder builder;
 
+    private IndexHook luceneEditor;
+
     public LuceneHook(NodeBuilder builder) {
         this.builder = builder;
     }
 
+    // -----------------------------------------------------< IndexHook >--
+
     @Override
-    public NodeState processCommit(NodeState before, NodeState after)
-            throws CommitFailedException {
-        new LuceneEditor(builder).processCommit(before, after);
-        return after;
+    @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;
+        }
+    }
 }

Added: 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=1404071&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java Wed Oct 31 10:21:37 2012
@@ -0,0 +1,191 @@
+/*
+ * 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 javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.oak.api.Blob;
+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.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();
+
+    private final IndexWriter writer;
+
+    private final NodeBuilder node;
+
+    private final String path;
+
+    private boolean modified;
+
+    private IOException exception;
+
+    public LuceneIndexDiff(IndexWriter writer, NodeBuilder node, String path) {
+        this.writer = writer;
+        this.node = node;
+        this.path = path;
+    }
+
+    public void postProcess() throws IOException {
+        if (exception != null) {
+            throw exception;
+        }
+        if (modified) {
+            writer.updateDocument(newPathTerm(path),
+                    makeDocument(path, node.getNodeState()));
+        }
+    }
+
+    // -----------------------------------------------------< NodeStateDiff >--
+
+    @Override
+    public void propertyAdded(PropertyState after) {
+        modified = true;
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) {
+        modified = true;
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) {
+        modified = true;
+    }
+
+    @Override
+    public void childNodeAdded(String name, NodeState after) {
+        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());
+        }
+    }
+
+    @Override
+    public void childNodeChanged(String name, NodeState before, NodeState after) {
+        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;
+            }
+        }
+    }
+
+    @Override
+    public void childNodeDeleted(String name, NodeState before) {
+        if (NodeStateUtils.isHidden(name)) {
+            return;
+        }
+        if (exception == null) {
+            try {
+                deleteSubtree(concat(path, name), before);
+            } catch (IOException e) {
+                exception = e;
+            }
+        }
+    }
+
+    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());
+        }
+    }
+
+    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 "";
+    }
+
+    // -----------------------------------------------------< Closeable >--
+
+    @Override
+    public void close() throws IOException {
+        writer.close();
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexDiff.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=1404071&r1=1404070&r2=1404071&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 Wed Oct 31 10:21:37 2012
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.property;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -24,7 +25,7 @@ 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.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 import com.google.common.collect.Maps;
 
@@ -37,28 +38,35 @@ import com.google.common.collect.Maps;
  * 
  */
 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 NodeState processCommit(NodeState before, NodeState after)
-            throws CommitFailedException {
-        Map<String, List<PropertyIndexUpdate>> indexes = Maps.newHashMap();
-        PropertyIndexDiff diff = new PropertyIndexDiff(builder, indexes);
-        after.compareAgainstBaseState(before, diff);
+    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();
             }
         }
-
-        return builder.getNodeState();
     }
 
-
+    @Override
+    public void close() throws IOException {
+        indexes = null;
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java?rev=1404071&r1=1404070&r2=1404071&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java Wed Oct 31 10:21:37 2012
@@ -16,12 +16,14 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinitionImpl;
+import org.apache.jackrabbit.oak.plugins.index.IndexHook;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
@@ -40,16 +42,21 @@ public class LuceneIndexTest implements 
         NodeState root = MemoryNodeState.EMPTY_NODE;
 
         NodeBuilder builder = root.builder();
-        NodeBuilder index = builder.child("oak:index").child("lucene");
-        IndexDefinition testDef = new IndexDefinitionImpl("lucene",
-                TYPE_LUCENE, "/oak:index/lucene");
+        builder.child("oak:index").child("lucene")
+                .setProperty(JCR_PRIMARYTYPE, INDEX_DEFINITIONS_NODE_TYPE)
+                .setProperty("type", TYPE_LUCENE);
 
         NodeState before = builder.getNodeState();
         builder.setProperty("foo", "bar");
         NodeState after = builder.getNodeState();
 
-        new LuceneHook(index).processCommit(before, after);
+        IndexHook l = new LuceneHook(builder);
+        after.compareAgainstBaseState(before, l.preProcess());
+        l.postProcess();
+        l.close();
 
+        IndexDefinition testDef = new IndexDefinitionImpl("lucene",
+                TYPE_LUCENE, "/oak:index/lucene");
         QueryIndex queryIndex = new LuceneIndex(testDef);
         FilterImpl filter = new FilterImpl(null);
         filter.restrictPath("/", Filter.PathRestriction.EXACT);

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java?rev=1404071&r1=1404070&r2=1404071&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java Wed Oct 31 10:21:37 2012
@@ -20,6 +20,7 @@ import java.util.Arrays;
 
 import com.google.common.collect.ImmutableSet;
 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;
@@ -62,8 +63,12 @@ public class PropertyIndexTest {
         withoutIndex = System.nanoTime() - withoutIndex;
 
         // ... then see how adding an index affects the code
-        lookup = new PropertyIndexLookup(
-                new PropertyIndexHook(builder).processCommit(before, after));
+        IndexHook p = new PropertyIndexHook(builder);
+        after.compareAgainstBaseState(before, p.preProcess());
+        p.postProcess();
+        p.close();
+
+        lookup = new PropertyIndexLookup(builder.getNodeState());
         long withIndex = System.nanoTime();
         assertEquals(ImmutableSet.of("a", "b"), lookup.find("foo", "abc"));
         assertEquals(ImmutableSet.of("b"), lookup.find("foo", "def"));
@@ -81,7 +86,9 @@ public class PropertyIndexTest {
 
         // Add index definition
         NodeBuilder builder = root.builder();
-        builder.child("oak:index").child("fooIndex").setProperty("propertyNames", Arrays.asList("foo", "extrafoo"), Type.STRINGS);
+        builder.child("oak:index").child("fooIndex")
+                .setProperty("jcr:primaryType", "oak:queryIndexDefinition", Type.NAME)
+                .setProperty("propertyNames", Arrays.asList("foo", "extrafoo"), Type.STRINGS);
         NodeState before = builder.getNodeState();
 
         // Add some content and process it through the property index hook
@@ -106,8 +113,12 @@ public class PropertyIndexTest {
         withoutIndex = System.nanoTime() - withoutIndex;
 
         // ... then see how adding an index affects the code
-        lookup = new PropertyIndexLookup(
-                new PropertyIndexHook(builder).processCommit(before, after));
+        IndexHook p = new PropertyIndexHook(builder);
+        after.compareAgainstBaseState(before, p.preProcess());
+        p.postProcess();
+        p.close();
+
+        lookup = new PropertyIndexLookup(builder.getNodeState());
         long withIndex = System.nanoTime();
         assertEquals(ImmutableSet.of("a", "b"), lookup.find("foo", "abc"));
         assertEquals(ImmutableSet.of("b"), lookup.find("foo", "def"));