You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@lucene.apache.org by us...@apache.org on 2009/06/10 12:09:23 UTC

svn commit: r783280 - in /lucene/java/trunk: ./ src/java/org/apache/lucene/index/ src/test/org/apache/lucene/index/

Author: uschindler
Date: Wed Jun 10 10:09:20 2009
New Revision: 783280

URL: http://svn.apache.org/viewvc?rev=783280&view=rev
Log:
LUCENE-1453: Remove the complicated closeDir handling from DirectoryReader which no longer "owns" the FSDirectory. A FilterIndexReader, that is wrapped around the DirectoryReader by IndexReader.open(), will handle the closing of the dir. This wrapper and all File/String IndexReader.open()'s will be removed in 3.0.

Added:
    lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryOwningReader.java   (with props)
Modified:
    lucene/java/trunk/CHANGES.txt
    lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryReader.java
    lucene/java/trunk/src/java/org/apache/lucene/index/IndexReader.java
    lucene/java/trunk/src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java
    lucene/java/trunk/src/test/org/apache/lucene/index/TestIndexReaderClone.java

Modified: lucene/java/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/java/trunk/CHANGES.txt?rev=783280&r1=783279&r2=783280&view=diff
==============================================================================
--- lucene/java/trunk/CHANGES.txt (original)
+++ lucene/java/trunk/CHANGES.txt Wed Jun 10 10:09:20 2009
@@ -481,10 +481,12 @@
 5. LUCENE-1442: Multiple-valued NOT_ANALYZED fields can double-count
    token offsets.  (Mike McCandless)
 
-6. LUCENE-1453: Ensure IndexReader.reopen() does not result in
-   incorrectly closing the shared FSDirectory.  This bug would only
-   happen if you use IndexReader.open with a File or String argument.
-   (Mark Miller via Mike McCandless)
+6. LUCENE-1453: Ensure IndexReader.reopen()/clone() does not result in
+   incorrectly closing the shared FSDirectory. This bug would only
+   happen if you use IndexReader.open() with a File or String argument.
+   The returned readers are wrapped by a FilterIndexReader that
+   correctly handles closing of directory after reopen()/clone(). 
+   (Mark Miller, Uwe Schindler, Mike McCandless)
 
 7. LUCENE-1457: Fix possible overflow bugs during binary
    searches. (Mark Miller via Mike McCandless)

Added: lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryOwningReader.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryOwningReader.java?rev=783280&view=auto
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryOwningReader.java (added)
+++ lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryOwningReader.java Wed Jun 10 10:09:20 2009
@@ -0,0 +1,105 @@
+package org.apache.lucene.index;
+
+/**
+ * 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.
+ */
+
+import java.io.IOException;
+
+/** 
+ * This class keeps track of closing the underlying directory. It is used to wrap
+ * DirectoryReaders, that are created using a String/File parameter
+ * in IndexReader.open() with FSDirectory.getDirectory().
+ * @deprecated This helper class is removed with all String/File
+ * IndexReader.open() methods in Lucene 3.0
+ */
+final class DirectoryOwningReader extends FilterIndexReader implements Cloneable {
+
+  DirectoryOwningReader(final IndexReader in) {
+    super(in);
+    this.ref = new SegmentReader.Ref();
+    assert this.ref.refCount() == 1;
+  }
+
+  private DirectoryOwningReader(final IndexReader in, final SegmentReader.Ref ref) {
+    super(in);
+    this.ref = ref;
+    ref.incRef();
+  }
+
+  public IndexReader reopen() throws CorruptIndexException, IOException {
+    ensureOpen();
+    final IndexReader r = in.reopen();
+    if (r != in)
+      return new DirectoryOwningReader(r, ref);
+    return this;
+  }
+
+  public IndexReader reopen(boolean openReadOnly) throws CorruptIndexException, IOException {
+    ensureOpen();
+    final IndexReader r = in.reopen(openReadOnly);
+    if (r != in)
+      return new DirectoryOwningReader(r, ref);
+    return this;
+  }
+
+  public IndexReader reopen(final IndexCommit commit) throws CorruptIndexException, IOException {
+    ensureOpen();
+    final IndexReader r = in.reopen(commit);
+    if (r != in)
+      return new DirectoryOwningReader(r, ref);
+    return this;
+  }
+
+  public Object clone() {
+    ensureOpen();
+    return new DirectoryOwningReader((IndexReader) in.clone(), ref);
+  }
+
+  public IndexReader clone(boolean openReadOnly) throws CorruptIndexException, IOException {
+    ensureOpen();
+    return new DirectoryOwningReader(in.clone(openReadOnly), ref);
+  }
+
+  protected void doClose() throws IOException {
+    IOException ioe = null;
+    // close the reader, record exception
+    try {
+      super.doClose();
+    } catch (IOException e) {
+      ioe = e;
+    }
+    // close the directory, record exception
+    if (ref.decRef() == 0) {
+      try {
+        in.directory().close();
+      } catch (IOException e) {
+        if (ioe == null) ioe = e;
+      }
+    }
+    // throw the first exception
+    if (ioe != null) throw ioe;
+  }
+
+  /**
+   * This member contains the ref counter, that is passed to each instance after cloning/reopening,
+   * and is global to all DirectoryOwningReader derived from the original one.
+   * This reuses the class {@link SegmentReader.Ref}
+   */
+  private final SegmentReader.Ref ref;
+
+}
+

Propchange: lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryOwningReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryReader.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryReader.java?rev=783280&r1=783279&r2=783280&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryReader.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/index/DirectoryReader.java Wed Jun 10 10:09:20 2009
@@ -36,7 +36,6 @@
 import org.apache.lucene.store.Lock;
 import org.apache.lucene.store.LockObtainFailedException;
 import org.apache.lucene.store.AlreadyClosedException;
-import org.apache.lucene.store.FSDirectory;
 
 /** 
  * An IndexReader which reads indexes with multiple segments.
@@ -44,7 +43,6 @@
 class DirectoryReader extends IndexReader implements Cloneable {
   protected Directory directory;
   protected boolean readOnly;
-  protected boolean closeDirectory;
 
   IndexWriter writer;
 
@@ -64,48 +62,23 @@
   private int numDocs = -1;
   private boolean hasDeletions = false;
 
-  static IndexReader open(final Directory directory, final boolean closeDirectory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly) throws CorruptIndexException, IOException {
-    SegmentInfos.FindSegmentsFile finder = new SegmentInfos.FindSegmentsFile(directory) {
-
+  static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly) throws CorruptIndexException, IOException {
+    return (IndexReader) new SegmentInfos.FindSegmentsFile(directory) {
       protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
-
         SegmentInfos infos = new SegmentInfos();
         infos.read(directory, segmentFileName);
-
         if (readOnly)
-          return new ReadOnlyDirectoryReader(directory, infos, deletionPolicy, closeDirectory);
+          return new ReadOnlyDirectoryReader(directory, infos, deletionPolicy);
         else
-          return new DirectoryReader(directory, infos, deletionPolicy, closeDirectory, false);
+          return new DirectoryReader(directory, infos, deletionPolicy, false);
       }
-    };
-
-    IndexReader reader = null;
-    try {
-      reader = (IndexReader) finder.run(commit);
-    } finally {
-      // We passed false above for closeDirectory so that
-      // the directory would not be closed before we were
-      // done retrying, so at this point if we truly failed
-      // to open a reader, which means an exception is being
-      // thrown, then close the directory now:
-      if (reader == null && closeDirectory) {
-        try {
-          directory.close();
-        } catch (IOException ioe) {
-          // suppress, so we keep throwing original failure
-          // from opening the reader
-        }
-      }
-    }
-
-    return reader;
+    }.run(commit);
   }
 
   /** Construct reading the named set of readers. */
-  DirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, boolean closeDirectory, boolean readOnly) throws IOException {
+  DirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, boolean readOnly) throws IOException {
     this.directory = directory;
     this.readOnly = readOnly;
-    this.closeDirectory = closeDirectory;
     this.segmentInfos = sis;
     this.deletionPolicy = deletionPolicy;
 
@@ -147,7 +120,6 @@
   DirectoryReader(IndexWriter writer, SegmentInfos infos) throws IOException {
     this.directory = writer.getDirectory();
     this.readOnly = true;
-    this.closeDirectory = false;
     this.segmentInfos = infos;
     if (!readOnly) {
       // We assume that this segments_N was previously
@@ -198,11 +170,10 @@
   }
 
   /** This contructor is only used for {@link #reopen()} */
-  DirectoryReader(Directory directory, SegmentInfos infos, boolean closeDirectory, SegmentReader[] oldReaders, int[] oldStarts,
+  DirectoryReader(Directory directory, SegmentInfos infos, SegmentReader[] oldReaders, int[] oldStarts,
                      Map oldNormsCache, boolean readOnly, boolean doClone) throws IOException {
     this.directory = directory;
     this.readOnly = readOnly;
-    this.closeDirectory = closeDirectory;
     this.segmentInfos = infos;
     if (!readOnly) {
       // We assume that this segments_N was previously
@@ -347,7 +318,6 @@
     DirectoryReader newReader = doReopen((SegmentInfos) segmentInfos.clone(), true, openReadOnly);
 
     if (this != newReader) {
-      newReader.closeDirectory = closeDirectory;
       newReader.deletionPolicy = deletionPolicy;
     }
     newReader.writer = writer;
@@ -445,54 +415,21 @@
       }
     }
 
-    final SegmentInfos.FindSegmentsFile finder = new SegmentInfos.FindSegmentsFile(directory) {
-
+    return (IndexReader) new SegmentInfos.FindSegmentsFile(directory) {
       protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
         SegmentInfos infos = new SegmentInfos();
         infos.read(directory, segmentFileName);
         return doReopen(infos, false, openReadOnly);
       }
-    };
-
-    DirectoryReader reader = null;
-
-    /* TODO: Remove this in 3.0 - the directory is then
-     * no longer owned by the IndexReader and must not be
-     * closed.
-     * While trying to reopen, we temporarily mark our
-     * closeDirectory as false.  This way any exceptions hit
-     * partway while opening the reader, which is expected
-     * eg if writer is committing, won't close our
-     * directory.  We restore this value below:
-     */
-    final boolean myCloseDirectory = closeDirectory; // @deprectated
-    closeDirectory = false;
-
-    try {
-      reader = (DirectoryReader) finder.run(commit);
-    } finally {
-      if (myCloseDirectory) {
-        assert directory instanceof FSDirectory;
-        // Restore my closeDirectory
-        closeDirectory = true;
-        if (reader != null && reader != this) {
-          // Success, and a new reader was actually opened
-          reader.closeDirectory = true;
-          // Clone the directory
-          reader.directory = FSDirectory.getDirectory(((FSDirectory) directory).getFile());
-        }
-      }
-    }
-
-    return reader;
+    }.run(commit);
   }
 
   private synchronized DirectoryReader doReopen(SegmentInfos infos, boolean doClone, boolean openReadOnly) throws CorruptIndexException, IOException {
     DirectoryReader reader;
 	  if (openReadOnly) {
-      reader = new ReadOnlyDirectoryReader(directory, infos, closeDirectory, subReaders, starts, normsCache, doClone);
+      reader = new ReadOnlyDirectoryReader(directory, infos, subReaders, starts, normsCache, doClone);
     } else {
-      reader = new DirectoryReader(directory, infos, closeDirectory, subReaders, starts, normsCache, false, doClone);
+      reader = new DirectoryReader(directory, infos, subReaders, starts, normsCache, false, doClone);
     }
     reader.setDisableFakeNorms(getDisableFakeNorms());
     return reader;
@@ -868,11 +805,17 @@
   }
 
   protected synchronized void doClose() throws IOException {
-    for (int i = 0; i < subReaders.length; i++)
-      subReaders[i].decRef();
-
-    if (closeDirectory)
-      directory.close();
+    IOException ioe = null;
+    for (int i = 0; i < subReaders.length; i++) {
+      // try to close each reader, even if an exception is thrown
+      try {
+        subReaders[i].decRef();
+      } catch (IOException e) {
+        if (ioe == null) ioe = e;
+      }
+    }
+    // throw the first exception
+    if (ioe != null) throw ioe;
   }
 
   public Collection getFieldNames (IndexReader.FieldOption fieldNames) {

Modified: lucene/java/trunk/src/java/org/apache/lucene/index/IndexReader.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/index/IndexReader.java?rev=783280&r1=783279&r2=783280&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/index/IndexReader.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/index/IndexReader.java Wed Jun 10 10:09:20 2009
@@ -199,7 +199,7 @@
       throw new AlreadyClosedException("this IndexReader is closed");
     }
   }
-
+  
   /** Returns a read/write IndexReader reading the index in an FSDirectory in the named
    *  path.
    * @throws CorruptIndexException if the index is corrupt
@@ -208,7 +208,7 @@
    * Use {@link #open(Directory, boolean)} instead
    * @param path the path to the index directory */
   public static IndexReader open(String path) throws CorruptIndexException, IOException {
-    return open(FSDirectory.getDirectory(path), true, null, null, false);
+    return new DirectoryOwningReader(open(FSDirectory.getDirectory(path), null, null, false));
   }
 
   /** Returns an IndexReader reading the index in an
@@ -225,7 +225,7 @@
    * Use {@link #open(Directory, boolean)} instead
    */
   public static IndexReader open(String path, boolean readOnly) throws CorruptIndexException, IOException {
-    return open(FSDirectory.getDirectory(path), true, null, null, readOnly);
+    return new DirectoryOwningReader(open(FSDirectory.getDirectory(path), null, null, readOnly));
   }
 
   /** Returns a read/write IndexReader reading the index in an FSDirectory in the named
@@ -237,7 +237,7 @@
    * Use {@link #open(Directory, boolean)} instead
    */
   public static IndexReader open(File path) throws CorruptIndexException, IOException {
-    return open(FSDirectory.getDirectory(path), true, null, null, false);
+    return new DirectoryOwningReader(open(FSDirectory.getDirectory(path), null, null, false));
   }
 
   /** Returns an IndexReader reading the index in an
@@ -254,7 +254,7 @@
    * Use {@link #open(Directory, boolean)} instead
    */
   public static IndexReader open(File path, boolean readOnly) throws CorruptIndexException, IOException {
-    return open(FSDirectory.getDirectory(path), true, null, null, readOnly);
+    return new DirectoryOwningReader(open(FSDirectory.getDirectory(path), null, null, readOnly));
   }
 
   /** Returns a read/write IndexReader reading the index in
@@ -266,7 +266,7 @@
    * Use {@link #open(Directory, boolean)} instead
    */
   public static IndexReader open(final Directory directory) throws CorruptIndexException, IOException {
-    return open(directory, false, null, null, false);
+    return open(directory, null, null, false);
   }
 
   /** Returns an IndexReader reading the index in the given
@@ -280,7 +280,7 @@
    * @throws IOException if there is a low-level IO error
    */
   public static IndexReader open(final Directory directory, boolean readOnly) throws CorruptIndexException, IOException {
-    return open(directory, false, null, null, readOnly);
+    return open(directory, null, null, readOnly);
   }
 
   /** Expert: returns a read/write IndexReader reading the index in the given
@@ -292,7 +292,7 @@
    * @throws IOException if there is a low-level IO error
    */
   public static IndexReader open(final IndexCommit commit) throws CorruptIndexException, IOException {
-    return open(commit.getDirectory(), false, null, commit, false);
+    return open(commit.getDirectory(), null, commit, false);
   }
 
   /** Expert: returns an IndexReader reading the index in the given
@@ -306,7 +306,7 @@
    * @throws IOException if there is a low-level IO error
    */
   public static IndexReader open(final IndexCommit commit, boolean readOnly) throws CorruptIndexException, IOException {
-    return open(commit.getDirectory(), false, null, commit, readOnly);
+    return open(commit.getDirectory(), null, commit, readOnly);
   }
 
   /** Expert: returns a read/write IndexReader reading the index in the given
@@ -321,7 +321,7 @@
    * @throws IOException if there is a low-level IO error
    */
   public static IndexReader open(final Directory directory, IndexDeletionPolicy deletionPolicy) throws CorruptIndexException, IOException {
-    return open(directory, false, deletionPolicy, null, false);
+    return open(directory, deletionPolicy, null, false);
   }
 
   /** Expert: returns an IndexReader reading the index in
@@ -339,7 +339,7 @@
    * @throws IOException if there is a low-level IO error
    */
   public static IndexReader open(final Directory directory, IndexDeletionPolicy deletionPolicy, boolean readOnly) throws CorruptIndexException, IOException {
-    return open(directory, false, deletionPolicy, null, readOnly);
+    return open(directory, deletionPolicy, null, readOnly);
   }
 
   /** Expert: returns a read/write IndexReader reading the index in the given
@@ -357,7 +357,7 @@
    * @throws IOException if there is a low-level IO error
    */
   public static IndexReader open(final IndexCommit commit, IndexDeletionPolicy deletionPolicy) throws CorruptIndexException, IOException {
-    return open(commit.getDirectory(), false, deletionPolicy, commit, false);
+    return open(commit.getDirectory(), deletionPolicy, commit, false);
   }
 
   /** Expert: returns an IndexReader reading the index in
@@ -377,11 +377,11 @@
    * @throws IOException if there is a low-level IO error
    */
   public static IndexReader open(final IndexCommit commit, IndexDeletionPolicy deletionPolicy, boolean readOnly) throws CorruptIndexException, IOException {
-    return open(commit.getDirectory(), false, deletionPolicy, commit, readOnly);
+    return open(commit.getDirectory(), deletionPolicy, commit, readOnly);
   }
 
-  private static IndexReader open(final Directory directory, final boolean closeDirectory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly) throws CorruptIndexException, IOException {
-    return DirectoryReader.open(directory, closeDirectory, deletionPolicy, commit, readOnly);
+  private static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly) throws CorruptIndexException, IOException {
+    return DirectoryReader.open(directory, deletionPolicy, commit, readOnly);
   }
 
   /**
@@ -577,9 +577,11 @@
    */
   public static long getCurrentVersion(File directory) throws CorruptIndexException, IOException {
     Directory dir = FSDirectory.getDirectory(directory);
-    long version = getCurrentVersion(dir);
-    dir.close();
-    return version;
+    try {
+      return getCurrentVersion(dir);
+    } finally {
+      dir.close();
+    }
   }
 
   /**
@@ -1196,9 +1198,11 @@
    */
   public static boolean isLocked(String directory) throws IOException {
     Directory dir = FSDirectory.getDirectory(directory);
-    boolean result = isLocked(dir);
-    dir.close();
-    return result;
+    try {
+      return isLocked(dir);
+    } finally {
+      dir.close();
+    }
   }
 
   /**

Modified: lucene/java/trunk/src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java?rev=783280&r1=783279&r2=783280&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java Wed Jun 10 10:09:20 2009
@@ -23,12 +23,12 @@
 import java.util.Map;
 
 class ReadOnlyDirectoryReader extends DirectoryReader {
-  ReadOnlyDirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, boolean closeDirectory) throws IOException {
-    super(directory, sis, deletionPolicy, closeDirectory, true);
+  ReadOnlyDirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy) throws IOException {
+    super(directory, sis, deletionPolicy, true);
   }
 
-  ReadOnlyDirectoryReader(Directory directory, SegmentInfos infos, boolean closeDirectory, SegmentReader[] oldReaders, int[] oldStarts, Map oldNormsCache, boolean doClone) throws IOException {
-    super(directory, infos, closeDirectory, oldReaders, oldStarts, oldNormsCache, true, doClone);
+  ReadOnlyDirectoryReader(Directory directory, SegmentInfos infos, SegmentReader[] oldReaders, int[] oldStarts, Map oldNormsCache, boolean doClone) throws IOException {
+    super(directory, infos, oldReaders, oldStarts, oldNormsCache, true, doClone);
   }
   
   ReadOnlyDirectoryReader(IndexWriter writer, SegmentInfos infos) throws IOException {

Modified: lucene/java/trunk/src/test/org/apache/lucene/index/TestIndexReaderClone.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/index/TestIndexReaderClone.java?rev=783280&r1=783279&r2=783280&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/index/TestIndexReaderClone.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/index/TestIndexReaderClone.java Wed Jun 10 10:09:20 2009
@@ -17,6 +17,9 @@
  * limitations under the License.
  */
 
+import java.io.File;
+import java.io.IOException;
+
 import org.apache.lucene.index.SegmentReader.Norm;
 import org.apache.lucene.search.Similarity;
 import org.apache.lucene.analysis.SimpleAnalyzer;
@@ -25,7 +28,9 @@
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.LockObtainFailedException;
 import org.apache.lucene.store.MockRAMDirectory;
+import org.apache.lucene.store.FSDirectory;
 import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.store.AlreadyClosedException;
 
 /**
  * Tests cloning multiple types of readers, modifying the deletedDocs and norms
@@ -51,6 +56,34 @@
     dir1.close();
   }
 
+  // LUCENE-1453
+  public void testFSDirectoryClone() throws Exception {
+
+    String tempDir = System.getProperty("java.io.tmpdir");
+    if (tempDir == null)
+      throw new IOException("java.io.tmpdir undefined, cannot run test");
+    File indexDir2 = new File(tempDir, "FSDirIndexReaderClone");
+
+    Directory dir1 = FSDirectory.getDirectory(indexDir2);
+    TestIndexReaderReopen.createIndex(dir1, false);
+
+    IndexReader reader = IndexReader.open(indexDir2);
+    IndexReader readOnlyReader = (IndexReader) reader.clone();
+    reader.close();
+    readOnlyReader.close();
+
+    // Make sure we didn't pick up too many incRef's along
+    // the way -- this close should be the final close:
+    dir1.close();
+
+    try {
+      dir1.listAll();
+      fail("did not hit AlreadyClosedException");
+    } catch (AlreadyClosedException ace) {
+      // expected
+    }
+  }
+
   // open non-readOnly reader1, clone to non-readOnly
   // reader2, make sure we can change reader2
   public void testCloneNoChangesStillReadOnly() throws Exception {