/* * 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 System.Collections.Generic; // Used only for WRITE_LOCK_NAME in deprecated create=true case: using System.IO; using Lucene.Net.Support; using IndexFileNameFilter = Lucene.Net.Index.IndexFileNameFilter; using IndexWriter = Lucene.Net.Index.IndexWriter; using Constants = Lucene.Net.Util.Constants; namespace Lucene.Net.Store { /// /// Base class for Directory implementations that store index /// files in the file system. There are currently three core /// subclasses: /// /// /// /// is a straightforward /// implementation using java.io.RandomAccessFile. /// However, it has poor concurrent performance /// (multiple threads will bottleneck) as it /// synchronizes when multiple threads read from the /// same file. /// /// uses java.nio's /// FileChannel's positional io when reading to avoid /// synchronization when reading from the same file. /// Unfortunately, due to a Windows-only Sun /// JRE bug this is a poor choice for Windows, but /// on all other platforms this is the preferred /// choice. Applications using or /// Future#cancel(boolean) (on Java 1.5) should use /// instead. See java doc /// for details. /// /// /// /// uses memory-mapped IO when /// reading. This is a good choice if you have plenty /// of virtual memory relative to your index size, eg /// if you are running on a 64 bit JRE, or you are /// running on a 32 bit JRE but your index sizes are /// small enough to fit into the virtual memory space. /// Java has currently the limitation of not being able to /// unmap files from user code. The files are unmapped, when GC /// releases the byte buffers. Due to /// /// this bug in Sun's JRE, MMapDirectory's /// is unable to close the underlying OS file handle. Only when /// GC finally collects the underlying objects, which could be /// quite some time later, will the file handle be closed. /// This will consume additional transient disk usage: on Windows, /// attempts to delete or overwrite the files will result in an /// exception; on other platforms, which typically have a "delete on /// last close" semantics, while such operations will succeed, the bytes /// are still consuming space on disk. For many applications this /// limitation is not a problem (e.g. if you have plenty of disk space, /// and you don't rely on overwriting files on Windows) but it's still /// an important limitation to be aware of. This class supplies a /// (possibly dangerous) workaround mentioned in the bug report, /// which may fail on non-Sun JVMs. /// /// Applications using or /// Future#cancel(boolean) (on Java 1.5) should use /// instead. See /// java doc for details. /// /// /// Unfortunately, because of system peculiarities, there is /// no single overall best implementation. Therefore, we've /// added the method, to allow Lucene to choose /// the best FSDirectory implementation given your /// environment, and the known limitations of each /// implementation. For users who have no reason to prefer a /// specific implementation, it's best to simply use ///. For all others, you should instantiate the /// desired implementation directly. /// ///

The locking implementation is by default ///, but can be changed by /// passing in a custom instance. ///

public abstract class FSDirectory : Directory { private static System.Security.Cryptography.HashAlgorithm DIGESTER; static FSDirectory() { try { DIGESTER = Cryptography.HashAlgorithm; } catch (System.Exception e) { throw new System.SystemException(e.ToString(), e); } } private bool checked_Renamed; internal void CreateDir() { if (!checked_Renamed) { if (!this.internalDirectory.Exists) { try { this.internalDirectory.Create(); } catch (Exception) { throw new System.IO.IOException("Cannot create directory: " + internalDirectory); } this.internalDirectory.Refresh(); // need to see the creation } checked_Renamed = true; } } /// Initializes the directory to create a new file with the given name. /// This method should be used in . /// protected internal void InitOutput(System.String name) { EnsureOpen(); CreateDir(); System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)); if (file.Exists) // delete existing, if any { try { file.Delete(); } catch (Exception) { throw new System.IO.IOException("Cannot overwrite: " + file); } } } /// The underlying filesystem directory protected internal System.IO.DirectoryInfo internalDirectory = null; /// Create a new FSDirectory for the named location (ctor for subclasses). /// the path of the directory /// /// the lock factory to use, or null for the default /// (); /// /// IOException protected internal FSDirectory(System.IO.DirectoryInfo path, LockFactory lockFactory) { // new ctors use always NativeFSLockFactory as default: if (lockFactory == null) { lockFactory = new NativeFSLockFactory(); } // Set up lockFactory with cascaded defaults: if an instance was passed in, // use that; else if locks are disabled, use NoLockFactory; else if the // system property Lucene.Net.Store.FSDirectoryLockFactoryClass is set, // instantiate that; else, use SimpleFSLockFactory: internalDirectory = path; // due to differences in how Java & .NET refer to files, the checks are a bit different if (!internalDirectory.Exists && System.IO.File.Exists(internalDirectory.FullName)) { throw new NoSuchDirectoryException("file '" + internalDirectory.FullName + "' exists but is not a directory"); } SetLockFactory(lockFactory); // for filesystem based LockFactory, delete the lockPrefix, if the locks are placed // in index dir. If no index dir is given, set ourselves if (lockFactory is FSLockFactory) { FSLockFactory lf = (FSLockFactory)lockFactory; System.IO.DirectoryInfo dir = lf.LockDir; // if the lock factory has no lockDir set, use the this directory as lockDir if (dir == null) { lf.LockDir = this.internalDirectory; lf.LockPrefix = null; } else if (dir.FullName.Equals(this.internalDirectory.FullName)) { lf.LockPrefix = null; } } } /// Creates an FSDirectory instance, trying to pick the /// best implementation given the current environment. /// The directory returned uses the . /// ///

Currently this returns as /// NIOFSDirectory is currently not supported. /// ///

NOTE: this method may suddenly change which /// implementation is returned from release to release, in /// the event that higher performance defaults become /// possible; if the precise implementation is important to /// your application, please instantiate it directly, /// instead. On 64 bit systems, it may also good to /// return , but this is disabled /// because of officially missing unmap support in Java. /// For optimal performance you should consider using /// this implementation on 64 bit JVMs. /// ///

See above ///

public static FSDirectory Open(string path) { return Open(new DirectoryInfo(path), null); } /// Creates an FSDirectory instance, trying to pick the /// best implementation given the current environment. /// The directory returned uses the . /// ///

Currently this returns as /// NIOFSDirectory is currently not supported. /// ///

NOTE: this method may suddenly change which /// implementation is returned from release to release, in /// the event that higher performance defaults become /// possible; if the precise implementation is important to /// your application, please instantiate it directly, /// instead. On 64 bit systems, it may also good to /// return , but this is disabled /// because of officially missing unmap support in Java. /// For optimal performance you should consider using /// this implementation on 64 bit JVMs. /// ///

See above ///

public static FSDirectory Open(System.IO.DirectoryInfo path) { return Open(path, null); } /// Just like , but allows you to /// also specify a custom . /// public static FSDirectory Open(System.IO.DirectoryInfo path, LockFactory lockFactory) { /* For testing: MMapDirectory dir=new MMapDirectory(path, lockFactory); dir.setUseUnmap(true); return dir; */ if (Constants.WINDOWS) { return new SimpleFSDirectory(path, lockFactory); } else { //NIOFSDirectory is not implemented in Lucene.Net //return new NIOFSDirectory(path, lockFactory); return new SimpleFSDirectory(path, lockFactory); } } /// Lists all files (not subdirectories) in the /// directory. This method never returns null (throws /// instead). /// /// /// NoSuchDirectoryException if the directory /// does not exist, or does exist but is not a /// directory. /// /// IOException if list() returns null public static System.String[] ListAll(System.IO.DirectoryInfo dir) { if (!dir.Exists) { throw new NoSuchDirectoryException("directory '" + dir.FullName + "' does not exist"); } else if (System.IO.File.Exists(dir.FullName)) { throw new NoSuchDirectoryException("File '" + dir.FullName + "' does not exist"); } // Exclude subdirs, only the file names, not the paths System.IO.FileInfo[] files = dir.GetFiles(); System.String[] result = new System.String[files.Length]; for (int i = 0; i < files.Length; i++) { result[i] = files[i].Name; } // no reason to return null, if the directory cannot be listed, an exception // will be thrown on the above call to dir.GetFiles() // use of LINQ to create the return value array may be a bit more efficient return result; } /// Lists all files (not subdirectories) in the /// directory. /// /// /// public override System.String[] ListAll() { EnsureOpen(); return ListAll(internalDirectory); } /// Returns true iff a file with the given name exists. public override bool FileExists(System.String name) { EnsureOpen(); System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)); return file.Exists; } /// Returns the time the named file was last modified. public override long FileModified(System.String name) { EnsureOpen(); System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)); return (long)file.LastWriteTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds; //{{LUCENENET-353}} } /// Returns the time the named file was last modified. public static long FileModified(System.IO.FileInfo directory, System.String name) { System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(directory.FullName, name)); return (long)file.LastWriteTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds; //{{LUCENENET-353}} } /// Set the modified time of an existing file to now. public override void TouchFile(System.String name) { EnsureOpen(); System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)); file.LastWriteTime = System.DateTime.Now; } /// Returns the length in bytes of a file in the directory. public override long FileLength(System.String name) { EnsureOpen(); System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)); return file.Exists ? file.Length : 0; } /// Removes an existing file in the directory. public override void DeleteFile(System.String name) { EnsureOpen(); System.IO.FileInfo file = new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)); try { file.Delete(); } catch (Exception) { throw new System.IO.IOException("Cannot delete " + file); } } public override void Sync(System.String name) { EnsureOpen(); System.IO.FileInfo fullFile = new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)); bool success = false; int retryCount = 0; System.IO.IOException exc = null; while (!success && retryCount < 5) { retryCount++; System.IO.FileStream file = null; try { try { file = new System.IO.FileStream(fullFile.FullName, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.ReadWrite); FileSupport.Sync(file); success = true; } finally { if (file != null) file.Close(); } } catch (System.IO.IOException ioe) { if (exc == null) exc = ioe; // Pause 5 msec System.Threading.Thread.Sleep(5); } } if (!success && exc != null) // Throw original exception throw exc; } // Inherit javadoc public override IndexInput OpenInput(System.String name) { EnsureOpen(); return OpenInput(name, BufferedIndexInput.BUFFER_SIZE); } /// So we can do some byte-to-hexchar conversion below private static readonly char[] HEX_DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; public override string GetLockId() { EnsureOpen(); System.String dirName; // name to be hashed try { dirName = internalDirectory.FullName; } catch (System.IO.IOException e) { throw new System.SystemException(e.ToString(), e); } byte[] digest; lock (DIGESTER) { digest = DIGESTER.ComputeHash(System.Text.Encoding.UTF8.GetBytes(dirName)); } System.Text.StringBuilder buf = new System.Text.StringBuilder(); buf.Append("lucene-"); for (int i = 0; i < digest.Length; i++) { int b = digest[i]; buf.Append(HEX_DIGITS[(b >> 4) & 0xf]); buf.Append(HEX_DIGITS[b & 0xf]); } return buf.ToString(); } protected override void Dispose(bool disposing) { lock (this) { isOpen = false; } } // Java Lucene implements GetFile() which returns a FileInfo. // For Lucene.Net, GetDirectory() is more appropriate public virtual DirectoryInfo Directory { get { EnsureOpen(); return internalDirectory; } } /// For debug output. public override System.String ToString() { return this.GetType().FullName + "@" + internalDirectory + " lockFactory=" + LockFactory; } /// Default read chunk size. This is a conditional /// default: on 32bit JVMs, it defaults to 100 MB. On /// 64bit JVMs, it's Integer.MAX_VALUE. /// /// /// public static readonly int DEFAULT_READ_CHUNK_SIZE = Constants.JRE_IS_64BIT ? int.MaxValue: 100 * 1024 * 1024; // LUCENE-1566 private int chunkSize = DEFAULT_READ_CHUNK_SIZE; /// The maximum number of bytes to read at once from the /// underlying file during . /// /// /// public int ReadChunkSize { get { // LUCENE-1566 return chunkSize; } set { // LUCENE-1566 if (value <= 0) { throw new System.ArgumentException("chunkSize must be positive"); } if (!Constants.JRE_IS_64BIT) { this.chunkSize = value; } } } } }