KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > lucene > store > FSDirectory


1 package org.apache.lucene.store;
2
3 /**
4  * Copyright 2004 The Apache Software Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import java.io.File JavaDoc;
20 import java.io.FileInputStream JavaDoc;
21 import java.io.FileOutputStream JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.RandomAccessFile JavaDoc;
24 import java.security.MessageDigest JavaDoc;
25 import java.security.NoSuchAlgorithmException JavaDoc;
26 import java.util.Hashtable JavaDoc;
27
28 import org.apache.lucene.index.IndexFileNameFilter;
29
30 /**
31  * Straightforward implementation of {@link Directory} as a directory of files.
32  *
33  * @see Directory
34  * @author Doug Cutting
35  */

36 public class FSDirectory extends Directory {
37     
38   /** This cache of directories ensures that there is a unique Directory
39    * instance per path, so that synchronization on the Directory can be used to
40    * synchronize access between readers and writers.
41    *
42    * This should be a WeakHashMap, so that entries can be GC'd, but that would
43    * require Java 1.2. Instead we use refcounts...
44    */

45   private static final Hashtable JavaDoc DIRECTORIES = new Hashtable JavaDoc();
46
47   private static boolean disableLocks = false;
48
49   /**
50    * Set whether Lucene's use of lock files is disabled. By default,
51    * lock files are enabled. They should only be disabled if the index
52    * is on a read-only medium like a CD-ROM.
53    */

54   public static void setDisableLocks(boolean doDisableLocks) {
55     FSDirectory.disableLocks = doDisableLocks;
56   }
57
58   /**
59    * Returns whether Lucene's use of lock files is disabled.
60    * @return true if locks are disabled, false if locks are enabled.
61    */

62   public static boolean getDisableLocks() {
63     return FSDirectory.disableLocks;
64   }
65
66   /**
67    * Directory specified by <code>org.apache.lucene.lockDir</code>
68    * or <code>java.io.tmpdir</code> system property
69    */

70   public static final String JavaDoc LOCK_DIR =
71     System.getProperty("org.apache.lucene.lockDir",
72       System.getProperty("java.io.tmpdir"));
73
74   /** The default class which implements filesystem-based directories. */
75   private static Class JavaDoc IMPL;
76   static {
77     try {
78       String JavaDoc name =
79         System.getProperty("org.apache.lucene.FSDirectory.class",
80                            FSDirectory.class.getName());
81       IMPL = Class.forName(name);
82     } catch (ClassNotFoundException JavaDoc e) {
83       throw new RuntimeException JavaDoc("cannot load FSDirectory class: " + e.toString());
84     } catch (SecurityException JavaDoc se) {
85       try {
86         IMPL = Class.forName(FSDirectory.class.getName());
87       } catch (ClassNotFoundException JavaDoc e) {
88         throw new RuntimeException JavaDoc("cannot load default FSDirectory class: " + e.toString());
89       }
90     }
91   }
92
93   private static MessageDigest JavaDoc DIGESTER;
94
95   static {
96     try {
97       DIGESTER = MessageDigest.getInstance("MD5");
98     } catch (NoSuchAlgorithmException JavaDoc e) {
99         throw new RuntimeException JavaDoc(e.toString());
100     }
101   }
102
103   /** A buffer optionally used in renameTo method */
104   private byte[] buffer = null;
105
106   /** Returns the directory instance for the named location.
107    *
108    * <p>Directories are cached, so that, for a given canonical path, the same
109    * FSDirectory instance will always be returned. This permits
110    * synchronization on directories.
111    *
112    * @param path the path to the directory.
113    * @param create if true, create, or erase any existing contents.
114    * @return the FSDirectory for the named file. */

115   public static FSDirectory getDirectory(String JavaDoc path, boolean create)
116       throws IOException JavaDoc {
117     return getDirectory(new File JavaDoc(path), create);
118   }
119
120   /** Returns the directory instance for the named location.
121    *
122    * <p>Directories are cached, so that, for a given canonical path, the same
123    * FSDirectory instance will always be returned. This permits
124    * synchronization on directories.
125    *
126    * @param file the path to the directory.
127    * @param create if true, create, or erase any existing contents.
128    * @return the FSDirectory for the named file. */

129   public static FSDirectory getDirectory(File JavaDoc file, boolean create)
130     throws IOException JavaDoc {
131     file = new File JavaDoc(file.getCanonicalPath());
132     FSDirectory dir;
133     synchronized (DIRECTORIES) {
134       dir = (FSDirectory)DIRECTORIES.get(file);
135       if (dir == null) {
136         try {
137           dir = (FSDirectory)IMPL.newInstance();
138         } catch (Exception JavaDoc e) {
139           throw new RuntimeException JavaDoc("cannot load FSDirectory class: " + e.toString());
140         }
141         dir.init(file, create);
142         DIRECTORIES.put(file, dir);
143       } else if (create) {
144         dir.create();
145       }
146     }
147     synchronized (dir) {
148       dir.refCount++;
149     }
150     return dir;
151   }
152
153   private File JavaDoc directory = null;
154   private int refCount;
155   private File JavaDoc lockDir;
156
157   protected FSDirectory() {}; // permit subclassing
158

159   private void init(File JavaDoc path, boolean create) throws IOException JavaDoc {
160     directory = path;
161
162     if (LOCK_DIR == null) {
163       lockDir = directory;
164     }
165     else {
166       lockDir = new File JavaDoc(LOCK_DIR);
167     }
168     // Ensure that lockDir exists and is a directory.
169
if (!lockDir.exists()) {
170       if (!lockDir.mkdirs())
171         throw new IOException JavaDoc("Cannot create directory: " + lockDir);
172     } else if (!lockDir.isDirectory()) {
173       throw new IOException JavaDoc("Found regular file where directory expected: " + lockDir);
174     }
175     if (create) {
176       create();
177     }
178
179     if (!directory.isDirectory())
180       throw new IOException JavaDoc(path + " not a directory");
181   }
182
183   private synchronized void create() throws IOException JavaDoc {
184     if (!directory.exists())
185       if (!directory.mkdirs())
186         throw new IOException JavaDoc("Cannot create directory: " + directory);
187
188     if (!directory.isDirectory())
189       throw new IOException JavaDoc(directory + " not a directory");
190
191     String JavaDoc[] files = directory.list(new IndexFileNameFilter()); // clear old files
192
for (int i = 0; i < files.length; i++) {
193       File JavaDoc file = new File JavaDoc(directory, files[i]);
194       if (!file.delete())
195         throw new IOException JavaDoc("Cannot delete " + files[i]);
196     }
197
198     String JavaDoc lockPrefix = getLockPrefix().toString(); // clear old locks
199
files = lockDir.list();
200     if (files == null)
201       throw new IOException JavaDoc("Cannot read lock directory " + lockDir.getAbsolutePath());
202     for (int i = 0; i < files.length; i++) {
203       if (!files[i].startsWith(lockPrefix))
204         continue;
205       File JavaDoc lockFile = new File JavaDoc(lockDir, files[i]);
206       if (!lockFile.delete())
207         throw new IOException JavaDoc("Cannot delete " + files[i]);
208     }
209   }
210
211   /** Returns an array of strings, one for each file in the directory. */
212   public String JavaDoc[] list() {
213     return directory.list();
214   }
215
216   /** Returns true iff a file with the given name exists. */
217   public boolean fileExists(String JavaDoc name) {
218     File JavaDoc file = new File JavaDoc(directory, name);
219     return file.exists();
220   }
221
222   /** Returns the time the named file was last modified. */
223   public long fileModified(String JavaDoc name) {
224     File JavaDoc file = new File JavaDoc(directory, name);
225     return file.lastModified();
226   }
227
228   /** Returns the time the named file was last modified. */
229   public static long fileModified(File JavaDoc directory, String JavaDoc name) {
230     File JavaDoc file = new File JavaDoc(directory, name);
231     return file.lastModified();
232   }
233
234   /** Set the modified time of an existing file to now. */
235   public void touchFile(String JavaDoc name) {
236     File JavaDoc file = new File JavaDoc(directory, name);
237     file.setLastModified(System.currentTimeMillis());
238   }
239
240   /** Returns the length in bytes of a file in the directory. */
241   public long fileLength(String JavaDoc name) {
242     File JavaDoc file = new File JavaDoc(directory, name);
243     return file.length();
244   }
245
246   /** Removes an existing file in the directory. */
247   public void deleteFile(String JavaDoc name) throws IOException JavaDoc {
248     File JavaDoc file = new File JavaDoc(directory, name);
249     if (!file.delete())
250       throw new IOException JavaDoc("Cannot delete " + file);
251   }
252
253   /** Renames an existing file in the directory. */
254   public synchronized void renameFile(String JavaDoc from, String JavaDoc to)
255       throws IOException JavaDoc {
256     File JavaDoc old = new File JavaDoc(directory, from);
257     File JavaDoc nu = new File JavaDoc(directory, to);
258
259     /* This is not atomic. If the program crashes between the call to
260        delete() and the call to renameTo() then we're screwed, but I've
261        been unable to figure out how else to do this... */

262
263     if (nu.exists())
264       if (!nu.delete())
265         throw new IOException JavaDoc("Cannot delete " + nu);
266
267     // Rename the old file to the new one. Unfortunately, the renameTo()
268
// method does not work reliably under some JVMs. Therefore, if the
269
// rename fails, we manually rename by copying the old file to the new one
270
if (!old.renameTo(nu)) {
271       java.io.InputStream JavaDoc in = null;
272       java.io.OutputStream JavaDoc out = null;
273       try {
274         in = new FileInputStream JavaDoc(old);
275         out = new FileOutputStream JavaDoc(nu);
276         // see if the buffer needs to be initialized. Initialization is
277
// only done on-demand since many VM's will never run into the renameTo
278
// bug and hence shouldn't waste 1K of mem for no reason.
279
if (buffer == null) {
280           buffer = new byte[1024];
281         }
282         int len;
283         while ((len = in.read(buffer)) >= 0) {
284           out.write(buffer, 0, len);
285         }
286
287         // delete the old file.
288
old.delete();
289       }
290       catch (IOException JavaDoc ioe) {
291         throw new IOException JavaDoc("Cannot rename " + old + " to " + nu);
292       }
293       finally {
294         if (in != null) {
295           try {
296             in.close();
297           } catch (IOException JavaDoc e) {
298             throw new RuntimeException JavaDoc("Cannot close input stream: " + e.toString());
299           }
300         }
301         if (out != null) {
302           try {
303             out.close();
304           } catch (IOException JavaDoc e) {
305             throw new RuntimeException JavaDoc("Cannot close output stream: " + e.toString());
306           }
307         }
308       }
309     }
310   }
311
312   /** Creates a new, empty file in the directory with the given name.
313       Returns a stream writing this file. */

314   public IndexOutput createOutput(String JavaDoc name) throws IOException JavaDoc {
315     File JavaDoc file = new File JavaDoc(directory, name);
316     if (file.exists() && !file.delete()) // delete existing, if any
317
throw new IOException JavaDoc("Cannot overwrite: " + file);
318
319     return new FSIndexOutput(file);
320   }
321
322   /** Returns a stream reading an existing file. */
323   public IndexInput openInput(String JavaDoc name) throws IOException JavaDoc {
324     return new FSIndexInput(new File JavaDoc(directory, name));
325   }
326
327   /**
328    * So we can do some byte-to-hexchar conversion below
329    */

330   private static final char[] HEX_DIGITS =
331   {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
332
333   /** Constructs a {@link Lock} with the specified name. Locks are implemented
334    * with {@link File#createNewFile()}.
335    *
336    * @param name the name of the lock file
337    * @return an instance of <code>Lock</code> holding the lock
338    */

339   public Lock makeLock(String JavaDoc name) {
340     StringBuffer JavaDoc buf = getLockPrefix();
341     buf.append("-");
342     buf.append(name);
343
344     // create a lock file
345
final File JavaDoc lockFile = new File JavaDoc(lockDir, buf.toString());
346
347     return new Lock() {
348       public boolean obtain() throws IOException JavaDoc {
349         if (disableLocks)
350           return true;
351
352         if (!lockDir.exists()) {
353           if (!lockDir.mkdirs()) {
354             throw new IOException JavaDoc("Cannot create lock directory: " + lockDir);
355           }
356         }
357
358         return lockFile.createNewFile();
359       }
360       public void release() {
361         if (disableLocks)
362           return;
363         lockFile.delete();
364       }
365       public boolean isLocked() {
366         if (disableLocks)
367           return false;
368         return lockFile.exists();
369       }
370
371       public String JavaDoc toString() {
372         return "Lock@" + lockFile;
373       }
374     };
375   }
376
377   private StringBuffer JavaDoc getLockPrefix() {
378     String JavaDoc dirName; // name to be hashed
379
try {
380       dirName = directory.getCanonicalPath();
381     } catch (IOException JavaDoc e) {
382       throw new RuntimeException JavaDoc(e.toString());
383     }
384
385     byte digest[];
386     synchronized (DIGESTER) {
387       digest = DIGESTER.digest(dirName.getBytes());
388     }
389     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
390     buf.append("lucene-");
391     for (int i = 0; i < digest.length; i++) {
392       int b = digest[i];
393       buf.append(HEX_DIGITS[(b >> 4) & 0xf]);
394       buf.append(HEX_DIGITS[b & 0xf]);
395     }
396
397     return buf;
398   }
399
400   /** Closes the store to future operations. */
401   public synchronized void close() {
402     if (--refCount <= 0) {
403       synchronized (DIRECTORIES) {
404         DIRECTORIES.remove(directory);
405       }
406     }
407   }
408
409   public File JavaDoc getFile() {
410     return directory;
411   }
412
413   /** For debug output. */
414   public String JavaDoc toString() {
415     return this.getClass().getName() + "@" + directory;
416   }
417 }
418
419
420 class FSIndexInput extends BufferedIndexInput {
421
422   private class Descriptor extends RandomAccessFile JavaDoc {
423     public long position;
424     public Descriptor(File JavaDoc file, String JavaDoc mode) throws IOException JavaDoc {
425       super(file, mode);
426     }
427   }
428
429   private Descriptor file = null;
430   boolean isClone;
431   private long length;
432
433   public FSIndexInput(File JavaDoc path) throws IOException JavaDoc {
434     file = new Descriptor(path, "r");
435     length = file.length();
436   }
437
438   /** IndexInput methods */
439   protected void readInternal(byte[] b, int offset, int len)
440        throws IOException JavaDoc {
441     synchronized (file) {
442       long position = getFilePointer();
443       if (position != file.position) {
444         file.seek(position);
445         file.position = position;
446       }
447       int total = 0;
448       do {
449         int i = file.read(b, offset+total, len-total);
450         if (i == -1)
451           throw new IOException JavaDoc("read past EOF");
452         file.position += i;
453         total += i;
454       } while (total < len);
455     }
456   }
457
458   public void close() throws IOException JavaDoc {
459     if (!isClone)
460       file.close();
461   }
462
463   protected void seekInternal(long position) {
464   }
465
466   public long length() {
467     return length;
468   }
469
470   protected void finalize() throws IOException JavaDoc {
471     close(); // close the file
472
}
473
474   public Object JavaDoc clone() {
475     FSIndexInput clone = (FSIndexInput)super.clone();
476     clone.isClone = true;
477     return clone;
478   }
479
480   /** Method used for testing. Returns true if the underlying
481    * file descriptor is valid.
482    */

483   boolean isFDValid() throws IOException JavaDoc {
484     return file.getFD().valid();
485   }
486 }
487
488
489 class FSIndexOutput extends BufferedIndexOutput {
490   RandomAccessFile JavaDoc file = null;
491
492   public FSIndexOutput(File JavaDoc path) throws IOException JavaDoc {
493     file = new RandomAccessFile JavaDoc(path, "rw");
494   }
495
496   /** output methods: */
497   public void flushBuffer(byte[] b, int size) throws IOException JavaDoc {
498     file.write(b, 0, size);
499   }
500   public void close() throws IOException JavaDoc {
501     super.close();
502     file.close();
503   }
504
505   /** Random-access methods */
506   public void seek(long pos) throws IOException JavaDoc {
507     super.seek(pos);
508     file.seek(pos);
509   }
510   public long length() throws IOException JavaDoc {
511     return file.length();
512   }
513
514   protected void finalize() throws IOException JavaDoc {
515     file.close(); // close the file
516
}
517
518 }
519
Popular Tags