package org.peerbox.watchservice.filetree.persistency; import java.nio.file.Path; import java.util.List; import org.peerbox.app.DbContext; import org.peerbox.watchservice.filetree.composite.FileComponent; import org.sql2o.Connection; import org.sql2o.Sql2o; import com.google.inject.Inject; public class LocalFileDao { private static final String FILE_TABLE = "files"; private static final String DEFAULT_COLUMNS = "id, path, is_file, content_hash, " + "is_synchronized, is_uploaded, " + "current_state, next_state"; private final Sql2o sql2o; private final DbContext dbContext; @Inject public LocalFileDao(DbContext dbContext) { this.dbContext = dbContext; this.sql2o = new Sql2o(this.dbContext.getDataSource()); createTable(); } public void createTable() { final String tableSql = "CREATE TABLE IF NOT EXISTS " + 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, " + "is_synchronized BOOLEAN NOT NULL DEFAULT(false), " + "is_uploaded BOOLEAN NOT NULL DEFAULT(false), " + "current_state NVARCHAR(32), " + "next_state NVARCHAR(32), " + "to_delete BOOLEAN NOT NULL DEFAULT(false) " + ");"; final String indexSql = "CREATE UNIQUE INDEX IF NOT EXISTS files_path " + "ON "+ FILE_TABLE + " (path);"; try (Connection con = sql2o.beginTransaction()) { con.createQuery(tableSql).executeUpdate(); con.createQuery(indexSql).executeUpdate(); con.commit(); } } public List<FileComponent> getAllFiles() { final String sql = String.format( "SELECT %s FROM %s ORDER BY path ASC;", DEFAULT_COLUMNS, FILE_TABLE); try (Connection con = sql2o.open()) { return con.createQuery(sql) .executeAndFetch(new FileComponentResultSetHandler()); } } public FileComponent getFileByPath(final Path path) { final String sql = String.format( "SELECT %s FROM %s WHERE path = :path;", DEFAULT_COLUMNS, FILE_TABLE); try (Connection con = sql2o.open()) { return con.createQuery(sql) .addParameter("path", path.toString()) .executeAndFetchFirst(new FileComponentResultSetHandler()); } } public void deleteByPath(final Path file) { final String sql = String.format( "DELETE FROM %s WHERE path = :path", FILE_TABLE); try (Connection con = sql2o.open()) { con.createQuery(sql) .addParameter("path", file.toString()) .executeUpdate(); } } public void persistFile(final FileComponent file) { final String sql = "MERGE INTO " + FILE_TABLE + " " + "(path, is_file, content_hash, is_synchronized, is_uploaded, current_state, next_state, to_delete) " + "KEY(path) " + "VALUES (:path, :is_file, :content_hash, :is_synchronized, :is_uploaded, :current_state, :next_state, :to_delete);"; try (Connection con = sql2o.open()) { con.createQuery(sql) .addParameter("path", file.getPath().toString()) .addParameter("is_file", file.isFile()) .addParameter("content_hash", file.getContentHash()) .addParameter("is_synchronized", file.isSynchronized()) .addParameter("is_uploaded", file.isUploaded()) .addParameter("current_state", file.getAction().getCurrentState().getStateType().toString()) .addParameter("next_state", file.getAction().getNextState().getStateType().toString()) .addParameter("to_delete", false) .executeUpdate(); } } public void dumpCsv() { DaoUtils.dumpTableToCsv(FILE_TABLE, sql2o); } public void persistAndReplaceFileComponents(List<FileComponent> files) { final String markToDelete = String.format( "UPDATE %s SET to_delete = true;", FILE_TABLE); final String deleteStale = String.format( "DELETE FROM %s WHERE to_delete = true", FILE_TABLE); final String sql = "MERGE INTO " + FILE_TABLE + " " + "(path, is_file, content_hash, is_synchronized, is_uploaded, current_state, next_state, to_delete) " + "KEY(path) " + "VALUES (:path, :is_file, :content_hash, :is_synchronized, :is_uploaded, :current_state, :next_state, :to_delete);"; try (Connection con = sql2o.beginTransaction()) { // mark to delete con.createQuery(markToDelete).executeUpdate(); for (FileComponent file : files) { // insert or update con.createQuery(sql) .addParameter("path", file.getPath().toString()) .addParameter("is_file", file.isFile()) .addParameter("content_hash", file.getContentHash()) .addParameter("is_synchronized", file.isSynchronized()) .addParameter("is_uploaded", file.isUploaded()) .addParameter("current_state", file.getAction().getCurrentState().getStateType().toString()) .addParameter("next_state", file.getAction().getNextState().getStateType().toString()) .addParameter("to_delete", false) .executeUpdate(); } // cleanup deleted files con.createQuery(deleteStale).executeUpdate(); con.commit(); } } public Boolean isSynchronizedByPath(Path path) { final String sql = String.format( "SELECT is_synchronized isSynchronized FROM %s WHERE path = :path", FILE_TABLE); Boolean isSync = null; try(Connection con = sql2o.open()) { isSync = con.createQuery(sql) .addParameter("path", path.toString()) .executeScalar(Boolean.class); } return isSync; } }