package org.peerbox.watchservice.filetree.persistency;
import java.nio.file.Path;
import java.util.List;
import org.hive2hive.core.processes.files.list.FileNode;
import org.peerbox.app.DbContext;
import org.peerbox.app.manager.file.FileInfo;
import org.peerbox.watchservice.PathUtils;
import org.sql2o.Connection;
import org.sql2o.Sql2o;
import com.google.inject.Inject;
public class RemoteFileDao {
private static final String REMOTE_FILE_TABLE = "remote_files";
/* aliases are important to match Java bean */
private static final String DEFAULT_COLUMNS = "path, is_file isFile, content_hash contentHash";
private final DbContext dbContext;
private final Sql2o sql2o;
@Inject
public RemoteFileDao(DbContext dbContext) {
this.dbContext = dbContext;
this.sql2o = new Sql2o(this.dbContext.getDataSource());
createTable();
}
public String getTableName() {
return REMOTE_FILE_TABLE;
}
public boolean tableExists() {
final String sql = String.format(
"SELECT tbl.cnt FROM "
+ "(SELECT count(*) cnt FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = UPPER('%s')) as tbl",
REMOTE_FILE_TABLE);
try (Connection con = sql2o.open()) {
int cnt = con.createQuery(sql).executeScalar(Integer.class);
return cnt > 0;
}
}
/**
* Creates tables and indices (if they do not exist yet)
*/
public void createTable() {
final String tableSql =
"CREATE TABLE IF NOT EXISTS " + REMOTE_FILE_TABLE + " ( "
+ "id IDENTITY NOT NULL PRIMARY KEY auto_increment, "
+ "path NVARCHAR NOT NULL UNIQUE, "
+ "is_file BOOLEAN NOT NULL, "
+ "content_hash NVARCHAR(64) NOT NULL, "
+ "to_delete BOOLEAN NOT NULL DEFAULT(FALSE)"
+ ");";
final String indexSql =
"CREATE UNIQUE INDEX IF NOT EXISTS remote_files_path "
+ "ON "+ REMOTE_FILE_TABLE + " (path);";
try (Connection con = sql2o.beginTransaction()) {
con.createQuery(tableSql).executeUpdate();
con.createQuery(indexSql).executeUpdate();
con.commit();
}
}
/**
* Persists the given list of file nodes by either inserting new files or updating existing
* information.
*
* @param fileNodes
*/
public void persistAndReplaceFileNodes(List<FileNode> fileNodes) {
/*
* procedure works as follows (in a transaction):
* - mark all records as to_delete(true)
* - update or insert records with given list, set to_delete(false)
* - delete all records that still have to_delete(true), i.e. records that were not updated
*/
final String markToDelete = String.format(
"UPDATE %s SET to_delete = true;", REMOTE_FILE_TABLE);
final String deleteStale = String.format(
"DELETE FROM %s WHERE to_delete = true", REMOTE_FILE_TABLE);
final String insert = String.format(
"MERGE INTO %s (path, is_file, content_hash, to_delete) "
+ "KEY (path) "
+ "VALUES ( :path, :is_file, :content_hash, :to_delete );", REMOTE_FILE_TABLE);
try (Connection con = sql2o.beginTransaction()) {
// mark to delete
con.createQuery(markToDelete).executeUpdate();
for (FileNode node : fileNodes) {
String hash = node.isFile() ? PathUtils.base64Encode(node.getMd5()) : "";
// insert or update
con.createQuery(insert)
.addParameter("path", node.getFile().toString())
.addParameter("is_file", node.isFile())
.addParameter("content_hash", hash)
.addParameter("to_delete", false)
.executeUpdate();
}
// cleanup deleted files
con.createQuery(deleteStale).executeUpdate();
con.commit();
}
}
public List<FileInfo> getAllFileNodeAttributes() {
final String sql = String.format(
"SELECT %s FROM %s ORDER BY path ASC;", DEFAULT_COLUMNS, REMOTE_FILE_TABLE);
try (Connection con = sql2o.open()) {
return con.createQuery(sql).executeAndFetch(new FileInfoResultSetHandler());
}
}
public void deleteByPath(Path file) {
final String sql = String.format(
"DELETE FROM %s WHERE path = :path", REMOTE_FILE_TABLE);
try (Connection con = sql2o.open()) {
con.createQuery(sql)
.addParameter("path", file.toString())
.executeUpdate();
}
}
public void dumpCsv() {
DaoUtils.dumpTableToCsv(REMOTE_FILE_TABLE, sql2o);
}
}