package water.persist;
import java.io.*;
import java.net.URI;
import java.util.ArrayList;
import water.*;
import water.api.FSIOException;
import water.fvec.NFSFileVec;
import water.util.Log;
/**
* Persistence backend using local file system.
*/
final class PersistFS extends Persist {
final File _root;
final File _dir;
PersistFS(File root) {
_root = root;
_dir = new File(root, "ice" + H2O.API_PORT);
//deleteRecursive(_dir);
// Make the directory as-needed
root.mkdirs();
if( !(root.isDirectory() && root.canRead() && root.canWrite()) )
H2O.die("ice_root not a read/writable directory");
}
public void cleanUp() { deleteRecursive(_dir); }
private static void deleteRecursive(File path) {
if( !path.exists() ) return;
if( path.isDirectory() )
for (File f : path.listFiles())
deleteRecursive(f);
path.delete();
}
private File getFile(Value v) {
return new File(_dir, getIceName(v));
}
@Override public byte[] load(Value v) throws IOException {
File f = getFile(v);
if( f.length() < v._max ) { // Should be fully on disk...
// or it's a racey delete of a spilled value
assert !v.isPersisted() : f.length() + " " + v._max + " " + v._key;
return null; // No value
}
try (FileInputStream s = new FileInputStream(f)) {
AutoBuffer ab = new AutoBuffer(s.getChannel(), true, Value.ICE);
byte[] b = ab.getA1(v._max);
ab.close();
return b;
}
}
// Store Value v to disk.
@Override public void store(Value v) throws IOException {
assert !v.isPersisted();
File dirs = new File(_dir, getIceDirectory(v._key));
if( !dirs.mkdirs() && !dirs.exists() )
throw new java.io.IOException("mkdirs failed making "+dirs);
try(FileOutputStream s = new FileOutputStream(getFile(v))) {
byte[] m = v.memOrLoad(); // we are not single threaded anymore
if( m != null && m.length != v._max ) {
Log.warn("Value size mismatch? " + v._key + " byte[].len=" + m.length+" v._max="+v._max);
v._max = m.length; // Implies update of underlying POJO, then re-serializing it without K/V storing it
}
new AutoBuffer(s.getChannel(), false, Value.ICE).putA1(m, m.length).close();
} catch( AutoBuffer.AutoBufferException abe ) {
throw abe._ioe;
}
}
@Override public void delete(Value v) {
getFile(v).delete(); // Silently ignore errors
// Attempt to delete empty containing directory
new File(_dir, getIceDirectory(v._key)).delete();
}
@Override public long getUsableSpace() {
return _root.getUsableSpace();
}
@Override public long getTotalSpace() {
return _root.getTotalSpace();
}
@Override
public Key uriToKey(URI uri) {
return NFSFileVec.make(new File(uri.toString()))._key;
}
@Override
public ArrayList<String> calcTypeaheadMatches(String src, int limit) {
assert false;
return new ArrayList<>();
}
@Override
public void importFiles(String path, String pattern, ArrayList<String> files, ArrayList<String> keys, ArrayList<String> fails, ArrayList<String> dels) {
assert false;
}
@Override
public OutputStream create(String path, boolean overwrite) {
File f;
boolean windowsPath = path.matches("^[a-zA-Z]:.*$");
if (windowsPath) {
f = new File(path);
}
else {
f = new File(URI.create(path));
}
if (f.exists() && !overwrite)
throw new FSIOException(path, "File already exists");
try {
if (!f.getParentFile().exists()) {
// Shortcut since we know that this is local FS
f.getParentFile().mkdirs();
}
return new FileOutputStream(f, false);
} catch (IOException e) {
throw new FSIOException(path, e);
}
}
@Override
public PersistEntry[] list(String path) {
File f = new File(URI.create(path));
if (f.isFile()) {
return new PersistEntry[] { getPersistEntry(f) };
} else if (f.isDirectory()) {
File[] files = f.listFiles();
PersistEntry[] entries = new PersistEntry[files.length];
for (int i = 0; i < files.length; i++) {
entries[i] = getPersistEntry(files[i]);
}
return entries;
}
throw H2O.unimpl();
}
@Override
public InputStream open(String path) {
try {
File f = new File(URI.create(path));
return new FileInputStream(f);
} catch (FileNotFoundException e) {
throw new FSIOException(path, "File not found");
} catch (Exception e) {
throw new FSIOException(path, e);
}
}
@Override
public boolean mkdirs(String path) {
return new File(URI.create(path)).mkdirs();
}
@Override
public boolean exists(String path) {
return new File(URI.create(path)).exists();
}
@Override
public boolean isDirectory(String path) {
return new File(URI.create(path)).isDirectory();
}
private PersistEntry getPersistEntry(File f) {
return new PersistEntry(f.getName(), f.length(), f.lastModified());
}
}