Index: src/test/org/apache/lucene/store/MockRAMInputStream.java =================================================================== --- src/test/org/apache/lucene/store/MockRAMInputStream.java (revision 0) +++ src/test/org/apache/lucene/store/MockRAMInputStream.java (revision 0) @@ -0,0 +1,62 @@ +package org.apache.lucene.store; + +/** + * 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. + */ + +/** + * Used by MockRAMDirectory to create an input stream that + * keeps track of when it's been closed. + */ + +public class MockRAMInputStream extends RAMInputStream { + private MockRAMDirectory dir; + private String name; + + /** Construct an empty output buffer. */ + public MockRAMInputStream(MockRAMDirectory dir, String name, RAMFile f) { + super(f); + this.name = name; + this.dir = dir; + } + + public void close() { + super.close(); + synchronized(dir.openFiles) { + Integer v = (Integer) dir.openFiles.get(name); + if (v.intValue() == 1) { + dir.openFiles.remove(name); + } else { + v = new Integer(v.intValue()-1); + dir.openFiles.put(name, v); + } + } + } + + public Object clone() { + MockRAMInputStream clone = (MockRAMInputStream) super.clone(); + synchronized(dir.openFiles) { + if (dir.openFiles.containsKey(name)) { + Integer v = (Integer) dir.openFiles.get(name); + v = new Integer(v.intValue()+1); + dir.openFiles.put(name, v); + } else { + throw new RuntimeException("BUG: cloned file was not open?"); + } + } + return clone; + } +} Property changes on: src/test/org/apache/lucene/store/MockRAMInputStream.java ___________________________________________________________________ Name: svn:eol-style + native Index: src/test/org/apache/lucene/store/MockRAMDirectory.java =================================================================== --- src/test/org/apache/lucene/store/MockRAMDirectory.java (revision 514122) +++ src/test/org/apache/lucene/store/MockRAMDirectory.java (working copy) @@ -19,8 +19,11 @@ import java.io.IOException; import java.io.File; +import java.io.FileNotFoundException; import java.util.Iterator; import java.util.Random; +import java.util.Map; +import java.util.HashMap; /** * This is a subclass of RAMDirectory that adds methods @@ -35,18 +38,37 @@ long maxUsedSize; double randomIOExceptionRate; Random randomState; + boolean noDeleteOpenFile = true; + // NOTE: we cannot initialize the Map here due to the + // order in which our constructor actually does this + // member initialization vs when it calls super. It seems + // like super is called, then our members are initialized: + Map openFiles; + public MockRAMDirectory() throws IOException { super(); + if (openFiles == null) { + openFiles = new HashMap(); + } } public MockRAMDirectory(String dir) throws IOException { super(dir); + if (openFiles == null) { + openFiles = new HashMap(); + } } public MockRAMDirectory(Directory dir) throws IOException { super(dir); + if (openFiles == null) { + openFiles = new HashMap(); + } } public MockRAMDirectory(File dir) throws IOException { super(dir); + if (openFiles == null) { + openFiles = new HashMap(); + } } public void setMaxSizeInBytes(long maxSize) { @@ -68,6 +90,17 @@ } /** + * Emulate windows whereby deleting an open file is not + * allowed (raise IOException). + */ + public void setNoDeleteOpenFile(boolean value) { + this.noDeleteOpenFile = value; + } + public boolean getNoDeleteOpenFile() { + return noDeleteOpenFile; + } + + /** * If 0.0, no exceptions will be thrown. Else this should * be a double 0.0 - 1.0. We will randomly throw an * IOException on the first write to an OutputStream based @@ -91,7 +124,26 @@ } } + public synchronized void deleteFile(String name) throws IOException { + synchronized(openFiles) { + if (noDeleteOpenFile && openFiles.containsKey(name)) { + throw new IOException("MockRAMDirectory: file \"" + name + "\" is still open: cannot delete"); + } + } + super.deleteFile(name); + } + public IndexOutput createOutput(String name) { + if (openFiles == null) { + openFiles = new HashMap(); + } + synchronized(openFiles) { + if (noDeleteOpenFile && openFiles.containsKey(name)) { + // RuntimeException instead of IOException because + // super() does not throw IOException currently: + throw new RuntimeException("MockRAMDirectory: file \"" + name + "\" is still open: cannot overwrite"); + } + } RAMFile file = new RAMFile(this); synchronized (this) { RAMFile existing = (RAMFile)fileMap.get(name); @@ -105,6 +157,26 @@ return new MockRAMOutputStream(this, file); } + public IndexInput openInput(String name) throws IOException { + synchronized(openFiles) { + if (openFiles.containsKey(name)) { + Integer v = (Integer) openFiles.get(name); + v = new Integer(v.intValue()+1); + openFiles.put(name, v); + } else { + openFiles.put(name, new Integer(1)); + } + } + + RAMFile file; + synchronized (this) { + file = (RAMFile)fileMap.get(name); + } + if (file == null) + throw new FileNotFoundException(name); + return new MockRAMInputStream(this, name, file); + } + /** Provided for testing purposes. Use sizeInBytes() instead. */ public synchronized final long getRecomputedSizeInBytes() { long size = 0; Index: src/test/org/apache/lucene/search/TestMultiSearcher.java =================================================================== --- src/test/org/apache/lucene/search/TestMultiSearcher.java (revision 514162) +++ src/test/org/apache/lucene/search/TestMultiSearcher.java (working copy) @@ -149,7 +149,6 @@ // no exception should happen at this point Document d = hits2.doc(i); } - mSearcher2.close(); // test the subSearcher() method: Query subSearcherQuery = parser.parse("id:doc1"); @@ -161,6 +160,7 @@ hits2 = mSearcher2.search(subSearcherQuery); assertEquals(1, hits2.length()); assertEquals(1, mSearcher2.subSearcher(hits2.id(0))); // hit from searchers2[1] + mSearcher2.close(); //-------------------------------------------------------------------- // scenario 3 Index: src/test/org/apache/lucene/index/TestIndexReader.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexReader.java (revision 514122) +++ src/test/org/apache/lucene/index/TestIndexReader.java (working copy) @@ -386,6 +386,38 @@ } + // Make sure you can set norms & commit, and there are + // no extra norms files left: + public void testWritingNormsNoReader() throws IOException + { + Directory dir = new MockRAMDirectory(); + IndexWriter writer = null; + IndexReader reader = null; + Term searchTerm = new Term("content", "aaa"); + + // add 1 documents with term : aaa + writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true); + writer.setUseCompoundFile(false); + addDoc(writer, searchTerm.text()); + writer.close(); + + // now open reader & set norm for doc 0 (writes to + // _0_1.s0) + reader = IndexReader.open(dir); + reader.setNorm(0, "content", (float) 2.0); + reader.close(); + + // now open reader again & set norm for doc 0 (writes to _0_2.s0) + reader = IndexReader.open(dir); + reader.setNorm(0, "content", (float) 2.0); + reader.close(); + assertFalse("failed to remove first generation norms file on writing second generation", + dir.fileExists("_0_1.s0")); + + dir.close(); + } + + public void testDeleteReaderWriterConflictUnoptimized() throws IOException{ deleteReaderWriterConflict(false); } Index: src/java/org/apache/lucene/index/SegmentReader.java =================================================================== --- src/java/org/apache/lucene/index/SegmentReader.java (revision 514122) +++ src/java/org/apache/lucene/index/SegmentReader.java (working copy) @@ -92,6 +92,13 @@ } this.dirty = false; } + + public void close() throws IOException { + if (in != null) { + in.close(); + in = null; + } + } } private Hashtable norms = new Hashtable(); @@ -457,6 +464,7 @@ byte[] bytes = new byte[maxDoc()]; norms(field, bytes, 0); norm.bytes = bytes; // cache it + norm.close(); } return norm.bytes; } @@ -527,7 +535,7 @@ Enumeration enumerator = norms.elements(); while (enumerator.hasMoreElements()) { Norm norm = (Norm) enumerator.nextElement(); - norm.in.close(); + norm.close(); } } } Index: src/java/org/apache/lucene/store/RAMDirectory.java =================================================================== --- src/java/org/apache/lucene/store/RAMDirectory.java (revision 514122) +++ src/java/org/apache/lucene/store/RAMDirectory.java (working copy) @@ -173,7 +173,7 @@ /** Removes an existing file in the directory. * @throws IOException if the file does not exist */ - public synchronized final void deleteFile(String name) throws IOException { + public synchronized void deleteFile(String name) throws IOException { RAMFile file = (RAMFile)fileMap.get(name); if (file!=null) { fileMap.remove(name); @@ -215,7 +215,7 @@ } /** Returns a stream reading an existing file. */ - public final IndexInput openInput(String name) throws IOException { + public IndexInput openInput(String name) throws IOException { RAMFile file; synchronized (this) { file = (RAMFile)fileMap.get(name);