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 ch...@apache.org on 2015/05/19 07:24:27 UTC

svn commit: r1680169 - in /jackrabbit/oak/branches/1.2: ./ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/ oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/

Author: chetanm
Date: Tue May 19 05:24:26 2015
New Revision: 1680169

URL: http://svn.apache.org/r1680169
Log:
OAK-2809 - Save Lucene directory listing as array property

Merging 1677609

Modified:
    jackrabbit/oak/branches/1.2/   (props changed)
    jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
    jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
    jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
    jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
    jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java
    jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java
    jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
    jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectoryTest.java

Propchange: jackrabbit/oak/branches/1.2/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue May 19 05:24:26 2015
@@ -1,3 +1,3 @@
 /jackrabbit/oak/branches/1.0:1665962
-/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414,1673436,1673644,1673662-1673664,1673669,1673695,1674046,1674065,1674075,1674107,1674228,1674880,1675055,1675332,1675354,1675357,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676725,1677579,1677581,1677939,1678173,1678758,1678938,1679165,1679191,1679235
+/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414,1673436,1673644,1673662-1673664,1673669,1673695,1674046,1674065,1674075,1674107,1674228,1674880,1675055,1675332,1675354,1675357,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676725,1677579,1677581,1677609,1677939,1678173,1678758,1678938,1679165,1679191,1679235
 /jackrabbit/trunk:1345480

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java Tue May 19 05:24:26 2015
@@ -197,6 +197,8 @@ class IndexDefinition implements Aggrega
 
     private final int suggesterUpdateFrequencyMinutes;
 
+    private final boolean saveDirListing;
+
     public IndexDefinition(NodeState root, NodeState defn) {
         this(root, defn, null);
     }
@@ -257,6 +259,7 @@ class IndexDefinition implements Aggrega
         this.maxExtractLength = determineMaxExtractLength();
         this.suggesterUpdateFrequencyMinutes = getOptionalValue(defn, LuceneIndexConstants.SUGGEST_UPDATE_FREQUENCY_MINUTES, 60);
         this.scorerProviderName = getOptionalValue(defn, LuceneIndexConstants.PROP_SCORER_PROVIDER, null);
+        this.saveDirListing = getOptionalValue(defn, LuceneIndexConstants.SAVE_DIR_LISTING, false);
     }
 
     public boolean isFullTextEnabled() {
@@ -361,6 +364,10 @@ class IndexDefinition implements Aggrega
         return scorerProviderName;
     }
 
+    public boolean saveDirListing() {
+        return saveDirListing;
+    }
+
     @Override
     public String toString() {
         return "Lucene Index : " + indexName;

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java Tue May 19 05:24:26 2015
@@ -46,7 +46,7 @@ class IndexNode {
         IndexDefinition definition = new IndexDefinition(root, defnNodeState, indexPath);
         NodeState data = defnNodeState.getChildNode(INDEX_DATA_CHILD_NAME);
         if (data.exists()) {
-            directory = new OakDirectory(new ReadOnlyBuilder(data), definition);
+            directory = new OakDirectory(new ReadOnlyBuilder(data), definition, true);
             if (cloner != null){
                 directory = cloner.wrap(indexPath, definition, directory);
             }

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java Tue May 19 05:24:26 2015
@@ -255,4 +255,11 @@ public interface LuceneIndexConstants {
      * 'IS NOT NULL' constraint
      */
     String PROP_NOT_NULL_CHECK_ENABLED = "notNullCheckEnabled";
+
+    /**
+     * Boolean property indicating that Lucene directory content
+     * should be saved as part of NodeState itself as a multi value property
+     * to allow faster reads (OAK-2809)
+     */
+    String SAVE_DIR_LISTING = "saveDirectoryListing";
 }

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java Tue May 19 05:24:26 2015
@@ -80,7 +80,7 @@ public class LuceneIndexEditorContext {
             throws IOException {
         String path = definition.getString(PERSISTENCE_PATH);
         if (path == null) {
-            return new OakDirectory(definition.child(INDEX_DATA_CHILD_NAME), indexDefinition);
+            return new OakDirectory(definition.child(INDEX_DATA_CHILD_NAME), indexDefinition, false);
         } else {
             // try {
             File file = new File(path);
@@ -118,6 +118,8 @@ public class LuceneIndexEditorContext {
 
     private Parser parser;
 
+    private Directory directory;
+
     /**
      * The media types supported by the parser used.
      */
@@ -144,7 +146,8 @@ public class LuceneIndexEditorContext {
     IndexWriter getWriter() throws IOException {
         if (writer == null) {
             final long start = PERF_LOGGER.start();
-            writer = new IndexWriter(newIndexDirectory(definition, definitionBuilder), config);
+            directory = newIndexDirectory(definition, definitionBuilder);
+            writer = new IndexWriter(directory, config);
             PERF_LOGGER.end(start, -1, "Created IndexWriter for directory {}", definition);
         }
         return writer;
@@ -168,6 +171,8 @@ public class LuceneIndexEditorContext {
 
             writer.close();
 
+            directory.close();
+
             //OAK-2029 Record the last updated status so
             //as to make IndexTracker detect changes when index
             //is stored in file system

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java Tue May 19 05:24:26 2015
@@ -16,20 +16,23 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
-import com.google.common.collect.Iterables;
-import com.google.common.io.ByteStreams;
 import java.io.ByteArrayInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.io.ByteStreams;
 import com.google.common.primitives.Ints;
 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.NodeBuilder;
+import org.apache.jackrabbit.oak.util.PerfLogger;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.IOContext;
 import org.apache.lucene.store.IndexInput;
@@ -37,7 +40,9 @@ import org.apache.lucene.store.IndexOutp
 import org.apache.lucene.store.Lock;
 import org.apache.lucene.store.LockFactory;
 import org.apache.lucene.store.NoLockFactory;
+import org.slf4j.LoggerFactory;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkElementIndex;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkPositionIndexes;
@@ -46,37 +51,45 @@ import static com.google.common.collect.
 import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
 import static org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED;
 import static org.apache.jackrabbit.oak.api.Type.BINARIES;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
 
 /**
  * Implementation of the Lucene {@link Directory} (a flat list of files)
  * based on an Oak {@link NodeBuilder}.
  */
 class OakDirectory extends Directory {
-
+    static final PerfLogger PERF_LOGGER = new PerfLogger(LoggerFactory.getLogger(OakDirectory.class.getName() + ".perf"));
+    static final String PROP_DIR_LISTING = "dirListing";
     static final String PROP_BLOB_SIZE = "blobSize";
     protected final NodeBuilder directoryBuilder;
     private final IndexDefinition definition;
     private LockFactory lockFactory;
+    private final boolean readOnly;
+    private final Set<String> fileNames = Sets.newConcurrentHashSet();
 
-    public OakDirectory(NodeBuilder directoryBuilder, IndexDefinition definition) {
+    public OakDirectory(NodeBuilder directoryBuilder, IndexDefinition definition, boolean readOnly) {
         this.lockFactory = NoLockFactory.getNoLockFactory();
         this.directoryBuilder = directoryBuilder;
         this.definition = definition;
+        this.readOnly = readOnly;
+        this.fileNames.addAll(getListing());
     }
 
     @Override
     public String[] listAll() throws IOException {
-        return Iterables.toArray(
-                directoryBuilder.getChildNodeNames(), String.class);
+        return fileNames.toArray(new String[fileNames.size()]);
     }
 
     @Override
     public boolean fileExists(String name) throws IOException {
-        return directoryBuilder.hasChildNode(name);
+        return fileNames.contains(name);
     }
 
     @Override
     public void deleteFile(String name) throws IOException {
+        checkArgument(!readOnly, "Read only directory");
+        fileNames.remove(name);
         directoryBuilder.getChildNode(name).remove();
     }
 
@@ -94,6 +107,7 @@ class OakDirectory extends Directory {
     @Override
     public IndexOutput createOutput(String name, IOContext context)
             throws IOException {
+        checkArgument(!readOnly, "Read only directory");
         NodeBuilder file;
         if (!directoryBuilder.hasChildNode(name)) {
             file = directoryBuilder.child(name);
@@ -101,6 +115,7 @@ class OakDirectory extends Directory {
         } else {
             file = directoryBuilder.child(name);
         }
+        fileNames.add(name);
         return new OakIndexOutput(name, file);
     }
 
@@ -133,7 +148,9 @@ class OakDirectory extends Directory {
 
     @Override
     public void close() throws IOException {
-        // do nothing
+        if (!readOnly && definition.saveDirListing()) {
+            directoryBuilder.setProperty(createProperty(PROP_DIR_LISTING, fileNames, STRINGS));
+        }
     }
 
     @Override
@@ -146,6 +163,24 @@ class OakDirectory extends Directory {
         return lockFactory;
     }
 
+    private Set<String> getListing(){
+        long start = PERF_LOGGER.start();
+        Iterable<String> fileNames = null;
+        if (definition.saveDirListing()) {
+            PropertyState listing = directoryBuilder.getProperty(PROP_DIR_LISTING);
+            if (listing != null) {
+                fileNames = listing.getValue(Type.STRINGS);
+            }
+        }
+
+        if (fileNames == null){
+            fileNames = directoryBuilder.getChildNodeNames();
+        }
+        Set<String> result = ImmutableSet.copyOf(fileNames);
+        PERF_LOGGER.end(start, 100, "Directory listing performed. Total {} files", result.size());
+        return result;
+    }
+
     /**
      * Size of the blob entries to which the Lucene files are split.
      * Set to higher than the 4kB inline limit for the BlobStore,

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java Tue May 19 05:24:26 2015
@@ -60,6 +60,7 @@ import static org.apache.jackrabbit.oak.
 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;
 
 public class LuceneIndexEditorTest {
@@ -159,6 +160,24 @@ public class LuceneIndexEditorTest {
         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(LuceneIndexConstants.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

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java Tue May 19 05:24:26 2015
@@ -340,7 +340,7 @@ public class LucenePropertyIndexTest ext
         root.commit();
 
         Tree test = root.getTree("/").addChild("test");
-        test.addChild("a").setProperty("tags", of("a","b"), Type.STRINGS);
+        test.addChild("a").setProperty("tags", of("a", "b"), Type.STRINGS);
         test.addChild("b").setProperty("tags", of("a","c"), Type.STRINGS);
         root.commit();
 
@@ -364,8 +364,8 @@ public class LucenePropertyIndexTest ext
         String explain = explain(q);
         System.out.println(explain);
         String luceneQuery = explain.substring(0, explain.indexOf('\n'));
-        assertEquals("[nt:unstructured] as [content] /* lucene:test1(/oak:index/test1) " + 
-                "+(tags:Products:A tags:Products:A/B) " + 
+        assertEquals("[nt:unstructured] as [content] /* lucene:test1(/oak:index/test1) " +
+                "+(tags:Products:A tags:Products:A/B) " +
                 "+(tags:DocTypes:A tags:DocTypes:B tags:DocTypes:C tags:ProblemType:A)",
                 luceneQuery);
     }
@@ -1287,6 +1287,7 @@ public class LucenePropertyIndexTest ext
         def.setProperty(REINDEX_PROPERTY_NAME, true);
         def.setProperty(LuceneIndexConstants.FULL_TEXT_ENABLED, false);
         def.setProperty(PropertyStates.createProperty(LuceneIndexConstants.INCLUDE_PROPERTY_NAMES, propNames, Type.STRINGS));
+        def.setProperty(LuceneIndexConstants.SAVE_DIR_LISTING, true);
         return index.getChild(INDEX_DEFINITIONS_NAME).getChild(name);
     }
 

Modified: jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectoryTest.java?rev=1680169&r1=1680168&r2=1680169&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectoryTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectoryTest.java Tue May 19 05:24:26 2015
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.Type;
@@ -38,6 +39,7 @@ import org.apache.lucene.store.IndexOutp
 import org.junit.Test;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Sets.newHashSet;
 import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
 import static org.apache.jackrabbit.oak.api.Type.BINARIES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_DATA_CHILD_NAME;
@@ -57,21 +59,21 @@ public class OakDirectoryTest {
 
     @Test
     public void writes_DefaultSetup() throws Exception{
-        Directory dir = createDir(builder);
+        Directory dir = createDir(builder, false);
         assertWrites(dir, IndexDefinition.DEFAULT_BLOB_SIZE);
     }
 
     @Test
     public void writes_CustomBlobSize() throws Exception{
         builder.setProperty(LuceneIndexConstants.BLOB_SIZE, 300);
-        Directory dir = createDir(builder);
+        Directory dir = createDir(builder, false);
         assertWrites(dir, 300);
     }
 
     @Test
     public void testCompatibility() throws Exception{
         builder.setProperty(LuceneIndexConstants.BLOB_SIZE, OakDirectory.DEFAULT_BLOB_SIZE);
-        Directory dir = createDir(builder);
+        Directory dir = createDir(builder, false);
         byte[] data = assertWrites(dir, OakDirectory.DEFAULT_BLOB_SIZE);
 
         NodeBuilder testNode = builder.child(INDEX_DATA_CHILD_NAME).child("test");
@@ -90,7 +92,7 @@ public class OakDirectoryTest {
 
     @Test //OAK-2388
     public void testOverflow() throws Exception{
-        Directory dir = createDir(builder);
+        Directory dir = createDir(builder, false);
         NodeBuilder file = builder.child(INDEX_DATA_CHILD_NAME).child("test.txt");
         int blobSize = 32768;
         int dataSize = 90844;
@@ -103,7 +105,23 @@ public class OakDirectoryTest {
         file.setProperty(PropertyStates.createProperty("jcr:data", blobs, Type.BINARIES));
 
         IndexInput input  = dir.openInput("test.txt", IOContext.DEFAULT);
-        assertEquals((long)blobSize * (dataSize - 1), input.length());
+        assertEquals((long) blobSize * (dataSize - 1), input.length());
+    }
+
+    @Test
+    public void saveListing() throws Exception{
+        builder.setProperty(LuceneIndexConstants.SAVE_DIR_LISTING, true);
+        Directory dir = createDir(builder, false);
+        Set<String> fileNames = newHashSet();
+        for (int i = 0; i < 10; i++) {
+            String fileName = "foo" + i;
+            createFile(dir, fileName);
+            fileNames.add(fileName);
+        }
+        dir.close();
+
+        dir = createDir(builder, true);
+        assertEquals(fileNames, newHashSet(dir.listAll()));
     }
 
     byte[] assertWrites(Directory dir, int blobSize) throws IOException {
@@ -132,8 +150,18 @@ public class OakDirectoryTest {
         return data;
     }
 
-    private Directory createDir(NodeBuilder builder){
-        return new OakDirectory(builder.child(INDEX_DATA_CHILD_NAME), new IndexDefinition(root, builder.getNodeState()));
+    private int createFile(Directory dir, String fileName) throws IOException {
+        int size = rnd.nextInt(1000) + 1;
+        byte[] data = randomBytes(size);
+        IndexOutput o = dir.createOutput(fileName, IOContext.DEFAULT);
+        o.writeBytes(data, data.length);
+        o.close();
+        return size;
+    }
+
+    private Directory createDir(NodeBuilder builder, boolean readOnly){
+        return new OakDirectory(builder.child(INDEX_DATA_CHILD_NAME),
+                new IndexDefinition(root, builder.getNodeState()), readOnly);
     }
 
     byte[] randomBytes(int size) {