/*
* 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.
*/
using System;
using Directory = Lucene.Net.Store.Directory;
using Lock = Lucene.Net.Store.Lock;
using LockObtainFailedException = Lucene.Net.Store.LockObtainFailedException;
namespace Lucene.Net.Index
{
/// IndexReader implementation that has access to a Directory.
/// Instances that have a SegmentInfos object (i. e. segmentInfos != null)
/// "own" the directory, which means that they try to acquire a write lock
/// whenever index modifications are performed.
///
abstract public class DirectoryIndexReader : IndexReader
{
private class AnonymousClassFindSegmentsFile : SegmentInfos.FindSegmentsFile
{
private void InitBlock(bool closeDirectory, Lucene.Net.Index.IndexDeletionPolicy deletionPolicy)
{
this.closeDirectory = closeDirectory;
this.deletionPolicy = deletionPolicy;
}
private bool closeDirectory;
private Lucene.Net.Index.IndexDeletionPolicy deletionPolicy;
internal AnonymousClassFindSegmentsFile(bool closeDirectory, Lucene.Net.Index.IndexDeletionPolicy deletionPolicy, Lucene.Net.Store.Directory Param1) : base(Param1)
{
InitBlock(closeDirectory, deletionPolicy);
}
protected internal override System.Object DoBody(System.String segmentFileName)
{
SegmentInfos infos = new SegmentInfos();
infos.Read(directory, segmentFileName);
DirectoryIndexReader reader;
if (infos.Count == 1)
{
// index is optimized
reader = SegmentReader.Get(infos, infos.Info(0), closeDirectory);
}
else
{
reader = new MultiSegmentReader(directory, infos, closeDirectory);
}
reader.SetDeletionPolicy(deletionPolicy);
return reader;
}
}
private class AnonymousClassFindSegmentsFile1 : SegmentInfos.FindSegmentsFile
{
private void InitBlock(DirectoryIndexReader enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private DirectoryIndexReader enclosingInstance;
public DirectoryIndexReader Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
internal AnonymousClassFindSegmentsFile1(DirectoryIndexReader enclosingInstance, Lucene.Net.Store.Directory Param1) : base(Param1)
{
InitBlock(enclosingInstance);
}
protected internal override System.Object DoBody(System.String segmentFileName)
{
SegmentInfos infos = new SegmentInfos();
infos.Read(directory, segmentFileName);
DirectoryIndexReader newReader = Enclosing_Instance.DoReopen(infos);
if (Enclosing_Instance != newReader)
{
newReader.Init(directory, infos, Enclosing_Instance.closeDirectory);
newReader.deletionPolicy = Enclosing_Instance.deletionPolicy;
}
return newReader;
}
}
protected internal Directory directory;
protected internal bool closeDirectory;
private IndexDeletionPolicy deletionPolicy;
private SegmentInfos segmentInfos;
private Lock writeLock;
private bool stale;
/// Used by commit() to record pre-commit state in case
/// rollback is necessary
///
private bool rollbackHasChanges;
private SegmentInfos rollbackSegmentInfos;
internal virtual void Init(Directory directory, SegmentInfos segmentInfos, bool closeDirectory)
{
this.directory = directory;
this.segmentInfos = segmentInfos;
this.closeDirectory = closeDirectory;
}
protected internal DirectoryIndexReader()
{
}
internal DirectoryIndexReader(Directory directory, SegmentInfos segmentInfos, bool closeDirectory) : base()
{
Init(directory, segmentInfos, closeDirectory);
}
internal static DirectoryIndexReader Open(Directory directory, bool closeDirectory, IndexDeletionPolicy deletionPolicy)
{
return (DirectoryIndexReader) new AnonymousClassFindSegmentsFile(closeDirectory, deletionPolicy, directory).Run();
}
public override IndexReader Reopen()
{
lock (this)
{
EnsureOpen();
if (this.hasChanges || this.IsCurrent())
{
// the index hasn't changed - nothing to do here
return this;
}
return (DirectoryIndexReader) new AnonymousClassFindSegmentsFile1(this, directory).Run();
}
}
/// Re-opens the index using the passed-in SegmentInfos
protected internal abstract DirectoryIndexReader DoReopen(SegmentInfos infos);
public virtual void SetDeletionPolicy(IndexDeletionPolicy deletionPolicy)
{
this.deletionPolicy = deletionPolicy;
}
/// Returns the directory this index resides in.
public override Directory Directory()
{
EnsureOpen();
return directory;
}
/// Version number when this IndexReader was opened.
public override long GetVersion()
{
EnsureOpen();
return segmentInfos.GetVersion();
}
/// Check whether this IndexReader is still using the
/// current (i.e., most recently committed) version of the
/// index. If a writer has committed any changes to the
/// index since this reader was opened, this will return
/// false
, in which case you must open a new
/// IndexReader in order to see the changes. See the
/// description of the autoCommit
/// flag which controls when the {@link IndexWriter}
/// actually commits changes to the index.
///
///
/// CorruptIndexException if the index is corrupt
/// IOException if there is a low-level IO error
public override bool IsCurrent()
{
EnsureOpen();
return SegmentInfos.ReadCurrentVersion(directory) == segmentInfos.GetVersion();
}
/// Checks is the index is optimized (if it has a single segment and no deletions)
/// true
if the index is optimized; false
otherwise
///
public override bool IsOptimized()
{
EnsureOpen();
return segmentInfos.Count == 1 && HasDeletions() == false;
}
protected internal override void DoClose()
{
if (closeDirectory)
directory.Close();
}
/// Commit changes resulting from delete, undeleteAll, or
/// setNorm operations
///
/// If an exception is hit, then either no changes or all
/// changes will have been committed to the index
/// (transactional semantics).
///
/// IOException if there is a low-level IO error
protected internal override void DoCommit()
{
if (hasChanges)
{
if (segmentInfos != null)
{
// Default deleter (for backwards compatibility) is
// KeepOnlyLastCommitDeleter:
IndexFileDeleter deleter = new IndexFileDeleter(directory, deletionPolicy == null ? new KeepOnlyLastCommitDeletionPolicy() : deletionPolicy, segmentInfos, null, null);
// Checkpoint the state we are about to change, in
// case we have to roll back:
StartCommit();
bool success = false;
try
{
CommitChanges();
segmentInfos.Write(directory);
success = true;
}
finally
{
if (!success)
{
// Rollback changes that were made to
// SegmentInfos but failed to get [fully]
// committed. This way this reader instance
// remains consistent (matched to what's
// actually in the index):
RollbackCommit();
// Recompute deletable files & remove them (so
// partially written .del files, etc, are
// removed):
deleter.Refresh();
}
}
// Have the deleter remove any now unreferenced
// files due to this commit:
deleter.Checkpoint(segmentInfos, true);
if (writeLock != null)
{
writeLock.Release(); // release write lock
writeLock = null;
}
}
else
CommitChanges();
}
hasChanges = false;
}
protected internal abstract void CommitChanges();
/// Tries to acquire the WriteLock on this directory.
/// this method is only valid if this IndexReader is directory owner.
///
///
/// StaleReaderException if the index has changed
/// since this reader was opened
///
/// CorruptIndexException if the index is corrupt
/// LockObtainFailedException if another writer
/// has this index open (write.lock
could not
/// be obtained)
///
/// IOException if there is a low-level IO error
protected internal override void AcquireWriteLock()
{
if (segmentInfos != null)
{
EnsureOpen();
if (stale)
throw new StaleReaderException("IndexReader out of date and no longer valid for delete, undelete, or setNorm operations");
if (this.writeLock == null)
{
Lock writeLock = directory.MakeLock(IndexWriter.WRITE_LOCK_NAME);
if (!writeLock.Obtain(IndexWriter.WRITE_LOCK_TIMEOUT))
// obtain write lock
{
throw new LockObtainFailedException("Index locked for write: " + writeLock);
}
this.writeLock = writeLock;
// we have to check whether index has changed since this reader was opened.
// if so, this reader is no longer valid for deletion
if (SegmentInfos.ReadCurrentVersion(directory) > segmentInfos.GetVersion())
{
stale = true;
this.writeLock.Release();
this.writeLock = null;
throw new StaleReaderException("IndexReader out of date and no longer valid for delete, undelete, or setNorm operations");
}
}
}
}
/// Should internally checkpoint state that will change
/// during commit so that we can rollback if necessary.
///
internal virtual void StartCommit()
{
if (segmentInfos != null)
{
rollbackSegmentInfos = (SegmentInfos) segmentInfos.Clone();
}
rollbackHasChanges = hasChanges;
}
/// Rolls back state to just before the commit (this is
/// called by commit() if there is some exception while
/// committing).
///
internal virtual void RollbackCommit()
{
if (segmentInfos != null)
{
for (int i = 0; i < segmentInfos.Count; i++)
{
// Rollback each segmentInfo. Because the
// SegmentReader holds a reference to the
// SegmentInfo we can't [easily] just replace
// segmentInfos, so we reset it in place instead:
segmentInfos.Info(i).Reset(rollbackSegmentInfos.Info(i));
}
rollbackSegmentInfos = null;
}
hasChanges = rollbackHasChanges;
}
/// Release the write lock, if needed.
~DirectoryIndexReader()
{
try
{
if (writeLock != null)
{
writeLock.Release(); // release write lock
writeLock = null;
}
}
finally
{
// {{Aroush-2.3.1}} do we need to call Finalize() here?
}
}
}
}