Index: lucene/contrib/misc/src/java/org/apache/lucene/index/MultiPassIndexSplitter.java =================================================================== --- lucene/contrib/misc/src/java/org/apache/lucene/index/MultiPassIndexSplitter.java (revision 1238713) +++ lucene/contrib/misc/src/java/org/apache/lucene/index/MultiPassIndexSplitter.java (working copy) @@ -100,7 +100,8 @@ null) .setOpenMode(OpenMode.CREATE)); System.err.println("Writing part " + (i + 1) + " ..."); - w.addIndexes(input); + // pass the subreaders directly, as our wrapper's numDocs/hasDeletetions are not up-to-date + w.addIndexes(input.getSequentialSubReaders()); w.close(); } System.err.println("Done."); @@ -204,23 +205,8 @@ } } - // override this as MultiReader precalculates the number of deletions - // (this method is never used by MultiPassIndexSplitter) - @Override - public int numDocs() { - int n = 0; - for (int i = 0; i < subReaders.length; i++) - n += subReaders[i].numDocs(); - return n; - } - - // override this as MultiReader precalculates the number of deletions - // (this method is never used by MultiPassIndexSplitter) - @Override - public boolean hasDeletions() { - return (maxDoc() != numDocs()); - } - + // no need to override numDocs/hasDeletions, + // as we pass the subreaders directly to IW.addIndexes(). } private static final class FakeDeleteAtomicIndexReader extends FilterIndexReader { Index: lucene/src/java/org/apache/lucene/index/BaseMultiReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/BaseMultiReader.java (revision 1238713) +++ lucene/src/java/org/apache/lucene/index/BaseMultiReader.java (working copy) @@ -19,13 +19,12 @@ import java.io.IOException; -import org.apache.lucene.util.Bits; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.ReaderUtil; abstract class BaseMultiReader extends CompositeReader { protected final R[] subReaders; - protected final int[] starts; // 1st docno for each segment + protected final int[] starts; // 1st docno for each reader private final int maxDoc; private final int numDocs; private final boolean hasDeletions; @@ -50,14 +49,14 @@ } @Override - public Fields getTermVectors(int docID) throws IOException { + public final Fields getTermVectors(int docID) throws IOException { ensureOpen(); final int i = readerIndex(docID); // find segment num return subReaders[i].getTermVectors(docID - starts[i]); // dispatch to segment } @Override - public int numDocs() { + public final int numDocs() { // Don't call ensureOpen() here (it could affect performance) return numDocs; } @@ -69,20 +68,20 @@ } @Override - public void document(int docID, StoredFieldVisitor visitor) throws CorruptIndexException, IOException { + public final void document(int docID, StoredFieldVisitor visitor) throws CorruptIndexException, IOException { ensureOpen(); final int i = readerIndex(docID); // find segment num subReaders[i].document(docID - starts[i], visitor); // dispatch to segment reader } @Override - public boolean hasDeletions() { + public final boolean hasDeletions() { // Don't call ensureOpen() here (it could affect performance) return hasDeletions; } @Override - public int docFreq(String field, BytesRef t) throws IOException { + public final int docFreq(String field, BytesRef t) throws IOException { ensureOpen(); int total = 0; // sum freqs in segments for (int i = 0; i < subReaders.length; i++) { @@ -100,7 +99,7 @@ } @Override - public IndexReader[] getSequentialSubReaders() { + public final R[] getSequentialSubReaders() { return subReaders; } } Index: lucene/src/java/org/apache/lucene/index/DirectoryReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/DirectoryReader.java (revision 1238713) +++ lucene/src/java/org/apache/lucene/index/DirectoryReader.java (working copy) @@ -20,16 +20,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.lucene.search.SearcherManager; // javadocs import org.apache.lucene.store.Directory; -import org.apache.lucene.store.IOContext; -import org.apache.lucene.util.IOUtils; /** DirectoryReader is an implementation of {@link CompositeReader} that can read indexes in a {@link Directory}. @@ -53,14 +49,10 @@ IndexReader instance; use your own (non-Lucene) objects instead. */ -public final class DirectoryReader extends BaseMultiReader { - static int DEFAULT_TERMS_INDEX_DIVISOR = 1; +public abstract class DirectoryReader extends BaseMultiReader { + public static final int DEFAULT_TERMS_INDEX_DIVISOR = 1; protected final Directory directory; - private final IndexWriter writer; - private final SegmentInfos segmentInfos; - private final int termInfosIndexDivisor; - private final boolean applyAllDeletes; /** Returns a IndexReader reading the index in the given * Directory @@ -69,7 +61,7 @@ * @throws IOException if there is a low-level IO error */ public static DirectoryReader open(final Directory directory) throws CorruptIndexException, IOException { - return open(directory, null, DEFAULT_TERMS_INDEX_DIVISOR); + return StandardDirectoryReader.open(directory, null, DEFAULT_TERMS_INDEX_DIVISOR); } /** Expert: Returns a IndexReader reading the index in the given @@ -89,7 +81,7 @@ * @throws IOException if there is a low-level IO error */ public static DirectoryReader open(final Directory directory, int termInfosIndexDivisor) throws CorruptIndexException, IOException { - return open(directory, null, termInfosIndexDivisor); + return StandardDirectoryReader.open(directory, null, termInfosIndexDivisor); } /** @@ -122,10 +114,9 @@ * @throws IOException if there is a low-level IO error */ public static DirectoryReader open(final IndexCommit commit) throws CorruptIndexException, IOException { - return open(commit.getDirectory(), commit, DEFAULT_TERMS_INDEX_DIVISOR); + return StandardDirectoryReader.open(commit.getDirectory(), commit, DEFAULT_TERMS_INDEX_DIVISOR); } - /** Expert: returns an IndexReader reading the index in the given * {@link IndexCommit} and termInfosIndexDivisor. * @param commit the commit point to open @@ -143,171 +134,9 @@ * @throws IOException if there is a low-level IO error */ public static DirectoryReader open(final IndexCommit commit, int termInfosIndexDivisor) throws CorruptIndexException, IOException { - return open(commit.getDirectory(), commit, termInfosIndexDivisor); + return StandardDirectoryReader.open(commit.getDirectory(), commit, termInfosIndexDivisor); } - DirectoryReader(SegmentReader[] readers, Directory directory, IndexWriter writer, - SegmentInfos sis, int termInfosIndexDivisor, boolean applyAllDeletes) throws IOException { - super(readers); - this.directory = directory; - this.writer = writer; - this.segmentInfos = sis; - this.termInfosIndexDivisor = termInfosIndexDivisor; - this.applyAllDeletes = applyAllDeletes; - } - - private static DirectoryReader open(final Directory directory, final IndexCommit commit, - final int termInfosIndexDivisor) throws CorruptIndexException, IOException { - return (DirectoryReader) new SegmentInfos.FindSegmentsFile(directory) { - @Override - protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException { - SegmentInfos sis = new SegmentInfos(); - sis.read(directory, segmentFileName); - final SegmentReader[] readers = new SegmentReader[sis.size()]; - for (int i = sis.size()-1; i >= 0; i--) { - IOException prior = null; - boolean success = false; - try { - readers[i] = new SegmentReader(sis.info(i), termInfosIndexDivisor, IOContext.READ); - success = true; - } catch(IOException ex) { - prior = ex; - } finally { - if (!success) - IOUtils.closeWhileHandlingException(prior, readers); - } - } - return new DirectoryReader(readers, directory, null, sis, termInfosIndexDivisor, false); - } - }.run(commit); - } - - // Used by near real-time search - static DirectoryReader open(IndexWriter writer, SegmentInfos infos, boolean applyAllDeletes) throws IOException { - // IndexWriter synchronizes externally before calling - // us, which ensures infos will not change; so there's - // no need to process segments in reverse order - final int numSegments = infos.size(); - - List readers = new ArrayList(); - final Directory dir = writer.getDirectory(); - - final SegmentInfos segmentInfos = (SegmentInfos) infos.clone(); - int infosUpto = 0; - for (int i=0;i 0 || writer.getKeepFullyDeletedSegments()) { - readers.add(reader); - infosUpto++; - } else { - reader.close(); - segmentInfos.remove(infosUpto); - } - success = true; - } catch(IOException ex) { - prior = ex; - } finally { - if (!success) - IOUtils.closeWhileHandlingException(prior, readers); - } - } - return new DirectoryReader(readers.toArray(new SegmentReader[readers.size()]), - dir, writer, segmentInfos, writer.getConfig().getReaderTermsIndexDivisor(), applyAllDeletes); - } - - /** This constructor is only used for {@link #doOpenIfChanged()} */ - private static DirectoryReader open(Directory directory, IndexWriter writer, SegmentInfos infos, SegmentReader[] oldReaders, - int termInfosIndexDivisor) throws IOException { - // we put the old SegmentReaders in a map, that allows us - // to lookup a reader using its segment name - final Map segmentReaders = new HashMap(); - - if (oldReaders != null) { - // create a Map SegmentName->SegmentReader - for (int i = 0; i < oldReaders.length; i++) { - segmentReaders.put(oldReaders[i].getSegmentName(), Integer.valueOf(i)); - } - } - - SegmentReader[] newReaders = new SegmentReader[infos.size()]; - - // remember which readers are shared between the old and the re-opened - // DirectoryReader - we have to incRef those readers - boolean[] readerShared = new boolean[infos.size()]; - - for (int i = infos.size() - 1; i>=0; i--) { - // find SegmentReader for this segment - Integer oldReaderIndex = segmentReaders.get(infos.info(i).name); - if (oldReaderIndex == null) { - // this is a new segment, no old SegmentReader can be reused - newReaders[i] = null; - } else { - // there is an old reader for this segment - we'll try to reopen it - newReaders[i] = oldReaders[oldReaderIndex.intValue()]; - } - - boolean success = false; - IOException prior = null; - try { - SegmentReader newReader; - if (newReaders[i] == null || infos.info(i).getUseCompoundFile() != newReaders[i].getSegmentInfo().getUseCompoundFile()) { - - // this is a new reader; in case we hit an exception we can close it safely - newReader = new SegmentReader(infos.info(i), termInfosIndexDivisor, IOContext.READ); - readerShared[i] = false; - newReaders[i] = newReader; - } else { - if (newReaders[i].getSegmentInfo().getDelGen() == infos.info(i).getDelGen()) { - // No change; this reader will be shared between - // the old and the new one, so we must incRef - // it: - readerShared[i] = true; - newReaders[i].incRef(); - } else { - readerShared[i] = false; - // Steal the ref returned by SegmentReader ctor: - assert infos.info(i).dir == newReaders[i].getSegmentInfo().dir; - assert infos.info(i).hasDeletions(); - newReaders[i] = new SegmentReader(infos.info(i), newReaders[i].core, IOContext.READ); - } - } - success = true; - } catch (IOException ex) { - prior = ex; - } finally { - if (!success) { - for (i++; i < infos.size(); i++) { - if (newReaders[i] != null) { - try { - if (!readerShared[i]) { - // this is a new subReader that is not used by the old one, - // we can close it - newReaders[i].close(); - } else { - // this subReader is also used by the old reader, so instead - // closing we must decRef it - newReaders[i].decRef(); - } - } catch (IOException ex) { - if (prior == null) prior = ex; - } - } - } - } - // throw the first exception - if (prior != null) throw prior; - } - } - return new DirectoryReader(newReaders, directory, writer, - infos, termInfosIndexDivisor, false); - } - /** * If the index has changed since the provided reader was * opened, open and return a new reader; else, return @@ -418,209 +247,6 @@ return newReader; } - /** {@inheritDoc} */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append(getClass().getSimpleName()); - buffer.append('('); - final String segmentsFile = segmentInfos.getCurrentSegmentFileName(); - if (segmentsFile != null) { - buffer.append(segmentsFile).append(":").append(segmentInfos.getVersion()); - } - if (writer != null) { - buffer.append(":nrt"); - } - for(int i=0;iThis method - * returns the version recorded in the commit that the - * reader opened. This version is advanced every time - * a change is made with {@link IndexWriter}.

- */ - public long getVersion() { - ensureOpen(); - return segmentInfos.getVersion(); - } - - /** - * Retrieve the String userData optionally passed to - * IndexWriter#commit. This will return null if {@link - * IndexWriter#commit(Map)} has never been called for - * this index. - */ - public Map getCommitUserData() { - ensureOpen(); - return segmentInfos.getUserData(); - } - - /** - * Check whether any new changes have occurred to the - * index since this reader was opened. - * - *

If this reader was created by calling {@link #open}, - * then this method checks if any further commits - * (see {@link IndexWriter#commit}) have occurred in the - * directory.

- * - *

If instead this reader is a near real-time reader - * (ie, obtained by a call to {@link - * IndexWriter#getReader}, or by calling {@link #openIfChanged} - * on a near real-time reader), then this method checks if - * either a new commmit has occurred, or any new - * uncommitted changes have taken place via the writer. - * Note that even if the writer has only performed - * merging, this method will still return false.

- * - *

In any event, if this returns false, you should call - * {@link #openIfChanged} to get a new reader that sees the - * changes.

- * - * @throws CorruptIndexException if the index is corrupt - * @throws IOException if there is a low-level IO error - */ - public boolean isCurrent() throws CorruptIndexException, IOException { - ensureOpen(); - if (writer == null || writer.isClosed()) { - // we loaded SegmentInfos from the directory - return SegmentInfos.readCurrentVersion(directory) == segmentInfos.getVersion(); - } else { - return writer.nrtIsCurrent(segmentInfos); - } - } - - @Override - protected synchronized void doClose() throws IOException { - 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; - } - } - - if (writer != null) { - // Since we just closed, writer may now be able to - // delete unused files: - writer.deletePendingFiles(); - } - - // throw the first exception - if (ioe != null) throw ioe; - } - - /** Returns the directory this index resides in. */ - public Directory directory() { - // Don't ensureOpen here -- in certain cases, when a - // cloned/reopened reader needs to commit, it may call - // this method on the closed original reader - return directory; - } - - /** This returns the current indexDivisor as - * specified when the reader was opened. - */ - public int getTermInfosIndexDivisor() { - ensureOpen(); - return termInfosIndexDivisor; - } - - /** - * Expert: return the IndexCommit that this reader has opened. - *

- * @lucene.experimental - */ - public IndexCommit getIndexCommit() throws IOException { - ensureOpen(); - return new ReaderCommit(segmentInfos, directory); - } - /** Returns all commit points that exist in the Directory. * Normally, because the default is {@link * KeepOnlyLastCommitDeletionPolicy}, there would be only @@ -645,7 +271,7 @@ latest.read(dir); final long currentGen = latest.getGeneration(); - commits.add(new ReaderCommit(latest, dir)); + commits.add(new StandardDirectoryReader.ReaderCommit(latest, dir)); for(int i=0;i files; - Directory dir; - long generation; - final Map userData; - private final int segmentCount; + protected DirectoryReader(Directory directory, AtomicReader[] readers) throws CorruptIndexException, IOException { + super(readers); + this.directory = directory; + } + + /** Returns the directory this index resides in. */ + public final Directory directory() { + // Don't ensureOpen here -- in certain cases, when a + // cloned/reopened reader needs to commit, it may call + // this method on the closed original reader + return directory; + } - ReaderCommit(SegmentInfos infos, Directory dir) throws IOException { - segmentsFileName = infos.getCurrentSegmentFileName(); - this.dir = dir; - userData = infos.getUserData(); - files = Collections.unmodifiableCollection(infos.files(dir, true)); - generation = infos.getGeneration(); - segmentCount = infos.size(); - } + /** Implement this method to support {@link #openIfChanged(DirectoryReader)}. + * If this reader does not support reopen, return {@code null}, so + * client code is happy. This should be consistent with {@link #isCurrent} + * (should always return {@code true}) if reopen is not supported. + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + * @return null if there are no changes; else, a new + * DirectoryReader instance. + */ + protected abstract DirectoryReader doOpenIfChanged() throws CorruptIndexException, IOException; - @Override - public String toString() { - return "DirectoryReader.ReaderCommit(" + segmentsFileName + ")"; - } + /** Implement this method to support {@link #openIfChanged(DirectoryReader,IndexCommit)}. + * If this reader does not support reopen from a specific {@link IndexCommit}, + * throw {@link UnsupportedOperationException}. + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + * @return null if there are no changes; else, a new + * DirectoryReader instance. + */ + protected abstract DirectoryReader doOpenIfChanged(final IndexCommit commit) throws CorruptIndexException, IOException; - @Override - public int getSegmentCount() { - return segmentCount; - } + /** Implement this method to support {@link #openIfChanged(DirectoryReader,IndexWriter,boolean)}. + * If this reader does not support reopen from {@link IndexWriter}, + * throw {@link UnsupportedOperationException}. + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + * @return null if there are no changes; else, a new + * DirectoryReader instance. + */ + protected abstract DirectoryReader doOpenIfChanged(IndexWriter writer, boolean applyAllDeletes) throws CorruptIndexException, IOException; - @Override - public String getSegmentsFileName() { - return segmentsFileName; - } + /** + * Version number when this IndexReader was opened. + * + *

This method + * returns the version recorded in the commit that the + * reader opened. This version is advanced every time + * a change is made with {@link IndexWriter}.

+ */ + public abstract long getVersion(); - @Override - public Collection getFileNames() { - return files; - } + /** + * Retrieve the String userData optionally passed to + * IndexWriter#commit. This will return null if {@link + * IndexWriter#commit(Map)} has never been called for + * this index. + */ + public abstract Map getCommitUserData(); - @Override - public Directory getDirectory() { - return dir; - } + /** + * Check whether any new changes have occurred to the + * index since this reader was opened. + * + *

If this reader was created by calling {@link #open}, + * then this method checks if any further commits + * (see {@link IndexWriter#commit}) have occurred in the + * directory.

+ * + *

If instead this reader is a near real-time reader + * (ie, obtained by a call to {@link + * IndexWriter#getReader}, or by calling {@link #openIfChanged} + * on a near real-time reader), then this method checks if + * either a new commmit has occurred, or any new + * uncommitted changes have taken place via the writer. + * Note that even if the writer has only performed + * merging, this method will still return false.

+ * + *

In any event, if this returns false, you should call + * {@link #openIfChanged} to get a new reader that sees the + * changes.

+ * + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + */ + public abstract boolean isCurrent() throws CorruptIndexException, IOException; - @Override - public long getGeneration() { - return generation; - } + /** + * Expert: return the IndexCommit that this reader has opened. + *

+ * @lucene.experimental + */ + public abstract IndexCommit getIndexCommit() throws CorruptIndexException, IOException; - @Override - public boolean isDeleted() { - return false; - } - - @Override - public Map getUserData() { - return userData; - } - - @Override - public void delete() { - throw new UnsupportedOperationException("This IndexCommit does not support deletions"); - } - } } Index: lucene/src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- lucene/src/java/org/apache/lucene/index/IndexWriter.java (revision 1238713) +++ lucene/src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -364,7 +364,7 @@ // just like we do when loading segments_N synchronized(this) { maybeApplyDeletes(applyAllDeletes); - r = DirectoryReader.open(this, segmentInfos, applyAllDeletes); + r = StandardDirectoryReader.open(this, segmentInfos, applyAllDeletes); if (infoStream.isEnabled("IW")) { infoStream.message("IW", "return reader version=" + r.getVersion() + " reader=" + r); } Index: lucene/src/java/org/apache/lucene/index/StandardDirectoryReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/StandardDirectoryReader.java (revision 0) +++ lucene/src/java/org/apache/lucene/index/StandardDirectoryReader.java (working copy) @@ -0,0 +1,411 @@ +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; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IOContext; +import org.apache.lucene.util.IOUtils; + +final class StandardDirectoryReader extends DirectoryReader { + + private final IndexWriter writer; + private final SegmentInfos segmentInfos; + private final int termInfosIndexDivisor; + private final boolean applyAllDeletes; + + /** called only from static open() methods */ + StandardDirectoryReader(Directory directory, AtomicReader[] readers, IndexWriter writer, + SegmentInfos sis, int termInfosIndexDivisor, boolean applyAllDeletes) throws IOException { + super(directory, readers); + this.writer = writer; + this.segmentInfos = sis; + this.termInfosIndexDivisor = termInfosIndexDivisor; + this.applyAllDeletes = applyAllDeletes; + } + + /** called from DirectoryReader.open(...) methods */ + static DirectoryReader open(final Directory directory, final IndexCommit commit, + final int termInfosIndexDivisor) throws CorruptIndexException, IOException { + return (DirectoryReader) new SegmentInfos.FindSegmentsFile(directory) { + @Override + protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException { + SegmentInfos sis = new SegmentInfos(); + sis.read(directory, segmentFileName); + final SegmentReader[] readers = new SegmentReader[sis.size()]; + for (int i = sis.size()-1; i >= 0; i--) { + IOException prior = null; + boolean success = false; + try { + readers[i] = new SegmentReader(sis.info(i), termInfosIndexDivisor, IOContext.READ); + success = true; + } catch(IOException ex) { + prior = ex; + } finally { + if (!success) + IOUtils.closeWhileHandlingException(prior, readers); + } + } + return new StandardDirectoryReader(directory, readers, null, sis, termInfosIndexDivisor, false); + } + }.run(commit); + } + + /** Used by near real-time search */ + static DirectoryReader open(IndexWriter writer, SegmentInfos infos, boolean applyAllDeletes) throws IOException { + // IndexWriter synchronizes externally before calling + // us, which ensures infos will not change; so there's + // no need to process segments in reverse order + final int numSegments = infos.size(); + + List readers = new ArrayList(); + final Directory dir = writer.getDirectory(); + + final SegmentInfos segmentInfos = (SegmentInfos) infos.clone(); + int infosUpto = 0; + for (int i=0;i 0 || writer.getKeepFullyDeletedSegments()) { + readers.add(reader); + infosUpto++; + } else { + reader.close(); + segmentInfos.remove(infosUpto); + } + success = true; + } catch(IOException ex) { + prior = ex; + } finally { + if (!success) + IOUtils.closeWhileHandlingException(prior, readers); + } + } + return new StandardDirectoryReader(dir, readers.toArray(new SegmentReader[readers.size()]), + writer, segmentInfos, writer.getConfig().getReaderTermsIndexDivisor(), applyAllDeletes); + } + + /** This constructor is only used for {@link #doOpenIfChanged()} */ + private static DirectoryReader open(Directory directory, IndexWriter writer, SegmentInfos infos, AtomicReader[] oldReaders, + int termInfosIndexDivisor) throws IOException { + // we put the old SegmentReaders in a map, that allows us + // to lookup a reader using its segment name + final Map segmentReaders = new HashMap(); + + if (oldReaders != null) { + // create a Map SegmentName->SegmentReader + for (int i = 0; i < oldReaders.length; i++) { + segmentReaders.put(((SegmentReader) oldReaders[i]).getSegmentName(), Integer.valueOf(i)); + } + } + + SegmentReader[] newReaders = new SegmentReader[infos.size()]; + + // remember which readers are shared between the old and the re-opened + // DirectoryReader - we have to incRef those readers + boolean[] readerShared = new boolean[infos.size()]; + + for (int i = infos.size() - 1; i>=0; i--) { + // find SegmentReader for this segment + Integer oldReaderIndex = segmentReaders.get(infos.info(i).name); + if (oldReaderIndex == null) { + // this is a new segment, no old SegmentReader can be reused + newReaders[i] = null; + } else { + // there is an old reader for this segment - we'll try to reopen it + newReaders[i] = (SegmentReader) oldReaders[oldReaderIndex.intValue()]; + } + + boolean success = false; + IOException prior = null; + try { + SegmentReader newReader; + if (newReaders[i] == null || infos.info(i).getUseCompoundFile() != newReaders[i].getSegmentInfo().getUseCompoundFile()) { + + // this is a new reader; in case we hit an exception we can close it safely + newReader = new SegmentReader(infos.info(i), termInfosIndexDivisor, IOContext.READ); + readerShared[i] = false; + newReaders[i] = newReader; + } else { + if (newReaders[i].getSegmentInfo().getDelGen() == infos.info(i).getDelGen()) { + // No change; this reader will be shared between + // the old and the new one, so we must incRef + // it: + readerShared[i] = true; + newReaders[i].incRef(); + } else { + readerShared[i] = false; + // Steal the ref returned by SegmentReader ctor: + assert infos.info(i).dir == newReaders[i].getSegmentInfo().dir; + assert infos.info(i).hasDeletions(); + newReaders[i] = new SegmentReader(infos.info(i), newReaders[i].core, IOContext.READ); + } + } + success = true; + } catch (IOException ex) { + prior = ex; + } finally { + if (!success) { + for (i++; i < infos.size(); i++) { + if (newReaders[i] != null) { + try { + if (!readerShared[i]) { + // this is a new subReader that is not used by the old one, + // we can close it + newReaders[i].close(); + } else { + // this subReader is also used by the old reader, so instead + // closing we must decRef it + newReaders[i].decRef(); + } + } catch (IOException ex) { + if (prior == null) prior = ex; + } + } + } + } + // throw the first exception + if (prior != null) throw prior; + } + } + return new StandardDirectoryReader(directory, newReaders, writer, infos, termInfosIndexDivisor, false); + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append(getClass().getSimpleName()); + buffer.append('('); + final String segmentsFile = segmentInfos.getCurrentSegmentFileName(); + if (segmentsFile != null) { + buffer.append(segmentsFile).append(":").append(segmentInfos.getVersion()); + } + if (writer != null) { + buffer.append(":nrt"); + } + for(int i=0;i getCommitUserData() { + ensureOpen(); + return segmentInfos.getUserData(); + } + + @Override + public boolean isCurrent() throws CorruptIndexException, IOException { + ensureOpen(); + if (writer == null || writer.isClosed()) { + // we loaded SegmentInfos from the directory + return SegmentInfos.readCurrentVersion(directory) == segmentInfos.getVersion(); + } else { + return writer.nrtIsCurrent(segmentInfos); + } + } + + @Override + protected synchronized void doClose() throws IOException { + 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; + } + } + + if (writer != null) { + // Since we just closed, writer may now be able to + // delete unused files: + writer.deletePendingFiles(); + } + + // throw the first exception + if (ioe != null) throw ioe; + } + + @Override + public IndexCommit getIndexCommit() throws IOException { + ensureOpen(); + return new ReaderCommit(segmentInfos, directory); + } + + static final class ReaderCommit extends IndexCommit { + private String segmentsFileName; + Collection files; + Directory dir; + long generation; + final Map userData; + private final int segmentCount; + + ReaderCommit(SegmentInfos infos, Directory dir) throws IOException { + segmentsFileName = infos.getCurrentSegmentFileName(); + this.dir = dir; + userData = infos.getUserData(); + files = Collections.unmodifiableCollection(infos.files(dir, true)); + generation = infos.getGeneration(); + segmentCount = infos.size(); + } + + @Override + public String toString() { + return "DirectoryReader.ReaderCommit(" + segmentsFileName + ")"; + } + + @Override + public int getSegmentCount() { + return segmentCount; + } + + @Override + public String getSegmentsFileName() { + return segmentsFileName; + } + + @Override + public Collection getFileNames() { + return files; + } + + @Override + public Directory getDirectory() { + return dir; + } + + @Override + public long getGeneration() { + return generation; + } + + @Override + public boolean isDeleted() { + return false; + } + + @Override + public Map getUserData() { + return userData; + } + + @Override + public void delete() { + throw new UnsupportedOperationException("This IndexCommit does not support deletions"); + } + } +} Index: lucene/src/java/org/apache/lucene/index/StandardDirectoryReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/StandardDirectoryReader.java (revision 1238503) +++ lucene/src/java/org/apache/lucene/index/StandardDirectoryReader.java (working copy) Property changes on: lucene/src/java/org/apache/lucene/index/StandardDirectoryReader.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native Index: lucene/src/test/org/apache/lucene/index/TestDocValuesIndexing.java =================================================================== --- lucene/src/test/org/apache/lucene/index/TestDocValuesIndexing.java (revision 1238713) +++ lucene/src/test/org/apache/lucene/index/TestDocValuesIndexing.java (working copy) @@ -696,7 +696,7 @@ w.forceMerge(1); DirectoryReader r = w.getReader(); w.close(); - assertEquals(17, ((AtomicReader) r.getSequentialSubReaders()[0]).docValues("field").load().getInt(0)); + assertEquals(17, r.getSequentialSubReaders()[0].docValues("field").load().getInt(0)); r.close(); d.close(); } Index: lucene/src/test/org/apache/lucene/index/TestIndexReader.java =================================================================== --- lucene/src/test/org/apache/lucene/index/TestIndexReader.java (revision 1238713) +++ lucene/src/test/org/apache/lucene/index/TestIndexReader.java (working copy) @@ -700,7 +700,7 @@ DirectoryReader r2 = DirectoryReader.openIfChanged(r); assertNotNull(r2); r.close(); - AtomicReader sub0 = (AtomicReader) r2.getSequentialSubReaders()[0]; + AtomicReader sub0 = r2.getSequentialSubReaders()[0]; final int[] ints2 = FieldCache.DEFAULT.getInts(sub0, "number", false); r2.close(); assertTrue(ints == ints2); Index: solr/core/src/test/org/apache/solr/core/TestConfig.java =================================================================== --- solr/core/src/test/org/apache/solr/core/TestConfig.java (revision 1238713) +++ solr/core/src/test/org/apache/solr/core/TestConfig.java (working copy) @@ -126,9 +126,6 @@ IndexReaderFactory irf = h.getCore().getIndexReaderFactory(); StandardIndexReaderFactory sirf = (StandardIndexReaderFactory) irf; assertEquals(12, sirf.termInfosIndexDivisor); - SolrQueryRequest req = req(); - assertEquals(12, req.getSearcher().getIndexReader().getTermInfosIndexDivisor()); - req.close(); }