fs-watcher uses its own database
add test for fs-watcher
Change-Id: Iac54e5d5c748f80099ef798a6515993ab432c725
diff --git a/gui/fs-watcher.cc b/gui/fs-watcher.cc
index 18d004d..f646a9c 100644
--- a/gui/fs-watcher.cc
+++ b/gui/fs-watcher.cc
@@ -21,6 +21,7 @@
*/
#include "fs-watcher.h"
+#include "db-helper.h"
#include "logging.h"
#include <boost/bind.hpp>
@@ -35,7 +36,6 @@
FsWatcher::FsWatcher (QString dirPath,
LocalFile_Change_Callback onChange, LocalFile_Change_Callback onDelete,
- FileState *fileState,
QObject* parent)
: QObject(parent)
, m_watcher (new QFileSystemWatcher())
@@ -43,11 +43,12 @@
, m_dirPath (dirPath)
, m_onChange (onChange)
, m_onDelete (onDelete)
- , m_fileState (fileState)
{
_LOG_DEBUG ("Monitor dir: " << m_dirPath.toStdString ());
// add main directory to monitor
+ initFileStateDb();
+
m_watcher->addPath (m_dirPath);
// register signals (callback functions)
@@ -57,17 +58,18 @@
m_scheduler->start ();
Scheduler::scheduleOneTimeTask (m_scheduler, 0,
- bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, m_dirPath, false/* don't remove incomplete files*/),
+ bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, m_dirPath),
"rescan-r-" + m_dirPath.toStdString ()); // only one task will be scheduled per directory
Scheduler::scheduleOneTimeTask (m_scheduler, 0,
- bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, m_dirPath, true),
+ bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, m_dirPath),
"rescan-" +m_dirPath.toStdString ()); // only one task will be scheduled per directory
}
FsWatcher::~FsWatcher()
{
m_scheduler->shutdown ();
+ sqlite3_close(m_db);
}
void
@@ -79,23 +81,23 @@
if (!filesystem::exists (filesystem::path (absPathTriggeredDir)))
{
Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
- bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, dirPath, true/* ignore incomplete file flag. the whole directory got removed*/),
+ bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, dirPath),
"r-" + dirPath.toStdString ()); // only one task will be scheduled per directory
}
else
{
// m_executor.execute (bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath));
Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
- bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath, false),
+ bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath),
dirPath.toStdString ()); // only one task will be scheduled per directory
// m_executor.execute (bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath));
Scheduler::scheduleOneTimeTask (m_scheduler, 300,
- bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath, true),
+ bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath),
"rescan-"+dirPath.toStdString ()); // only one task will be scheduled per directory
Scheduler::scheduleOneTimeTask (m_scheduler, 300,
- bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, m_dirPath, false/* don't remove incomplete files*/),
+ bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, m_dirPath),
"rescan-r-" + m_dirPath.toStdString ()); // only one task will be scheduled per directory
}
}
@@ -105,7 +107,7 @@
{
if (!filePath.startsWith (m_dirPath))
{
- _LOG_ERROR ("Got notification about a file not from the monitored directory");
+ _LOG_ERROR ("Got notification about a file not from the monitored directory: " << filePath.toStdString());
return;
}
filesystem::path absPathTriggeredFile (filePath.toStdString ());
@@ -133,7 +135,7 @@
}
void
-FsWatcher::ScanDirectory_NotifyUpdates_Execute (QString dirPath, bool notifyCallbacks)
+FsWatcher::ScanDirectory_NotifyUpdates_Execute (QString dirPath)
{
_LOG_TRACE (" >> ScanDirectory_NotifyUpdates_Execute");
@@ -169,9 +171,11 @@
relFile.remove (0, m_dirPath.size ());
filesystem::path aFile (relFile.toStdString ());
- if (notifyCallbacks ||
- !m_fileState->LookupFile (aFile.relative_path ().generic_string ()) /* file does not exist there, but exists locally: added */)
+ if (
+ //!m_fileState->LookupFile (aFile.relative_path ().generic_string ()) /* file does not exist there, but exists locally: added */)
+ !fileExists(aFile.relative_path().c_str()) /*file does not exist in db, but exists in fs: add */)
{
+ addFile(aFile.relative_path().c_str());
DidFileChanged (absFilePath);
}
}
@@ -185,7 +189,7 @@
void
-FsWatcher::ScanDirectory_NotifyRemovals_Execute (QString dirPath, bool removeIncomplete)
+FsWatcher::ScanDirectory_NotifyRemovals_Execute (QString dirPath)
{
_LOG_DEBUG ("Triggered DirPath: " << dirPath.toStdString ());
@@ -194,6 +198,7 @@
filesystem::path triggeredDir (dirPath.toStdString ());
+ /*
FileItemsPtr files = m_fileState->LookupFilesInFolderRecursively (triggeredDir.relative_path ().generic_string ());
for (std::list<FileItem>::iterator file = files->begin (); file != files->end (); file ++)
{
@@ -209,8 +214,116 @@
}
}
}
+ */
+
+ vector<string> files;
+ getFilesInDir(triggeredDir.relative_path().c_str(), files);
+ for (vector<string>::iterator file = files.begin(); file != files.end(); file++)
+ {
+ filesystem::path targetFile = filesystem::path (m_dirPath.toStdString()) / file->c_str();
+ if (!filesystem::exists (targetFile))
+ {
+ deleteFile(file->c_str());
+ m_onDelete(file->c_str());
+ }
+ }
}
+const string INIT_DATABASE = "\
+CREATE TABLE IF NOT EXISTS \n\
+ Files( \n\
+ filename TEXT NOT NULL, \n\
+ PRIMARY KEY (filename) \n\
+); \n\
+CREATE INDEX filename_index ON Files (filename); \n\
+";
+
+void
+FsWatcher::initFileStateDb()
+{
+ filesystem::path dbFolder = filesystem::path (m_dirPath.toStdString().c_str()) / ".chronoshare" / "fs_watcher";
+ filesystem::create_directories(dbFolder);
+
+ int res = sqlite3_open((dbFolder / "filestate.db").c_str(), &m_db);
+ if (res != SQLITE_OK)
+ {
+ BOOST_THROW_EXCEPTION(Error::Db() << errmsg_info_str("Cannot open database: " + (dbFolder / "filestate.db").string()));
+ }
+
+ char *errmsg = 0;
+ res = sqlite3_exec(m_db, INIT_DATABASE.c_str(), NULL, NULL, &errmsg);
+ if (res != SQLITE_OK && errmsg != 0)
+ {
+ // _LOG_TRACE ("Init \"error\": " << errmsg);
+ cout << "FS-Watcher DB error: " << errmsg << endl;
+ sqlite3_free (errmsg);
+ }
+}
+
+bool
+FsWatcher::fileExists(const filesystem::path &filename)
+{
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(m_db, "SELECT * FROM Files WHERE filename = ?;", -1, &stmt, 0);
+ sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
+ bool retval = false;
+ if (sqlite3_step (stmt) == SQLITE_ROW)
+ {
+ retval = true;
+ }
+ sqlite3_finalize(stmt);
+
+ return retval;
+}
+
+void
+FsWatcher::addFile(const filesystem::path &filename)
+{
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(m_db, "INSERT OR IGNORE INTO Files (filename) VALUES (?);", -1, &stmt, 0);
+ sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
+ sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+}
+
+void
+FsWatcher::deleteFile(const filesystem::path &filename)
+{
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(m_db, "DELETE FROM Files WHERE filename = ?;", -1, &stmt, 0);
+ sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
+ sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+}
+
+void
+FsWatcher::getFilesInDir(const filesystem::path &dir, vector<string> &files)
+{
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(m_db, "SELECT * FROM Files WHERE filename LIKE ?;", -1, &stmt, 0);
+
+ string dirStr = dir.string();
+ ostringstream escapedDir;
+ for (string::const_iterator ch = dirStr.begin (); ch != dirStr.end (); ch ++)
+ {
+ if (*ch == '%')
+ escapedDir << "\\%";
+ else
+ escapedDir << *ch;
+ }
+ escapedDir << "/" << "%";
+ string escapedDirStr = escapedDir.str ();
+ sqlite3_bind_text (stmt, 1, escapedDirStr.c_str (), escapedDirStr.size (), SQLITE_STATIC);
+
+ while(sqlite3_step(stmt) == SQLITE_ROW)
+ {
+ string filename(reinterpret_cast<const char *>(sqlite3_column_text (stmt, 0)), sqlite3_column_bytes(stmt, 0));
+ files.push_back(filename);
+ }
+
+ sqlite3_finalize (stmt);
+
+}
#if WAF
#include "fs-watcher.moc"