add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / store / NIOFSDirectory.java
1 package org.apache.lucene.store;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements. See the NOTICE file distributed with this
6  * work for additional information regarding copyright ownership. The ASF
7  * licenses this file to You under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  * http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16  * License for the specific language governing permissions and limitations under
17  * the License.
18  */
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23 import java.nio.channels.ClosedChannelException; // javadoc
24 import java.nio.channels.FileChannel;
25 import java.util.concurrent.Future; // javadoc
26
27 /**
28  * An {@link FSDirectory} implementation that uses java.nio's FileChannel's
29  * positional read, which allows multiple threads to read from the same file
30  * without synchronizing.
31  * <p>
32  * This class only uses FileChannel when reading; writing is achieved with
33  * {@link FSDirectory.FSIndexOutput}.
34  * <p>
35  * <b>NOTE</b>: NIOFSDirectory is not recommended on Windows because of a bug in
36  * how FileChannel.read is implemented in Sun's JRE. Inside of the
37  * implementation the position is apparently synchronized. See <a
38  * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265734">here</a>
39  * for details.
40  * </p>
41  * <p>
42  * <font color="red"><b>NOTE:</b> Accessing this class either directly or
43  * indirectly from a thread while it's interrupted can close the
44  * underlying file descriptor immediately if at the same time the thread is
45  * blocked on IO. The file descriptor will remain closed and subsequent access
46  * to {@link NIOFSDirectory} will throw a {@link ClosedChannelException}. If
47  * your application uses either {@link Thread#interrupt()} or
48  * {@link Future#cancel(boolean)} you should use {@link SimpleFSDirectory} in
49  * favor of {@link NIOFSDirectory}.</font>
50  * </p>
51  */
52 public class NIOFSDirectory extends FSDirectory {
53
54   /** Create a new NIOFSDirectory for the named location.
55    * 
56    * @param path the path of the directory
57    * @param lockFactory the lock factory to use, or null for the default
58    * ({@link NativeFSLockFactory});
59    * @throws IOException
60    */
61   public NIOFSDirectory(File path, LockFactory lockFactory) throws IOException {
62     super(path, lockFactory);
63   }
64
65   /** Create a new NIOFSDirectory for the named location and {@link NativeFSLockFactory}.
66    *
67    * @param path the path of the directory
68    * @throws IOException
69    */
70   public NIOFSDirectory(File path) throws IOException {
71     super(path, null);
72   }
73
74   /** Creates an IndexInput for the file with the given name. */
75   @Override
76   public IndexInput openInput(String name, int bufferSize) throws IOException {
77     ensureOpen();
78     return new NIOFSIndexInput(new File(getDirectory(), name), bufferSize, getReadChunkSize());
79   }
80
81   protected static class NIOFSIndexInput extends SimpleFSDirectory.SimpleFSIndexInput {
82
83     private ByteBuffer byteBuf; // wraps the buffer for NIO
84
85     private byte[] otherBuffer;
86     private ByteBuffer otherByteBuf;
87
88     final FileChannel channel;
89
90     public NIOFSIndexInput(File path, int bufferSize, int chunkSize) throws IOException {
91       super(path, bufferSize, chunkSize);
92       channel = file.getChannel();
93     }
94
95     @Override
96     protected void newBuffer(byte[] newBuffer) {
97       super.newBuffer(newBuffer);
98       byteBuf = ByteBuffer.wrap(newBuffer);
99     }
100
101     @Override
102     public void close() throws IOException {
103       if (!isClone && file.isOpen) {
104         // Close the channel & file
105         try {
106           channel.close();
107         } finally {
108           file.close();
109         }
110       }
111     }
112
113     @Override
114     protected void readInternal(byte[] b, int offset, int len) throws IOException {
115
116       final ByteBuffer bb;
117
118       // Determine the ByteBuffer we should use
119       if (b == buffer && 0 == offset) {
120         // Use our own pre-wrapped byteBuf:
121         assert byteBuf != null;
122         byteBuf.clear();
123         byteBuf.limit(len);
124         bb = byteBuf;
125       } else {
126         if (offset == 0) {
127           if (otherBuffer != b) {
128             // Now wrap this other buffer; with compound
129             // file, we are repeatedly called with its
130             // buffer, so we wrap it once and then re-use it
131             // on subsequent calls
132             otherBuffer = b;
133             otherByteBuf = ByteBuffer.wrap(b);
134           } else
135             otherByteBuf.clear();
136           otherByteBuf.limit(len);
137           bb = otherByteBuf;
138         } else {
139           // Always wrap when offset != 0
140           bb = ByteBuffer.wrap(b, offset, len);
141         }
142       }
143
144       int readOffset = bb.position();
145       int readLength = bb.limit() - readOffset;
146       assert readLength == len;
147
148       long pos = getFilePointer();
149
150       try {
151         while (readLength > 0) {
152           final int limit;
153           if (readLength > chunkSize) {
154             // LUCENE-1566 - work around JVM Bug by breaking
155             // very large reads into chunks
156             limit = readOffset + chunkSize;
157           } else {
158             limit = readOffset + readLength;
159           }
160           bb.limit(limit);
161           int i = channel.read(bb, pos);
162           if (i == -1) {
163             throw new IOException("read past EOF");
164           }
165           pos += i;
166           readOffset += i;
167           readLength -= i;
168         }
169       } catch (OutOfMemoryError e) {
170         // propagate OOM up and add a hint for 32bit VM Users hitting the bug
171         // with a large chunk size in the fast path.
172         final OutOfMemoryError outOfMemoryError = new OutOfMemoryError(
173               "OutOfMemoryError likely caused by the Sun VM Bug described in "
174               + "https://issues.apache.org/jira/browse/LUCENE-1566; try calling FSDirectory.setReadChunkSize "
175               + "with a value smaller than the current chunk size (" + chunkSize + ")");
176         outOfMemoryError.initCause(e);
177         throw outOfMemoryError;
178       }
179     }
180   }
181 }