Author: tommaso
Date: Wed May 27 07:27:28 2015
New Revision: 1681920

URL: http://svn.apache.org/r1681920
Log:
OAK-2799 - close cloned OakIndexInput instances on close of main one

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/OakDirectory.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 Wed May 27 07:27:28 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,1675054-1675055,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677939,1677991,1678173,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679235,1680182,1680222,1680232,1680236,1680461,1680805-1680806,1680903,1681282,1681767
+/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,1675054-1675055,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677939,1677991,1678173,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679235,1680182,1680222,1680232,1680236,1680461,1680805-1680806,1680903,1681282,1681767,1681918
 /jackrabbit/trunk:1345480

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=1681920&r1=1681919&r2=1681920&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
 Wed May 27 07:27:28 2015
@@ -21,6 +21,7 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -33,6 +34,7 @@ import org.apache.jackrabbit.oak.api.Pro
 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.AlreadyClosedException;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.IOContext;
 import org.apache.lucene.store.IndexInput;
@@ -40,6 +42,7 @@ 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.apache.lucene.util.WeakIdentityMap;
 import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -364,29 +367,41 @@ class OakDirectory extends Directory {
     private static class OakIndexInput extends IndexInput {
 
         private final OakIndexFile file;
+        private boolean isClone = false;
+        private final WeakIdentityMap<OakIndexInput, Boolean> clones;
 
         public OakIndexInput(String name, NodeBuilder file) {
             super(name);
             this.file = new OakIndexFile(name, file);
+            clones = WeakIdentityMap.newConcurrentHashMap();
         }
 
         private OakIndexInput(OakIndexInput that) {
             super(that.toString());
             this.file = new OakIndexFile(that.file);
+            clones = null;
         }
 
         @Override
         public OakIndexInput clone() {
-            return new OakIndexInput(this);
+            // TODO : shouldn't we call super#clone ?
+            OakIndexInput clonedIndexInput = new OakIndexInput(this);
+            clonedIndexInput.isClone = true;
+            if (clones != null) {
+                clones.put(clonedIndexInput, Boolean.TRUE);
+            }
+            return clonedIndexInput;
         }
 
         @Override
         public void readBytes(byte[] b, int o, int n) throws IOException {
+            checkNotClosed();
             file.readBytes(b, o, n);
         }
 
         @Override
         public byte readByte() throws IOException {
+            checkNotClosed();
             byte[] b = new byte[1];
             readBytes(b, 0, 1);
             return b[0];
@@ -394,16 +409,19 @@ class OakDirectory extends Directory {
 
         @Override
         public void seek(long pos) throws IOException {
+            checkNotClosed();
             file.seek(pos);
         }
 
         @Override
         public long length() {
+            checkNotClosed();
             return file.length;
         }
 
         @Override
         public long getFilePointer() {
+            checkNotClosed();
             return file.position;
         }
 
@@ -411,6 +429,20 @@ class OakDirectory extends Directory {
         public void close() {
             file.blob = null;
             file.data = null;
+
+            if (clones != null) {
+                for (Iterator<OakIndexInput> it = clones.keyIterator(); 
it.hasNext();) {
+                    final OakIndexInput clone = it.next();
+                    assert clone.isClone;
+                    clone.close();
+                }
+            }
+        }
+
+        private void checkNotClosed() {
+            if (file.blob == null && file.data == null) {
+                throw new AlreadyClosedException("Already closed: " + this);
+            }
         }
 
     }

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=1681920&r1=1681919&r2=1681920&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
 Wed May 27 07:27:28 2015
@@ -32,6 +32,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.IOContext;
 import org.apache.lucene.store.IndexInput;
@@ -47,6 +48,7 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 public class OakDirectoryTest {
     private Random rnd = new Random();
@@ -169,4 +171,125 @@ public class OakDirectoryTest {
         rnd.nextBytes(data);
         return data;
     }
+
+    @Test
+    public void testCloseOnOriginalIndexInput() throws Exception {
+        Directory dir = createDir(builder, false);
+        NodeBuilder file = 
builder.child(INDEX_DATA_CHILD_NAME).child("test.txt");
+        int dataSize = 1024;
+        List<? super Blob> blobs = new ArrayList<Blob>(dataSize);
+        for (int i = 0; i < dataSize; i++) {
+            blobs.add(new ArrayBasedBlob(new byte[0]));
+        }
+        file.setProperty(PropertyStates.createProperty("jcr:data", blobs, 
Type.BINARIES));
+        IndexInput input = dir.openInput("test.txt", IOContext.DEFAULT);
+        input.close();
+        assertClosed(input);
+    }
+
+    @Test
+    public void testCloseOnClonedIndexInputs() throws Exception {
+        Directory dir = createDir(builder, false);
+        NodeBuilder file = 
builder.child(INDEX_DATA_CHILD_NAME).child("test.txt");
+        int dataSize = 1024;
+        List<? super Blob> blobs = new ArrayList<Blob>(dataSize);
+        for (int i = 0; i < dataSize; i++) {
+            blobs.add(new ArrayBasedBlob(new byte[0]));
+        }
+        file.setProperty(PropertyStates.createProperty("jcr:data", blobs, 
Type.BINARIES));
+        IndexInput input = dir.openInput("test.txt", IOContext.DEFAULT);
+        IndexInput clone1 = input.clone();
+        IndexInput clone2 = input.clone();
+        input.close();
+        assertClosed(input);
+        assertClosed(clone1);
+        assertClosed(clone2);
+    }
+
+    private void assertClosed(IndexInput input) throws IOException {
+        try {
+            input.length();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.seek(0);
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.getFilePointer();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readInt();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readShort();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readLong();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readByte();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readString();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readStringSet();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readStringStringMap();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readVInt();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readVLong();
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readBytes(null, 0, 0);
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+        try {
+            input.readBytes(null, 0, 0, false);
+            fail("cannot use IndexInput once closed");
+        } catch (AlreadyClosedException e) {
+            // expected exception
+        }
+    }
 }


Reply via email to