package eu.fbk.knowledgestore.runtime;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import javax.annotation.Nullable;
import org.apache.hadoop.fs.*;
import org.slf4j.LoggerFactory;
/**
* Utility methods for dealing with files using the Hadoop API.
*/
public final class Files {
private static FileSystem rawLocalFileSystem = null;
public static FileSystem getFileSystem(final String url, final Map<String, String> properties)
throws IOException {
final URI uri = URI.create(url.replace('\\', '/'));
org.apache.hadoop.conf.Configuration conf;
conf = new org.apache.hadoop.conf.Configuration(true);
conf.set("fs.default.name", uri.toString());
for (final Map.Entry<String, String> entry : properties.entrySet()) {
final String name = entry.getKey();
final String value = entry.getValue();
conf.set(name, value);
}
FileSystem fs;
if (url.startsWith("file://")) {
fs = getRawLocalFileSystem();
}
else {
fs = FileSystem.get(conf);
}
fs.setWorkingDirectory(new Path(uri.getPath()));
return fs;
}
public static FileSystem getRawLocalFileSystem() {
synchronized (Files.class) {
if (Files.rawLocalFileSystem == null) {
LoggerFactory.getLogger(Files.class).debug(
"You can safely ignore the following reported IOException - it's the way "
+ "Hadoop people report where the Configuration is initialized )");
final org.apache.hadoop.conf.Configuration conf;
conf = new org.apache.hadoop.conf.Configuration(true);
Files.rawLocalFileSystem = new RawLocalFileSystem();
try {
Files.rawLocalFileSystem.initialize(//
new File(System.getProperty("user.dir")).toURI(), //
conf);
} catch (final IOException ex) {
throw new Error("Failed to initialize local raw filesystem (!)", ex);
}
}
return Files.rawLocalFileSystem;
}
}
@Nullable
public static FSDataInputStream readWithBackup(final FileSystem fs, final Path path)
throws IOException {
// we keep track of filesystem exceptions (but it's unclear when they are thrown)
IOException exception = null;
try {
// 1. try to read the requested file
final FSDataInputStream result = fs.open(path);
if (result != null) {
return result;
}
} catch (final IOException ex) {
exception = ex;
}
final Path backupPath = new Path(path.getParent() + "/." + path.getName() + ".backup");
try {
// 2. on failure, try to read its backup
final FSDataInputStream result = fs.open(backupPath);
if (result != null) {
return result;
}
} catch (final IOException ex) {
if (exception == null) {
exception = ex;
}
}
// 3. only on failure check whether the two files exist
final boolean fileExists = fs.exists(path);
final boolean backupExists = fs.exists(backupPath);
// 4. if they don't exist it's ok, just report this returning null
if (!fileExists && !backupExists) {
return null;
}
// 5. otherwise we throw an exception (possibly the ones got before)
if (exception == null) {
exception = new IOException("Cannot read " + (fileExists ? path : backupExists)
+ " (file reported to exist)");
}
throw exception;
}
public static FSDataOutputStream writeWithBackup(final FileSystem fs, final Path path)
throws IOException {
// compute paths of new and backup files
final Path newPath = new Path(path.getParent() + "/." + path.getName() + ".new");
final Path backupPath = new Path(path.getParent() + "/." + path.getName() + ".backup");
// 1. delete filename.new if it exists
Files.delete(fs, newPath);
// 2. if filename exists, rename it to filename.backup (deleting old backup)
if (fs.exists(path)) {
Files.delete(fs, backupPath);
Files.rename(fs, path, backupPath);
}
// 3. create filename.new, returning a stream for writing its content
return new FSDataOutputStream(fs.create(newPath), null) {
@Override
public void close() throws IOException {
super.close();
// 4. rename filename.new to filename
Files.rename(fs, newPath, path);
}
};
}
public static void delete(final FileSystem fs, final Path path) throws IOException {
IOException exception = null;
try {
if (fs.delete(path, false)) {
return;
}
} catch (final IOException ex) {
exception = ex;
}
if (fs.exists(path)) {
throw exception != null ? exception : new IOException("Cannot delete " + path);
}
}
public static void rename(final FileSystem fs, final Path from, final Path to)
throws IOException {
if (from.equals(to)) {
return;
}
final boolean renamed = fs.rename(from, to);
if (!renamed) {
String message = "Cannot rename " + from + " to " + to;
if (fs.exists(to)) {
message += ": destination already exists";
} else if (fs.exists(from)) {
message += ": source does not exist";
}
throw new IOException(message);
}
}
@Nullable
public static FileStatus stat(final FileSystem fs, final Path path) throws IOException {
try {
final FileStatus status = fs.getFileStatus(path);
if (status != null) {
return status;
}
} catch (final IOException ex) {
if (fs.exists(path)) {
throw ex;
}
}
return null;
}
private Files() {
}
}