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 to...@apache.org on 2018/09/25 12:24:18 UTC

svn commit: r1841926 [10/14] - in /jackrabbit/oak/trunk: oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/ oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/util/ oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/sca...

Added: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor2Test.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor2Test.java?rev=1841926&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor2Test.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor2Test.java Tue Sep 25 12:24:15 2018
@@ -0,0 +1,353 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.IndexCommitCallback;
+import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.plugins.index.IndexingContext;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditor;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorContext;
+import org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil;
+import org.apache.jackrabbit.oak.plugins.index.lucene.util.IndexDefinitionBuilder;
+import org.apache.jackrabbit.oak.plugins.index.lucene.writer.LuceneIndexWriter;
+import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.search.PropertyDefinition;
+import org.apache.jackrabbit.oak.plugins.index.search.PropertyUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.search.spi.editor.FulltextIndexWriterFactory;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.lucene.index.IndexableField;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.oak.InitialContentHelper.INITIAL_CONTENT;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.TYPE_LUCENE;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class LuceneIndexEditor2Test {
+
+    private NodeState root = INITIAL_CONTENT;
+    private NodeState before = root;
+    private IndexUpdateCallback updateCallback = mock(IndexUpdateCallback.class);
+    private ExtractedTextCache extractedTextCache = new ExtractedTextCache(0, 0);
+    private TestIndexingContext indexingContext = new TestIndexingContext();
+    private TestWriterFactory writerFactory = new TestWriterFactory();
+    private TestPropertyUpdateCallback propCallback = new TestPropertyUpdateCallback();
+    private TestWriter writer = new TestWriter();
+    private String indexPath = "/oak:index/fooIndex";
+
+    @Test
+    public void basics() throws Exception{
+        IndexDefinitionBuilder defnb = new IndexDefinitionBuilder();
+        defnb.indexRule("nt:base").property("foo").propertyIndex();
+
+        NodeState defnState = defnb.build();
+        IndexDefinition defn = new IndexDefinition(root, defnState, indexPath);
+        LuceneIndexEditorContext ctx = newContext(defnState.builder(), defn, true);
+        EditorHook hook = createHook(ctx);
+
+        updateBefore(defnb);
+        NodeBuilder builder = before.builder();
+        builder.child("a").setProperty("foo", "bar");
+
+        hook.processCommit(root, builder.getNodeState(), CommitInfo.EMPTY);
+
+        assertThat(writer.docs.keySet(), containsInAnyOrder("/a"));
+    }
+
+    @Test
+    public void simplePropertyUpdateCallback() throws Exception{
+        IndexDefinitionBuilder defnb = new IndexDefinitionBuilder();
+        defnb.indexRule("nt:base").property("foo").propertyIndex();
+
+        NodeState defnState = defnb.build();
+        IndexDefinition defn = new IndexDefinition(root, defnState, indexPath);
+        LuceneIndexEditorContext ctx = newContext(defnState.builder(), defn, true);
+        ctx.setPropertyUpdateCallback(propCallback);
+
+        EditorHook hook = createHook(ctx);
+
+        updateBefore(defnb);
+
+        //Property added
+        NodeBuilder builder = before.builder();
+        builder.child("a").setProperty("foo", "bar");
+        builder.child("a").setProperty("foo2", "bar");
+        builder.child("a").child("b");
+
+        before = hook.processCommit(root, builder.getNodeState(), CommitInfo.EMPTY);
+        propCallback.state.assertState("/a", "foo", UpdateState.ADDED);
+        assertEquals(1, propCallback.invocationCount);
+        assertEquals(1, propCallback.doneInvocationCount);
+        propCallback.reset();
+
+        //Property updated
+        builder = before.builder();
+        builder.child("a").setProperty("foo", "bar2");
+        builder.child("a").setProperty("foo2", "bar2");
+        before = hook.processCommit(before, builder.getNodeState(), CommitInfo.EMPTY);
+
+        propCallback.state.assertState("/a", "foo", UpdateState.UPDATED);
+
+        assertEquals(1, propCallback.invocationCount);
+        propCallback.reset();
+
+        //Property deleted
+        builder = before.builder();
+        builder.child("a").removeProperty("foo");
+        builder.child("a").removeProperty("foo2");
+        before = hook.processCommit(before, builder.getNodeState(), CommitInfo.EMPTY);
+
+        propCallback.state.assertState("/a", "foo", UpdateState.DELETED);
+        assertEquals(1, propCallback.invocationCount);
+        propCallback.reset();
+    }
+
+    @Test
+    public void relativeProperties() throws Exception{
+        IndexDefinitionBuilder defnb = new IndexDefinitionBuilder();
+        defnb.indexRule("nt:base").property("jcr:content/metadata/foo").propertyIndex();
+        defnb.aggregateRule("nt:base").include("*");
+
+        NodeState defnState = defnb.build();
+        IndexDefinition defn = new IndexDefinition(root, defnState, indexPath);
+        LuceneIndexEditorContext ctx = newContext(defnState.builder(), defn, true);
+        ctx.setPropertyUpdateCallback(propCallback);
+
+        EditorHook hook = createHook(ctx);
+
+        updateBefore(defnb);
+
+        //Property added
+        NodeBuilder builder = before.builder();
+        builder.child("a").child("jcr:content").child("metadata").setProperty("foo", "bar");
+        builder.child("a").setProperty("foo2", "bar");
+
+        before = hook.processCommit(root, builder.getNodeState(), CommitInfo.EMPTY);
+        propCallback.state.assertState("/a", "jcr:content/metadata/foo", UpdateState.ADDED);
+        assertEquals(1, propCallback.invocationCount);
+        propCallback.reset();
+
+        //Property updated
+        builder = before.builder();
+        builder.child("a").child("jcr:content").child("metadata").setProperty("foo", "bar2");
+        builder.child("a").setProperty("foo2", "bar2");
+        before = hook.processCommit(before, builder.getNodeState(), CommitInfo.EMPTY);
+
+        propCallback.state.assertState("/a", "jcr:content/metadata/foo", UpdateState.UPDATED);
+
+        assertEquals(1, propCallback.invocationCount);
+        propCallback.reset();
+
+        //Property deleted
+        builder = before.builder();
+        builder.child("a").child("jcr:content").child("metadata").removeProperty("foo");
+        builder.child("a").removeProperty("foo2");
+        before = hook.processCommit(before, builder.getNodeState(), CommitInfo.EMPTY);
+
+        propCallback.state.assertState("/a", "jcr:content/metadata/foo", UpdateState.DELETED);
+        assertEquals(1, propCallback.invocationCount);
+        propCallback.reset();
+    }
+
+    private void updateBefore(IndexDefinitionBuilder defnb) {
+        NodeBuilder builder = before.builder();
+        NodeBuilder cb = TestUtil.child(builder, PathUtils.getParentPath(indexPath));
+        cb.setChildNode(PathUtils.getName(indexPath), defnb.build());
+        before = builder.getNodeState();
+    }
+
+    private EditorHook createHook(LuceneIndexEditorContext context) {
+        IndexEditorProvider provider = new IndexEditorProvider() {
+            @Nullable
+            @Override
+            public Editor getIndexEditor(@NotNull String type, @NotNull NodeBuilder definition,
+                                         @NotNull NodeState root, @NotNull IndexUpdateCallback callback)
+                    throws CommitFailedException {
+                if (TYPE_LUCENE.equals(type)) {
+                    return new LuceneIndexEditor(context);
+                }
+                return null;
+            }
+        };
+
+        String async = context.isAsyncIndexing() ? "async" : null;
+        IndexUpdateProvider updateProvider = new IndexUpdateProvider(provider, async, false);
+        return new EditorHook(updateProvider);
+    }
+
+    private LuceneIndexEditorContext newContext(NodeBuilder defnBuilder, IndexDefinition defn, boolean asyncIndex) {
+        return new LuceneIndexEditorContext(root, defnBuilder, defn, updateCallback, writerFactory,
+                extractedTextCache, null, indexingContext, asyncIndex);
+    }
+
+
+    private static class TestPropertyUpdateCallback implements PropertyUpdateCallback {
+        int invocationCount;
+        CallbackState state;
+        int doneInvocationCount;
+
+        @Override
+        public void propertyUpdated(String nodePath, String propertyRelativePath, PropertyDefinition pd,
+                                    PropertyState before, PropertyState after) {
+            assertNotNull(nodePath);
+            assertNotNull(propertyRelativePath);
+            assertNotNull(pd);
+
+            if (before == null && after == null) {
+                fail("Both states cannot be null at same time");
+            }
+
+            state = new CallbackState(nodePath, propertyRelativePath, pd, before, after);
+            invocationCount++;
+        }
+
+        @Override
+        public void done() throws CommitFailedException {
+            doneInvocationCount++;
+        }
+
+        void reset(){
+            state = null;
+            invocationCount = 0;
+            doneInvocationCount = 0;
+        }
+    }
+
+    enum UpdateState {ADDED, UPDATED, DELETED}
+
+    private static class CallbackState {
+        final String nodePath;
+        final String propertyPath;
+        final PropertyDefinition pd;
+        final PropertyState before;
+        final PropertyState after;
+
+
+        public CallbackState(String nodePath, String propertyPath, PropertyDefinition pd,
+                             PropertyState before, PropertyState after) {
+            this.nodePath = nodePath;
+            this.propertyPath = propertyPath;
+            this.pd = pd;
+            this.before = before;
+            this.after = after;
+        }
+
+        public void assertState(String expectedPath, String expectedName, UpdateState us) {
+            assertEquals(expectedPath, nodePath);
+            assertEquals(expectedName, propertyPath);
+
+            switch (us) {
+                case ADDED: assertNotNull(after); assertNull(before); break;
+                case UPDATED: assertNotNull(after); assertNotNull(before); break;
+                case DELETED: assertNull(after); assertNotNull(before); break;
+            }
+        }
+    }
+
+
+    private class TestWriterFactory implements FulltextIndexWriterFactory {
+        @Override
+        public LuceneIndexWriter newInstance(IndexDefinition definition,
+                                             NodeBuilder definitionBuilder, boolean reindex) {
+            return writer;
+        }
+    }
+
+    private static class TestWriter implements LuceneIndexWriter {
+        Set<String> deletedPaths = new HashSet<>();
+        Map<String, Iterable<? extends IndexableField>> docs = new HashMap<>();
+        boolean closed;
+
+        @Override
+        public void updateDocument(String path, Iterable<? extends IndexableField> doc) throws IOException {
+            docs.put(path, doc);
+        }
+
+        @Override
+        public void deleteDocuments(String path) throws IOException {
+            deletedPaths.add(path);
+        }
+
+        @Override
+        public boolean close(long timestamp) throws IOException {
+            closed = true;
+            return true;
+        }
+    }
+
+    private class TestIndexingContext implements IndexingContext {
+        CommitInfo info = CommitInfo.EMPTY;
+        boolean reindexing;
+        boolean async;
+
+        @Override
+        public String getIndexPath() {
+            return indexPath;
+        }
+
+        @Override
+        public CommitInfo getCommitInfo() {
+            return info;
+        }
+
+        @Override
+        public boolean isReindexing() {
+            return reindexing;
+        }
+
+        @Override
+        public boolean isAsync() {
+            return async;
+        }
+
+        @Override
+        public void indexUpdateFailed(Exception e) {
+
+        }
+
+        @Override
+        public void registerIndexCommitCallback(IndexCommitCallback callback) {
+
+        }
+    }
+}

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

Added: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProviderTest.java?rev=1841926&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProviderTest.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProviderTest.java Tue Sep 25 12:24:15 2018
@@ -0,0 +1,194 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.plugins.index.ContextAwareCallback;
+import org.apache.jackrabbit.oak.plugins.index.IndexCommitCallback;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.IndexingContext;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditor;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorContext;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil;
+import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.DocumentQueue;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
+import org.apache.jackrabbit.oak.spi.commit.CommitContext;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.SimpleCommitContext;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.oak.InitialContentHelper.INITIAL_CONTENT;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.TYPE_LUCENE;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLucenePropertyIndexDefinition;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class LuceneIndexEditorProviderTest {
+    private NodeState root = INITIAL_CONTENT;
+    private NodeBuilder builder = root.builder();
+
+    @Test
+    public void readOnlyBuilderUsedForSync() throws Exception {
+        LuceneIndexEditorProvider editorProvider = new LuceneIndexEditorProvider(null,
+                null,
+                null,
+                null,
+                Mounts.defaultMountInfoProvider());
+        editorProvider.setIndexingQueue(mock(DocumentQueue.class));
+
+        IndexUpdateCallback callback = new TestCallback("/oak:index/fooIndex", newCommitInfo(), false, false);
+        NodeBuilder defnBuilder = createIndexDefinition("fooIndex").builder();
+        Editor editor = editorProvider.getIndexEditor(TYPE_LUCENE, defnBuilder, root, callback);
+        LuceneIndexEditor luceneEditor = (LuceneIndexEditor) editor;
+
+        NodeBuilder builderFromContext =
+                (NodeBuilder) FieldUtils.readField(luceneEditor.getContext(), "definitionBuilder", true);
+
+        try {
+            builderFromContext.setProperty("foo", "bar");
+            fail("Should have been read only builder");
+        } catch (UnsupportedOperationException ignore) {
+
+        }
+    }
+
+    @Test
+    public void reuseOldIndexDefinition() throws Exception{
+        IndexTracker tracker = mock(IndexTracker.class);
+        LuceneIndexEditorProvider editorProvider = new LuceneIndexEditorProvider(null,
+                tracker,
+                null,
+                null,
+                Mounts.defaultMountInfoProvider());
+        editorProvider.setIndexingQueue(mock(DocumentQueue.class));
+        //Set up a different IndexDefinition which needs to be returned
+        //from tracker with a marker property
+        NodeBuilder testBuilder = createIndexDefinition("fooIndex").builder();
+        testBuilder.setProperty("foo", "bar");
+        LuceneIndexDefinition defn = new LuceneIndexDefinition(root, testBuilder.getNodeState(), "/foo");
+        when(tracker.getIndexDefinition("/oak:index/fooIndex")).thenReturn(defn);
+
+        IndexUpdateCallback callback = new TestCallback("/oak:index/fooIndex", newCommitInfo(), false, false);
+        NodeBuilder defnBuilder = createIndexDefinition("fooIndex").builder();
+        Editor editor = editorProvider.getIndexEditor(TYPE_LUCENE, defnBuilder, root, callback);
+        LuceneIndexEditor luceneEditor = (LuceneIndexEditor) editor;
+        LuceneIndexEditorContext context = luceneEditor.getContext();
+
+        //Definition should reflect the marker property
+        assertEquals("bar", context.getDefinition().getDefinitionNodeState().getString("foo"));
+    }
+
+    @Test
+    public void editorNullInCaseOfReindex() throws Exception{
+        LuceneIndexEditorProvider editorProvider = new LuceneIndexEditorProvider(null,
+                null,
+                null,
+                null,
+                Mounts.defaultMountInfoProvider());
+        editorProvider.setIndexingQueue(mock(DocumentQueue.class));
+        IndexUpdateCallback callback = new TestCallback("/oak:index/fooIndex", newCommitInfo(), true, false);
+        NodeBuilder defnBuilder = createIndexDefinition("fooIndex").builder();
+        Editor editor = editorProvider.getIndexEditor(TYPE_LUCENE, defnBuilder, root, callback);
+        assertNull(editor);
+    }
+
+    private NodeState createIndexDefinition(String idxName) {
+        NodeBuilder idx = newLucenePropertyIndexDefinition(builder.child("oak:index"),
+                idxName, ImmutableSet.of("foo"), "async");
+        TestUtil.enableIndexingMode(idx, FulltextIndexConstants.IndexingMode.NRT);
+        LuceneIndexEditorContext.configureUniqueId(idx);
+        LuceneIndexDefinition.updateDefinition(idx);
+        return idx.getNodeState();
+    }
+
+    private CommitInfo newCommitInfo() {
+        CommitInfo info = new CommitInfo("admin", "s1",
+                ImmutableMap.<String, Object>of(CommitContext.NAME, new SimpleCommitContext()));
+        return info;
+    }
+
+    private static class TestCallback implements IndexUpdateCallback, IndexingContext, ContextAwareCallback {
+        private final String indexPath;
+        private final CommitInfo commitInfo;
+        private final boolean reindexing;
+        private final boolean async;
+
+        private TestCallback(String indexPath, CommitInfo commitInfo, boolean reindexing, boolean async) {
+            this.indexPath = indexPath;
+            this.commitInfo = commitInfo;
+            this.reindexing = reindexing;
+            this.async = async;
+        }
+
+        @Override
+        public String getIndexPath() {
+            return indexPath;
+        }
+
+        @Override
+        public CommitInfo getCommitInfo() {
+            return commitInfo;
+        }
+
+        @Override
+        public boolean isReindexing() {
+            return reindexing;
+        }
+
+        @Override
+        public boolean isAsync() {
+            return async;
+        }
+
+        @Override
+        public void indexUpdateFailed(Exception e) {
+
+        }
+
+        @Override
+        public void indexUpdate() throws CommitFailedException {
+
+        }
+
+        @Override
+        public IndexingContext getIndexingContext() {
+            return this;
+        }
+
+        @Override
+        public void registerIndexCommitCallback(IndexCommitCallback callback) {
+
+        }
+    }
+
+}
\ No newline at end of file

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

Added: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java?rev=1841926&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java Tue Sep 25 12:24:15 2018
@@ -0,0 +1,631 @@
+/*
+ * 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.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.commons.CIHelper;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.CachingFileDataStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreUtils;
+import org.apache.jackrabbit.oak.plugins.index.CompositeIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
+import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
+import org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexCopier;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexNode;
+import org.apache.jackrabbit.oak.plugins.index.lucene.directory.OakDirectory;
+import org.apache.jackrabbit.oak.plugins.index.lucene.writer.MultiplexersLucene;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
+import org.apache.jackrabbit.oak.plugins.index.search.FieldNames;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexFormatVersion;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+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.jackrabbit.test.ISO8601;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.queryparser.classic.QueryParser;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.Directory;
+import org.jetbrains.annotations.NotNull;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static javax.jcr.PropertyType.TYPENAME_STRING;
+import static org.apache.jackrabbit.oak.InitialContentHelper.INITIAL_CONTENT;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.newLuceneIndexDefinitionV2;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLuceneIndexDefinition;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INCLUDE_PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+
+@RunWith(Parameterized.class)
+public class LuceneIndexEditorTest {
+    private EditorHook HOOK;
+
+    private NodeState root = INITIAL_CONTENT;
+
+    private NodeBuilder builder = root.builder();
+
+    private IndexTracker tracker = new IndexTracker();
+
+    private LuceneIndexNode indexNode;
+
+    @Rule
+    public final TemporaryFolder temporaryFolder = new TemporaryFolder(new File("target"));
+
+    @Parameterized.Parameter
+    public boolean useBlobStore;
+
+    @Parameterized.Parameters(name = "{index}: useBlobStore ({0})")
+    public static List<Boolean[]> fixtures() {
+        return ImmutableList.of(new Boolean[] {true}, new Boolean[] {false});
+    }
+
+    @Before
+    public void setup() throws Exception {
+        if (useBlobStore) {
+            LuceneIndexEditorProvider provider = new LuceneIndexEditorProvider();
+            CachingFileDataStore ds = DataStoreUtils
+                .createCachingFDS(temporaryFolder.newFolder().getAbsolutePath(),
+                    temporaryFolder.newFolder().getAbsolutePath());
+            provider.setBlobStore(new DataStoreBlobStore(ds));
+            HOOK = new EditorHook(new IndexUpdateProvider(provider));
+        } else {
+            HOOK = new EditorHook(new IndexUpdateProvider(new LuceneIndexEditorProvider()));
+        }
+    }
+
+    @Test
+    public void testLuceneWithFullText() throws Exception {
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder idxnb = newLuceneIndexDefinitionV2(index, "lucene",
+                of(TYPENAME_STRING));
+        LuceneIndexDefinition defn = new LuceneIndexDefinition(root, idxnb.getNodeState(), "/foo");
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+        builder.child("test").setProperty("price", 100);
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        //system fields starts with ':' so need to be escaped
+        assertEquals("/test", query(escape(FieldNames.createAnalyzedFieldName("foo"))+":fox", defn));
+        assertNull("Non string properties not indexed by default",
+                getPath(NumericRangeQuery.newLongRange("price", 100L, 100L, true, true)));
+    }
+
+    @Test
+    public void noChangeIfNonIndexedDelete() throws Exception{
+        NodeState before = builder.getNodeState();
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene", of(TYPENAME_STRING));
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of("foo"), STRINGS));
+
+
+        builder.child("test").setProperty("foo", "bar");
+        builder.child("test").child("a");
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+        assertEquals("/test", getPath(new TermQuery(new Term("foo", "bar"))));
+
+        NodeState luceneIdxState1 = NodeStateUtils.getNode(indexed, "/oak:index/lucene");
+
+        before = indexed;
+        builder = indexed.builder();
+        builder.getChildNode("test").getChildNode("a").remove();
+        after = builder.getNodeState();
+        indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+
+        NodeState luceneIdxState2 = NodeStateUtils.getNode(indexed, "/oak:index/lucene");
+        assertEquals(luceneIdxState1, luceneIdxState2);
+    }
+
+    private String escape(String name) {
+        return name.replace(":", "\\:");
+    }
+
+    @Test
+    public void testLuceneWithNonFullText() throws Exception {
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene",
+                of(TYPENAME_STRING));
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of("foo", "price", "weight", "bool", "creationTime"), STRINGS));
+        LuceneIndexDefinition defn = new LuceneIndexDefinition(root, nb.getNodeState(), "/foo");
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+        builder.child("test").setProperty("bar", "kite is flying");
+        builder.child("test").setProperty("price", 100);
+        builder.child("test").setProperty("weight", 10.0);
+        builder.child("test").setProperty("bool", true);
+        builder.child("test").setProperty("truth", true);
+        builder.child("test").setProperty("creationTime", createCal("05/06/2014"));
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        assertNull("Fulltext search should not work", query("foo:fox",defn));
+        assertEquals("/test", getPath(new TermQuery(new Term("foo", "fox is jumping"))));
+        assertNull("bar must NOT be indexed", getPath(new TermQuery(new Term("bar", "kite is flying"))));
+
+        //Long
+        assertEquals("/test", getPath(NumericRangeQuery.newDoubleRange("weight", 8D, 12D, true, true)));
+
+        //Double
+        assertEquals("/test", getPath(NumericRangeQuery.newLongRange("price", 100L, 100L, true, true)));
+
+        //Boolean
+        assertEquals("/test", getPath(new TermQuery(new Term("bool", "true"))));
+        assertNull("truth must NOT be indexed", getPath(new TermQuery(new Term("truth", "true"))));
+
+        //Date
+        assertEquals("/test", getPath(NumericRangeQuery.newLongRange("creationTime",
+                dateToTime("05/05/2014"), dateToTime("05/07/2014"), true, true)));
+    }
+
+    @Test
+    public void noOfDocsIndexedNonFullText() throws Exception {
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene",
+                of(TYPENAME_STRING));
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of("foo"), STRINGS));
+
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+        builder.child("test2").setProperty("bar", "kite is flying");
+        builder.child("test3").setProperty("foo", "wind is blowing");
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        assertEquals(2, getSearcher().getIndexReader().numDocs());
+    }
+
+    @Test
+    public void saveDirectoryListing() throws Exception {
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene",
+            of(TYPENAME_STRING));
+        nb.setProperty(LuceneIndexConstants.SAVE_DIR_LISTING, true);
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of("foo"), STRINGS));
+
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        NodeState dir = indexed.getChildNode("oak:index").getChildNode("lucene").getChildNode(":data");
+        assertTrue(dir.hasProperty(OakDirectory.PROP_DIR_LISTING));
+    }
+
+    /**
+     * 1. Index property foo in /test
+     * 2. Then modify some other property in /test
+     *
+     * This should not cause the index to be updated
+     */
+    @Test
+    public void nonIncludedPropertyChange() throws Exception {
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene",
+                of(TYPENAME_STRING));
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of("foo"),
+                STRINGS));
+
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+        builder.child("test2").setProperty("foo", "bird is chirping");
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        assertEquals(2, getSearcher().getIndexReader().numDocs());
+
+        assertEquals("/test", getPath(new TermQuery(new Term("foo", "fox is jumping"))));
+
+        releaseIndexNode();
+        before = indexed;
+        builder = before.builder();
+        builder.child("test").setProperty("bar", "kite is flying");
+        after = builder.getNodeState();
+        indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        assertEquals(2, getSearcher().getIndexReader().numDocs());
+        assertEquals("change in non included property should not cause " +
+                "index update",0, getSearcher().getIndexReader().numDeletedDocs());
+    }
+
+    @Test
+    public void testLuceneWithRelativeProperty() throws Exception {
+        // OAK-6833
+        assumeFalse(CIHelper.windows());
+
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene",
+                of(TYPENAME_STRING));
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of("foo", "jcr:content/mime",
+                "jcr:content/metadata/type"), STRINGS));
+
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+        builder.child("test").child("jcr:content").setProperty("mime", "text");
+        builder.child("test").child("jcr:content").child("metadata").setProperty("type", "image");
+        builder.child("jcr:content").setProperty("count", "text");
+        builder.child("jcr:content").child("boom").child("metadata").setProperty("type", "image");
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        assertEquals(1, getSearcher().getIndexReader().numDocs());
+
+        assertEquals("/test", getPath(new TermQuery(new Term("foo", "fox is jumping"))));
+        assertEquals("/test", getPath(new TermQuery(new Term("jcr:content/mime", "text"))));
+        assertEquals("/test", getPath(new TermQuery(new Term("jcr:content/metadata/type", "image"))));
+        assertNull("bar must NOT be indexed", getPath(new TermQuery(new Term("count", "text"))));
+
+        releaseIndexNode();
+        before = indexed;
+        builder = before.builder();
+        builder.child("test").child("jcr:content").setProperty("mime", "pdf");
+        after = builder.getNodeState();
+        indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        assertEquals("/test", getPath(new TermQuery(new Term("jcr:content/mime", "pdf"))));
+
+        releaseIndexNode();
+        before = indexed;
+        builder = before.builder();
+        builder.child("test").child("jcr:content").remove();
+        after = builder.getNodeState();
+        indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+        assertNull("removes must be persisted too, 1st level",
+                getPath(new TermQuery(new Term("jcr:content/mime", "pdf"))));
+        assertNull("removes must be persisted too, 2nd level",
+                getPath(new TermQuery(new Term("jcr:content/metadata/type",
+                        "image"))));
+    }
+
+    @Test
+    public void indexVersionSwitchOnReindex() throws Exception{
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinition(index, "lucene",
+                of(TYPENAME_STRING));
+
+        //1. Trigger a index so that next index step does not see it as a fresh index
+        NodeState indexed = HOOK.processCommit(EMPTY_NODE, builder.getNodeState(), CommitInfo.EMPTY);
+        builder = indexed.builder();
+
+        //By default logic would use current version. To simulate upgrade we forcefully set
+        //version to V1
+        builder.child(INDEX_DEFINITIONS_NAME).child("lucene").setProperty(IndexDefinition.INDEX_VERSION,
+                IndexFormatVersion.V1.getVersion());
+
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+        NodeState after = builder.getNodeState();
+
+        indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        assertEquals(IndexFormatVersion.V1, new IndexDefinition(root,
+                indexed.getChildNode(INDEX_DEFINITIONS_NAME).getChildNode("lucene"), "/foo").getVersion());
+
+        //3. Trigger a reindex and version should switch to current
+        builder = indexed.builder();
+        before = indexed;
+        builder.child(INDEX_DEFINITIONS_NAME).child("lucene").setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
+        after = builder.getNodeState();
+        indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        assertEquals(IndexFormatVersion.getDefault(), new IndexDefinition(root,
+                indexed.getChildNode(INDEX_DEFINITIONS_NAME).getChildNode("lucene"), "/foo").getVersion());
+
+    }
+
+    @Test
+    public void autoFormatUpdate() throws Exception{
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene",
+                of(TYPENAME_STRING));
+
+        //1. Trigger a index so that next index step does not see it as a fresh index
+        NodeState indexed = HOOK.processCommit(EMPTY_NODE, builder.getNodeState(), CommitInfo.EMPTY);
+
+        IndexDefinition defn = new IndexDefinition(root, indexed.getChildNode(INDEX_DEFINITIONS_NAME).getChildNode("lucene"), "/foo");
+        assertFalse(defn.isOfOldFormat());
+    }
+
+    @Test
+    public void copyOnWriteAndLocks() throws Exception {
+        assumeFalse(CIHelper.windows());
+
+        ExecutorService executorService = Executors.newFixedThreadPool(2);
+        IndexCopier copier = new IndexCopier(executorService, temporaryFolder.getRoot());
+
+        FailOnDemandEditorProvider failingProvider = new FailOnDemandEditorProvider();
+        EditorHook hook = new EditorHook(
+                new IndexUpdateProvider(
+                        new CompositeIndexEditorProvider(
+                                failingProvider,
+                                new LuceneIndexEditorProvider(copier))));
+
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, "lucene", of(TYPENAME_STRING));
+        IndexUtils.createIndexDefinition(index, "failingIndex", false, false, of("foo"), null);
+
+
+        //1. Get initial set indexed. So that next cycle is normal indexing
+        NodeState indexed = hook.processCommit(EMPTY_NODE, builder.getNodeState(), CommitInfo.EMPTY);
+        builder = indexed.builder();
+
+        NodeState before = indexed;
+        builder.child("test").setProperty("a", "fox is jumping");
+        NodeState after = builder.getNodeState();
+
+        //2. Ensure that Lucene gets triggered but close is not called
+        failingProvider.setShouldFail(true);
+        try {
+            hook.processCommit(before, after, CommitInfo.EMPTY);
+            fail();
+        } catch (CommitFailedException ignore){
+
+        }
+
+        //3. Disable the troubling editor
+        failingProvider.setShouldFail(false);
+
+        //4. Now commit should process fine
+        hook.processCommit(before, after, CommitInfo.EMPTY);
+
+        executorService.shutdown();
+    }
+
+
+    @Test
+    public void multiplexingWriter() throws Exception{
+        newLucenePropertyIndex("lucene", "foo");
+        MountInfoProvider mip = Mounts.newBuilder()
+                .mount("foo", "/libs", "/apps").build();
+        EditorHook hook = new EditorHook(
+                new IndexUpdateProvider(
+                        new LuceneIndexEditorProvider(null, new ExtractedTextCache(0, 0), null, mip)));
+
+        NodeState indexed = hook.processCommit(EMPTY_NODE, builder.getNodeState(), CommitInfo.EMPTY);
+        builder = indexed.builder();
+        NodeState before = indexed;
+        builder.child("content").child("en").setProperty("foo", "bar");
+        builder.child("libs").child("install").setProperty("foo", "bar");
+        NodeState after = builder.getNodeState();
+
+        indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
+        builder = indexed.builder();
+
+        assertEquals(1, numDocs(mip.getMountByName("foo")));
+        assertEquals(1, numDocs(mip.getDefaultMount()));
+    }
+
+    private int numDocs(Mount m) throws IOException {
+        String indexDirName = MultiplexersLucene.getIndexDirName(m);
+        NodeBuilder defnBuilder = builder.child(INDEX_DEFINITIONS_NAME).child("lucene");
+        Directory d = new OakDirectory(defnBuilder, indexDirName, new LuceneIndexDefinition(root, defnBuilder.getNodeState(), "/foo"), true);
+        IndexReader r = DirectoryReader.open(d);
+        return r.numDocs();
+    }
+
+
+    //@Test
+    public void checkLuceneIndexFileUpdates() throws Exception{
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinition(index, "lucene",
+                of(TYPENAME_STRING));
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of("foo" , "bar", "baz"), STRINGS));
+        //nb.removeProperty(REINDEX_PROPERTY_NAME);
+
+        NodeState before = builder.getNodeState();
+        builder.child("test").setProperty("foo", "fox is jumping");
+
+        //InfoStream.setDefault(new PrintStreamInfoStream(System.out));
+        before = commitAndDump(before, builder.getNodeState());
+
+        builder = before.builder();
+        builder.child("test2").setProperty("bar", "ship is sinking");
+        before = commitAndDump(before, builder.getNodeState());
+
+        builder = before.builder();
+        builder.child("test3").setProperty("baz", "horn is blowing");
+        before = commitAndDump(before, builder.getNodeState());
+
+        builder = before.builder();
+        builder.child("test2").remove();
+        before = commitAndDump(before, builder.getNodeState());
+
+        builder = before.builder();
+        builder.child("test2").setProperty("bar", "ship is back again");
+        before = commitAndDump(before, builder.getNodeState());
+    }
+
+    @After
+    public void releaseIndexNode(){
+        if(indexNode != null){
+            indexNode.release();
+        }
+        indexNode = null;
+    }
+
+    private NodeState newLucenePropertyIndex(String indexName, String propName){
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        NodeBuilder nb = newLuceneIndexDefinitionV2(index, indexName,
+                of(TYPENAME_STRING));
+        nb.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
+        nb.setProperty(createProperty(INCLUDE_PROPERTY_NAMES, of(propName), STRINGS));
+        return builder.getNodeState();
+    }
+
+    private String query(String query, LuceneIndexDefinition defn) throws IOException, ParseException {
+        QueryParser queryParser = new QueryParser(VERSION, "", defn.getAnalyzer());
+        return getPath(queryParser.parse(query));
+    }
+
+    private String getPath(Query query) throws IOException {
+        TopDocs td = getSearcher().search(query, 100);
+        if (td.totalHits > 0){
+            if(td.totalHits > 1){
+                fail("More than 1 result found for query " + query);
+            }
+            return getSearcher().getIndexReader().document(td.scoreDocs[0].doc).get(FieldNames.PATH);
+        }
+        return null;
+    }
+
+    private IndexSearcher getSearcher(){
+        if(indexNode == null){
+            indexNode = tracker.acquireIndexNode("/oak:index/lucene");
+        }
+        return indexNode.getSearcher();
+    }
+
+    private NodeState commitAndDump(NodeState before, NodeState after) throws CommitFailedException, IOException {
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+        dumpIndexDir();
+        return indexed;
+    }
+
+    private void dumpIndexDir() throws IOException {
+        Directory dir = ((DirectoryReader)getSearcher().getIndexReader()).directory();
+
+        System.out.println("================");
+        String[] fileNames = dir.listAll();
+        Arrays.sort(fileNames);
+        for (String file : fileNames){
+            System.out.printf("%s - %d %n", file, dir.fileLength(file));
+        }
+        releaseIndexNode();
+    }
+
+    public static Calendar createCal(String dt) throws java.text.ParseException {
+        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(sdf.parse(dt));
+        return cal;
+    }
+
+    static long dateToTime(String dt) throws java.text.ParseException {
+        return FieldFactory.dateToLong(ISO8601.format(createCal(dt)));
+    }
+
+    private static class FailOnDemandEditorProvider implements IndexEditorProvider {
+
+        private boolean shouldFail;
+
+        @Override
+        public Editor getIndexEditor(@NotNull String type, @NotNull NodeBuilder definition,
+                                     @NotNull NodeState root,
+                                     @NotNull IndexUpdateCallback callback) throws CommitFailedException {
+            if (PropertyIndexEditorProvider.TYPE.equals(type)) {
+                return new FailOnDemandEditor();
+            }
+            return null;
+        }
+
+        public void setShouldFail(boolean shouldFail) {
+            this.shouldFail = shouldFail;
+        }
+
+        private class FailOnDemandEditor extends DefaultEditor implements IndexEditor {
+            @Override
+            public void leave(NodeState before, NodeState after)
+                    throws CommitFailedException {
+                throwExceptionIfTold();
+                super.leave(before, after);
+            }
+
+            void throwExceptionIfTold() throws CommitFailedException {
+                if (shouldFail) {
+                    throw new CommitFailedException("commit",1 , null);
+                }
+            }
+        }
+    }
+
+}

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

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexExclusionQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexExclusionQueryTest.java?rev=1841926&r1=1841925&r2=1841926&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexExclusionQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexExclusionQueryTest.java Tue Sep 25 12:24:15 2018
@@ -24,8 +24,8 @@ import static org.apache.jackrabbit.JcrC
 import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
 import static org.apache.jackrabbit.oak.api.Type.DATE;
 import static org.apache.jackrabbit.oak.api.Type.STRINGS;
-import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.EXCLUDE_PROPERTY_NAMES;
-import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_TYPES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.EXCLUDE_PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INCLUDE_PROPERTY_TYPES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.TYPE_LUCENE;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.useV2;
 

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProviderTest.java?rev=1841926&r1=1841925&r2=1841926&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProviderTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProviderTest.java Tue Sep 25 12:24:15 2018
@@ -21,7 +21,6 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.io.File;
 
-import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfo;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoService;
 import org.apache.jackrabbit.oak.plugins.index.IndexInfo;
@@ -36,11 +35,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
-import static com.google.common.collect.Lists.newArrayList;
-import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookupTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookupTest.java?rev=1841926&r1=1841925&r2=1841926&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookupTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookupTest.java Tue Sep 25 12:24:15 2018
@@ -19,6 +19,7 @@
 
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import org.apache.jackrabbit.oak.plugins.index.search.IndexLookup;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -43,11 +44,11 @@ public class LuceneIndexLookupTest {
         newLuceneIndexDefinition(index, "l1", of(TYPENAME_STRING));
         newLuceneIndexDefinition(index, "l2", of(TYPENAME_STRING));
 
-        LuceneIndexLookup lookup = new LuceneIndexLookup(builder.getNodeState());
+        IndexLookup lookup = LuceneIndexLookupUtil.getLuceneIndexLookup(builder.getNodeState());
         FilterImpl f = FilterImpl.newTestInstance();
         f.restrictPath("/", Filter.PathRestriction.EXACT);
         assertEquals(of("/oak:index/l1", "/oak:index/l2"),
-                lookup.collectIndexNodePaths(f));
+            lookup.collectIndexNodePaths(f));
     }
 
     @Test
@@ -61,14 +62,14 @@ public class LuceneIndexLookupTest {
         index = builder.child("a").child("b").child(INDEX_DEFINITIONS_NAME);
         newLuceneIndexDefinition(index, "l3", of(TYPENAME_STRING));
 
-        LuceneIndexLookup lookup = new LuceneIndexLookup(builder.getNodeState());
+        IndexLookup lookup = LuceneIndexLookupUtil.getLuceneIndexLookup(builder.getNodeState());
         FilterImpl f = FilterImpl.newTestInstance();
         f.restrictPath("/a", Filter.PathRestriction.EXACT);
         assertEquals(of("/oak:index/l1", "/a/oak:index/l2"),
-                lookup.collectIndexNodePaths(f));
+            lookup.collectIndexNodePaths(f));
 
         f.restrictPath("/a/b", Filter.PathRestriction.EXACT);
         assertEquals(of("/oak:index/l1", "/a/oak:index/l2", "/a/b/oak:index/l3"),
-                lookup.collectIndexNodePaths(f));
+            lookup.collectIndexNodePaths(f));
     }
 }

Added: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderServiceTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderServiceTest.java?rev=1841926&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderServiceTest.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderServiceTest.java Tue Sep 25 12:24:15 2018
@@ -0,0 +1,448 @@
+/*
+ * 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.File;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
+import org.apache.jackrabbit.oak.api.jmx.CheckpointMBean;
+import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.CachingFileDataStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreUtils;
+import org.apache.jackrabbit.oak.plugins.document.spi.JournalPropertyService;
+import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoService;
+import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.IndexPathService;
+import org.apache.jackrabbit.oak.plugins.index.fulltext.PreExtractedTextProvider;
+import org.apache.jackrabbit.oak.plugins.index.importer.IndexImporterProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.CopyOnReadStatsMBean;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexAugmentorFactory;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexCopier;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LoggingInfoStream;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexProviderService;
+import org.apache.jackrabbit.oak.plugins.index.lucene.directory.BufferedOakDirectory;
+import org.apache.jackrabbit.oak.plugins.index.lucene.property.PropertyIndexCleaner;
+import org.apache.jackrabbit.oak.plugins.index.lucene.reader.DefaultIndexReaderFactory;
+import org.apache.jackrabbit.oak.plugins.index.lucene.score.ScorerProviderFactory;
+import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
+import org.apache.jackrabbit.oak.spi.commit.BackgroundObserver;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.util.InfoStream;
+import org.apache.sling.testing.mock.osgi.MockOsgi;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.osgi.framework.ServiceReference;
+
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.TYPE_LUCENE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+public class LuceneIndexProviderServiceTest {
+    /*
+        The test case uses raw config name and not access it via
+         constants in LuceneIndexProviderService to ensure that change
+         in names are detected
+     */
+
+    @Rule
+    public final TemporaryFolder folder = new TemporaryFolder(new File("target"));
+
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    private LuceneIndexProviderService service = new LuceneIndexProviderService();
+
+    private Whiteboard wb;
+
+    private MountInfoProvider mip;
+
+    @Before
+    public void setUp(){
+        mip = Mounts.newBuilder().build();
+        context.registerService(MountInfoProvider.class, mip);
+        context.registerService(StatisticsProvider.class, StatisticsProvider.NOOP);
+        context.registerService(ScorerProviderFactory.class, ScorerProviderFactory.DEFAULT);
+        context.registerService(IndexAugmentorFactory.class, new IndexAugmentorFactory());
+        context.registerService(NodeStore.class, new MemoryNodeStore());
+        context.registerService(IndexPathService.class, mock(IndexPathService.class));
+        context.registerService(AsyncIndexInfoService.class, mock(AsyncIndexInfoService.class));
+        context.registerService(CheckpointMBean.class, mock(CheckpointMBean.class));
+
+        wb = new OsgiWhiteboard(context.bundleContext());
+        MockOsgi.injectServices(service, context.bundleContext());
+    }
+
+    @After
+    public void after(){
+        IndexDefinition.setDisableStoredIndexDefinition(false);
+    }
+
+    @Test
+    public void defaultSetup() throws Exception{
+        MockOsgi.activate(service, context.bundleContext(), getDefaultConfig());
+
+        assertNotNull(context.getService(QueryIndexProvider.class));
+        assertNotNull(context.getService(Observer.class));
+        assertNotNull(context.getService(IndexEditorProvider.class));
+
+        LuceneIndexEditorProvider editorProvider =
+                (LuceneIndexEditorProvider) context.getService(IndexEditorProvider.class);
+        assertNotNull(editorProvider.getIndexCopier());
+        assertNotNull(editorProvider.getIndexingQueue());
+
+        IndexCopier indexCopier = service.getIndexCopier();
+        assertNotNull("IndexCopier should be initialized as CopyOnRead is enabled by default", indexCopier);
+        assertTrue(indexCopier.isPrefetchEnabled());
+        assertFalse(IndexDefinition.isDisableStoredIndexDefinition());
+
+        assertNotNull("CopyOnRead should be enabled by default", context.getService(CopyOnReadStatsMBean.class));
+        assertNotNull(context.getService(CacheStatsMBean.class));
+
+        assertTrue(context.getService(Observer.class) instanceof BackgroundObserver);
+        assertEquals(InfoStream.NO_OUTPUT, InfoStream.getDefault());
+
+        assertEquals(1024, BooleanQuery.getMaxClauseCount());
+
+        assertNotNull(FieldUtils.readDeclaredField(service, "documentQueue", true));
+
+        assertNotNull(context.getService(JournalPropertyService.class));
+        assertNotNull(context.getService(IndexImporterProvider.class));
+
+        assertNotNull(WhiteboardUtils.getServices(wb, Runnable.class, r -> r instanceof PropertyIndexCleaner));
+
+        MockOsgi.deactivate(service, context.bundleContext());
+
+        IndexTracker tracker = (IndexTracker) FieldUtils.readDeclaredField(service, "tracker", true);
+        assertNotNull(tracker.getAsyncIndexInfoService());
+    }
+
+    @Test
+    public void typeProperty() throws Exception{
+        MockOsgi.activate(service, context.bundleContext(), getDefaultConfig());
+        ServiceReference sr = context.bundleContext().getServiceReference(IndexEditorProvider.class.getName());
+        assertEquals(TYPE_LUCENE, sr.getProperty("type"));
+    }
+
+    @Test
+    public void disableOpenIndexAsync() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("enableOpenIndexAsync", false);
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        assertTrue(context.getService(Observer.class) instanceof LuceneIndexProvider);
+
+        MockOsgi.deactivate(service, context.bundleContext());
+    }
+
+    @Test
+    public void enableCopyOnWrite() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("enableCopyOnWriteSupport", true);
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        LuceneIndexEditorProvider editorProvider =
+                (LuceneIndexEditorProvider) context.getService(IndexEditorProvider.class);
+
+        assertNotNull(editorProvider);
+        assertNotNull(editorProvider.getIndexCopier());
+
+        MockOsgi.deactivate(service, context.bundleContext());
+    }
+
+    // OAK-7357
+    @Test
+    public void disableCoRCoW() throws Exception {
+        // inject ds as OAK-7357 revealed ABD bean had a bug - which comes into play only with blob stores
+        CachingFileDataStore ds = DataStoreUtils
+                .createCachingFDS(folder.newFolder().getAbsolutePath(),
+                        folder.newFolder().getAbsolutePath());
+
+        context.registerService(GarbageCollectableBlobStore.class, new DataStoreBlobStore(ds));
+
+        // re-init service and inject references
+        service = new LuceneIndexProviderService();
+        MockOsgi.injectServices(service, context.bundleContext());
+
+        Map<String,Object> config = getDefaultConfig();
+        config.put("enableCopyOnReadSupport", false);
+        config.put("enableCopyOnWriteSupport", false);
+
+        // activation should work
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        // get lucene index provider
+        LuceneIndexProvider lip = null;
+        for (QueryIndexProvider qip : context.getServices(QueryIndexProvider.class, null)) {
+            if (qip instanceof LuceneIndexProvider) {
+                lip = (LuceneIndexProvider)qip;
+                break;
+            }
+        }
+        assertNotNull(lip);
+        IndexTracker tracker = lip.getTracker();
+
+        // access reader factory with reflection and implicitly assert that it's DefaultIndexReaderFactory
+        Field readerFactorFld = IndexTracker.class.getDeclaredField("readerFactory");
+        readerFactorFld.setAccessible(true);
+        DefaultIndexReaderFactory readerFactory = (DefaultIndexReaderFactory)readerFactorFld.get(tracker);
+
+        Field mipFld = DefaultIndexReaderFactory.class.getDeclaredField("mountInfoProvider");
+        mipFld.setAccessible(true);
+        // OAK-7408: LIPS was using default tracker ctor and hence reader factor used default mounts
+        assertEquals("Reader factory not using configured MountInfoProvider", mip, mipFld.get(readerFactory));
+
+        // de-activation should work
+        MockOsgi.deactivate(service, context.bundleContext());
+    }
+
+    @Test
+    public void enablePrefetchIndexFiles() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("prefetchIndexFiles", true);
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        IndexCopier indexCopier = service.getIndexCopier();
+        assertTrue(indexCopier.isPrefetchEnabled());
+
+        MockOsgi.deactivate(service, context.bundleContext());
+    }
+
+    @Test
+    public void debugLogging() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("debug", true);
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        assertEquals(LoggingInfoStream.INSTANCE, InfoStream.getDefault());
+        MockOsgi.deactivate(service, context.bundleContext());
+    }
+
+    @Test
+    public void enableExtractedTextCaching() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("extractedTextCacheSizeInMB", 11);
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        ExtractedTextCache textCache = service.getExtractedTextCache();
+        assertNotNull(textCache.getCacheStats());
+        assertNotNull(context.getService(CacheStatsMBean.class));
+
+        assertEquals(11 * FileUtils.ONE_MB, textCache.getCacheStats().getMaxTotalWeight());
+
+        MockOsgi.deactivate(service, context.bundleContext());
+
+        assertNull(context.getService(CacheStatsMBean.class));
+    }
+
+    @Test
+    public void preExtractedTextProvider() throws Exception{
+        MockOsgi.activate(service, context.bundleContext(), getDefaultConfig());
+        LuceneIndexEditorProvider editorProvider =
+                (LuceneIndexEditorProvider) context.getService(IndexEditorProvider.class);
+        assertNull(editorProvider.getExtractedTextCache().getExtractedTextProvider());
+        assertFalse(editorProvider.getExtractedTextCache().isAlwaysUsePreExtractedCache());
+
+        //Mock OSGi does not support components
+        //context.registerService(PreExtractedTextProvider.class, new DummyProvider());
+        service.bindExtractedTextProvider(mock(PreExtractedTextProvider.class));
+
+        assertNotNull(editorProvider.getExtractedTextCache().getExtractedTextProvider());
+    }
+
+    @Test
+    public void preExtractedProviderBindBeforeActivate() throws Exception{
+        service.bindExtractedTextProvider(mock(PreExtractedTextProvider.class));
+        MockOsgi.activate(service, context.bundleContext(), getDefaultConfig());
+        LuceneIndexEditorProvider editorProvider =
+                (LuceneIndexEditorProvider) context.getService(IndexEditorProvider.class);
+        assertNotNull(editorProvider.getExtractedTextCache().getExtractedTextProvider());
+    }
+
+    @Test
+    public void alwaysUsePreExtractedCache() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("alwaysUsePreExtractedCache", "true");
+        MockOsgi.activate(service, context.bundleContext(), config);
+        LuceneIndexEditorProvider editorProvider =
+                (LuceneIndexEditorProvider) context.getService(IndexEditorProvider.class);
+        assertTrue(editorProvider.getExtractedTextCache().isAlwaysUsePreExtractedCache());
+    }
+
+    @Test
+    public void booleanQuerySize() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("booleanClauseLimit", 4000);
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        assertEquals(4000, BooleanQuery.getMaxClauseCount());
+    }
+
+    @Test
+    public void indexDefnStorafe() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("disableStoredIndexDefinition", true);
+        MockOsgi.activate(service, context.bundleContext(), config);
+
+        assertTrue(IndexDefinition.isDisableStoredIndexDefinition());
+    }
+
+
+    @Test
+    public void blobStoreRegistered() throws Exception{
+        MockOsgi.activate(service, context.bundleContext(), getDefaultConfig());
+        LuceneIndexEditorProvider editorProvider =
+            (LuceneIndexEditorProvider) context.getService(IndexEditorProvider.class);
+        assertNull(editorProvider.getBlobStore());
+
+        /* Register a blob store */
+        CachingFileDataStore ds = DataStoreUtils
+            .createCachingFDS(folder.newFolder().getAbsolutePath(),
+                folder.newFolder().getAbsolutePath());
+
+        context.registerService(GarbageCollectableBlobStore.class, new DataStoreBlobStore(ds));
+        reactivate();
+
+        editorProvider =
+                (LuceneIndexEditorProvider) context.getService(IndexEditorProvider.class);
+        assertNotNull(editorProvider.getBlobStore());
+    }
+
+    @Test
+    public void executorPoolBehaviour() throws Exception{
+        MockOsgi.activate(service, context.bundleContext(), getDefaultConfig());
+        ExecutorService executor = service.getExecutorService();
+
+        final CountDownLatch latch1 = new CountDownLatch(1);
+        final CountDownLatch latch2 = new CountDownLatch(1);
+
+        Callable cb1 = new Callable() {
+            @Override
+            public Object call() throws Exception {
+                latch1.await();
+                return null;
+            }
+        };
+
+        Callable cb2 = new Callable() {
+            @Override
+            public Object call() throws Exception {
+                latch2.countDown();
+                return null;
+            }
+        };
+
+        executor.submit(cb1);
+        executor.submit(cb2);
+
+        //Even if one task gets stuck the other task must get completed
+        assertTrue("Second task not executed", latch2.await(1, TimeUnit.MINUTES));
+        latch1.countDown();
+
+        MockOsgi.deactivate(service, context.bundleContext());
+    }
+
+
+    @Test
+    public void singleBlobPerIndexFileConfig() throws Exception {
+        Map<String, Object> config = getDefaultConfig();
+        config.put("enableSingleBlobIndexFiles", "true");
+        MockOsgi.activate(service, context.bundleContext(), config);
+        assertTrue("Enabling property must reflect in BufferedOakDirectory state",
+                BufferedOakDirectory.isEnableWritingSingleBlobIndexFile());
+        MockOsgi.deactivate(service, context.bundleContext());
+
+        config = getDefaultConfig();
+        config.put("enableSingleBlobIndexFiles", "false");
+        MockOsgi.activate(service, context.bundleContext(), config);
+        assertFalse("Enabling property must reflect in BufferedOakDirectory state",
+                BufferedOakDirectory.isEnableWritingSingleBlobIndexFile());
+        MockOsgi.deactivate(service, context.bundleContext());
+    }
+
+    @Test
+    public void cleanerRegistration() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("propIndexCleanerIntervalInSecs", 142);
+
+        MockOsgi.activate(service, context.bundleContext(), config);
+        ServiceReference[] sr = context.bundleContext().getAllServiceReferences(Runnable.class.getName(),
+                "(scheduler.name="+ PropertyIndexCleaner.class.getName()+")");
+        assertEquals(sr.length, 1);
+
+        assertEquals(142L, sr[0].getProperty("scheduler.period"));
+    }
+
+    @Test
+    public void cleanerRegistrationDisabled() throws Exception{
+        Map<String,Object> config = getDefaultConfig();
+        config.put("propIndexCleanerIntervalInSecs", 0);
+
+        MockOsgi.activate(service, context.bundleContext(), config);
+        ServiceReference[] sr = context.bundleContext().getAllServiceReferences(Runnable.class.getName(),
+                "(scheduler.name="+ PropertyIndexCleaner.class.getName()+")");
+        assertNull(sr);
+    }
+
+    private void reactivate() {
+        MockOsgi.deactivate(service, context.bundleContext());
+        service = new LuceneIndexProviderService();
+
+        MockOsgi.injectServices(service, context.bundleContext());
+        MockOsgi.activate(service, context.bundleContext(), getDefaultConfig());
+    }
+
+    private Map<String,Object> getDefaultConfig(){
+        Map<String,Object> config = new HashMap<String, Object>();
+        config.put("localIndexDir", folder.getRoot().getAbsolutePath());
+        return config;
+    }
+}

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