summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2013-05-07 16:39:50 +0200
committerPatrick Ohly <patrick.ohly@intel.com>2013-05-16 11:24:48 +0200
commitb6b75de59ba284b0497e450d2bbe76523d1ee523 (patch)
treef4e30e372d6eb766b9d0c36c82adf4eb230c6243
parentd3eee8a0390558795ace8be503b44d76be385b13 (diff)
PIM: new return value for SyncPeer(), new SyncProgress signal (FDO #63417)
The SyncPeer() result is derived from the sync statistics. To have them available, the "sync done" signal must include the SyncReport. Start and end of a sync could already be detected; "modified" signals while a sync runs depends on a new signal inside the SyncContext when switching from one cycle to the next and at the end of the last one.
-rw-r--r--src/dbus/server/client.cpp2
-rw-r--r--src/dbus/server/dbus-sync.cpp3
-rw-r--r--src/dbus/server/pim/README23
-rw-r--r--src/dbus/server/pim/manager.cpp47
-rw-r--r--src/dbus/server/pim/manager.h17
-rw-r--r--src/dbus/server/pim/pim-manager-api.txt26
-rwxr-xr-xsrc/dbus/server/pim/testpim.py93
-rw-r--r--src/dbus/server/session-common.h77
-rw-r--r--src/dbus/server/session-helper.cpp11
-rw-r--r--src/dbus/server/session-helper.h7
-rw-r--r--src/dbus/server/session.cpp27
-rw-r--r--src/dbus/server/session.h20
-rw-r--r--src/syncevo/SyncContext.cpp3
-rw-r--r--src/syncevo/SyncContext.h6
14 files changed, 315 insertions, 47 deletions
diff --git a/src/dbus/server/client.cpp b/src/dbus/server/client.cpp
index 3c100f20..37d15994 100644
--- a/src/dbus/server/client.cpp
+++ b/src/dbus/server/client.cpp
@@ -50,7 +50,7 @@ void Client::detach(Resource *resource)
// give clients a chance to query the session
m_server.delaySessionDestruction(session);
// allow other sessions to start
- session->done();
+ session->done(false);
}
}
// this will trigger removal of the resource if
diff --git a/src/dbus/server/dbus-sync.cpp b/src/dbus/server/dbus-sync.cpp
index 87ebdc19..5303234d 100644
--- a/src/dbus/server/dbus-sync.cpp
+++ b/src/dbus/server/dbus-sync.cpp
@@ -90,6 +90,9 @@ DBusSync::DBusSync(const SessionCommon::SyncParams &params,
SYNC_NONE,
0, 0, 0);
}
+
+ // Forward the SourceSyncedSignal via D-Bus.
+ m_sourceSyncedSignal.connect(boost::bind(m_helper.emitSourceSynced, _1, _2));
}
DBusSync::~DBusSync()
diff --git a/src/dbus/server/pim/README b/src/dbus/server/pim/README
index 97d53a39..e351e0a8 100644
--- a/src/dbus/server/pim/README
+++ b/src/dbus/server/pim/README
@@ -247,6 +247,29 @@ Not supported via the API at the moment:
- selecting a specific phone address book
- selecting which vCard properties get cached
+Syncing
+=======
+
+SetSync() in SyncEvolution will return a dict with all of the
+following entries set:
+ "modified": boolean - data was modified
+ "added" : integer - number of new contacts
+ "updated" : integer - number of updated contacts
+ "removed" : integer - number of deleted contacts
+
+In other words, the caller can reliably detect when nothing changed,
+but when contacts were modified or added, it needs to read them to
+determine which kind of properties were modified or added.
+
+The SyncProgress is triggered by SyncEvolution with three different
+keys (in this order, with "modified" occuring zero or more times):
+ "started" "modified"* "done"
+
+"started" and "done" send an empty data dictionary. "modified" sends
+the same dictionary as the one returned by SyncPeer(), if contact data
+was modified. So by definition, "modified" will be True in the
+dictionary, but is included anyway for the sake of consistency.
+
Contact Data
============
diff --git a/src/dbus/server/pim/manager.cpp b/src/dbus/server/pim/manager.cpp
index 1b8a949a..2ed3fbe2 100644
--- a/src/dbus/server/pim/manager.cpp
+++ b/src/dbus/server/pim/manager.cpp
@@ -80,7 +80,8 @@ Manager::Manager(const boost::shared_ptr<Server> &server) :
MANAGER_IFACE),
m_mainThread(g_thread_self()),
m_server(server),
- m_locale(LocaleFactory::createFactory())
+ m_locale(LocaleFactory::createFactory()),
+ emitSyncProgress(*this, "SyncProgress")
{
}
@@ -143,6 +144,7 @@ void Manager::init()
add(this, &Manager::addContact, "AddContact");
add(this, &Manager::modifyContact, "ModifyContact");
add(this, &Manager::removeContact, "RemoveContact");
+ add(emitSyncProgress);
// Ready, make it visible via D-Bus.
activate();
@@ -1298,7 +1300,7 @@ void Manager::doRemovePeer(const boost::shared_ptr<Session> &session,
result->done();
}
-void Manager::syncPeer(const boost::shared_ptr<GDBusCXX::Result0> &result,
+void Manager::syncPeer(const boost::shared_ptr<GDBusCXX::Result1<SyncResult> > &result,
const std::string &uid)
{
checkPeerUID(uid);
@@ -1311,12 +1313,30 @@ void Manager::syncPeer(const boost::shared_ptr<GDBusCXX::Result0> &result,
boost::bind(&Manager::doSyncPeer, this, _1, result, uid));
}
-static void doneSyncPeer(const boost::shared_ptr<GDBusCXX::Result0> &result,
- SyncMLStatus status)
+static Manager::SyncResult SyncReport2Result(const SyncReport &report)
+{
+ Manager::SyncResult result;
+ int added = 0, updated = 0, removed = 0;
+ if (!report.empty()) {
+ const SyncSourceReport &source = report.begin()->second;
+ added = source.getItemStat(SyncSourceReport::ITEM_LOCAL, SyncSourceReport::ITEM_ADDED, SyncSourceReport::ITEM_TOTAL);
+ updated = source.getItemStat(SyncSourceReport::ITEM_LOCAL, SyncSourceReport::ITEM_UPDATED, SyncSourceReport::ITEM_TOTAL);
+ removed = source.getItemStat(SyncSourceReport::ITEM_LOCAL, SyncSourceReport::ITEM_REMOVED, SyncSourceReport::ITEM_TOTAL);
+ }
+ result["modified"] = added || updated || removed;
+ result["added"] = added;
+ result["updated"] = updated;
+ result["removed"] = removed;
+ return result;
+}
+
+static void doneSyncPeer(const boost::shared_ptr<GDBusCXX::Result1<Manager::SyncResult> > &result,
+ SyncMLStatus status,
+ const SyncReport &report)
{
if (status == STATUS_OK ||
status == STATUS_HTTP_OK) {
- result->done();
+ result->done(SyncReport2Result(report));
} else if (status == (SyncMLStatus)sysync::LOCERR_USERABORT) {
result->failed(GDBusCXX::dbus_error(MANAGER_ERROR_ABORTED, "running sync aborted, probably by StopSync()"));
} else {
@@ -1325,15 +1345,28 @@ static void doneSyncPeer(const boost::shared_ptr<GDBusCXX::Result0> &result,
}
void Manager::doSyncPeer(const boost::shared_ptr<Session> &session,
- const boost::shared_ptr<GDBusCXX::Result0> &result,
+ const boost::shared_ptr<GDBusCXX::Result1<SyncResult> > &result,
const std::string &uid)
{
+ // Keep client informed about progress.
+ emitSyncProgress(uid, "started", SyncResult());
+ session->m_doneSignal.connect(boost::bind(boost::ref(emitSyncProgress), uid, "done", SyncResult()));
+ session->m_sourceSynced.connect(boost::bind(&Manager::report2SyncProgress, m_self, uid, _1, _2));
// After sync(), the session is tracked as the active sync session
// by the server. It was removed from our own m_pending list by
// doSession().
session->sync("ephemeral", SessionCommon::SourceModes_t());
// Relay result to caller when done.
- session->m_doneSignal.connect(boost::bind(doneSyncPeer, result, _1));
+ session->m_doneSignal.connect(boost::bind(doneSyncPeer, result, _1, _2));
+}
+
+void Manager::report2SyncProgress(const std::string &uid,
+ const std::string &sourceName,
+ const SyncSourceReport &source)
+{
+ SyncReport report;
+ report.addSyncSourceReport("foo", source);
+ emitSyncProgress(uid, "modified", SyncReport2Result(report));
}
void Manager::stopSync(const boost::shared_ptr<GDBusCXX::Result0> &result,
diff --git a/src/dbus/server/pim/manager.h b/src/dbus/server/pim/manager.h
index 884407e6..ef838061 100644
--- a/src/dbus/server/pim/manager.h
+++ b/src/dbus/server/pim/manager.h
@@ -28,6 +28,7 @@
#include "folks.h"
#include "locale-factory.h"
#include "../server.h"
+#include "../session.h"
#include <syncevo/EDSClient.h>
#include <syncevo/declarations.h>
@@ -135,14 +136,26 @@ class Manager : public GDBusCXX::DBusObjectHelper
const std::string &uid);
public:
+ typedef std::map<std::string, boost::variant<bool, int> > SyncResult;
/** Manager.SyncPeer() */
- void syncPeer(const boost::shared_ptr<GDBusCXX::Result0> &result,
+ void syncPeer(const boost::shared_ptr<GDBusCXX::Result1<SyncResult> > &result,
const std::string &uid);
+
+ /** Manager.SyncProgress */
+ GDBusCXX::EmitSignal3<const std::string &,
+ const std::string &,
+ const SyncResult &> emitSyncProgress;
+
+
private:
void doSyncPeer(const boost::shared_ptr<Session> &session,
- const boost::shared_ptr<GDBusCXX::Result0> &result,
+ const boost::shared_ptr<GDBusCXX::Result1<SyncResult> > &result,
const std::string &uid);
+ void report2SyncProgress(const std::string &uid,
+ const std::string &sourceName,
+ const SyncSourceReport &source);
+
public:
/** Manager.StopSync() */
void stopSync(const boost::shared_ptr<GDBusCXX::Result0> &result,
diff --git a/src/dbus/server/pim/pim-manager-api.txt b/src/dbus/server/pim/pim-manager-api.txt
index 7b28643b..3283c7c4 100644
--- a/src/dbus/server/pim/pim-manager-api.txt
+++ b/src/dbus/server/pim/pim-manager-api.txt
@@ -238,13 +238,21 @@ Methods:
part of the active address books, it will be removed
automatically.
- void SyncPeer(string uid)
+ dict SyncPeer(string uid)
Retrieve contacts from the peer and ensure that the local
cache is identical to the address book of the peer. The call
- returns once the operation is complete. Only if there was no
- error can the caller assume that the cache is up-to-date.
- Otherwise it is in an undefined state.
+ returns once the operation is complete.
+
+ Only if there was no error can the caller assume that the cache
+ is up-to-date. In this case, a string to variant dictionary
+ is returned which provided additional information about the sync.
+ The content of the dictionary is implementation dependent.
+
+ If the call fails, no dictionary is returned and the local
+ cache may or may not be up-to-date. It may or may not have been
+ updated. The caller needs to check the local cache to find out
+ what it contains.
void StopSync(string uid)
@@ -307,6 +315,16 @@ Methods:
Remove the contact and all of its associated data (like the
photo, if the photo file is owned by the contact storage).
+Signals:
+
+ SyncProgress(string uid, string event, dict data)
+
+ Provides information about a running sync for the peer with
+ the given "uid". The "event" string describes what happened
+ and the "data" dictionary provides further information about
+ it with a mapping from event specific string keys to variants
+ as value.
+
Service: org._01.pim.contacts
Interface: org._01.pim.contacts.ViewControl
diff --git a/src/dbus/server/pim/testpim.py b/src/dbus/server/pim/testpim.py
index 6f384b75..9e5efc58 100755
--- a/src/dbus/server/pim/testpim.py
+++ b/src/dbus/server/pim/testpim.py
@@ -649,6 +649,24 @@ END:VCARD(\r|\n)*''',
expected = sources.copy()
peers = {}
+ syncProgress = []
+ signal = bus.add_signal_receiver(lambda uid, event, data: (logging.printf('received SyncProgress: %s, %s, %s', uid, event, data), syncProgress.append((uid, event, data)), logging.printf('progress %s' % syncProgress)),
+ 'SyncProgress',
+ 'org._01.pim.contacts.Manager',
+ None, #'org._01.pim.contacts',
+ '/org/01/pim/contacts',
+ byte_arrays=True,
+ utf8_strings=True)
+ def checkSync(expected, result):
+ self.assertEqual(expected, result)
+ while not (uid, 'done', {}) in syncProgress:
+ self.loopIteration('added signal')
+ self.assertEqual([(uid, 'started', {}),
+ (uid, 'modified', expected),
+ (uid, 'done', {})],
+ syncProgress)
+
+
# Must be the Bluetooth MAC address (like A0:4E:04:1E:AD:30)
# of a phone which is paired, currently connected, and
# supports both PBAP and SyncML. SyncML is needed for putting
@@ -707,7 +725,9 @@ END:VCARD(\r|\n)*''',
# Remember current list of files and modification time stamp.
files = listsyncevo()
- # Remove all data locally.
+ # Remove all data locally. There may or may not have been data
+ # locally, because the database of the peer might have existed
+ # from previous tests.
self.manager.SyncPeer(uid,
timeout=self.timeout)
# TODO: check that syncPhone() really used PBAP - but how?
@@ -817,8 +837,14 @@ END:VCARD
output.write(john)
output.close()
self.syncPhone(phone, uid)
- self.manager.SyncPeer(uid,
- timeout=self.timeout)
+ syncProgress = []
+ result = self.manager.SyncPeer(uid,
+ timeout=self.timeout)
+ checkSync({'modified': True,
+ 'added': 1,
+ 'updated': 0,
+ 'removed': 0},
+ result)
# Also exclude modified database files.
self.assertEqual(files, listsyncevo(exclude=exclude))
@@ -834,16 +860,28 @@ END:VCARD
peers[uid],
timeout=self.timeout)
files = listsyncevo(exclude=exclude)
- self.manager.SyncPeer(uid,
- timeout=self.timeout)
+ syncProgress = []
+ result = self.manager.SyncPeer(uid,
+ timeout=self.timeout)
+ checkSync({'modified': False,
+ 'added': 0,
+ 'updated': 0,
+ 'removed': 0},
+ result)
exclude.append(logdir + '(/$)')
self.assertEqual(files, listsyncevo(exclude=exclude))
self.assertEqual(2, len(os.listdir(logdir)))
# At most one!
- self.manager.SyncPeer(uid,
- timeout=self.timeout)
+ syncProgress = []
+ result = self.manager.SyncPeer(uid,
+ timeout=self.timeout)
+ checkSync({'modified': False,
+ 'added': 0,
+ 'updated': 0,
+ 'removed': 0},
+ result)
exclude.append(logdir + '(/$)')
self.assertEqual(files, listsyncevo(exclude=exclude))
self.assertEqual(2, len(os.listdir(logdir)))
@@ -854,12 +892,49 @@ END:VCARD
peers[uid],
timeout=self.timeout)
files = listsyncevo(exclude=exclude)
- self.manager.SyncPeer(uid,
- timeout=self.timeout)
+ syncProgress = []
+ result = self.manager.SyncPeer(uid,
+ timeout=self.timeout)
+ checkSync({'modified': False,
+ 'added': 0,
+ 'updated': 0,
+ 'removed': 0},
+ result)
exclude.append(logdir + '(/$)')
self.assertEqual(files, listsyncevo(exclude=exclude))
self.assertEqual(4, len(os.listdir(logdir)))
+ # Update contact.
+ john = '''BEGIN:VCARD
+VERSION:3.0
+FN:John Doe
+N:Doe;John
+END:VCARD'''
+ output = open(item, "w")
+ output.write(john)
+ output.close()
+ self.syncPhone(phone, uid)
+ syncProgress = []
+ result = self.manager.SyncPeer(uid,
+ timeout=self.timeout)
+ checkSync({'modified': True,
+ 'added': 0,
+ 'updated': 1,
+ 'removed': 0},
+ result)
+
+ # Remove contact.
+ os.unlink(item)
+ self.syncPhone(phone, uid)
+ syncProgress = []
+ result = self.manager.SyncPeer(uid,
+ timeout=self.timeout)
+ checkSync({'modified': True,
+ 'added': 0,
+ 'updated': 0,
+ 'removed': 1},
+ result)
+
# Test invalid maxsession values.
with self.assertRaisesRegexp(dbus.DBusException,
"negative 'maxsessions' not allowed: -1"):
diff --git a/src/dbus/server/session-common.h b/src/dbus/server/session-common.h
index ce8bb1c4..83fe246c 100644
--- a/src/dbus/server/session-common.h
+++ b/src/dbus/server/session-common.h
@@ -195,6 +195,83 @@ namespace GDBusCXX {
base::append(builder, array);
}
};
+
+ template <> struct dbus_traits<SyncReport> :
+ public dbus_traits< std::string >
+ {
+ typedef dbus_traits< std::string > base;
+
+ typedef SyncReport host_type;
+ typedef const SyncReport &arg_type;
+
+#ifdef GDBUS_CXX_GIO
+ static void get(GDBusCXX::ExtractArgs &context,
+ GDBusCXX::reader_type &iter, host_type &report)
+ {
+ std::string dump;
+ base::get(context, iter, dump);
+ report = SyncReport(dump);
+ }
+#else
+ static void get(GDBusCXX::connection_type *conn, GDBusCXX::message_type *msg,
+ GDBusCXX::reader_type &iter, host_type &report)
+ {
+ std::string dump;
+ base::get(conn, msg, iter, dump);
+ report = SyncReport(dump);
+ }
+#endif
+
+ static void append(GDBusCXX::builder_type &builder, arg_type report)
+ {
+ base::append(builder, report.toString());
+ }
+ };
+
+ template <> struct dbus_traits<SyncSourceReport> :
+ public dbus_traits< std::string >
+ {
+ typedef dbus_traits< std::string > base;
+
+ typedef SyncSourceReport host_type;
+ typedef const SyncSourceReport &arg_type;
+
+#ifdef GDBUS_CXX_GIO
+ static void get(GDBusCXX::ExtractArgs &context,
+ GDBusCXX::reader_type &iter, host_type &source)
+ {
+ std::string dump;
+ base::get(context, iter, dump);
+ SyncReport report = SyncReport(dump);
+ const SyncSourceReport *foo = report.findSyncSourceReport("foo");
+ if (!foo) {
+ SE_THROW("incomplete SyncReport");
+ }
+ source = *foo;
+ }
+#else
+ static void get(GDBusCXX::connection_type *conn, GDBusCXX::message_type *msg,
+ GDBusCXX::reader_type &iter, host_type &source)
+ {
+ std::string dump;
+ base::get(conn, msg, iter, dump);
+ SyncReport report = SyncReport(dump);
+ const SyncSourceReport *foo = report.findSyncSourceReport("foo");
+ if (!foo) {
+ SE_THROW("incomplete SyncReport");
+ }
+ source = *foo;
+ }
+#endif
+
+ static void append(GDBusCXX::builder_type &builder, arg_type source)
+ {
+ SyncReport report;
+ report.addSyncSourceReport("foo", source);
+ base::append(builder, report.toString());
+ }
+ };
+
}
#endif // SESSION_COMMON_H
diff --git a/src/dbus/server/session-helper.cpp b/src/dbus/server/session-helper.cpp
index 4507576f..b84d85ca 100644
--- a/src/dbus/server/session-helper.cpp
+++ b/src/dbus/server/session-helper.cpp
@@ -125,6 +125,7 @@ SessionHelper::SessionHelper(GMainLoop *loop,
emitLogOutput(*this, "LogOutput"),
emitSyncProgress(*this, "SyncProgress"),
emitSourceProgress(*this, "SourceProgress"),
+ emitSourceSynced(*this, "SourceSynced"),
emitWaiting(*this, "Waiting"),
emitSyncSuccessStart(*this, "SyncSuccessStart"),
emitConfigChanged(*this, "ConfigChanged"),
@@ -141,6 +142,7 @@ SessionHelper::SessionHelper(GMainLoop *loop,
add(emitLogOutput);
add(emitSyncProgress);
add(emitSourceProgress);
+ add(emitSourceSynced);
add(emitWaiting);
add(emitSyncSuccessStart);
add(emitConfigChanged);
@@ -184,18 +186,19 @@ bool SessionHelper::connected()
}
void SessionHelper::sync(const SessionCommon::SyncParams &params,
- const boost::shared_ptr< GDBusCXX::Result1<bool> > &result)
+ const boost::shared_ptr< GDBusCXX::Result2<bool, SyncReport> > &result)
{
m_operation = boost::bind(&SessionHelper::doSync, this, params, result);
g_main_loop_quit(m_loop);
}
bool SessionHelper::doSync(const SessionCommon::SyncParams &params,
- const boost::shared_ptr< GDBusCXX::Result1<bool> > &result)
+ const boost::shared_ptr< GDBusCXX::Result2<bool, SyncReport> > &result)
{
try {
m_sync.reset(new DBusSync(params, *this));
- SyncMLStatus status = m_sync->sync();
+ SyncReport report;
+ SyncMLStatus status = m_sync->sync(&report);
if (status) {
// Clear the abort signal, to allow the process to send
// out the D-Bus response. Our parent will signal us again
@@ -205,7 +208,7 @@ bool SessionHelper::doSync(const SessionCommon::SyncParams &params,
"sync failed",
status);
}
- result->done(true);
+ result->done(true, report);
} catch (...) {
dbusErrorCallback(result);
}
diff --git a/src/dbus/server/session-helper.h b/src/dbus/server/session-helper.h
index e06b28fd..40d7d4af 100644
--- a/src/dbus/server/session-helper.h
+++ b/src/dbus/server/session-helper.h
@@ -57,14 +57,14 @@ class SessionHelper : public GDBusCXX::DBusObjectHelper,
/** called by main event loop: initiate a sync operation */
void sync(const SessionCommon::SyncParams &params,
- const boost::shared_ptr< GDBusCXX::Result1<bool> > &result);
+ const boost::shared_ptr< GDBusCXX::Result2<bool, SyncReport> > &result);
/**
* called by run(): do the sync operation
* @return true if the helper is meant to terminate
*/
bool doSync(const SessionCommon::SyncParams &params,
- const boost::shared_ptr< GDBusCXX::Result1<bool> > &result);
+ const boost::shared_ptr< GDBusCXX::Result2<bool, SyncReport> > &result);
void restore(const std::string &configName,
const string &dir, bool before, const std::vector<std::string> &sources,
@@ -110,6 +110,9 @@ class SessionHelper : public GDBusCXX::DBusObjectHelper,
std::string, SyncMode,
int32_t, int32_t, int32_t, true> emitSourceProgress;
+ /** SyncContext::m_sourceSyncedSignal */
+ GDBusCXX::EmitSignal2<std::string, SyncSourceReport> emitSourceSynced;
+
/** SyncContext::reportStepCmd -> true/false for "waiting on IO" */
GDBusCXX::EmitSignal1<bool, true> emitWaiting;
diff --git a/src/dbus/server/session.cpp b/src/dbus/server/session.cpp
index 8b2394c7..4662d7d4 100644
--- a/src/dbus/server/session.cpp
+++ b/src/dbus/server/session.cpp
@@ -74,6 +74,7 @@ public:
m_logOutput(*this, "LogOutput", false),
m_syncProgress(*this, "SyncProgress", false),
m_sourceProgress(*this, "SourceProgress", false),
+ m_sourceSynced(*this, "SourceSynced", false),
m_waiting(*this, "Waiting", false),
m_syncSuccessStart(*this, "SyncSuccessStart", false),
m_configChanged(*this, "ConfigChanged", false),
@@ -87,7 +88,7 @@ public:
/* GDBusCXX::DBusClientCall1<std::vector<StringMap> > m_getReports; */
/* GDBusCXX::DBusClientCall0 m_checkSource; */
/* GDBusCXX::DBusClientCall1<ReadOperations::SourceDatabases_t> m_getDatabases; */
- GDBusCXX::DBusClientCall1<bool> m_sync;
+ GDBusCXX::DBusClientCall2<bool, SyncReport> m_sync;
GDBusCXX::DBusClientCall1<bool> m_restore;
GDBusCXX::DBusClientCall1<bool> m_execute;
/* GDBusCXX::DBusClientCall0 m_serverShutdown; */
@@ -103,6 +104,7 @@ public:
GDBusCXX::SignalWatch6<sysync::TProgressEventEnum,
std::string, SyncMode,
int32_t, int32_t, int32_t> m_sourceProgress;
+ GDBusCXX::SignalWatch2<std::string, SyncSourceReport> m_sourceSynced;
GDBusCXX::SignalWatch1<bool> m_waiting;
GDBusCXX::SignalWatch0 m_syncSuccessStart;
GDBusCXX::SignalWatch0 m_configChanged;
@@ -410,7 +412,7 @@ void Session::sync2(const std::string &mode, const SessionCommon::SourceModes_t
// the error is recorded before ending the session. Premature
// exits by the helper are handled by D-Bus, which then will abort
// the pending method call.
- m_helper->m_sync.start(params, boost::bind(&Session::dbusResultCb, m_me, "sync()", _1, _2));
+ m_helper->m_sync.start(params, boost::bind(&Session::dbusResultCb, m_me, "sync()", _1, _2, _3));
}
void Session::abort()
@@ -599,7 +601,7 @@ void Session::passwordRequest(const std::string &descr, const ConfigPasswordKey
m_passwordRequest = m_server.passwordRequest(descr, key, m_me);
}
-void Session::dbusResultCb(const std::string &operation, bool success, const std::string &error) throw()
+void Session::dbusResultCb(const std::string &operation, bool success, const SyncReport &report, const std::string &error) throw()
{
PushLogger<Logger> guard(m_me);
try {
@@ -609,7 +611,7 @@ void Session::dbusResultCb(const std::string &operation, bool success, const std
success ? "<<successfully>>" :
"<<unsuccessfully>>");
if (error.empty()) {
- doneCb(success);
+ doneCb(success, report);
} else {
// Translate back into local exception, will be handled by
// catch clause and (eventually) failureCb().
@@ -656,7 +658,7 @@ void Session::failureCb() throw()
m_error = error;
}
// will fire status signal, including the error
- doneCb();
+ doneCb(false);
}
} catch (...) {
// fatal problem, log it and terminate
@@ -664,7 +666,7 @@ void Session::failureCb() throw()
}
}
-void Session::doneCb(bool success) throw()
+void Session::doneCb(bool success, const SyncReport &report) throw()
{
PushLogger<Logger> guard(m_me);
try {
@@ -695,7 +697,7 @@ void Session::doneCb(bool success) throw()
m_configName.c_str(),
m_setConfig ? "modified" : "not modified",
m_error);
- m_doneSignal((SyncMLStatus)m_error);
+ m_doneSignal((SyncMLStatus)m_error, report);
// now also kill helper
m_helper.reset();
@@ -718,7 +720,8 @@ void Session::doneCb(bool success) throw()
Session::~Session()
{
SE_LOG_DEBUG(NULL, "session %s deconstructing", getPath());
- doneCb();
+ // If we are not done yet, then something went wrong.
+ doneCb(false);
}
/** child has quit before connecting, invoke result.failed() with suitable exception pending */
@@ -911,6 +914,7 @@ void Session::onConnect(const GDBusCXX::DBusConnectionPtr &conn) throw ()
// Activate signal watch on helper signals.
m_helper->m_syncProgress.activate(boost::bind(&Session::syncProgress, this, _1, _2, _3, _4));
m_helper->m_sourceProgress.activate(boost::bind(&Session::sourceProgress, this, _1, _2, _3, _4, _5, _6));
+ m_helper->m_sourceSynced.activate(boost::bind(boost::ref(m_sourceSynced), _1, _2));
m_helper->m_waiting.activate(boost::bind(&Session::setWaiting, this, _1));
m_helper->m_syncSuccessStart.activate(boost::bind(boost::ref(Session::m_syncSuccessStartSignal)));
m_helper->m_configChanged.activate(boost::bind(boost::ref(m_server.m_configChangedSignal), ""));
@@ -958,7 +962,8 @@ void Session::onQuit(int status) throw ()
}
m_server.addTimeout(boost::bind(&Session::doneCb,
m_me,
- false),
+ false,
+ SyncReport()),
0.1 /* seconds */);
} catch (...) {
Exception::handle();
@@ -1245,7 +1250,7 @@ void Session::restore2(const string &dir, bool before, const std::vector<std::st
// helper is ready, tell it what to do
m_helper->m_restore.start(m_configName, dir, before, sources,
- boost::bind(&Session::dbusResultCb, m_me, "restore()", _1, _2));
+ boost::bind(&Session::dbusResultCb, m_me, "restore()", _1, SyncReport(), _2));
}
void Session::execute(const vector<string> &args, const map<string, string> &vars)
@@ -1276,7 +1281,7 @@ void Session::execute2(const vector<string> &args, const map<string, string> &va
// helper is ready, tell it what to do
m_helper->m_execute.start(args, vars,
- boost::bind(&Session::dbusResultCb, m_me, "execute()", _1, _2));
+ boost::bind(&Session::dbusResultCb, m_me, "execute()", _1, SyncReport(), _2));
}
/*Implementation of Session.CheckPresence */
diff --git a/src/dbus/server/session.h b/src/dbus/server/session.h
index df23f196..e1df0254 100644
--- a/src/dbus/server/session.h
+++ b/src/dbus/server/session.h
@@ -81,6 +81,9 @@ class Session : public GDBusCXX::DBusObjectHelper,
SYNC_ILLEGAL
};
+ typedef std::map<std::string, SourceStatus> SourceStatuses_t;
+ typedef std::map<std::string, SourceProgress> SourceProgresses_t;
+
private:
Server &m_server;
std::vector<std::string> m_flags;
@@ -203,11 +206,9 @@ class Session : public GDBusCXX::DBusObjectHelper,
/** progress data, holding progress calculation related info */
ProgressData m_progData;
- typedef std::map<std::string, SourceStatus> SourceStatuses_t;
SourceStatuses_t m_sourceStatus;
uint32_t m_error;
- typedef std::map<std::string, SourceProgress> SourceProgresses_t;
SourceProgresses_t m_sourceProgress;
// syncProgress() and sourceProgress() turn raw data from helper
@@ -291,6 +292,7 @@ class Session : public GDBusCXX::DBusObjectHelper,
/** like fireStatus() for progress information */
void fireProgress(bool flush = false);
+public:
/** Session.StatusChanged */
GDBusCXX::EmitSignal3<const std::string &,
uint32_t,
@@ -299,7 +301,6 @@ class Session : public GDBusCXX::DBusObjectHelper,
GDBusCXX::EmitSignal2<int32_t,
const SourceProgresses_t &> emitProgress;
-public:
/**
* Sessions must always be held in a shared pointer
* because some operations depend on that. This
@@ -323,7 +324,7 @@ public:
* explicitly mark an idle session as completed, even if it doesn't
* get deleted yet (exceptions not expected by caller)
*/
- void done() throw () { doneCb(); }
+ void done(bool success) throw () { doneCb(success); }
private:
Session(Server &server,
@@ -444,9 +445,13 @@ public:
SyncSuccessStartSignal_t m_syncSuccessStartSignal;
/** sync completed (may have failed) */
- typedef boost::signals2::signal<void (SyncMLStatus)> DoneSignal_t;
+ typedef boost::signals2::signal<void (SyncMLStatus, SyncReport)> DoneSignal_t;
DoneSignal_t m_doneSignal;
+ /** a source was synced, emitted multiple times during a multi-cycle sync */
+ typedef boost::signals2::signal<void (const std::string &, const SyncSourceReport &)> SourceSyncedSignal_t;
+ SourceSyncedSignal_t m_sourceSynced;
+
/**
* Called by server when the session is ready to run.
* Only the session itself can deactivate itself.
@@ -466,7 +471,7 @@ private:
/** set m_syncFilter and m_sourceFilters to config */
virtual bool setFilters(SyncConfig &config);
- void dbusResultCb(const std::string &operation, bool success, const std::string &error) throw();
+ void dbusResultCb(const std::string &operation, bool success, const SyncReport &report, const std::string &error) throw();
/**
* to be called inside a catch() clause: returns error for any
@@ -481,8 +486,9 @@ private:
*
* @param success if false, then ensure that m_error is set
* before finalizing the session
+ * @param report valid only in case of success
*/
- void doneCb(bool success = true) throw();
+ void doneCb(bool success, const SyncReport &report = SyncReport()) throw();
};
SE_END_CXX
diff --git a/src/syncevo/SyncContext.cpp b/src/syncevo/SyncContext.cpp
index a0344fa3..54964837 100644
--- a/src/syncevo/SyncContext.cpp
+++ b/src/syncevo/SyncContext.cpp
@@ -1768,6 +1768,8 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type,
source.recordFirstSync(extra1 == 2);
source.recordResumeSync(extra2 == 1);
} else if (SyncMode(mode) != SYNC_NONE) {
+ // Broadcast statistics before moving into next cycle.
+ m_sourceSyncedSignal(source.getName(), source);
// may happen when the source is used in multiple
// SyncML sessions; only remember the initial sync
// mode in that case and count all following syncs
@@ -3234,6 +3236,7 @@ SyncMLStatus SyncContext::sync(SyncReport *report)
// but some items failed, we report a "partial failure"
// status.
BOOST_FOREACH(SyncSource *source, sourceList) {
+ m_sourceSyncedSignal(source->getName(), *source);
if (source->getStatus() == STATUS_OK &&
(source->getItemStat(SyncSource::ITEM_LOCAL,
SyncSource::ITEM_ANY,
diff --git a/src/syncevo/SyncContext.h b/src/syncevo/SyncContext.h
index 4a4e1bfc..8269c24f 100644
--- a/src/syncevo/SyncContext.h
+++ b/src/syncevo/SyncContext.h
@@ -159,6 +159,12 @@ class SyncContext : public SyncConfig {
static InitMainSignal &GetInitMainSignal();
/**
+ * A signal invoked each time a source has gone through a sync cycle.
+ */
+ typedef boost::signals2::signal<void (const std::string &name, const SyncSourceReport &source)> SourceSyncedSignal;
+ SourceSyncedSignal m_sourceSyncedSignal;
+
+ /**
* true if binary was compiled as stable release
* (see gen-autotools.sh)
*/