/*
* 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;
namespace Lucene.Net.Store
{
/// A straightforward implementation of
/// using java.io.RandomAccessFile. However, this class has
/// poor concurrent performance (multiple threads will
/// bottleneck) as it synchronizes when multiple threads
/// read from the same file. It's usually better to use
/// or instead.
///
public class SimpleFSDirectory : FSDirectory
{
/// Create a new SimpleFSDirectory for the named location.
///
///
/// the path of the directory
///
/// the lock factory to use, or null for the default.
///
/// IOException
public SimpleFSDirectory(System.IO.DirectoryInfo path, LockFactory lockFactory)
: base(path, lockFactory)
{
}
/// Create a new SimpleFSDirectory for the named location and the default lock factory.
///
///
/// the path of the directory
///
/// IOException
public SimpleFSDirectory(System.IO.DirectoryInfo path) : base(path, null)
{
}
/// Creates an IndexOutput for the file with the given name.
public override IndexOutput CreateOutput(System.String name)
{
InitOutput(name);
return new SimpleFSIndexOutput(new System.IO.FileInfo(System.IO.Path.Combine(internalDirectory.FullName, name)));
}
/// Creates an IndexInput for the file with the given name.
public override IndexInput OpenInput(System.String name, int bufferSize)
{
EnsureOpen();
Exception e = null;
for (var i = 0; i < 10; i++)
{
try
{
return new SimpleFSIndexInput(new System.IO.FileInfo(
System.IO.Path.Combine(internalDirectory.FullName, name)), bufferSize, ReadChunkSize);
}
catch (System.UnauthorizedAccessException ex)
{
e = ex;
System.Threading.Thread.Sleep(1);
}
}
throw e;
}
protected internal class SimpleFSIndexInput : BufferedIndexInput
{
// TODO: This is a bad way to handle memory and disposing
protected internal class Descriptor : System.IO.BinaryReader
{
// remember if the file is open, so that we don't try to close it
// more than once
protected internal volatile bool isOpen;
internal long position;
internal long length;
private bool isDisposed;
public Descriptor(/*FSIndexInput enclosingInstance,*/ System.IO.FileInfo file, System.IO.FileAccess mode)
: base(new System.IO.FileStream(file.FullName, System.IO.FileMode.Open, mode, System.IO.FileShare.ReadWrite))
{
isOpen = true;
length = file.Length;
}
protected override void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
if (isOpen)
{
isOpen = false;
}
}
isDisposed = true;
base.Dispose(disposing);
}
~Descriptor()
{
try
{
Dispose(false);
}
finally
{
}
}
}
protected internal Descriptor file;
internal bool isClone;
private bool isDisposed;
// LUCENE-1566 - maximum read length on a 32bit JVM to prevent incorrect OOM
protected internal int chunkSize;
public SimpleFSIndexInput(System.IO.FileInfo path, int bufferSize, int chunkSize)
: base(bufferSize)
{
file = new Descriptor(path, System.IO.FileAccess.Read);
this.chunkSize = chunkSize;
}
/// IndexInput methods
public override void ReadInternal(byte[] b, int offset, int len)
{
lock (file)
{
long position = FilePointer;
if (position != file.position)
{
file.BaseStream.Seek(position, System.IO.SeekOrigin.Begin);
file.position = position;
}
int total = 0;
try
{
do
{
int readLength;
if (total + chunkSize > len)
{
readLength = len - total;
}
else
{
// LUCENE-1566 - work around JVM Bug by breaking very large reads into chunks
readLength = chunkSize;
}
int i = file.Read(b, offset + total, readLength);
if (i == - 1)
{
throw new System.IO.IOException("read past EOF");
}
file.position += i;
total += i;
}
while (total < len);
}
catch (System.OutOfMemoryException e)
{
// propagate OOM up and add a hint for 32bit VM Users hitting the bug
// with a large chunk size in the fast path.
System.OutOfMemoryException outOfMemoryError = new System.OutOfMemoryException("OutOfMemoryError likely caused by the Sun VM Bug described in " + "https://issues.apache.org/jira/browse/LUCENE-1566; try calling FSDirectory.setReadChunkSize " + "with a a value smaller than the current chunks size (" + chunkSize + ")", e);
throw outOfMemoryError;
}
}
}
protected override void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
// only close the file if this is not a clone
if (!isClone && file != null)
{
file.Close();
file = null;
}
}
isDisposed = true;
}
public override void SeekInternal(long position)
{
}
public override long Length()
{
return file.length;
}
public override System.Object Clone()
{
SimpleFSIndexInput clone = (SimpleFSIndexInput) base.Clone();
clone.isClone = true;
return clone;
}
/// Method used for testing. Returns true if the underlying
/// file descriptor is valid.
///
public /*internal*/ virtual bool IsFDValid()
{
return file.BaseStream != null;
}
public bool isClone_ForNUnit
{
get { return isClone; }
}
}
/*protected internal*/ public class SimpleFSIndexOutput:BufferedIndexOutput
{
internal System.IO.FileStream file = null;
// remember if the file is open, so that we don't try to close it
// more than once
private volatile bool isOpen;
public SimpleFSIndexOutput(System.IO.FileInfo path)
{
file = new System.IO.FileStream(path.FullName, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite);
isOpen = true;
}
/// output methods:
public override void FlushBuffer(byte[] b, int offset, int size)
{
file.Write(b, offset, size);
// {{dougsale-2.4.0}}
// FSIndexOutput.Flush
// When writing frequently with small amounts of data, the data isn't flushed to disk.
// Thus, attempting to read the data soon after this method is invoked leads to
// BufferedIndexInput.Refill() throwing an IOException for reading past EOF.
// Test\Index\TestDoc.cs demonstrates such a situation.
// Forcing a flush here prevents said issue.
// {{DIGY 2.9.0}}
// This code is not available in Lucene.Java 2.9.X.
// Can there be a indexing-performance problem?
file.Flush();
}
protected override void Dispose(bool disposing)
{
// only close the file if it has not been closed yet
if (isOpen)
{
bool success = false;
try
{
base.Dispose(disposing);
success = true;
}
finally
{
isOpen = false;
if (!success)
{
try
{
file.Dispose();
}
catch (System.Exception)
{
// Suppress so we don't mask original exception
}
}
else
file.Dispose();
}
}
}
/// Random-access methods
public override void Seek(long pos)
{
base.Seek(pos);
file.Seek(pos, System.IO.SeekOrigin.Begin);
}
public override long Length
{
get { return file.Length; }
}
public override void SetLength(long length)
{
file.SetLength(length);
}
}
}
}