Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 514377) +++ CHANGES.txt (working copy) @@ -20,6 +20,11 @@ classes, package-private again (they were unnecessarily made public as part of LUCENE-701). (Mike McCandless) + 3. LUCENE-818: changed certain public methods of IndexWriter, + IndexReader (and its subclasses), FieldsReader to throw + AlreadyClosedException if they are accessed after being closed. + (Mike McCandless) + Bug fixes 1. LUCENE-804: Fixed build.xml to pack a fully compilable src dist. (Doron Cohen) Index: src/test/org/apache/lucene/index/TestMultiReader.java =================================================================== --- src/test/org/apache/lucene/index/TestMultiReader.java (revision 514377) +++ src/test/org/apache/lucene/index/TestMultiReader.java (working copy) @@ -85,15 +85,18 @@ assertEquals( 2, reader.numDocs() ); // Ensure undeleteAll survives commit/close/reopen: - reader.commit(); reader.close(); sis.read(dir); + + // Must re-open the readers from setUp(): + readers[0] = SegmentReader.get(new SegmentInfo("seg-1", 1, dir)); + readers[1] = SegmentReader.get(new SegmentInfo("seg-2", 1, dir)); + reader = new MultiReader(dir, sis, false, readers); assertEquals( 2, reader.numDocs() ); reader.deleteDocument(0); assertEquals( 1, reader.numDocs() ); - reader.commit(); reader.close(); sis.read(dir); reader = new MultiReader(dir, sis, false, readers); Index: src/test/org/apache/lucene/index/TestFieldsReader.java =================================================================== --- src/test/org/apache/lucene/index/TestFieldsReader.java (revision 514377) +++ src/test/org/apache/lucene/index/TestFieldsReader.java (working copy) @@ -23,6 +23,7 @@ import org.apache.lucene.search.Similarity; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.RAMDirectory; +import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.util._TestUtil; import java.io.File; @@ -133,6 +134,36 @@ } } + public void testLazyFieldsAfterClose() throws Exception { + assertTrue(dir != null); + assertTrue(fieldInfos != null); + FieldsReader reader = new FieldsReader(dir, "test", fieldInfos); + assertTrue(reader != null); + assertTrue(reader.size() == 1); + Set loadFieldNames = new HashSet(); + loadFieldNames.add(DocHelper.TEXT_FIELD_1_KEY); + loadFieldNames.add(DocHelper.TEXT_FIELD_UTF1_KEY); + Set lazyFieldNames = new HashSet(); + lazyFieldNames.add(DocHelper.LARGE_LAZY_FIELD_KEY); + lazyFieldNames.add(DocHelper.LAZY_FIELD_KEY); + lazyFieldNames.add(DocHelper.LAZY_FIELD_BINARY_KEY); + lazyFieldNames.add(DocHelper.TEXT_FIELD_UTF2_KEY); + lazyFieldNames.add(DocHelper.COMPRESSED_TEXT_FIELD_2_KEY); + SetBasedFieldSelector fieldSelector = new SetBasedFieldSelector(loadFieldNames, lazyFieldNames); + Document doc = reader.doc(0, fieldSelector); + assertTrue("doc is null and it shouldn't be", doc != null); + Fieldable field = doc.getFieldable(DocHelper.LAZY_FIELD_KEY); + assertTrue("field is null and it shouldn't be", field != null); + assertTrue("field is not lazy and it should be", field.isLazy()); + reader.close(); + try { + String value = field.stringValue(); + fail("did not hit AlreadyClosedException as expected"); + } catch (AlreadyClosedException e) { + // expected + } + } + public void testLoadFirst() throws Exception { assertTrue(dir != null); assertTrue(fieldInfos != null); Index: src/test/org/apache/lucene/index/TestIndexReader.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexReader.java (revision 514377) +++ src/test/org/apache/lucene/index/TestIndexReader.java (working copy) @@ -26,6 +26,7 @@ import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.LockObtainFailedException; +import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.WhitespaceAnalyzer; import org.apache.lucene.document.Document; @@ -273,21 +274,21 @@ try { reader.deleteDocument(4); fail("deleteDocument after close failed to throw IOException"); - } catch (IOException e) { + } catch (AlreadyClosedException e) { // expected } try { reader.setNorm(5, "aaa", 2.0f); fail("setNorm after close failed to throw IOException"); - } catch (IOException e) { + } catch (AlreadyClosedException e) { // expected } try { reader.undeleteAll(); fail("undeleteAll after close failed to throw IOException"); - } catch (IOException e) { + } catch (AlreadyClosedException e) { // expected } } Index: src/test/org/apache/lucene/index/TestIndexWriter.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexWriter.java (revision 514377) +++ src/test/org/apache/lucene/index/TestIndexWriter.java (working copy) @@ -19,6 +19,7 @@ import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; +import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.MockRAMDirectory; import org.apache.lucene.store.LockFactory; @@ -653,6 +654,25 @@ } } + public void testChangesAfterClose() throws IOException { + Directory dir = new RAMDirectory(); + + IndexWriter writer = null; + + writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true); + addDoc(writer); + + // close + writer.close(); + try { + addDoc(writer); + fail("did not hit AlreadyClosedException"); + } catch (AlreadyClosedException e) { + // expected + } + } + + // Simulate a corrupt index by removing one of the cfs // files and make sure we get an IOException trying to // open the index: @@ -722,7 +742,6 @@ IndexSearcher searcher = new IndexSearcher(dir); Hits hits = searcher.search(new TermQuery(searchTerm)); assertEquals("did not get right number of hits", 100, hits.length()); - writer.close(); writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true); writer.close(); Index: src/java/org/apache/lucene/index/MultiReader.java =================================================================== --- src/java/org/apache/lucene/index/MultiReader.java (revision 514377) +++ src/java/org/apache/lucene/index/MultiReader.java (working copy) @@ -20,6 +20,7 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.FieldSelector; import org.apache.lucene.store.Directory; +import org.apache.lucene.store.AlreadyClosedException; import java.io.IOException; import java.util.Collection; @@ -72,19 +73,15 @@ } - /** Return an array of term frequency vectors for the specified document. - * The array contains a vector for each vectorized field in the document. - * Each vector vector contains term numbers and frequencies for all terms - * in a given vectorized field. - * If no such fields existed, the method returns null. - */ - public TermFreqVector[] getTermFreqVectors(int n) throws IOException { + public TermFreqVector[] getTermFreqVectors(int n) throws IOException, AlreadyClosedException { + ensureOpen(); int i = readerIndex(n); // find segment num return subReaders[i].getTermFreqVectors(n - starts[i]); // dispatch to segment } public TermFreqVector getTermFreqVector(int n, String field) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); int i = readerIndex(n); // find segment num return subReaders[i].getTermFreqVector(n - starts[i], field); } @@ -104,7 +101,8 @@ } // inherit javadoc - public Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException { + public Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); int i = readerIndex(n); // find segment num return subReaders[i].document(n - starts[i], fieldSelector); // dispatch to segment reader } @@ -114,7 +112,9 @@ return subReaders[i].isDeleted(n - starts[i]); // dispatch to segment reader } - public boolean hasDeletions() { return hasDeletions; } + public boolean hasDeletions() { + return hasDeletions; + } protected void doDelete(int n) throws CorruptIndexException, IOException { numDocs = -1; // invalidate cache @@ -152,7 +152,8 @@ return hi; } - public boolean hasNorms(String field) throws IOException { + public boolean hasNorms(String field) throws IOException, AlreadyClosedException { + ensureOpen(); for (int i = 0; i < subReaders.length; i++) { if (subReaders[i].hasNorms(field)) return true; } @@ -165,7 +166,8 @@ return ones; } - public synchronized byte[] norms(String field) throws IOException { + public synchronized byte[] norms(String field) throws IOException, AlreadyClosedException { + ensureOpen(); byte[] bytes = (byte[])normsCache.get(field); if (bytes != null) return bytes; // cache hit @@ -180,7 +182,8 @@ } public synchronized void norms(String field, byte[] result, int offset) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); byte[] bytes = (byte[])normsCache.get(field); if (bytes==null && !hasNorms(field)) bytes=fakeNorms(); if (bytes != null) // cache hit @@ -197,26 +200,31 @@ subReaders[i].setNorm(n-starts[i], field, value); // dispatch } - public TermEnum terms() throws IOException { + public TermEnum terms() throws IOException, AlreadyClosedException { + ensureOpen(); return new MultiTermEnum(subReaders, starts, null); } - public TermEnum terms(Term term) throws IOException { + public TermEnum terms(Term term) throws IOException, AlreadyClosedException { + ensureOpen(); return new MultiTermEnum(subReaders, starts, term); } - public int docFreq(Term t) throws IOException { + public int docFreq(Term t) throws IOException, AlreadyClosedException { + ensureOpen(); int total = 0; // sum freqs in segments for (int i = 0; i < subReaders.length; i++) total += subReaders[i].docFreq(t); return total; } - public TermDocs termDocs() throws IOException { + public TermDocs termDocs() throws IOException, AlreadyClosedException { + ensureOpen(); return new MultiTermDocs(subReaders, starts); } - public TermPositions termPositions() throws IOException { + public TermPositions termPositions() throws IOException, AlreadyClosedException { + ensureOpen(); return new MultiTermPositions(subReaders, starts); } @@ -251,11 +259,9 @@ subReaders[i].close(); } - /** - * @see IndexReader#getFieldNames(IndexReader.FieldOption) - */ - public Collection getFieldNames (IndexReader.FieldOption fieldNames) { + public Collection getFieldNames (IndexReader.FieldOption fieldNames) throws AlreadyClosedException { // maintain a unique set of field names + ensureOpen(); Set fieldSet = new HashSet(); for (int i = 0; i < subReaders.length; i++) { IndexReader reader = subReaders[i]; Index: src/java/org/apache/lucene/index/FieldsReader.java =================================================================== --- src/java/org/apache/lucene/index/FieldsReader.java (revision 514377) +++ src/java/org/apache/lucene/index/FieldsReader.java (working copy) @@ -20,6 +20,7 @@ import org.apache.lucene.document.*; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IndexInput; +import org.apache.lucene.store.AlreadyClosedException; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -46,6 +47,7 @@ private final IndexInput indexStream; private int size; + private boolean closed; private ThreadLocal fieldsStreamTL = new ThreadLocal(); @@ -59,19 +61,31 @@ } /** + * @throws AlreadyClosedException if this FieldsReader is closed + */ + protected void ensureOpen() throws AlreadyClosedException { + if (closed) { + throw new AlreadyClosedException("this FieldsReader is closed"); + } + } + + /** * Closes the underlying {@link org.apache.lucene.store.IndexInput} streams, including any ones associated with a * lazy implementation of a Field. This means that the Fields values will not be accessible. * * @throws IOException */ final void close() throws IOException { - fieldsStream.close(); - cloneableFieldsStream.close(); - indexStream.close(); - IndexInput localFieldsStream = (IndexInput) fieldsStreamTL.get(); - if (localFieldsStream != null) { - localFieldsStream.close(); - fieldsStreamTL.set(null); + if (!closed) { + fieldsStream.close(); + cloneableFieldsStream.close(); + indexStream.close(); + IndexInput localFieldsStream = (IndexInput) fieldsStreamTL.get(); + if (localFieldsStream != null) { + localFieldsStream.close(); + fieldsStreamTL.set(null); + } + closed = true; } } @@ -321,8 +335,10 @@ * The value of the field in Binary, or null. If null, the Reader or * String value is used. Exactly one of stringValue(), readerValue() and * binaryValue() must be set. + * @throws AlreadyClosedException if this FieldsReader is closed */ - public byte[] binaryValue() { + public byte[] binaryValue() throws AlreadyClosedException { + ensureOpen(); if (fieldsData == null) { final byte[] b = new byte[toRead]; IndexInput localFieldsStream = getFieldStream(); @@ -347,8 +363,10 @@ * The value of the field as a Reader, or null. If null, the String value * or binary value is used. Exactly one of stringValue(), readerValue(), * and binaryValue() must be set. + * @throws AlreadyClosedException if this FieldsReader is closed */ - public Reader readerValue() { + public Reader readerValue() throws AlreadyClosedException { + ensureOpen(); return fieldsData instanceof Reader ? (Reader) fieldsData : null; } @@ -356,8 +374,10 @@ * The value of the field as a String, or null. If null, the Reader value * or binary value is used. Exactly one of stringValue(), readerValue(), and * binaryValue() must be set. + * @throws AlreadyClosedException if this FieldsReader is closed */ - public String stringValue() { + public String stringValue() throws AlreadyClosedException { + ensureOpen(); if (fieldsData == null) { IndexInput localFieldsStream = getFieldStream(); try { @@ -379,19 +399,35 @@ return fieldsData instanceof String ? (String) fieldsData : null; } - public long getPointer() { + /** + * @throws AlreadyClosedException if this FieldsReader is closed + */ + public long getPointer() throws AlreadyClosedException { + ensureOpen(); return pointer; } - public void setPointer(long pointer) { + /** + * @throws AlreadyClosedException if this FieldsReader is closed + */ + public void setPointer(long pointer) throws AlreadyClosedException { + ensureOpen(); this.pointer = pointer; } - public int getToRead() { + /** + * @throws AlreadyClosedException if this FieldsReader is closed + */ + public int getToRead() throws AlreadyClosedException { + ensureOpen(); return toRead; } - public void setToRead(int toRead) { + /** + * @throws AlreadyClosedException if this FieldsReader is closed + */ + public void setToRead(int toRead) throws AlreadyClosedException { + ensureOpen(); this.toRead = toRead; } } Index: src/java/org/apache/lucene/index/IndexReader.java =================================================================== --- src/java/org/apache/lucene/index/IndexReader.java (revision 514377) +++ src/java/org/apache/lucene/index/IndexReader.java (working copy) @@ -25,6 +25,7 @@ import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.Lock; import org.apache.lucene.store.LockObtainFailedException; +import org.apache.lucene.store.AlreadyClosedException; import java.io.File; import java.io.FileOutputStream; @@ -115,8 +116,17 @@ private boolean directoryOwner; private boolean closeDirectory; protected IndexFileDeleter deleter; - private boolean isClosed; + private boolean closed; + /** + * @throws AlreadyClosedException if this IndexReader is closed + */ + protected void ensureOpen() throws AlreadyClosedException { + if (closed) { + throw new AlreadyClosedException("this IndexReader is closed"); + } + } + private SegmentInfos segmentInfos; private Lock writeLock; private boolean stale; @@ -190,8 +200,13 @@ }.run(); } - /** Returns the directory this index resides in. */ - public Directory directory() { return directory; } + /** Returns the directory this index resides in. + * @throws AlreadyClosedException if this IndexReader is closed + */ + public Directory directory() throws AlreadyClosedException { + ensureOpen(); + return directory; + } /** * Returns the time the index in the named directory was last modified. @@ -281,8 +296,10 @@ /** * Version number when this IndexReader was opened. + * @throws AlreadyClosedException if this IndexReader is closed */ - public long getVersion() { + public long getVersion() throws AlreadyClosedException { + ensureOpen(); return segmentInfos.getVersion(); } @@ -293,17 +310,21 @@ * * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public boolean isCurrent() throws CorruptIndexException, IOException { + public boolean isCurrent() throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); return SegmentInfos.readCurrentVersion(directory) == segmentInfos.getVersion(); } /** * Checks is the index is optimized (if it has a single segment and no deletions) * @return true if the index is optimized; false otherwise + * @throws AlreadyClosedException if this IndexReader is closed */ - public boolean isOptimized() { - return segmentInfos.size() == 1 && hasDeletions() == false; + public boolean isOptimized() throws AlreadyClosedException { + ensureOpen(); + return segmentInfos.size() == 1 && hasDeletions() == false; } /** @@ -318,10 +339,11 @@ * @return array of term frequency vectors. May be null if no term vectors have been * stored for the specified document. * @throws IOException if index cannot be accessed + * @throws AlreadyClosedException if this IndexReader is closed * @see org.apache.lucene.document.Field.TermVector */ abstract public TermFreqVector[] getTermFreqVectors(int docNumber) - throws IOException; + throws IOException, AlreadyClosedException; /** @@ -336,10 +358,11 @@ * @return term frequency vector May be null if field does not exist in the specified * document or term vector was not stored. * @throws IOException if index cannot be accessed + * @throws AlreadyClosedException if this IndexReader is closed * @see org.apache.lucene.document.Field.TermVector */ abstract public TermFreqVector getTermFreqVector(int docNumber, String field) - throws IOException; + throws IOException, AlreadyClosedException; /** * Returns true if an index exists at the specified directory. @@ -374,12 +397,15 @@ return SegmentInfos.getCurrentSegmentGeneration(directory) != -1; } - /** Returns the number of documents in this index. */ + /** Returns the number of documents in this index. + * @throws AlreadyClosedException if this IndexReader is closed + */ public abstract int numDocs(); /** Returns one greater than the largest possible document number. * This may be used to, e.g., determine how big to allocate an array which * will have an element for every document number in an index. + * @throws AlreadyClosedException if this IndexReader is closed */ public abstract int maxDoc(); @@ -387,8 +413,10 @@ Document in this index. * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public Document document(int n) throws CorruptIndexException, IOException { + public Document document(int n) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); return document(n, null); } @@ -411,22 +439,30 @@ * @see org.apache.lucene.document.FieldSelector * @see org.apache.lucene.document.SetBasedFieldSelector * @see org.apache.lucene.document.LoadFirstFieldSelector + * @throws AlreadyClosedException if this IndexReader is closed */ //When we convert to JDK 1.5 make this Set - public abstract Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException; + public abstract Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException, AlreadyClosedException; - /** Returns true if document n has been deleted */ + /** Returns true if document n has been deleted + * @throws AlreadyClosedException if this IndexReader is closed + */ public abstract boolean isDeleted(int n); - /** Returns true if any documents have been deleted */ + /** Returns true if any documents have been deleted + * @throws AlreadyClosedException if this IndexReader is closed + */ public abstract boolean hasDeletions(); - /** Returns true if there are norms stored for this field. */ - public boolean hasNorms(String field) throws IOException { + /** Returns true if there are norms stored for this field. + * @throws AlreadyClosedException if this IndexReader is closed + */ + public boolean hasNorms(String field) throws IOException, AlreadyClosedException { // backward compatible implementation. // SegmentReader has an efficient implementation. + ensureOpen(); return norms(field) != null; } @@ -434,16 +470,18 @@ * every document. This is used by the search code to score documents. * * @see org.apache.lucene.document.Field#setBoost(float) + * @throws AlreadyClosedException if this IndexReader is closed */ - public abstract byte[] norms(String field) throws IOException; + public abstract byte[] norms(String field) throws IOException, AlreadyClosedException; /** Reads the byte-encoded normalization factor for the named field of every * document. This is used by the search code to score documents. * * @see org.apache.lucene.document.Field#setBoost(float) + * @throws AlreadyClosedException if this IndexReader is closed */ public abstract void norms(String field, byte[] bytes, int offset) - throws IOException; + throws IOException, AlreadyClosedException; /** Expert: Resets the normalization factor for the named field of the named * document. The norm represents the product of the field's {@link @@ -459,11 +497,12 @@ * @throws LockObtainFailedException if another writer * has this index open (write.lock could not * be obtained) - * @throws IOException if this reader was closed already - * or there is a low-level IO error + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ public final synchronized void setNorm(int doc, String field, byte value) - throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException, AlreadyClosedException { + ensureOpen(); if(directoryOwner) acquireWriteLock(); hasChanges = true; @@ -486,28 +525,36 @@ * @throws LockObtainFailedException if another writer * has this index open (write.lock could not * be obtained) - * @throws IOException if this reader was closed already - * or there is a low-level IO error + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ public void setNorm(int doc, String field, float value) - throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException, AlreadyClosedException { + ensureOpen(); setNorm(doc, field, Similarity.encodeNorm(value)); } /** Returns an enumeration of all the terms in the index. * The enumeration is ordered by Term.compareTo(). Each term * is greater than all that precede it in the enumeration. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public abstract TermEnum terms() throws IOException; + public abstract TermEnum terms() throws IOException, AlreadyClosedException; /** Returns an enumeration of all terms after a given term. * The enumeration is ordered by Term.compareTo(). Each term * is greater than all that precede it in the enumeration. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public abstract TermEnum terms(Term t) throws IOException; + public abstract TermEnum terms(Term t) throws IOException, AlreadyClosedException; - /** Returns the number of documents containing the term t. */ - public abstract int docFreq(Term t) throws IOException; + /** Returns the number of documents containing the term t. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed + */ + public abstract int docFreq(Term t) throws IOException, AlreadyClosedException; /** Returns an enumeration of all the documents which contain * term. For each document, the document number, the frequency of @@ -518,15 +565,21 @@ * *

The enumeration is ordered by document number. Each document number * is greater than all that precede it in the enumeration. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public TermDocs termDocs(Term term) throws IOException { + public TermDocs termDocs(Term term) throws IOException, AlreadyClosedException { + ensureOpen(); TermDocs termDocs = termDocs(); termDocs.seek(term); return termDocs; } - /** Returns an unpositioned {@link TermDocs} enumerator. */ - public abstract TermDocs termDocs() throws IOException; + /** Returns an unpositioned {@link TermDocs} enumerator. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed + */ + public abstract TermDocs termDocs() throws IOException, AlreadyClosedException; /** Returns an enumeration of all the documents which contain * term. For each document, in addition to the document number @@ -543,15 +596,21 @@ *

This positional information faciliates phrase and proximity searching. *

The enumeration is ordered by document number. Each document number is * greater than all that precede it in the enumeration. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public TermPositions termPositions(Term term) throws IOException { + public TermPositions termPositions(Term term) throws IOException, AlreadyClosedException { + ensureOpen(); TermPositions termPositions = termPositions(); termPositions.seek(term); return termPositions; } - /** Returns an unpositioned {@link TermPositions} enumerator. */ - public abstract TermPositions termPositions() throws IOException; + /** Returns an unpositioned {@link TermPositions} enumerator. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed + */ + public abstract TermPositions termPositions() throws IOException, AlreadyClosedException; /** * Tries to acquire the WriteLock on this directory. @@ -564,12 +623,12 @@ * has this index open (write.lock could not * be obtained) * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - private void acquireWriteLock() throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + private void acquireWriteLock() throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException, AlreadyClosedException { + ensureOpen(); if (stale) throw new StaleReaderException("IndexReader out of date and no longer valid for delete, undelete, or setNorm operations"); - if (isClosed) - throw new IOException("this reader is closed"); if (writeLock == null) { Lock writeLock = directory.makeLock(IndexWriter.WRITE_LOCK_NAME); @@ -602,10 +661,11 @@ * @throws LockObtainFailedException if another writer * has this index open (write.lock could not * be obtained) - * @throws IOException if this reader was closed already - * or there is a low-level IO error + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public final synchronized void deleteDocument(int docNum) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + public final synchronized void deleteDocument(int docNum) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException, AlreadyClosedException { + ensureOpen(); if(directoryOwner) acquireWriteLock(); hasChanges = true; @@ -634,10 +694,11 @@ * @throws LockObtainFailedException if another writer * has this index open (write.lock could not * be obtained) - * @throws IOException if this reader was closed already - * or there is a low-level IO error + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public final int deleteDocuments(Term term) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + public final int deleteDocuments(Term term) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException, AlreadyClosedException { + ensureOpen(); TermDocs docs = termDocs(term); if (docs == null) return 0; int n = 0; @@ -660,10 +721,11 @@ * has this index open (write.lock could not * be obtained) * @throws CorruptIndexException if the index is corrupt - * @throws IOException if this reader was closed already - * or there is a low-level IO error + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ - public final synchronized void undeleteAll() throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + public final synchronized void undeleteAll() throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException, AlreadyClosedException { + ensureOpen(); if(directoryOwner) acquireWriteLock(); hasChanges = true; @@ -793,20 +855,18 @@ * Closes files associated with this index. * Also saves any new deletions to disk. * No other methods should be called after this has been called. - * @throws IOException if this reader was closed already - * or there is a low-level IO error + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ public final synchronized void close() throws IOException { - if (directoryOwner && isClosed) { - throw new IOException("this reader is already closed"); + if (!closed) { + commit(); + doClose(); + if (directoryOwner) + closed = true; + if(closeDirectory) + directory.close(); } - commit(); - doClose(); - if(closeDirectory) - directory.close(); - if (directoryOwner) { - isClosed = true; - } } /** Implements close. */ @@ -831,8 +891,9 @@ * @param fldOption specifies which field option should be available for the returned fields * @return Collection of Strings indicating the names of the fields. * @see IndexReader.FieldOption + * @throws AlreadyClosedException if this IndexReader is closed */ - public abstract Collection getFieldNames(FieldOption fldOption); + public abstract Collection getFieldNames(FieldOption fldOption) throws AlreadyClosedException; /** * Returns true iff the index in the named directory is Index: src/java/org/apache/lucene/index/FilterIndexReader.java =================================================================== --- src/java/org/apache/lucene/index/FilterIndexReader.java (revision 514377) +++ src/java/org/apache/lucene/index/FilterIndexReader.java (working copy) @@ -19,6 +19,7 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.FieldSelector; +import org.apache.lucene.store.AlreadyClosedException; import java.io.IOException; @@ -91,44 +92,81 @@ } public TermFreqVector[] getTermFreqVectors(int docNumber) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); return in.getTermFreqVectors(docNumber); } public TermFreqVector getTermFreqVector(int docNumber, String field) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); return in.getTermFreqVector(docNumber, field); } - public int numDocs() { return in.numDocs(); } - public int maxDoc() { return in.maxDoc(); } + public int numDocs() { + return in.numDocs(); + } - public Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException { return in.document(n, fieldSelector); } + public int maxDoc() { + return in.maxDoc(); + } - public boolean isDeleted(int n) { return in.isDeleted(n); } - public boolean hasDeletions() { return in.hasDeletions(); } + public Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); + return in.document(n, fieldSelector); + } + + public boolean isDeleted(int n) { + return in.isDeleted(n); + } + + public boolean hasDeletions() { + return in.hasDeletions(); + } + protected void doUndeleteAll() throws CorruptIndexException, IOException {in.undeleteAll();} - public boolean hasNorms(String field) throws IOException { + public boolean hasNorms(String field) throws IOException, AlreadyClosedException { + ensureOpen(); return in.hasNorms(field); } - public byte[] norms(String f) throws IOException { return in.norms(f); } - public void norms(String f, byte[] bytes, int offset) throws IOException { + public byte[] norms(String f) throws IOException, AlreadyClosedException { + ensureOpen(); + return in.norms(f); + } + + public void norms(String f, byte[] bytes, int offset) throws IOException, AlreadyClosedException { + ensureOpen(); in.norms(f, bytes, offset); } + protected void doSetNorm(int d, String f, byte b) throws CorruptIndexException, IOException { in.setNorm(d, f, b); } - public TermEnum terms() throws IOException { return in.terms(); } - public TermEnum terms(Term t) throws IOException { return in.terms(t); } + public TermEnum terms() throws IOException, AlreadyClosedException { + ensureOpen(); + return in.terms(); + } - public int docFreq(Term t) throws IOException { return in.docFreq(t); } + public TermEnum terms(Term t) throws IOException, AlreadyClosedException { + ensureOpen(); + return in.terms(t); + } - public TermDocs termDocs() throws IOException { return in.termDocs(); } + public int docFreq(Term t) throws IOException, AlreadyClosedException { + ensureOpen(); + return in.docFreq(t); + } - public TermPositions termPositions() throws IOException { + public TermDocs termDocs() throws IOException, AlreadyClosedException { + ensureOpen(); + return in.termDocs(); + } + + public TermPositions termPositions() throws IOException, AlreadyClosedException { + ensureOpen(); return in.termPositions(); } @@ -137,10 +175,18 @@ protected void doClose() throws IOException { in.close(); } - public Collection getFieldNames(IndexReader.FieldOption fieldNames) { + public Collection getFieldNames(IndexReader.FieldOption fieldNames) throws AlreadyClosedException { + ensureOpen(); return in.getFieldNames(fieldNames); } - public long getVersion() { return in.getVersion(); } - public boolean isCurrent() throws CorruptIndexException, IOException { return in.isCurrent(); } + public long getVersion() throws AlreadyClosedException { + ensureOpen(); + return in.getVersion(); + } + + public boolean isCurrent() throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); + return in.isCurrent(); + } } Index: src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- src/java/org/apache/lucene/index/IndexWriter.java (revision 514377) +++ src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -24,6 +24,7 @@ import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.Lock; import org.apache.lucene.store.LockObtainFailedException; +import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.RAMDirectory; import java.io.File; @@ -151,37 +152,55 @@ private boolean useCompoundFile = true; private boolean closeDir; + private boolean closed; + /** + * @throws AlreadyClosedException if this IndexWriter is closed + */ + protected void ensureOpen() throws AlreadyClosedException { + if (closed) { + throw new AlreadyClosedException("this IndexWriter is closed"); + } + } + /** Get the current setting of whether to use the compound file format. * Note that this just returns the value you set with setUseCompoundFile(boolean) * or the default. You cannot use this to query the status of an existing index. * @see #setUseCompoundFile(boolean) + * @throws AlreadyClosedException if this IndexWriter is closed */ - public boolean getUseCompoundFile() { + public boolean getUseCompoundFile() throws AlreadyClosedException { + ensureOpen(); return useCompoundFile; } /** Setting to turn on usage of a compound file. When on, multiple files * for each segment are merged into a single file once the segment creation * is finished. This is done regardless of what directory is in use. + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setUseCompoundFile(boolean value) { + public void setUseCompoundFile(boolean value) throws AlreadyClosedException { + ensureOpen(); useCompoundFile = value; } /** Expert: Set the Similarity implementation used by this IndexWriter. * * @see Similarity#setDefault(Similarity) + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setSimilarity(Similarity similarity) { + public void setSimilarity(Similarity similarity) throws AlreadyClosedException { + ensureOpen(); this.similarity = similarity; } /** Expert: Return the Similarity implementation used by this IndexWriter. * *

This defaults to the current value of {@link Similarity#getDefault()}. + * @throws AlreadyClosedException if this IndexWriter is closed */ - public Similarity getSimilarity() { + public Similarity getSimilarity() throws AlreadyClosedException { + ensureOpen(); return this.similarity; } @@ -205,16 +224,22 @@ * must be scanned for each random term access. * * @see #DEFAULT_TERM_INDEX_INTERVAL + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setTermIndexInterval(int interval) { + public void setTermIndexInterval(int interval) throws AlreadyClosedException { + ensureOpen(); this.termIndexInterval = interval; } /** Expert: Return the interval between indexed terms. * * @see #setTermIndexInterval(int) + * @throws AlreadyClosedException if this IndexWriter is closed */ - public int getTermIndexInterval() { return termIndexInterval; } + public int getTermIndexInterval() throws AlreadyClosedException { + ensureOpen(); + return termIndexInterval; + } /** * Constructs an IndexWriter for the index in path. @@ -430,15 +455,19 @@ * Larger values are best for batched indexing and speedier searches. * *

The default value is {@link Integer#MAX_VALUE}. + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setMaxMergeDocs(int maxMergeDocs) { + public void setMaxMergeDocs(int maxMergeDocs) throws AlreadyClosedException { + ensureOpen(); this.maxMergeDocs = maxMergeDocs; } /** * @see #setMaxMergeDocs + * @throws AlreadyClosedException if this IndexWriter is closed */ - public int getMaxMergeDocs() { + public int getMaxMergeDocs() throws AlreadyClosedException { + ensureOpen(); return maxMergeDocs; } @@ -453,15 +482,19 @@ * the expected size. If you set it to Integer.MAX_VALUE, then the only limit * is your memory, but you should anticipate an OutOfMemoryError.

* By default, no more than 10,000 terms will be indexed for a field. + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setMaxFieldLength(int maxFieldLength) { + public void setMaxFieldLength(int maxFieldLength) throws AlreadyClosedException { + ensureOpen(); this.maxFieldLength = maxFieldLength; } /** * @see #setMaxFieldLength + * @throws AlreadyClosedException if this IndexWriter is closed */ - public int getMaxFieldLength() { + public int getMaxFieldLength() throws AlreadyClosedException { + ensureOpen(); return maxFieldLength; } @@ -474,8 +507,10 @@ *

The default value is 10. * * @throws IllegalArgumentException if maxBufferedDocs is smaller than 2 + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setMaxBufferedDocs(int maxBufferedDocs) { + public void setMaxBufferedDocs(int maxBufferedDocs) throws AlreadyClosedException { + ensureOpen(); if (maxBufferedDocs < 2) throw new IllegalArgumentException("maxBufferedDocs must at least be 2"); this.minMergeDocs = maxBufferedDocs; @@ -483,8 +518,10 @@ /** * @see #setMaxBufferedDocs + * @throws AlreadyClosedException if this IndexWriter is closed */ - public int getMaxBufferedDocs() { + public int getMaxBufferedDocs() throws AlreadyClosedException { + ensureOpen(); return minMergeDocs; } @@ -496,8 +533,10 @@ *

The default value is {@link #DEFAULT_MAX_BUFFERED_DELETE_TERMS}. * @throws IllegalArgumentException if maxBufferedDeleteTerms is smaller than 1

+ * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setMaxBufferedDeleteTerms(int maxBufferedDeleteTerms) { + public void setMaxBufferedDeleteTerms(int maxBufferedDeleteTerms) throws AlreadyClosedException { + ensureOpen(); if (maxBufferedDeleteTerms < 1) throw new IllegalArgumentException("maxBufferedDeleteTerms must at least be 1"); this.maxBufferedDeleteTerms = maxBufferedDeleteTerms; @@ -505,8 +544,10 @@ /** * @see #setMaxBufferedDeleteTerms + * @throws AlreadyClosedException if this IndexWriter is closed */ - public int getMaxBufferedDeleteTerms() { + public int getMaxBufferedDeleteTerms() throws AlreadyClosedException { + ensureOpen(); return maxBufferedDeleteTerms; } @@ -519,8 +560,10 @@ * interactively maintained. * *

This must never be less than 2. The default value is 10. + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setMergeFactor(int mergeFactor) { + public void setMergeFactor(int mergeFactor) throws AlreadyClosedException { + ensureOpen(); if (mergeFactor < 2) throw new IllegalArgumentException("mergeFactor cannot be less than 2"); this.mergeFactor = mergeFactor; @@ -528,37 +571,47 @@ /** * @see #setMergeFactor + * @throws AlreadyClosedException if this IndexWriter is closed */ - public int getMergeFactor() { + public int getMergeFactor() throws AlreadyClosedException { + ensureOpen(); return mergeFactor; } /** If non-null, information about merges and a message when * maxFieldLength is reached will be printed to this. + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setInfoStream(PrintStream infoStream) { + public void setInfoStream(PrintStream infoStream) throws AlreadyClosedException { + ensureOpen(); this.infoStream = infoStream; } /** * @see #setInfoStream + * @throws AlreadyClosedException if this IndexWriter is closed */ - public PrintStream getInfoStream() { + public PrintStream getInfoStream() throws AlreadyClosedException { + ensureOpen(); return infoStream; } /** * Sets the maximum time to wait for a write lock (in milliseconds) for this instance of IndexWriter. @see * @see #setDefaultWriteLockTimeout to change the default value for all instances of IndexWriter. + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void setWriteLockTimeout(long writeLockTimeout) { + public void setWriteLockTimeout(long writeLockTimeout) throws AlreadyClosedException { + ensureOpen(); this.writeLockTimeout = writeLockTimeout; } /** * @see #setWriteLockTimeout + * @throws AlreadyClosedException if this IndexWriter is closed */ - public long getWriteLockTimeout() { + public long getWriteLockTimeout() throws AlreadyClosedException { + ensureOpen(); return writeLockTimeout; } @@ -612,14 +665,17 @@ * @throws IOException if there is a low-level IO error */ public synchronized void close() throws CorruptIndexException, IOException { - flushRamSegments(); - ramDirectory.close(); - if (writeLock != null) { - writeLock.release(); // release write lock - writeLock = null; + if (!closed) { + flushRamSegments(); + ramDirectory.close(); + if (writeLock != null) { + writeLock.release(); // release write lock + writeLock = null; + } + closed = true; + if(closeDir) + directory.close(); } - if(closeDir) - directory.close(); } /** Release the write lock, if needed. */ @@ -634,19 +690,32 @@ } } - /** Returns the Directory used by this index. */ - public Directory getDirectory() { - return directory; + /** + * Returns the Directory used by this index. + * @throws AlreadyClosedException if this IndexWriter is closed + */ + public Directory getDirectory() throws AlreadyClosedException { + ensureOpen(); + return directory; } - /** Returns the analyzer used by this index. */ - public Analyzer getAnalyzer() { - return analyzer; + /** + * Returns the analyzer used by this index. + * @throws AlreadyClosedException if this IndexWriter is closed + */ + public Analyzer getAnalyzer() throws AlreadyClosedException { + ensureOpen(); + return analyzer; } - /** Returns the number of documents currently in this index. */ - public synchronized int docCount() { + /** + * Returns the number of documents currently in this + * index. + * @throws AlreadyClosedException if this IndexWriter is closed + */ + public synchronized int docCount() throws AlreadyClosedException { + ensureOpen(); int count = ramSegmentInfos.size(); for (int i = 0; i < segmentInfos.size(); i++) { SegmentInfo si = segmentInfos.info(i); @@ -722,8 +791,10 @@ * * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void addDocument(Document doc, Analyzer analyzer) throws CorruptIndexException, IOException { + public void addDocument(Document doc, Analyzer analyzer) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); SegmentInfo newSegmentInfo = buildSingleDocSegment(doc, analyzer); synchronized (this) { ramSegmentInfos.addElement(newSegmentInfo); @@ -745,8 +816,10 @@ * @param term the term to identify the documents to be deleted * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ - public synchronized void deleteDocuments(Term term) throws CorruptIndexException, IOException { + public synchronized void deleteDocuments(Term term) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); bufferDeleteTerm(term); maybeFlushRamSegments(); } @@ -758,8 +831,10 @@ * to be deleted * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ - public synchronized void deleteDocuments(Term[] terms) throws CorruptIndexException, IOException { + public synchronized void deleteDocuments(Term[] terms) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); for (int i = 0; i < terms.length; i++) { bufferDeleteTerm(terms[i]); } @@ -777,8 +852,10 @@ * @param doc the document to be added * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ - public void updateDocument(Term term, Document doc) throws CorruptIndexException, IOException { + public void updateDocument(Term term, Document doc) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); updateDocument(term, doc, getAnalyzer()); } @@ -794,9 +871,11 @@ * @param analyzer the analyzer to use when analyzing the document * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ public void updateDocument(Term term, Document doc, Analyzer analyzer) - throws CorruptIndexException, IOException { + throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); SegmentInfo newSegmentInfo = buildSingleDocSegment(doc, analyzer); synchronized (this) { bufferDeleteTerm(term); @@ -929,8 +1008,10 @@ * compound format.

* @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ - public synchronized void optimize() throws CorruptIndexException, IOException { + public synchronized void optimize() throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); flushRamSegments(); while (segmentInfos.size() > 1 || (segmentInfos.size() == 1 && @@ -1068,10 +1149,12 @@ * for details.

* @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ public synchronized void addIndexes(Directory[] dirs) - throws CorruptIndexException, IOException { + throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); optimize(); // start with zero or 1 seg int start = segmentInfos.size(); @@ -1126,9 +1209,10 @@ * on an Exception.

* @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ public synchronized void addIndexesNoOptimize(Directory[] dirs) - throws CorruptIndexException, IOException { + throws CorruptIndexException, IOException, AlreadyClosedException { // Adding indexes can be viewed as adding a sequence of segments S to // a sequence of segments T. Segments in T follow the invariants but // segments in S may not since they could come from multiple indexes. @@ -1157,6 +1241,7 @@ // 1 flush ram segments + ensureOpen(); flushRamSegments(); // 2 copy segment infos and find the highest level from dirs @@ -1264,10 +1349,12 @@ * on an Exception.

* @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ public synchronized void addIndexes(IndexReader[] readers) - throws CorruptIndexException, IOException { + throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); optimize(); // start with zero or 1 seg final String mergedName = newSegmentName(); @@ -1407,22 +1494,28 @@ * to the Directory. * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexWriter is closed */ - public final synchronized void flush() throws CorruptIndexException, IOException { + public final synchronized void flush() throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); flushRamSegments(); } /** Expert: Return the total size of all index files currently cached in memory. * Useful for size management with flushRamDocs() + * @throws AlreadyClosedException if this IndexWriter is closed */ - public final long ramSizeInBytes() { + public final long ramSizeInBytes() throws AlreadyClosedException { + ensureOpen(); return ramDirectory.sizeInBytes(); } /** Expert: Return the number of documents whose segments are currently cached in memory. * Useful when calling flushRamSegments() + * @throws AlreadyClosedException if this IndexWriter is closed */ - public final synchronized int numRamDocs() { + public final synchronized int numRamDocs() throws AlreadyClosedException { + ensureOpen(); return ramSegmentInfos.size(); } Index: src/java/org/apache/lucene/index/ParallelReader.java =================================================================== --- src/java/org/apache/lucene/index/ParallelReader.java (revision 514377) +++ src/java/org/apache/lucene/index/ParallelReader.java (working copy) @@ -21,6 +21,7 @@ import org.apache.lucene.document.Fieldable; import org.apache.lucene.document.FieldSelector; import org.apache.lucene.document.FieldSelectorResult; +import org.apache.lucene.store.AlreadyClosedException; import java.io.IOException; import java.util.SortedMap; @@ -66,8 +67,12 @@ /** Construct a ParallelReader. */ public ParallelReader() throws IOException { super(null); } - /** Add an IndexReader. */ - public void add(IndexReader reader) throws IOException { + /** Add an IndexReader. + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed + */ + public void add(IndexReader reader) throws IOException, AlreadyClosedException { + ensureOpen(); add(reader, false); } @@ -79,10 +84,13 @@ * of documents * @throws IllegalArgumentException if not all indexes have the same value * of {@link IndexReader#maxDoc()} + * @throws IOException if there is a low-level IO error + * @throws AlreadyClosedException if this IndexReader is closed */ public void add(IndexReader reader, boolean ignoreStoredFields) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); if (readers.size() == 0) { this.maxDoc = reader.maxDoc(); this.numDocs = reader.numDocs(); @@ -110,11 +118,17 @@ readers.add(reader); } - public int numDocs() { return numDocs; } + public int numDocs() { + return numDocs; + } - public int maxDoc() { return maxDoc; } + public int maxDoc() { + return maxDoc; + } - public boolean hasDeletions() { return hasDeletions; } + public boolean hasDeletions() { + return hasDeletions; + } // check first reader public boolean isDeleted(int n) { @@ -140,7 +154,8 @@ } // append fields from storedFieldReaders - public Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException { + public Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); Document result = new Document(); for (int i = 0; i < storedFieldReaders.size(); i++) { IndexReader reader = (IndexReader)storedFieldReaders.get(i); @@ -165,7 +180,8 @@ } // get all vectors - public TermFreqVector[] getTermFreqVectors(int n) throws IOException { + public TermFreqVector[] getTermFreqVectors(int n) throws IOException, AlreadyClosedException { + ensureOpen(); ArrayList results = new ArrayList(); Iterator i = fieldToReader.entrySet().iterator(); while (i.hasNext()) { @@ -181,23 +197,27 @@ } public TermFreqVector getTermFreqVector(int n, String field) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); IndexReader reader = ((IndexReader)fieldToReader.get(field)); return reader==null ? null : reader.getTermFreqVector(n, field); } - public boolean hasNorms(String field) throws IOException { + public boolean hasNorms(String field) throws IOException, AlreadyClosedException { + ensureOpen(); IndexReader reader = ((IndexReader)fieldToReader.get(field)); return reader==null ? false : reader.hasNorms(field); } - public byte[] norms(String field) throws IOException { + public byte[] norms(String field) throws IOException, AlreadyClosedException { + ensureOpen(); IndexReader reader = ((IndexReader)fieldToReader.get(field)); return reader==null ? null : reader.norms(field); } public void norms(String field, byte[] result, int offset) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); IndexReader reader = ((IndexReader)fieldToReader.get(field)); if (reader!=null) reader.norms(field, result, offset); @@ -210,32 +230,39 @@ reader.doSetNorm(n, field, value); } - public TermEnum terms() throws IOException { + public TermEnum terms() throws IOException, AlreadyClosedException { + ensureOpen(); return new ParallelTermEnum(); } - public TermEnum terms(Term term) throws IOException { + public TermEnum terms(Term term) throws IOException, AlreadyClosedException { + ensureOpen(); return new ParallelTermEnum(term); } - public int docFreq(Term term) throws IOException { + public int docFreq(Term term) throws IOException, AlreadyClosedException { + ensureOpen(); IndexReader reader = ((IndexReader)fieldToReader.get(term.field())); return reader==null ? 0 : reader.docFreq(term); } - public TermDocs termDocs(Term term) throws IOException { + public TermDocs termDocs(Term term) throws IOException, AlreadyClosedException { + ensureOpen(); return new ParallelTermDocs(term); } - public TermDocs termDocs() throws IOException { + public TermDocs termDocs() throws IOException, AlreadyClosedException { + ensureOpen(); return new ParallelTermDocs(); } - public TermPositions termPositions(Term term) throws IOException { + public TermPositions termPositions(Term term) throws IOException, AlreadyClosedException { + ensureOpen(); return new ParallelTermPositions(term); } - public TermPositions termPositions() throws IOException { + public TermPositions termPositions() throws IOException, AlreadyClosedException { + ensureOpen(); return new ParallelTermPositions(); } @@ -250,7 +277,8 @@ } - public Collection getFieldNames (IndexReader.FieldOption fieldNames) { + public Collection getFieldNames (IndexReader.FieldOption fieldNames) throws AlreadyClosedException { + ensureOpen(); Set fieldSet = new HashSet(); for (int i = 0; i < readers.size(); i++) { IndexReader reader = ((IndexReader)readers.get(i)); Index: src/java/org/apache/lucene/index/SegmentReader.java =================================================================== --- src/java/org/apache/lucene/index/SegmentReader.java (revision 514377) +++ src/java/org/apache/lucene/index/SegmentReader.java (working copy) @@ -23,6 +23,7 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; +import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.util.BitVector; import java.io.IOException; @@ -353,11 +354,13 @@ return files; } - public TermEnum terms() { + public TermEnum terms() throws AlreadyClosedException { + ensureOpen(); return tis.terms(); } - public TermEnum terms(Term t) throws IOException { + public TermEnum terms(Term t) throws IOException, AlreadyClosedException { + ensureOpen(); return tis.terms(t); } @@ -365,7 +368,8 @@ * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error */ - public synchronized Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException { + public synchronized Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException, AlreadyClosedException { + ensureOpen(); if (isDeleted(n)) throw new IllegalArgumentException ("attempt to access a deleted document"); @@ -376,15 +380,18 @@ return (deletedDocs != null && deletedDocs.get(n)); } - public TermDocs termDocs() throws IOException { + public TermDocs termDocs() throws IOException, AlreadyClosedException { + ensureOpen(); return new SegmentTermDocs(this); } - public TermPositions termPositions() throws IOException { + public TermPositions termPositions() throws IOException, AlreadyClosedException { + ensureOpen(); return new SegmentTermPositions(this); } - public int docFreq(Term t) throws IOException { + public int docFreq(Term t) throws IOException, AlreadyClosedException { + ensureOpen(); TermInfo ti = tis.get(t); if (ti != null) return ti.docFreq; @@ -406,7 +413,8 @@ /** * @see IndexReader#getFieldNames(IndexReader.FieldOption fldOption) */ - public Collection getFieldNames(IndexReader.FieldOption fieldOption) { + public Collection getFieldNames(IndexReader.FieldOption fieldOption) throws AlreadyClosedException { + ensureOpen(); Set fieldSet = new HashSet(); for (int i = 0; i < fieldInfos.size(); i++) { @@ -447,7 +455,8 @@ } - public synchronized boolean hasNorms(String field) { + public synchronized boolean hasNorms(String field) throws AlreadyClosedException { + ensureOpen(); return norms.containsKey(field); } @@ -479,7 +488,8 @@ } // returns fake norms if norms aren't available - public synchronized byte[] norms(String field) throws IOException { + public synchronized byte[] norms(String field) throws IOException, AlreadyClosedException { + ensureOpen(); byte[] bytes = getNorms(field); if (bytes==null) bytes=fakeNorms(); return bytes; @@ -499,8 +509,9 @@ /** Read norms into a pre-allocated array. */ public synchronized void norms(String field, byte[] bytes, int offset) - throws IOException { + throws IOException, AlreadyClosedException { + ensureOpen(); Norm norm = (Norm) norms.get(field); if (norm == null) { System.arraycopy(fakeNorms(), 0, bytes, offset, maxDoc()); @@ -589,8 +600,9 @@ * flag set. If the flag was not set, the method returns null. * @throws IOException */ - public TermFreqVector getTermFreqVector(int docNumber, String field) throws IOException { + public TermFreqVector getTermFreqVector(int docNumber, String field) throws IOException, AlreadyClosedException { // Check if this field is invalid or has no stored term vector + ensureOpen(); FieldInfo fi = fieldInfos.fieldInfo(field); if (fi == null || !fi.storeTermVector || termVectorsReaderOrig == null) return null; @@ -610,7 +622,8 @@ * If no such fields existed, the method returns null. * @throws IOException */ - public TermFreqVector[] getTermFreqVectors(int docNumber) throws IOException { + public TermFreqVector[] getTermFreqVectors(int docNumber) throws IOException, AlreadyClosedException { + ensureOpen(); if (termVectorsReaderOrig == null) return null; Index: src/java/org/apache/lucene/store/AlreadyClosedException.java =================================================================== --- src/java/org/apache/lucene/store/AlreadyClosedException.java (revision 0) +++ src/java/org/apache/lucene/store/AlreadyClosedException.java (revision 0) @@ -0,0 +1,28 @@ +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. + */ + +/** + * This exception is thrown when there is an attempt to + * access something that has already been closed. + */ +public class AlreadyClosedException extends IllegalStateException { + public AlreadyClosedException(String message) { + super(message); + } +} Property changes on: src/java/org/apache/lucene/store/AlreadyClosedException.java ___________________________________________________________________ Name: svn:eol-style + native