package com.senseidb.clue; import java.io.IOException; import java.io.OutputStream; import java.util.Collection; import java.util.EnumSet; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FsServerDefaults; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.lucene.store.BaseDirectory; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.LockFactory; import org.apache.lucene.store.OutputStreamIndexOutput; import com.senseidb.clue.util.CustomBufferedIndexInput; public class HdfsDirectory extends BaseDirectory { public static final int BUFFER_SIZE = 8192; private final FileSystem fs; private final Path dir; public HdfsDirectory(LockFactory lockFactory, Path dirPath, Configuration config) throws IOException { super(lockFactory); this.fs = FileSystem.get(config); dir = dirPath; } @Override public void close() throws IOException { fs.close(); } @Override public IndexOutput createOutput(String name, IOContext context) throws IOException { return new HdfsFileWriter(fs, new Path(dir, name), name); } @Override public void sync(Collection<String> strings) throws IOException { } @Override public void syncMetaData() throws IOException {} @Override public void rename(String source, String dest) throws IOException { } @Override public void deleteFile(String name) throws IOException { Path path = new Path(dir, name); fs.delete(path, false); } @Override public long fileLength(String name) throws IOException { return fs.getFileStatus(new Path(dir, name)).getLen(); } @Override public String[] listAll() throws IOException { FileStatus[] statuses = fs.listStatus(dir); if (statuses == null) { return new String[0]; } String[] files = new String[statuses.length]; for (int i = 0; i < statuses.length; i++) { files[i] = statuses[i].getPath().getName(); } return files; } @Override public IndexInput openInput(String name, IOContext context) throws IOException { Path path = new Path(dir, name); return new HdfsIndexInput( "HDFSIndexInput(path=\"" + path.getName() + "\")", fs, path, BUFFER_SIZE); } static class HdfsFileWriter extends OutputStreamIndexOutput { public static final String HDFS_SYNC_BLOCK = "clue.hdfs.sync.block"; public static final int BUFFER_SIZE = 16384; public HdfsFileWriter(FileSystem fileSystem, Path path, String name) throws IOException { super("fileSystem=" + fileSystem + " path=" + path, name, getOutputStream(fileSystem, path), BUFFER_SIZE); } private static final OutputStream getOutputStream(FileSystem fileSystem, Path path) throws IOException { Configuration conf = fileSystem.getConf(); FsServerDefaults fsDefaults = fileSystem.getServerDefaults(path); EnumSet<CreateFlag> flags = EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE); if (Boolean.getBoolean(HDFS_SYNC_BLOCK)) { flags.add(CreateFlag.SYNC_BLOCK); } return fileSystem.create(path, FsPermission.getDefault() .applyUMask(FsPermission.getUMask(conf)), flags, fsDefaults .getFileBufferSize(), fsDefaults.getReplication(), fsDefaults .getBlockSize(), null); } } static class HdfsIndexInput extends CustomBufferedIndexInput { private final Path path; private final FSDataInputStream inputStream; private final long length; private boolean clone = false; public HdfsIndexInput(String name, FileSystem fileSystem, Path path, int bufferSize) throws IOException { super(name); this.path = path; FileStatus fileStatus = fileSystem.getFileStatus(path); length = fileStatus.getLen(); inputStream = fileSystem.open(path, bufferSize); } @Override protected void readInternal(byte[] b, int offset, int length) throws IOException { inputStream.readFully(getFilePointer(), b, offset, length); } @Override protected void seekInternal(long pos) throws IOException { } @Override protected void closeInternal() throws IOException { if (!clone) { inputStream.close(); } } @Override public long length() { return length; } @Override public IndexInput clone() { HdfsIndexInput clone = (HdfsIndexInput) super.clone(); clone.clone = true; return clone; } } @Override public IndexOutput createTempOutput(String prefix, String suffix, IOContext context) throws IOException { throw new UnsupportedOperationException(); } }