package dlog.common.search;
import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.FileSystemManager; import org.apache.commons.vfs.RandomAccessContent; import org.apache.commons.vfs.VFS; import org.apache.commons.vfs.util.RandomAccessMode; import org.apache.lucene.store.BufferedIndexInput; import org.apache.lucene.store.BufferedIndexOutput; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.Lock;
/** * 扩展Lucene索引的存储,使用commons-vfs做为lucene的存储引擎 * @author Winter Lau * @email javayou@gmail.com * @home http://www./javayou */ public class VFSDirectory extends Directory { private FileSystemManager fileManager; private FileObject directory; private FileObject locker;
public VFSDirectory(String repository_uri, String lock_uri) throws IOException { this.fileManager = VFS.getManager(); this.directory = fileManager.resolveFile(repository_uri); if(!directory.exists()) directory.createFolder(); this.locker = fileManager.resolveFile(lock_uri); if(!locker.exists()) locker.createFolder(); }
@Override public void close() throws IOException { directory.close(); }
protected void finalize() throws IOException { close(); // close the directory }
@Override public IndexOutput createOutput(String name) throws IOException { FileObject file = this.fileManager.resolveFile(directory, name); return new VFSIndexOutput(file); }
@Override public IndexInput openInput(String name) throws IOException { FileObject file = this.fileManager.resolveFile(directory, name); return new VFSIndexInput(file); }
@Override public void deleteFile(String name) throws IOException { FileObject file = this.fileManager.resolveFile(directory, name); //System.out.println(file.getName().getFriendlyURI()); if(!file.delete()) throw new IOException("Cannot delete " + file); }
@Override public boolean fileExists(String name) throws IOException { FileObject file = this.fileManager.resolveFile(directory, name); return file.exists(); }
@Override public long fileLength(String name) throws IOException { FileObject file = this.fileManager.resolveFile(directory, name); return file.getContent().getSize(); }
@Override public long fileModified(String name) throws IOException { FileObject file = this.fileManager.resolveFile(directory, name); return file.getContent().getLastModifiedTime(); }
@Override public String[] list()throws IOException { List names = new ArrayList(); FileObject[] files = directory.getChildren();
for(int i=0;i
@Override public Lock makeLock(String name) {
final StringBuffer buf = getLockPrefix(); buf.append("-"); buf.append(name);
try{
final FileObject lockFile = this.fileManager.resolveFile(locker, buf.toString());
return new Lock() { public boolean obtain() throws IOException { lockFile.createFile(); return lockFile.exists(); }
public void release() { try { lockFile.delete(); } catch (FileSystemException e) { throw new RuntimeException("Cannot release lock : " + buf.toString(), e); } }
public boolean isLocked() { try { return lockFile.exists(); } catch (FileSystemException e) { throw new RuntimeException("Cannot check locking status : " + buf.toString(), e); } }
public String toString() { return "Lock@" + lockFile; } }; } catch (IOException e){ throw new RuntimeException("Request lock failed : " + buf.toString(), e); } }
private static MessageDigest DIGESTER;
/** * So we can do some byte-to-hexchar conversion below */ static char[] HEX_DIGITS = { ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘ };
static { try { DIGESTER = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e.toString(), e); } }
private StringBuffer getLockPrefix() {
String dirName; // name to be hashed
try { dirName = directory.getURL().toString(); } 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; }
@Override public void renameFile(String name1, String name2) throws IOException { FileObject file1 = this.fileManager.resolveFile(directory, name1); FileObject file2 = this.fileManager.resolveFile(directory, name2); try{ IOUtils.copy(file1.getContent().getInputStream(), file2.getContent().getOutputStream()); }finally{ close(file2); close(file1); } }
@Override public void touchFile(String name) throws IOException { FileObject file = this.fileManager.resolveFile(directory, name); file.getContent().setLastModifiedTime(System.currentTimeMillis()); }
private void close(FileObject file){ if(file!=null){ try{ file.close(); }catch (IOException e) { throw new RuntimeException("Cannot close input stream: " + e.toString(), e); } } }
private static class VFSIndexInput extends BufferedIndexInput {
private long length; private RandomAccessContent content;
public VFSIndexInput(FileObject file) throws IOException { this.content = file.getContent().getRandomAccessContent(RandomAccessMode.READ); this.length = content.length(); }
@Override public void close() throws IOException { this.content.close(); }
@Override public long length() { return length; }
@Override protected void readInternal(byte[] b, int off, int len) throws IOException { this.content.readFully(b, off, len); }
@Override protected void seekInternal(long position) throws IOException { this.content.seek(position); } }
private static class VFSIndexOutput throws BufferedIndexOutput {
private RandomAccessContent content;
public VFSIndexOutput(FileObject file) throws IOException{ if(!file.exists()) file.createFile(); this.content = file.getContent().getRandomAccessContent(RandomAccessMode.READWRITE); }
@Override protected void flushBuffer(byte[] b, int size) throws IOException { this.content.write(b, 0, size); }
@Override public void seek(long pos) throws IOException { super.seek(pos); //此行代码非常重要,否则会导致写文件错误 this.content.seek(pos); }
@Override public long length() throws IOException { return this.content.length(); }
@Override public void close() throws IOException { super.close(); this.content.close(); }
protected void finalize() throws IOException { close(); // close the file } } }
|