content: package org.apache.lucene.store;
  
  /**
   * 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.
   */
  
  import java.io.File;
  import java.io.IOException;
  import java.io.RandomAccessFile;
  
  /** A straightforward implementation of {@link FSDirectory}
   *  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
   *  {@link NIOFSDirectory} or {@link MMapDirectory} instead. */
  public class SimpleFSDirectory extends FSDirectory {
      
    /** Create a new SimpleFSDirectory for the named location.
     *
     * @param path the path of the directory
     * @param lockFactory the lock factory to use, or null for the default
     * ({@link NativeFSLockFactory});
     * @throws IOException
     */
    public SimpleFSDirectory(File path, LockFactory lockFactory) throws IOException {
      super(path, lockFactory);
    }
    
    /** Create a new SimpleFSDirectory for the named location and {@link NativeFSLockFactory}.
     *
     * @param path the path of the directory
     * @throws IOException
     */
    public SimpleFSDirectory(File path) throws IOException {
      super(path, null);
    }
  
    /** Creates an IndexInput for the file with the given name. */
    @Override
    public IndexInput openInput(String name, int bufferSize) throws IOException {
      ensureOpen();
      final File path = new File(directory, name);
      return new SimpleFSIndexInput("SimpleFSIndexInput(path=\"" + path.getPath() + "\")", path, bufferSize, getReadChunkSize());
    }
  
    protected static class SimpleFSIndexInput extends BufferedIndexInput {
    
      protected static class Descriptor extends RandomAccessFile {
        // remember if the file is open, so that we don't try to close it
        // more than once
        protected volatile boolean isOpen;
        long position;
        final long length;
        
        public Descriptor(File file, String mode) throws IOException {
          super(file, mode);
          isOpen=true;
          length=length();
        }
    
        @Override
        public void close() throws IOException {
          if (isOpen) {
            isOpen=false;
            super.close();
          }
        }
      }
    
      protected final Descriptor file;
      boolean isClone;
      //  LUCENE-1566 - maximum read length on a 32bit JVM to prevent incorrect OOM 
      protected final int chunkSize;
  
      /** @deprecated please pass resourceDesc */
      @Deprecated
      public SimpleFSIndexInput(File path, int bufferSize, int chunkSize) throws IOException {
        this("anonymous SimpleFSIndexInput", path, bufferSize, chunkSize);
      }
  
      public SimpleFSIndexInput(String resourceDesc, File path, int bufferSize, int chunkSize) throws IOException {
        super(resourceDesc, bufferSize);
        file = new Descriptor(path, "r");
        this.chunkSize = chunkSize;
      }
    
      /** IndexInput methods */
      @Override
      protected void readInternal(byte[] b, int offset, int len)
           throws IOException {
        synchronized (file) {
          long position = getFilePointer();
          if (position != file.position) {
            file.seek(position);
            file.position = position;
          }
          int total = 0;
  
          try {
            do {
              final 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;
              }
              final int i = file.read(b, offset + total, readLength);
              if (i == -1) {
                throw new IOException("read past EOF");
              }
              file.position += i;
              total += i;
            } while (total < len);
          } catch (OutOfMemoryError e) {
            // propagate OOM up and add a hint for 32bit VM Users hitting the bug
            // with a large chunk size in the fast path.
            final OutOfMemoryError outOfMemoryError = new OutOfMemoryError(
                "OutOfMemoryError likely caused by the Sun VM Bug described in "
                + "https://issues.apache.org/jira/browse/LUCENE-1566; try calling FSDirectory.setReadChunkSize "
                + "with a value smaller than the current chunk size (" + chunkSize + ")");
            outOfMemoryError.initCause(e);
            throw outOfMemoryError;
          } catch (IOException ioe) {
            IOException newIOE = new IOException(ioe.getMessage() + ": " + this);
            newIOE.initCause(ioe);
            throw newIOE;
          }
        }
      }
    
      @Override
      public void close() throws IOException {
        // only close the file if this is not a clone
        if (!isClone) file.close();
      }
    
      @Override
      protected void seekInternal(long position) {
      }
    
      @Override
      public long length() {
        return file.length;
      }
    
      @Override
      public Object clone() {
        SimpleFSIndexInput clone = (SimpleFSIndexInput)super.clone();
        clone.isClone = true;
        return clone;
      }
    
      /** Method used for testing. Returns true if the underlying
       *  file descriptor is valid.
       */
      boolean isFDValid() throws IOException {
        return file.getFD().valid();
      }
      
      @Override
      public void copyBytes(IndexOutput out, long numBytes) throws IOException {
        numBytes -= flushBuffer(out, numBytes);
        // If out is FSIndexOutput, the copy will be optimized
        out.copyBytes(this, numBytes);
      }
    }
  }
  