Directory抽象类比较常用的具体实现子类应该是FSDirectory类和RAMDirectory类。FSDirectory类是与文件系统的目录相关的,而RAMDirectory类是与内存相关的,即是指内存中的一个临时非永久的区域。
FSDirectory类源代码定义如下:
package org.apache.lucene.store;
import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.RandomAccessFile;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Hashtable;
import org.apache.lucene.index.IndexFileNameFilter;
import org.apache.lucene.index.IndexWriter;
public class FSDirectory extends Directory { // DIRECTORIES需要进行同步,用于存放指定键值对<File,FSDirectory>private static final Hashtable DIRECTORIES = new Hashtable();
private static boolean disableLocks = false;
/** * 设置锁文件是否可以使用 * They should only be disabled if the index * is on a read-only medium like a CD-ROM. */public static void setDisableLocks(boolean doDisableLocks) { FSDirectory.disableLocks = doDisableLocks;}
// 获取锁文件是否可以使用,返回true表示不可以使用public static boolean getDisableLocks() { return FSDirectory.disableLocks;}
// 锁目录,该属性已经废弃public static final String LOCK_DIR = System.getProperty("org.apache.lucene.lockDir", System.getProperty("java.io.tmpdir"));
// 定义一个静态内部类,该类是基于文件系统的private static Class IMPL;static { try { String name = System.getProperty("org.apache.lucene.FSDirectory.class", FSDirectory.class.getName()); IMPL = Class.forName(name); } catch (ClassNotFoundException e) { throw new RuntimeException("cannot load FSDirectory class: " + e.toString(), e); } catch (SecurityException se) { try { IMPL = Class.forName(FSDirectory.class.getName()); } catch (ClassNotFoundException e) { throw new RuntimeException("cannot load default FSDirectory class: " + e.toString(), e); } }}
private static MessageDigest DIGESTER;
static { try { DIGESTER = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e.toString(), e); }}
/** A buffer optionally used in renameTo method */private byte[] buffer = null;
// 静态方法,根据指定的路径path,返回该路径的一个FSDirectiry实例public static FSDirectory getDirectory(String path) throws IOException { return getDirectory(new File(path), null);}
// 静态方法,根据指定的路径path以及锁工厂LockFactory参数,返回该路径的一个FSDirectiry实例public static FSDirectory getDirectory(String path, LockFactory lockFactory) throws IOException { return getDirectory(new File(path), lockFactory);}
// 静态方法,根据指定的File对象,返回该路径的一个FSDirectiry实例
public static FSDirectory getDirectory(File file) throws IOException { return getDirectory(file, null);}
// 静态方法,根据指定File对象以及锁工厂LockFactory参数,返回该路径的一个FSDirectiry实例
// 该方法是获取FSDirectiry实例的核心方法,其他重载的该方法都是经过转换,调用该方法实现的public static FSDirectory getDirectory(File file, LockFactory lockFactory) throws IOException{ file = new File(file.getCanonicalPath());
if (file.exists() && !file.isDirectory()) // 如果file存在并且不是一个目录文件,抛出异常 throw new IOException(file + " not a directory");
if (!file.exists()) // 如果file不存在 if (!file.mkdirs()) // 如果创建由file指定的路径名所指定的目录失败,则很可能已经存在,抛出异常 throw new IOException("Cannot create directory: " + file);
FSDirectory dir; synchronized (DIRECTORIES) { dir = (FSDirectory)DIRECTORIES.get(file); if (dir == null) { try { dir = (FSDirectory)IMPL.newInstance(); // 调用静态内部类IMPL获取一个与文件系统目录有关的Directory类,并加载该类 } catch (Exception e) { throw new RuntimeException("cannot load FSDirectory class: " + e.toString(), e); // 加载失败 } dir.init(file, lockFactory); // 根据指定的file和lockFactory,调用该类Directory的init方法,进行FSDirectory的初始化初始化工作
DIRECTORIES.put(file, dir); } else { // 如果该目录dir管理所用的锁工厂实例为空,或者不是同一个锁工厂实例,抛出异常 if (lockFactory != null && lockFactory != dir.getLockFactory()) { throw new IOException("Directory was previously created with a different LockFactory instance; please pass null as the lockFactory instance and use setLockFactory to change it"); } } } synchronized (dir) { dir.refCount++; // 用于记录该目录dir被引用的计数增加1 } return dir;}// 获取FSDirectory实例,该方法已经废弃public static FSDirectory getDirectory(String path, boolean create) throws IOException { return getDirectory(new File(path), create);}
// 获取FSDirectory实例,该方法已经废弃public static FSDirectory getDirectory(File file, boolean create) throws IOException{ FSDirectory dir = getDirectory(file, null);
// This is now deprecated (creation should only be done // by IndexWriter): if (create) { dir.create(); }
return dir;}
private void create() throws IOException { if (directory.exists()) { // File directory是FSDirectory类的一个成员 String[] files = directory.list(IndexFileNameFilter.getFilter()); // 获取经过IndexFileNameFilter过滤后的该目录文件directory下的文件列表 if (files == null) // 如果获取的文件列表为空 throw new IOException("cannot read directory " + directory.getAbsolutePath() + ": list() returned null"); for (int i = 0; i < files.length; i++) { // 删除返回的文件列表中的文件 File file = new File(directory, files[i]); if (!file.delete()) throw new IOException("Cannot delete " + file); // 删除异常 } } lockFactory.clearLock(IndexWriter.WRITE_LOCK_NAME); // 删除操作完成后,从锁工厂中清除锁,即释放锁}
private File directory = null; // File directory是FSDirectory类的一个成员private int refCount; // 用于记录该目录dir被引用的计数增加1
protected FSDirectory() {}; // permit subclassing
private void init(File path, LockFactory lockFactory) throws IOException {
// 根据指定的file和lockFactory,调用该类Directory的init方法,进行FSDirectory的初始化初始化工作
directory = path;
boolean doClearLockID = false;
if (lockFactory == null) { // 锁工厂实例为null
if (disableLocks) { // 如果锁不可以使用 lockFactory = NoLockFactory.getNoLockFactory(); // 调用NoLockFactory类,获取NoLockFactory实例,为当前的锁工厂实例。其实NoLockFactory是一个单态(singleton)模式的工厂类,应用中只能有一个锁实例,不需要进行同步 } else { // 如果锁可以使用,获取锁工厂类名称的字符串描述 String lockClassName = System.getProperty("org.apache.lucene.store.FSDirectoryLockFactoryClass");
if (lockClassName != null && !lockClassName.equals("")) { // 如果获取的锁工厂类名称的字符串描述不为null,而且者不为空 Class c;
try { c = Class.forName(lockClassName); // 加载该锁工厂类 } catch (ClassNotFoundException e) { throw new IOException("unable to find LockClass " + lockClassName); }
try { lockFactory = (LockFactory) c.newInstance(); // 获取一个锁工厂的实例 } catch (IllegalAccessException e) { throw new IOException("IllegalAccessException when instantiating LockClass " + lockClassName); } catch (InstantiationException e) { throw new IOException("InstantiationException when instantiating LockClass " + lockClassName); } catch (ClassCastException e) { throw new IOException("unable to cast LockClass " + lockClassName + " instance to a LockFactory"); }
// 根据获取的锁工厂实例的类型来设置对文件File path加锁的方式
if (lockFactory instanceof NativeFSLockFactory) { ((NativeFSLockFactory) lockFactory).setLockDir(path); } else if (lockFactory instanceof SimpleFSLockFactory) { ((SimpleFSLockFactory) lockFactory).setLockDir(path); } } else { // 没有其他的锁工厂类可用,则使用默认的锁工厂类创建一个锁工厂实例 lockFactory = new SimpleFSLockFactory(path); doClearLockID = true; } } }
setLockFactory(lockFactory); // 设置当前FSDirectory相关锁工厂实例
if (doClearLockID) { // Clear the prefix because write.lock will be // stored in our directory: lockFactory.setLockPrefix(null); }}
// 返回目录文件File directory下的所有索引文件,索引文件使用文件名的字符串描述public String[] list() { return directory.list(IndexFileNameFilter.getFilter());}
// 如果文件name存在,则返回true public boolean fileExists(String name) { File file = new File(directory, name); // 根据File directory指定的抽象路径,以及name指定的名称创建一个文件 return file.exists();}
// 获取文件name最后修改的时间public long fileModified(String name) { File file = new File(directory, name); return file.lastModified();}
// 返回目录文件directory下名称为name的文件最后修改的时间public static long fileModified(File directory, String name) { File file = new File(directory, name); return file.lastModified();}
// 设置文件name当前修改的时间public void touchFile(String name) { File file = new File(directory, name); file.setLastModified(System.currentTimeMillis());}
// 获取文件name的长度 public long fileLength(String name) { File file = new File(directory, name); return file.length();}
// 删除目录文件directory下的name文件public void deleteFile(String name) throws IOException { File file = new File(directory, name); if (!file.delete()) throw new IOException("Cannot delete " + file);}
// 重新命名文件,该方法已经废弃public synchronized void renameFile(String from, String to) throws IOException { File old = new File(directory, from); File nu = new File(directory, to);
if (nu.exists()) if (!nu.delete()) throw new IOException("Cannot delete " + nu); if (!old.renameTo(nu)) { java.io.InputStream in = null; java.io.OutputStream out = null; try { in = new FileInputStream(old); out = new FileOutputStream(nu); if (buffer == null) { buffer = new byte[1024]; } int len; while ((len = in.read(buffer)) >= 0) { out.write(buffer, 0, len); }
old.delete(); } catch (IOException ioe) { IOException newExc = new IOException("Cannot rename " + old + " to " + nu); newExc.initCause(ioe); throw newExc; } finally { try { if (in != null) { try { in.close(); } catch (IOException e) { throw new RuntimeException("Cannot close input stream: " + e.toString(), e); } } } finally { if (out != null) { try { out.close(); } catch (IOException e) { throw new RuntimeException("Cannot close output stream: " + e.toString(), e); } } } } }}
// 创建一个名称为name的文件,返回一个输出流,以便对该文件进行写入操作public IndexOutput createOutput(String name) throws IOException {
File file = new File(directory, name); if (file.exists() && !file.delete()) // 如果name指定文件存在,或者没有被删除掉,抛出异常 throw new IOException("Cannot overwrite: " + file);
return new FSIndexOutput(file); // 返回文件File file的一个输出流}
// 读取名称为name的文件,返回一个输出流,以便定义对读取出来的内容进行操作public IndexInput openInput(String name) throws IOException { return new FSIndexInput(new File(directory, name));}
// 打开指定名称为name的文件,指定大小为缓冲区大小bufferSize,返回一个输入流public IndexInput openInput(String name, int bufferSize) throws IOException { return new FSIndexInput(new File(directory, name), bufferSize);}
// 一个字符缓冲区,将字节转换为十六进制 private static final char[] HEX_DIGITS ={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
// 获取锁的标识IDpublic String getLockID() { String dirName; try { dirName = directory.getCanonicalPath(); } catch (IOException e) { throw new RuntimeException(e.toString(), e); }
byte digest[]; synchronized (DIGESTER) { digest = DIGESTER.digest(dirName.getBytes()); } StringBuffer buf = new StringBuffer(); 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();}
// 关闭目录public synchronized void close() { if (--refCount <= 0) { synchronized (DIRECTORIES) { DIRECTORIES.remove(directory); } }}
public File getFile() { return directory;}
/** For debug output. */public String toString() { return this.getClass().getName() + "@" + directory;}
// FSIndexInput是一个静态内部类,用于管理文件系统中的索引文件输入流
protected static class FSIndexInput extends BufferedIndexInput { private static class Descriptor extends RandomAccessFile { // 用于记录文件打开状态的boolean型变量isOpen private boolean isOpen; long position; final long length; public Descriptor(File file, String mode) throws IOException { super(file, mode); isOpen=true; length=length(); } public void close() throws IOException { if (isOpen) { isOpen=false; super.close(); } } protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } } private final Descriptor file; boolean isClone; public FSIndexInput(File path) throws IOException { this(path, BufferedIndexInput.BUFFER_SIZE); } public FSIndexInput(File path, int bufferSize) throws IOException { super(bufferSize); file = new Descriptor(path, "r"); } // 比较底层的读取操作,主要是对字节操作 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; do { int i = file.read(b, offset+total, len-total); if (i == -1) throw new IOException("read past EOF"); file.position += i; total += i; } while (total < len); } } public void close() throws IOException { if (!isClone) file.close(); } protected void seekInternal(long position) { } public long length() { return file.length; } public Object clone() { FSIndexInput clone = (FSIndexInput)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(); }}
// FSIndexOutput是一个静态内部类,用于管理文件系统中的索引文件输出流,与FSIndexInput实现类似
protected static class FSIndexOutput extends BufferedIndexOutput { RandomAccessFile file = null; // 用于记录文件打开状态的boolean型变量isOpen private boolean isOpen; public FSIndexOutput(File path) throws IOException { file = new RandomAccessFile(path, "rw"); isOpen = true; } // 从缓冲区写入文件的方法 public void flushBuffer(byte[] b, int offset, int size) throws IOException { file.write(b, offset, size); } public void close() throws IOException { if (isOpen) { super.close(); file.close(); isOpen = false; } } // 随机访问的方法实现 public void seek(long pos) throws IOException { super.seek(pos); file.seek(pos); } public long length() throws IOException { return file.length(); }}}
上面FSDirectory类的实现挺复杂的,主要是在继承Directory抽象类的基础上,增加了文件系统目录锁特有的一些操作方式。
通过FSDirectory类源代码的阅读,关于文件系统目录FSDirectory类,总结几点:
1、锁工厂的获取及其管理;
2、对文件系统目录下索引文件的输入流和输出流的管理;
3、获取FSDirectory类实例;
4、获取锁工厂实例后,可以创建一个新的FSDirectory类实例,在此之前先要完成一些初始化工作;
5、继承自Directory抽象类,自然可以实现索引文件的的拷贝操作。
6、FSDirectory类中实现了很多静态内部类,这使得只能在FSDirectory类内部访问这些静态类,对外部透明。
