001: package org.apache.lucene.index;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import java.io.IOException;
021:
022: import org.apache.lucene.store.Directory;
023: import org.apache.lucene.store.Lock;
024: import org.apache.lucene.store.LockObtainFailedException;
025:
026: /**
027: * IndexReader implementation that has access to a Directory.
028: * Instances that have a SegmentInfos object (i. e. segmentInfos != null)
029: * "own" the directory, which means that they try to acquire a write lock
030: * whenever index modifications are performed.
031: */
032: abstract class DirectoryIndexReader extends IndexReader {
033: protected Directory directory;
034: protected boolean closeDirectory;
035: private IndexDeletionPolicy deletionPolicy;
036:
037: private SegmentInfos segmentInfos;
038: private Lock writeLock;
039: private boolean stale;
040:
041: /** Used by commit() to record pre-commit state in case
042: * rollback is necessary */
043: private boolean rollbackHasChanges;
044: private SegmentInfos rollbackSegmentInfos;
045:
046: void init(Directory directory, SegmentInfos segmentInfos,
047: boolean closeDirectory) {
048: this .directory = directory;
049: this .segmentInfos = segmentInfos;
050: this .closeDirectory = closeDirectory;
051: }
052:
053: protected DirectoryIndexReader() {
054: }
055:
056: DirectoryIndexReader(Directory directory,
057: SegmentInfos segmentInfos, boolean closeDirectory) {
058: super ();
059: init(directory, segmentInfos, closeDirectory);
060: }
061:
062: static DirectoryIndexReader open(final Directory directory,
063: final boolean closeDirectory,
064: final IndexDeletionPolicy deletionPolicy)
065: throws CorruptIndexException, IOException {
066:
067: return (DirectoryIndexReader) new SegmentInfos.FindSegmentsFile(
068: directory) {
069:
070: protected Object doBody(String segmentFileName)
071: throws CorruptIndexException, IOException {
072:
073: SegmentInfos infos = new SegmentInfos();
074: infos.read(directory, segmentFileName);
075:
076: DirectoryIndexReader reader;
077:
078: if (infos.size() == 1) { // index is optimized
079: reader = SegmentReader.get(infos, infos.info(0),
080: closeDirectory);
081: } else {
082: reader = new MultiSegmentReader(directory, infos,
083: closeDirectory);
084: }
085: reader.setDeletionPolicy(deletionPolicy);
086: return reader;
087: }
088: }.run();
089: }
090:
091: public final synchronized IndexReader reopen()
092: throws CorruptIndexException, IOException {
093: ensureOpen();
094:
095: if (this .hasChanges || this .isCurrent()) {
096: // the index hasn't changed - nothing to do here
097: return this ;
098: }
099:
100: return (DirectoryIndexReader) new SegmentInfos.FindSegmentsFile(
101: directory) {
102:
103: protected Object doBody(String segmentFileName)
104: throws CorruptIndexException, IOException {
105: SegmentInfos infos = new SegmentInfos();
106: infos.read(directory, segmentFileName);
107:
108: DirectoryIndexReader newReader = doReopen(infos);
109:
110: if (DirectoryIndexReader.this != newReader) {
111: newReader.init(directory, infos, closeDirectory);
112: newReader.deletionPolicy = deletionPolicy;
113: }
114:
115: return newReader;
116: }
117: }.run();
118: }
119:
120: /**
121: * Re-opens the index using the passed-in SegmentInfos
122: */
123: protected abstract DirectoryIndexReader doReopen(SegmentInfos infos)
124: throws CorruptIndexException, IOException;
125:
126: public void setDeletionPolicy(IndexDeletionPolicy deletionPolicy) {
127: this .deletionPolicy = deletionPolicy;
128: }
129:
130: /** Returns the directory this index resides in.
131: */
132: public Directory directory() {
133: ensureOpen();
134: return directory;
135: }
136:
137: /**
138: * Version number when this IndexReader was opened.
139: */
140: public long getVersion() {
141: ensureOpen();
142: return segmentInfos.getVersion();
143: }
144:
145: /**
146: * Check whether this IndexReader is still using the
147: * current (i.e., most recently committed) version of the
148: * index. If a writer has committed any changes to the
149: * index since this reader was opened, this will return
150: * <code>false</code>, in which case you must open a new
151: * IndexReader in order to see the changes. See the
152: * description of the <a href="IndexWriter.html#autoCommit"><code>autoCommit</code></a>
153: * flag which controls when the {@link IndexWriter}
154: * actually commits changes to the index.
155: *
156: * @throws CorruptIndexException if the index is corrupt
157: * @throws IOException if there is a low-level IO error
158: */
159: public boolean isCurrent() throws CorruptIndexException,
160: IOException {
161: ensureOpen();
162: return SegmentInfos.readCurrentVersion(directory) == segmentInfos
163: .getVersion();
164: }
165:
166: /**
167: * Checks is the index is optimized (if it has a single segment and no deletions)
168: * @return <code>true</code> if the index is optimized; <code>false</code> otherwise
169: */
170: public boolean isOptimized() {
171: ensureOpen();
172: return segmentInfos.size() == 1 && hasDeletions() == false;
173: }
174:
175: protected void doClose() throws IOException {
176: if (closeDirectory)
177: directory.close();
178: }
179:
180: /**
181: * Commit changes resulting from delete, undeleteAll, or
182: * setNorm operations
183: *
184: * If an exception is hit, then either no changes or all
185: * changes will have been committed to the index
186: * (transactional semantics).
187: * @throws IOException if there is a low-level IO error
188: */
189: protected void doCommit() throws IOException {
190: if (hasChanges) {
191: if (segmentInfos != null) {
192:
193: // Default deleter (for backwards compatibility) is
194: // KeepOnlyLastCommitDeleter:
195: IndexFileDeleter deleter = new IndexFileDeleter(
196: directory,
197: deletionPolicy == null ? new KeepOnlyLastCommitDeletionPolicy()
198: : deletionPolicy, segmentInfos, null,
199: null);
200:
201: // Checkpoint the state we are about to change, in
202: // case we have to roll back:
203: startCommit();
204:
205: boolean success = false;
206: try {
207: commitChanges();
208: segmentInfos.write(directory);
209: success = true;
210: } finally {
211:
212: if (!success) {
213:
214: // Rollback changes that were made to
215: // SegmentInfos but failed to get [fully]
216: // committed. This way this reader instance
217: // remains consistent (matched to what's
218: // actually in the index):
219: rollbackCommit();
220:
221: // Recompute deletable files & remove them (so
222: // partially written .del files, etc, are
223: // removed):
224: deleter.refresh();
225: }
226: }
227:
228: // Have the deleter remove any now unreferenced
229: // files due to this commit:
230: deleter.checkpoint(segmentInfos, true);
231:
232: if (writeLock != null) {
233: writeLock.release(); // release write lock
234: writeLock = null;
235: }
236: } else
237: commitChanges();
238: }
239: hasChanges = false;
240: }
241:
242: protected abstract void commitChanges() throws IOException;
243:
244: /**
245: * Tries to acquire the WriteLock on this directory.
246: * this method is only valid if this IndexReader is directory owner.
247: *
248: * @throws StaleReaderException if the index has changed
249: * since this reader was opened
250: * @throws CorruptIndexException if the index is corrupt
251: * @throws LockObtainFailedException if another writer
252: * has this index open (<code>write.lock</code> could not
253: * be obtained)
254: * @throws IOException if there is a low-level IO error
255: */
256: protected void acquireWriteLock() throws StaleReaderException,
257: CorruptIndexException, LockObtainFailedException,
258: IOException {
259: if (segmentInfos != null) {
260: ensureOpen();
261: if (stale)
262: throw new StaleReaderException(
263: "IndexReader out of date and no longer valid for delete, undelete, or setNorm operations");
264:
265: if (writeLock == null) {
266: Lock writeLock = directory
267: .makeLock(IndexWriter.WRITE_LOCK_NAME);
268: if (!writeLock.obtain(IndexWriter.WRITE_LOCK_TIMEOUT)) // obtain write lock
269: throw new LockObtainFailedException(
270: "Index locked for write: " + writeLock);
271: this .writeLock = writeLock;
272:
273: // we have to check whether index has changed since this reader was opened.
274: // if so, this reader is no longer valid for deletion
275: if (SegmentInfos.readCurrentVersion(directory) > segmentInfos
276: .getVersion()) {
277: stale = true;
278: this .writeLock.release();
279: this .writeLock = null;
280: throw new StaleReaderException(
281: "IndexReader out of date and no longer valid for delete, undelete, or setNorm operations");
282: }
283: }
284: }
285: }
286:
287: /**
288: * Should internally checkpoint state that will change
289: * during commit so that we can rollback if necessary.
290: */
291: void startCommit() {
292: if (segmentInfos != null) {
293: rollbackSegmentInfos = (SegmentInfos) segmentInfos.clone();
294: }
295: rollbackHasChanges = hasChanges;
296: }
297:
298: /**
299: * Rolls back state to just before the commit (this is
300: * called by commit() if there is some exception while
301: * committing).
302: */
303: void rollbackCommit() {
304: if (segmentInfos != null) {
305: for (int i = 0; i < segmentInfos.size(); i++) {
306: // Rollback each segmentInfo. Because the
307: // SegmentReader holds a reference to the
308: // SegmentInfo we can't [easily] just replace
309: // segmentInfos, so we reset it in place instead:
310: segmentInfos.info(i)
311: .reset(rollbackSegmentInfos.info(i));
312: }
313: rollbackSegmentInfos = null;
314: }
315:
316: hasChanges = rollbackHasChanges;
317: }
318:
319: /** Release the write lock, if needed. */
320: protected void finalize() throws Throwable {
321: try {
322: if (writeLock != null) {
323: writeLock.release(); // release write lock
324: writeLock = null;
325: }
326: } finally {
327: super.finalize();
328: }
329: }
330:
331: }
|