blob: e28759ef3c9c94f07a9624a18fdcef43f65f6c85 [file] [log] [blame]
using J2N.Text;
using Lucene.Net.Facet.Taxonomy.Directory;
using Lucene.Net.Facet.Taxonomy.WriterCache;
using Lucene.Net.Index;
using Lucene.Net.Store;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using Directory = Lucene.Net.Store.Directory;
namespace Lucene.Net.Replicator
{
/*
* 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.
*/
/// <summary>
/// A <see cref="IRevision"/> of a single index and taxonomy index files which comprises
/// the list of files from both indexes. This revision should be used whenever a
/// pair of search and taxonomy indexes need to be replicated together to
/// guarantee consistency of both on the replicating (client) side.
/// </summary>
/// <remarks>
/// @lucene.experimental
/// </remarks>
/// <seealso cref="IndexRevision"/>
public class IndexAndTaxonomyRevision : IRevision
{
/// <summary>
/// A <see cref="DirectoryTaxonomyWriter"/> which sets the underlying
/// <see cref="Index.IndexWriter"/>'s <see cref="IndexDeletionPolicy"/> to
/// <see cref="SnapshotDeletionPolicy"/>.
/// </summary>
public class SnapshotDirectoryTaxonomyWriter : DirectoryTaxonomyWriter
{
private SnapshotDeletionPolicy sdp;
private IndexWriter writer;
/// <summary>
/// <see cref="DirectoryTaxonomyWriter(Directory, OpenMode, ITaxonomyWriterCache)"/>
/// </summary>
/// <exception cref="IOException"></exception>
public SnapshotDirectoryTaxonomyWriter(Directory directory, OpenMode openMode, ITaxonomyWriterCache cache)
: base(directory, openMode, cache)
{
}
/// <summary>
/// <see cref="DirectoryTaxonomyWriter(Directory, OpenMode)"/>
/// </summary>
/// <exception cref="IOException"></exception>
public SnapshotDirectoryTaxonomyWriter(Directory directory, OpenMode openMode = OpenMode.CREATE_OR_APPEND)
: base(directory, openMode)
{
}
protected override IndexWriterConfig CreateIndexWriterConfig(OpenMode openMode)
{
IndexWriterConfig conf = base.CreateIndexWriterConfig(openMode);
conf.IndexDeletionPolicy = sdp = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy);
return conf;
}
protected override IndexWriter OpenIndexWriter(Directory directory, IndexWriterConfig config)
{
return writer = base.OpenIndexWriter(directory, config);
}
/// <summary>
/// Gets the <see cref="SnapshotDeletionPolicy"/> used by the underlying <see cref="Index.IndexWriter"/>.
/// </summary>
public virtual SnapshotDeletionPolicy DeletionPolicy { get { return sdp; } }
/// <summary>
/// Gets the <see cref="Index.IndexWriter"/> used by this <see cref="DirectoryTaxonomyWriter"/>.
/// </summary>
public virtual IndexWriter IndexWriter { get { return writer; } }
}
public const string INDEX_SOURCE = "index";
public const string TAXONOMY_SOURCE = "taxonomy";
private readonly IndexWriter indexWriter;
private readonly SnapshotDirectoryTaxonomyWriter taxonomyWriter;
private readonly IndexCommit indexCommit, taxonomyCommit;
private readonly SnapshotDeletionPolicy indexSdp, taxonomySdp;
private readonly string version;
private readonly IDictionary<string, IList<RevisionFile>> sourceFiles;
/// <summary>
/// Returns a map of the revision files from the given <see cref="IndexCommit"/>s of the search and taxonomy indexes.
/// </summary>
/// <exception cref="IOException"></exception>
public static IDictionary<string, IList<RevisionFile>> RevisionFiles(IndexCommit indexCommit, IndexCommit taxonomyCommit)
{
return new Dictionary<string, IList<RevisionFile>>{
{ INDEX_SOURCE, IndexRevision.RevisionFiles(indexCommit).Values.First() },
{ TAXONOMY_SOURCE, IndexRevision.RevisionFiles(taxonomyCommit).Values.First() }
};
}
/// <summary>
/// Returns a <see cref="string"/> representation of a revision's version from the given
/// <see cref="IndexCommit"/>s of the search and taxonomy indexes.
/// </summary>
/// <returns>a <see cref="string"/> representation of a revision's version from the given <see cref="IndexCommit"/>s of the search and taxonomy indexes.</returns>
public static string RevisionVersion(IndexCommit indexCommit, IndexCommit taxonomyCommit)
{
return string.Format("{0:X}:{1:X}", indexCommit.Generation, taxonomyCommit.Generation);
}
/// <summary>
/// Constructor over the given <see cref="IndexWriter"/>. Uses the last
/// <see cref="IndexCommit"/> found in the <see cref="Directory"/> managed by the given
/// writer.
/// </summary>
/// <exception cref="IOException"></exception>
public IndexAndTaxonomyRevision(IndexWriter indexWriter, SnapshotDirectoryTaxonomyWriter taxonomyWriter)
{
this.indexSdp = indexWriter.Config.IndexDeletionPolicy as SnapshotDeletionPolicy;
if (indexSdp == null)
throw new ArgumentException("IndexWriter must be created with SnapshotDeletionPolicy", "indexWriter");
this.indexWriter = indexWriter;
this.taxonomyWriter = taxonomyWriter;
this.taxonomySdp = taxonomyWriter.DeletionPolicy;
this.indexCommit = indexSdp.Snapshot();
this.taxonomyCommit = taxonomySdp.Snapshot();
this.version = RevisionVersion(indexCommit, taxonomyCommit);
this.sourceFiles = RevisionFiles(indexCommit, taxonomyCommit);
}
/// <summary>
/// Compares this <see cref="IndexAndTaxonomyRevision"/> to the given <see cref="version"/>.
/// </summary>
public virtual int CompareTo(string version)
{
string[] parts = version.Split(':').TrimEnd();
long indexGen = long.Parse(parts[0], NumberStyles.HexNumber);
long taxonomyGen = long.Parse(parts[1], NumberStyles.HexNumber);
long indexCommitGen = indexCommit.Generation;
long taxonomyCommitGen = taxonomyCommit.Generation;
if (indexCommitGen < indexGen)
return -1;
if (indexCommitGen > indexGen)
return 1;
return taxonomyCommitGen < taxonomyGen ? -1 : (taxonomyCommitGen > taxonomyGen ? 1 : 0);
}
public virtual int CompareTo(IRevision other)
{
if (other == null)
throw new ArgumentNullException("other");
IndexAndTaxonomyRevision itr = other as IndexAndTaxonomyRevision;
if(itr == null)
throw new ArgumentException(string.Format("Cannot compare IndexAndTaxonomyRevision to a {0}", other.GetType()), "other");
int cmp = indexCommit.CompareTo(itr.indexCommit);
return cmp != 0 ? cmp : taxonomyCommit.CompareTo(itr.taxonomyCommit);
}
public virtual string Version { get { return version; } }
public virtual IDictionary<string, IList<RevisionFile>> SourceFiles { get { return sourceFiles; } }
/// <exception cref="IOException"></exception>
public virtual Stream Open(string source, string fileName)
{
Debug.Assert(source.Equals(INDEX_SOURCE, StringComparison.Ordinal) || source.Equals(TAXONOMY_SOURCE, StringComparison.Ordinal), string.Format("invalid source; expected=({0} or {1}) got={2}", INDEX_SOURCE, TAXONOMY_SOURCE, source));
IndexCommit commit = source.Equals(INDEX_SOURCE, StringComparison.Ordinal) ? indexCommit : taxonomyCommit;
return new IndexInputStream(commit.Directory.OpenInput(fileName, IOContext.READ_ONCE));
}
/// <exception cref="IOException"></exception>
public virtual void Release()
{
try
{
indexSdp.Release(indexCommit);
}
finally
{
taxonomySdp.Release(taxonomyCommit);
}
try
{
indexWriter.DeleteUnusedFiles();
}
finally
{
taxonomyWriter.IndexWriter.DeleteUnusedFiles();
}
}
}
}