RPC call info_actions_folder seem to work almost perfectly
Use ccnpeek to test:
ccnpeek -c /localhost/<your-user-name>/chronoshare/<folder>/info/actions/folder/<nonce>/%00
or
ccnpeek -c /localhost/<your-user-name>/chronoshare/<folder>/info/actions/folder/<specific-folder>/<nonce>/%00
+ json_spirit used to format the whole message, not just parts
Change-Id: I40c15459a8ae0ef6278364c45e84e5cbd0b08241
diff --git a/src/action-log.cc b/src/action-log.cc
index 14b30ca..0e22483 100644
--- a/src/action-log.cc
+++ b/src/action-log.cc
@@ -474,7 +474,7 @@
{
sqlite3_stmt *stmt;
sqlite3_prepare_v2 (m_db,
- "SELECT device_name, seq_no, file_mtime, file_chmod, file_seg_num, file_hash "
+ "SELECT device_name, seq_no, strftime('%s', file_mtime), file_chmod, file_seg_num, file_hash "
" FROM ActionLog "
" WHERE action = 0 AND "
" filename=? AND "
@@ -644,20 +644,23 @@
}
-void
+bool
ActionLog::LookupActionsInFolderRecursively (const boost::function<void (const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &)> &visitor,
const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
{
_LOG_DEBUG ("LookupActionsInFolderRecursively: [" << folder << "]");
+ if (limit >= 0)
+ limit += 1; // to check if there is more data
+
sqlite3_stmt *stmt;
if (folder != "")
{
/// @todo Do something to improve efficiency of this query. Right now it is basically scanning the whole database
sqlite3_prepare_v2 (m_db,
- "SELECT device_name,seq_no,action,filename,directory,version,action_timestamp, "
- " file_hash,file_mtime,file_chmod,file_seg_num, "
+ "SELECT device_name,seq_no,action,filename,directory,version,strftime('%s', action_timestamp), "
+ " file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num, "
" parent_device_name,parent_seq_no "
" FROM ActionLog "
" WHERE is_prefix (?, directory)=1 "
@@ -674,8 +677,8 @@
else
{
sqlite3_prepare_v2 (m_db,
- "SELECT device_name,seq_no,action,filename,directory,version,action_timestamp, "
- " file_hash,file_mtime,file_chmod,file_seg_num, "
+ "SELECT device_name,seq_no,action,filename,directory,version,strftime('%s', action_timestamp), "
+ " file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num, "
" parent_device_name,parent_seq_no "
" FROM ActionLog "
" ORDER BY action_timestamp DESC "
@@ -688,6 +691,9 @@
while (sqlite3_step (stmt) == SQLITE_ROW)
{
+ if (limit == 1)
+ break;
+
ActionItem action;
Ccnx::Name device_name (sqlite3_column_blob (stmt, 0), sqlite3_column_bytes (stmt, 0));
@@ -711,11 +717,14 @@
}
visitor (device_name, seq_no, action);
+ limit --;
}
_LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
sqlite3_finalize (stmt);
+
+ return (limit == 1); // more data is available
}
diff --git a/src/action-log.h b/src/action-log.h
index cfcaf7a..cef0a65 100644
--- a/src/action-log.h
+++ b/src/action-log.h
@@ -105,7 +105,7 @@
/**
* @brief Lookup up to [limit] actions starting [offset] in decreasing order (by timestamp) and calling visitor(device_name,seqno,action) for each action
*/
- void
+ bool
LookupActionsInFolderRecursively (const boost::function<void (const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &)> &visitor,
const std::string &folder, int offset=0, int limit=-1);
diff --git a/src/state-server.cc b/src/state-server.cc
index 6f73950..c264edf 100644
--- a/src/state-server.cc
+++ b/src/state-server.cc
@@ -27,6 +27,7 @@
#include "periodic-task.h"
#include "simple-interval-generator.h"
#include <boost/lexical_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
INIT_LOGGER ("StateServer");
@@ -121,9 +122,77 @@
// }
// }
-void debugAction (const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action)
+void
+StateServer::formatActionJson (json_spirit::Array &actions,
+ const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action)
{
- std::cout << name << ", " << seq_no << ", " << action.filename () << std::endl;
+/*
+ * {
+ * "id": {
+ * "userName": "<NDN-NAME-OF-THE-USER>",
+ * "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ * },
+ * "timestamp": "<ACTION-TIMESTAMP>",
+ * "filename": "<FILENAME>",
+ *
+ * "action": "UPDATE | DELETE",
+ *
+ * // only if update
+ * "update": {
+ * "hash": "<FILE-HASH>",
+ * "timestamp": "<FILE-TIMESTAMP>",
+ * "chmod": "<FILE-MODE>",
+ * "segNum": "<NUMBER-OF-SEGMENTS (~file size)>"
+ * },
+ *
+ * // if parent_device_name is set
+ * "parentId": {
+ * "userName": "<NDN-NAME-OF-THE-USER>",
+ * "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ * }
+ * }
+ */
+
+ using namespace json_spirit;
+ using namespace boost::posix_time;
+
+ Object json;
+ Object id;
+
+ id.push_back (Pair ("userName", boost::lexical_cast<string> (name)));
+ id.push_back (Pair ("seqNo", seq_no));
+
+ json.push_back (Pair ("id", id));
+
+ json.push_back (Pair ("timestamp", to_iso_string (from_time_t (action.timestamp ()))));
+ json.push_back (Pair ("filename", action.filename ()));
+ json.push_back (Pair ("action", (action.action () == 0) ? "UPDATE" : "DELETE"));
+
+ if (action.action () == 0)
+ {
+ Object update;
+ update.push_back (Pair ("hash", boost::lexical_cast<string> (Hash (action.file_hash ().c_str (), action.file_hash ().size ()))));
+ update.push_back (Pair ("timestamp", to_iso_string (from_time_t (action.mtime ()))));
+
+ ostringstream chmod;
+ chmod << setbase (8) << setfill ('0') << setw (4) << action.mode ();
+ update.push_back (Pair ("chmod", chmod.str ()));
+
+ update.push_back (Pair ("segNum", action.seg_num ()));
+ json.push_back (Pair ("update", update));
+ }
+
+ if (action.has_parent_device_name ())
+ {
+ Object parentId;
+ Ccnx::Name parent_device_name (action.parent_device_name ().c_str (), action.parent_device_name ().size ());
+ id.push_back (Pair ("userName", boost::lexical_cast<string> (parent_device_name)));
+ id.push_back (Pair ("seqNo", action.parent_seq_no ()));
+
+ json.push_back (Pair ("parentId", parentId));
+ }
+
+ actions.push_back (json);
}
void
@@ -156,10 +225,36 @@
folder = interest.getCompFromBackAsString (2);
else // == 4
folder = "";
+/*
+ * {
+ * "actions": [
+ * ...
+ * ],
+ *
+ * // only if there are more actions available
+ * "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-ACTION>"
+ * }
+ */
- m_actionLog->LookupActionsInFolderRecursively (debugAction, folder, offset*100, 100);
+ using namespace json_spirit;
+ Object json;
- // m_ccnx->publishData (interest, "FAIL: Not implemented", 1);
+ Array actions;
+ bool more = m_actionLog->LookupActionsInFolderRecursively
+ (boost::bind (StateServer::formatActionJson, boost::ref(actions), _1, _2, _3),
+ folder, offset*10, 10);
+
+ json.push_back (Pair ("actions", actions));
+
+ if (more)
+ {
+ Ccnx::Name more = Name (interest.getPartialName (0, interest.size () - 1))(offset + 1);
+ json.push_back (Pair ("more", lexical_cast<string> (more)));
+ }
+
+ ostringstream os;
+ write_stream (Value (json), os, pretty_print | raw_utf8);
+ m_ccnx->publishData (interest, os.str (), 1);
}
catch (Ccnx::NameException &ne)
{
diff --git a/src/state-server.h b/src/state-server.h
index 24c9b05..b4d69e0 100644
--- a/src/state-server.h
+++ b/src/state-server.h
@@ -32,6 +32,13 @@
#include <boost/thread/locks.hpp>
#include "executor.h"
+#include "../contrib/json_spirit/json_spirit_writer_template.h"
+#include "../contrib/json_spirit/json_spirit_value.h"
+
+#ifndef JSON_SPIRIT_VALUE_ENABLED
+#error Please define JSON_SPIRIT_VALUE_ENABLED for the Value type to be enabled
+#endif
+
/**
* @brief Class serving state information from ChronoShare
*
@@ -56,9 +63,44 @@
* Actions are ordered in decreasing order (latest will go first).
*
* Each data packet contains up to 100 actions.
+ *
+ * TEMPORARILY LIMIT IS REDUCED TO 10 ! (for debug purposes)
+ *
* If more items are available, application data will specify URL for the next packet
*
* @todo SPECIFY FORMAT OF THIS FIELD
+ * Format of returned data (JSON):
+ * {
+ * "actions": [
+ * {
+ * "id": {
+ * "userName": "<NDN-NAME-OF-THE-USER>",
+ * "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ * },
+ * "timestamp": "<ACTION-TIMESTAMP>",
+ * "filename": "<FILENAME>",
+ *
+ * "action": "UPDATE | DELETE",
+ *
+ * // only if update
+ * "update": {
+ * "hash": "<FILE-HASH>",
+ * "timestamp": "<FILE-TIMESTAMP>",
+ * "chmod": "<FILE-MODE>",
+ * "segNum": "<NUMBER-OF-SEGMENTS (~file size)>"
+ * },
+ *
+ * // if parent_device_name is set
+ * "parentId": {
+ * "userName": "<NDN-NAME-OF-THE-USER>",
+ * "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ * };
+ * },
+ *
+ * // only if there are more actions available
+ * "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-ACTION>"
+ * }
+ *
*
* - file
*
@@ -120,6 +162,9 @@
void
deregisterPrefixes ();
+ static void
+ formatActionJson (json_spirit::Array &actions, const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action);
+
private:
Ccnx::CcnxWrapperPtr m_ccnx;
ActionLogPtr m_actionLog;
diff --git a/wscript b/wscript
index a20b70c..5b0e6a5 100644
--- a/wscript
+++ b/wscript
@@ -104,7 +104,7 @@
conf.load('boost')
- conf.check_boost(lib='system test iostreams filesystem regex thread')
+ conf.check_boost(lib='system test iostreams filesystem regex thread date_time')
boost_version = conf.env.BOOST_VERSION.split('_')
if int(boost_version[0]) < 1 or int(boost_version[1]) < 46:
@@ -168,7 +168,7 @@
target="chronoshare",
features=['cxx'],
source = bld.path.ant_glob(['src/**/*.cc', 'src/**/*.cpp', 'src/**/*.proto']),
- use = "BOOST BOOST_FILESYSTEM SQLITE3 LOG4CXX scheduler ccnx",
+ use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 LOG4CXX scheduler ccnx",
includes = "ccnx scheduler src executor",
)
@@ -188,7 +188,7 @@
features = "qt4 cxx cxxprogram",
defines = "WAF",
source = bld.path.ant_glob(['test/*.cc']),
- use = 'BOOST_TEST BOOST_FILESYSTEM LOG4CXX SQLITE3 QTCORE QTGUI ccnx database fs_watcher chronoshare',
+ use = 'BOOST_TEST BOOST_FILESYSTEM BOOST_DATE_TIME LOG4CXX SQLITE3 QTCORE QTGUI ccnx database fs_watcher chronoshare',
includes = "ccnx scheduler src executor gui fs-watcher",
install_prefix = None,
)
@@ -207,7 +207,7 @@
defines = "WAF",
source = bld.path.ant_glob(['gui/*.cpp', 'gui/*.cc', 'gui/*.qrc']),
includes = "ccnx scheduler executor fs-watcher gui src adhoc server . ",
- use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare http_server"
+ use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare http_server"
)
if Utils.unversioned_sys_platform () == "darwin":
@@ -246,7 +246,7 @@
defines = "WAF",
source = "cmd/csd.cc",
includes = "ccnx scheduler executor gui fs-watcher src . ",
- use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare"
+ use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare"
)
dump_db = bld (
@@ -254,7 +254,7 @@
features = "cxx cxxprogram",
source = "cmd/dump-db.cc",
includes = "ccnx scheduler executor gui fs-watcher src . ",
- use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE LOG4CXX fs_watcher ccnx database chronoshare"
+ use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 QTCORE LOG4CXX fs_watcher ccnx database chronoshare"
)
from waflib import TaskGen