summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Vrátil <dvratil@kde.org>2016-09-04 08:16:27 (GMT)
committerDaniel Vrátil <dvratil@kde.org>2016-09-04 08:39:42 (GMT)
commit93b7b841f7730bc5d5cad4a8a1bced7ef0a28032 (patch)
tree4956478c4b83200766311d97d3fa584b9dfeaab2
parente01392d98a041b8ddfac777b5842cb1a74ddcce8 (diff)
Reintroduce change notifications debugging
We now use the Notification system to introspect itself since we can now send notificatations about notifications to subscribers who are interested (like Akonadi Console). Since the debug notifications have an overhead (although a small one), we only generate them if there's at least one subscriber listenening to them.
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/changenotification.cpp118
-rw-r--r--src/core/changenotification.h83
-rw-r--r--src/core/monitor.h26
-rw-r--r--src/core/monitor_p.cpp62
-rw-r--r--src/core/monitor_p.h3
-rw-r--r--src/core/notificationsubscriber.cpp2
-rw-r--r--src/private/protocol.cpp130
-rw-r--r--src/private/protocol_p.h24
-rw-r--r--src/server/handler.cpp6
-rw-r--r--src/server/notificationmanager.cpp51
-rw-r--r--src/server/notificationmanager.h4
-rw-r--r--src/server/notificationsubscriber.cpp87
-rw-r--r--src/server/notificationsubscriber.h11
15 files changed, 575 insertions, 36 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index df5af7e..9077003 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,7 +21,7 @@ include(ECMQtDeclareLoggingCategory)
include(AkonadiMacros)
-set(PIM_VERSION "5.3.43")
+set(PIM_VERSION "5.3.44")
set(QT_REQUIRED_VERSION "5.5.0")
set(AKONADI_VERSION ${PIM_VERSION})
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 203bfb3..bb80e69 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -8,6 +8,7 @@ set(akonadicore_base_SRCS
attributefactory.cpp
cachepolicy.cpp
changemediator_p.cpp
+ changenotification.cpp
changenotificationdependenciesfactory.cpp
changerecorder.cpp
changerecorder_p.cpp
@@ -79,6 +80,7 @@ ecm_generate_headers(AkonadiCore_base_HEADERS
Attribute
AttributeFactory
CachePolicy
+ ChangeNotification
ChangeRecorder
Collection
CollectionColorAttribute
diff --git a/src/core/changenotification.cpp b/src/core/changenotification.cpp
new file mode 100644
index 0000000..58e29b7
--- /dev/null
+++ b/src/core/changenotification.cpp
@@ -0,0 +1,118 @@
+/*
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+
+#include "changenotification.h"
+#include "private/protocol_p.h"
+
+using namespace Akonadi;
+
+namespace Akonadi
+{
+
+class AKONADICORE_NO_EXPORT ChangeNotification::Private : public QSharedData
+{
+public:
+ Private()
+ : QSharedData()
+ {
+ }
+
+ Private(const Private &other)
+ : QSharedData(other)
+ , timestamp(other.timestamp)
+ , listeners(other.listeners)
+ , notification(other.notification)
+ , type(other.type)
+ {
+ }
+
+ QDateTime timestamp;
+ QVector<QByteArray> listeners;
+ Protocol::ChangeNotification notification;
+ ChangeNotification::Type type;
+};
+
+}
+
+ChangeNotification::ChangeNotification()
+ : d(new Private)
+{
+}
+
+ChangeNotification::ChangeNotification(const ChangeNotification &other)
+ : d(other.d)
+{
+}
+
+ChangeNotification::~ChangeNotification()
+{
+}
+
+ChangeNotification &ChangeNotification::operator=(const ChangeNotification &other)
+{
+ d = other.d;
+ return *this;
+}
+
+bool ChangeNotification::isValid() const
+{
+ return d->timestamp.isValid();
+}
+
+void ChangeNotification::setType(ChangeNotification::Type type)
+{
+ d->type = type;
+}
+
+ChangeNotification::Type ChangeNotification::type() const
+{
+ return d->type;
+}
+
+void ChangeNotification::setListeners(const QVector<QByteArray> &listeners)
+{
+ d->listeners = listeners;
+}
+
+QVector<QByteArray> ChangeNotification::listeners() const
+{
+ return d->listeners;
+}
+
+void ChangeNotification::setTimestamp(const QDateTime &timestamp)
+{
+ d->timestamp = timestamp;
+}
+
+QDateTime ChangeNotification::timestamp() const
+{
+ return d->timestamp;
+}
+
+Protocol::ChangeNotification ChangeNotification::notification() const
+{
+ return d->notification;
+}
+
+void ChangeNotification::setNotification(const Protocol::ChangeNotification &ntf)
+{
+ d->notification = ntf;
+}
+
diff --git a/src/core/changenotification.h b/src/core/changenotification.h
new file mode 100644
index 0000000..424dbcb
--- /dev/null
+++ b/src/core/changenotification.h
@@ -0,0 +1,83 @@
+/*
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#ifndef AKONADI_CHANGENOTIFICATION_H
+#define AKONADI_CHANGENOTIFICATION_H
+
+#include <QDateTime>
+#include <QVector>
+#include <QSharedDataPointer>
+
+#include <akonadicore_export.h>
+
+namespace Akonadi
+{
+namespace Protocol
+{
+class ChangeNotification;
+}
+
+/**
+ * Emitted by Monitor::debugNotification() signal.
+ *
+ * This is purely for debugging purposes and should never be used in regular
+ * applications.
+ *
+ * @since 5.4
+ */
+class AKONADICORE_EXPORT ChangeNotification
+{
+public:
+ enum Type {
+ Items,
+ Collection,
+ Tag,
+ Relation,
+ Subscription
+ };
+
+ explicit ChangeNotification();
+ ChangeNotification(const ChangeNotification &other);
+ ~ChangeNotification();
+
+ ChangeNotification &operator=(const ChangeNotification &other);
+
+ bool isValid() const;
+
+ QDateTime timestamp() const;
+ void setTimestamp(const QDateTime &timestamp);
+
+ QVector<QByteArray> listeners() const;
+ void setListeners(const QVector<QByteArray> &listeners);
+
+ Type type() const;
+ void setType(Type type);
+
+ Protocol::ChangeNotification notification() const;
+ void setNotification(const Protocol::ChangeNotification &ntf);
+
+private:
+ class Private;
+ QSharedDataPointer<Private> d;
+};
+
+}
+
+
+#endif
diff --git a/src/core/monitor.h b/src/core/monitor.h
index 4903995..59a9e31 100644
--- a/src/core/monitor.h
+++ b/src/core/monitor.h
@@ -39,6 +39,7 @@ class MonitorPrivate;
class Session;
class TagFetchScope;
class NotificationSubscriber;
+class ChangeNotification;
namespace Protocol
{
@@ -99,7 +100,18 @@ public:
* applications.
* @since 5.4
*/
- Subscribers
+ Subscribers,
+ /**
+ * Listens to all notifications being emitted by the server and provides
+ * additional information about them. This is only for debugging purposes
+ * and should not be used in real applications.
+ *
+ * @note Enabling monitoring this type has performance impact on the
+ * Akonadi Server.
+ *
+ * @since 5.4
+ */
+ Notifications
};
/**
@@ -721,6 +733,18 @@ Q_SIGNALS:
void notificationSubscriberRemoved(const Akonadi::NotificationSubscriber &subscriber);
/**
+ * This signal is emitted when Notifications are monitored and the server emits
+ * anny change notification.
+ *
+ * @since 5.4
+ *
+ * @note Getting introspection into all change notifications only makes sense
+ * if you want to globally debug Notifications. There is no reason to use
+ * this in regular applications.
+ */
+ void debugNotification(const Akonadi::ChangeNotification &notification);
+
+ /**
* This signal is emitted if the Monitor starts or stops monitoring @p collection explicitly.
* @param collection The collection
* @param monitored Whether the collection is now being monitored or not.
diff --git a/src/core/monitor_p.cpp b/src/core/monitor_p.cpp
index 9ed72d7..93a6698 100644
--- a/src/core/monitor_p.cpp
+++ b/src/core/monitor_p.cpp
@@ -31,6 +31,7 @@
#include "vectorhelper.h"
#include "akonadicore_debug.h"
#include "notificationsubscriber.h"
+#include "changenotification.h"
using namespace Akonadi;
class operation;
@@ -63,6 +64,8 @@ MonitorPrivate::~MonitorPrivate()
delete collectionCache;
delete itemCache;
delete tagCache;
+ ntfConnection->disconnect(q_ptr);
+ ntfConnection->deleteLater();
}
void MonitorPrivate::init()
@@ -345,6 +348,10 @@ bool MonitorPrivate::ensureDataAvailable(const Protocol::ChangeNotification &msg
return true;
}
+ if (msg.type() == Protocol::Command::DebugChangeNotification) {
+ return true;
+ }
+
if (msg.type() == Protocol::Command::CollectionChangeNotification
&& static_cast<const Protocol::CollectionChangeNotification&>(msg).operation() == Protocol::CollectionChangeNotification::Remove) {
//For collection removals the collection is gone anyways, so we can't fetch it. Rid will be set later on instead.
@@ -490,6 +497,34 @@ bool MonitorPrivate::emitNotification(const Protocol::ChangeNotification &msg)
subscriber.setIsAllMonitored(subNtf.isAllMonitored());
subscriber.setIsExclusive(subNtf.isExclusive());
someoneWasListening = emitSubscriptionChangeNotification(subNtf, subscriber);
+ } else if (msg.type() == Protocol::Command::DebugChangeNotification) {
+ const auto &changeNtf = static_cast<const Protocol::DebugChangeNotification&>(msg);
+ ChangeNotification notification;
+ notification.setListeners(changeNtf.listeners());
+ notification.setTimestamp(QDateTime::fromMSecsSinceEpoch(changeNtf.timestamp()));
+ notification.setNotification(changeNtf.notification());
+ switch (changeNtf.notification().type()) {
+ case Protocol::Command::ItemChangeNotification:
+ notification.setType(ChangeNotification::Items);
+ break;
+ case Protocol::Command::CollectionChangeNotification:
+ notification.setType(ChangeNotification::Collection);
+ break;
+ case Protocol::Command::TagChangeNotification:
+ notification.setType(ChangeNotification::Tag);
+ break;
+ case Protocol::Command::RelationChangeNotification:
+ notification.setType(ChangeNotification::Relation);
+ break;
+ case Protocol::Command::SubscriptionChangeNotification:
+ notification.setType(ChangeNotification::Subscription);
+ break;
+ default:
+ Q_ASSERT(false); // huh?
+ return false;
+ }
+
+ someoneWasListening = emitDebugChangeNotification(changeNtf, notification);
}
return someoneWasListening;
@@ -653,6 +688,15 @@ int MonitorPrivate::translateAndCompress(QQueue<Protocol::ChangeNotification> &n
void MonitorPrivate::commandReceived(qint64 tag, const Protocol::Command &command)
{
+ QByteArray subname = session->sessionId() + " - ";
+ if (!q_ptr->objectName().isEmpty()) {
+ subname += q_ptr->objectName().toLatin1();
+ } else {
+ subname += QByteArray::number(quintptr(q_ptr));
+ }
+ qDebug() << "=================== NOTIFY" << subname << command.type();
+
+
Q_Q(Monitor);
Q_UNUSED(tag);
if (command.isResponse()) {
@@ -698,6 +742,7 @@ void MonitorPrivate::commandReceived(qint64 tag, const Protocol::Command &comman
case Protocol::Command::TagChangeNotification:
case Protocol::Command::RelationChangeNotification:
case Protocol::Command::SubscriptionChangeNotification:
+ case Protocol::Command::DebugChangeNotification:
slotNotify(command);
break;
default:
@@ -1199,6 +1244,23 @@ bool MonitorPrivate::emitSubscriptionChangeNotification(const Protocol::Subscrip
return false;
}
+bool MonitorPrivate::emitDebugChangeNotification(const Protocol::DebugChangeNotification &msg,
+ const ChangeNotification &ntf)
+{
+ Q_UNUSED(msg);
+
+ if (!ntf.isValid()) {
+ return false;
+ }
+
+ if (q_ptr->receivers(SIGNAL(debugNotification(Akonadi::ChangeNotification))) == 0) {
+ return false;
+ }
+ Q_EMIT q_ptr->debugNotification(ntf);
+ return true;
+}
+
+
void MonitorPrivate::invalidateCaches(const Protocol::ChangeNotification &msg)
{
diff --git a/src/core/monitor_p.h b/src/core/monitor_p.h
index 73f7127..48a487c 100644
--- a/src/core/monitor_p.h
+++ b/src/core/monitor_p.h
@@ -46,6 +46,7 @@ namespace Akonadi
{
class Monitor;
+class ChangeNotification;
/**
* @internal
@@ -163,6 +164,8 @@ public:
bool emitSubscriptionChangeNotification(const Protocol::SubscriptionChangeNotification &msg,
const NotificationSubscriber &subscriber);
+ bool emitDebugChangeNotification(const Protocol::DebugChangeNotification &msg,
+ const ChangeNotification &ntf);
void serverStateChanged(Akonadi::ServerManager::State state);
diff --git a/src/core/notificationsubscriber.cpp b/src/core/notificationsubscriber.cpp
index f54846b..72f5480 100644
--- a/src/core/notificationsubscriber.cpp
+++ b/src/core/notificationsubscriber.cpp
@@ -21,7 +21,7 @@
namespace Akonadi
{
-class NotificationSubscriber::Private : public QSharedData
+class AKONADICORE_NO_EXPORT NotificationSubscriber::Private : public QSharedData
{
public:
explicit Private()
diff --git a/src/private/protocol.cpp b/src/private/protocol.cpp
index 7b18586..3597879 100644
--- a/src/private/protocol.cpp
+++ b/src/private/protocol.cpp
@@ -49,7 +49,7 @@ namespace Akonadi {
namespace Protocol {
int version() {
- return 54;
+ return 55;
}
}
@@ -142,6 +142,8 @@ QDebug operator<<(QDebug _dbg, Akonadi::Protocol::Command::Type type)
return dbg << "RelationChangeNotification";
case Akonadi::Protocol::Command::SubscriptionChangeNotification:
return dbg << "SubscriptionChangeNotification";
+ case Akonadi::Protocol::Command::DebugChangeNotification:
+ return dbg << "DebugChangeNotification";
case Akonadi::Protocol::Command::CreateSubscription:
return dbg << "CreateSubscription";
case Akonadi::Protocol::Command::ModifySubscription:
@@ -570,6 +572,7 @@ public:
registerType<Command::TagChangeNotification, TagChangeNotification, Response /* invalid */>();
registerType<Command::RelationChangeNotification, RelationChangeNotification, Response /* invalid */>();
registerType<Command::SubscriptionChangeNotification, SubscriptionChangeNotification, Response /* invalid */>();
+ registerType<Command::DebugChangeNotification, DebugChangeNotification, Response /* invalid */>();
registerType<Command::CreateSubscription, CreateSubscriptionCommand, CreateSubscriptionResponse>();
registerType<Command::ModifySubscription, ModifySubscriptionCommand, ModifySubscriptionResponse>();
}
@@ -8237,6 +8240,8 @@ bool ChangeNotification::isRemove() const
return static_cast<const Protocol::RelationChangeNotification*>(this)->operation() == RelationChangeNotification::Remove;
case Command::SubscriptionChangeNotification:
return static_cast<const Protocol::SubscriptionChangeNotification*>(this)->operation() == SubscriptionChangeNotification::Remove;
+ case Command::DebugChangeNotification:
+ return false;
default:
Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
}
@@ -8256,6 +8261,7 @@ bool ChangeNotification::isMove() const
case Command::TagChangeNotification:
case Command::RelationChangeNotification:
case Command::SubscriptionChangeNotification:
+ case Command::DebugChangeNotification:
return false;
default:
Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
@@ -10102,5 +10108,127 @@ DataStream &operator>>(DataStream &stream, SubscriptionChangeNotification &ntf)
}
+class DebugChangeNotificationPrivate : public ChangeNotificationPrivate
+{
+public:
+ explicit DebugChangeNotificationPrivate()
+ : ChangeNotificationPrivate(Command::DebugChangeNotification)
+ , timestamp(0)
+ {
+ }
+
+ DebugChangeNotificationPrivate(const DebugChangeNotificationPrivate &other)
+ : ChangeNotificationPrivate(other)
+ , notification(other.notification)
+ , listeners(other.listeners)
+ , timestamp(other.timestamp)
+ {
+ }
+
+ bool compare(const CommandPrivate * other) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::compare(other)
+ || COMPARE(notification)
+ || COMPARE(listeners)
+ || COMPARE(timestamp);
+ }
+
+ void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
+ {
+ blck.beginBlock("Notification");
+ notification.debugString(blck);
+ blck.endBlock();
+ blck.write("Listeners", listeners);
+ blck.write("Timestamp", timestamp);
+ }
+
+ DataStream & deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ Command::Type type;
+ ChangeNotificationPrivate::deserialize(stream)
+ >> type;
+ auto ntf = Factory::command(type);
+ stream >> ntf
+ >> listeners
+ >> timestamp;
+ notification = ntf;
+ return stream;
+ }
+
+ DataStream & serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ ChangeNotificationPrivate::serialize(stream)
+ << notification.type()
+ << notification
+ << listeners
+ << timestamp;
+ return stream;
+ }
+
+ CommandPrivate * clone() const Q_DECL_OVERRIDE
+ {
+ return new DebugChangeNotificationPrivate(*this);
+ }
+
+ ChangeNotification notification;
+ QVector<QByteArray> listeners;
+ qint64 timestamp;
+};
+
+AKONADI_DECLARE_PRIVATE(DebugChangeNotification)
+
+DebugChangeNotification::DebugChangeNotification()
+ : ChangeNotification(new DebugChangeNotificationPrivate)
+{
+}
+
+DebugChangeNotification::DebugChangeNotification(const Command &other)
+ : ChangeNotification(other)
+{
+ checkCopyInvariant(Command::DebugChangeNotification);
+}
+
+ChangeNotification DebugChangeNotification::notification() const
+{
+ return d_func()->notification;
+}
+
+void DebugChangeNotification::setNotification(const ChangeNotification &notification)
+{
+ d_func()->notification = notification;
+}
+
+QVector<QByteArray> DebugChangeNotification::listeners() const
+{
+ return d_func()->listeners;
+}
+
+void DebugChangeNotification::setListeners(const QVector<QByteArray> &listeners)
+{
+ d_func()->listeners = listeners;
+}
+
+qint64 DebugChangeNotification::timestamp() const
+{
+ return d_func()->timestamp;
+}
+
+void DebugChangeNotification::setTimestamp(qint64 timestamp)
+{
+ d_func()->timestamp = timestamp;
+}
+
+DataStream &operator<<(DataStream &stream, const DebugChangeNotification &ntf)
+{
+ return ntf.d_func()->serialize(stream);
+}
+
+DataStream &operator>>(DataStream &stream, DebugChangeNotification &ntf)
+{
+ return ntf.d_func()->deserialize(stream);
+}
+
+
+
} // namespace Protocol
} // namespace Akonadi
diff --git a/src/private/protocol_p.h b/src/private/protocol_p.h
index 5ff064e..97ec4ef 100644
--- a/src/private/protocol_p.h
+++ b/src/private/protocol_p.h
@@ -136,6 +136,7 @@ public:
TagChangeNotification,
RelationChangeNotification,
SubscriptionChangeNotification,
+ DebugChangeNotification,
CreateSubscription,
ModifySubscription,
@@ -2379,7 +2380,8 @@ public:
CollectionChanges,
TagChanges,
RelationChanges,
- SubscriptionChanges
+ SubscriptionChanges,
+ ChangeNotifications,
};
explicit ModifySubscriptionCommand();
@@ -2517,8 +2519,28 @@ private:
+class DebugChangeNotificationPrivate;
+class AKONADIPRIVATE_EXPORT DebugChangeNotification : public ChangeNotification
+{
+public:
+ explicit DebugChangeNotification();
+ DebugChangeNotification(const Command &other);
+
+ ChangeNotification notification() const;
+ void setNotification(const ChangeNotification &notification);
+
+ QVector<QByteArray> listeners() const;
+ void setListeners(const QVector<QByteArray> &listeners);
+ qint64 timestamp() const;
+ void setTimestamp(qint64 timestamp);
+private:
+ AKONADI_DECLARE_PRIVATE(DebugChangeNotification)
+
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::DebugChangeNotification &ntf);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::DebugChangeNotification &ntf);
+};
} // namespace Protocol
} // namespace Akonadi
diff --git a/src/server/handler.cpp b/src/server/handler.cpp
index d23ae40..a0b5bb1 100644
--- a/src/server/handler.cpp
+++ b/src/server/handler.cpp
@@ -213,6 +213,12 @@ Handler *Handler::findHandlerForCommandAuthenticated(Protocol::Command::Type cmd
Q_ASSERT_X(cmd != Protocol::Command::RelationChangeNotification, __FUNCTION__,
"RelationChangeNotification command is not allowed on this connection");
return Q_NULLPTR;
+ case Protocol::Command::SubscriptionChangeNotification:
+ Q_ASSERT_X(cmd != Protocol::Command::SubscriptionChangeNotification, __FUNCTION__,
+ "SubscriptionChangeNotification command is not allowed on this connection");
+ case Protocol::Command::DebugChangeNotification:
+ Q_ASSERT_X(cmd != Protocol::Command::DebugChangeNotification, __FUNCTION__,
+ "DebugChangeNotification command is not allowed on this connection");
case Protocol::Command::ModifySubscription:
Q_ASSERT_X(cmd != Protocol::Command::ModifySubscription, __FUNCTION__,
"ModifySubscription command is not allowed on this connection");
diff --git a/src/server/notificationmanager.cpp b/src/server/notificationmanager.cpp
index f3c1010..ed7da6a 100644
--- a/src/server/notificationmanager.cpp
+++ b/src/server/notificationmanager.cpp
@@ -32,12 +32,16 @@
#include <QCoreApplication>
#include <QThreadPool>
#include <QPointer>
+#include <QDateTime>
using namespace Akonadi;
using namespace Akonadi::Server;
NotificationManager::NotificationManager()
: AkThread()
+ , mTimer(Q_NULLPTR)
+ , mNotifyThreadPool(Q_NULLPTR)
+ , mDebugNotifications(0)
{
}
@@ -74,11 +78,21 @@ void NotificationManager::registerConnection(quintptr socketDescriptor)
Q_ASSERT(thread() == QThread::currentThread());
NotificationSubscriber *subscriber = new NotificationSubscriber(this, socketDescriptor);
- qDebug() << "NotificationManager: new connection (registered as" << subscriber << ")";
+ qCDebug(AKONADISERVER_LOG) << "New notification connection (registered as" << subscriber << ")";
connect(subscriber, &QObject::destroyed,
[this, subscriber]() {
mSubscribers.removeOne(subscriber);
});
+ connect(subscriber, &NotificationSubscriber::notificationDebuggingChanged,
+ this, [this](bool enabled) {
+ if (enabled) {
+ ++mDebugNotifications;
+ } else {
+ --mDebugNotifications;
+ }
+ Q_ASSERT(mDebugNotifications >= 0);
+ Q_ASSERT(mDebugNotifications <= mSubscribers.count());
+ });
mSubscribers.push_back(subscriber);
}
@@ -121,7 +135,9 @@ public:
void run() Q_DECL_OVERRIDE
{
if (mSubscriber) {
- mSubscriber->notify(mNotifications);
+ Q_FOREACH (const auto &ntf, mNotifications) {
+ mSubscriber->notify(ntf);
+ }
}
}
@@ -136,9 +152,36 @@ void NotificationManager::emitPendingNotifications()
return;
}
- Q_FOREACH (NotificationSubscriber *subscriber, mSubscribers) {
- mNotifyThreadPool->start(new NotifyRunnable(subscriber, mNotifications));
+ if (mDebugNotifications == 0) {
+ Q_FOREACH (NotificationSubscriber *subscriber, mSubscribers) {
+ mNotifyThreadPool->start(new NotifyRunnable(subscriber, mNotifications));
+ }
+ } else {
+ // When debugging notification we have to use a non-threaded approach
+ // so that we can work with return value of notify()
+ Q_FOREACH (const auto &notification, mNotifications) {
+ QVector<QByteArray> listeners;
+ Q_FOREACH (NotificationSubscriber *subscriber, mSubscribers) {
+ if (subscriber->notify(notification)) {
+ listeners.push_back(subscriber->subscriber());
+ }
+ }
+
+ emitDebugNotification(notification, listeners);
+ }
}
mNotifications.clear();
}
+
+void NotificationManager::emitDebugNotification(const Protocol::ChangeNotification &ntf,
+ const QVector<QByteArray> &listeners)
+{
+ Protocol::DebugChangeNotification debugNtf;
+ debugNtf.setNotification(ntf);
+ debugNtf.setListeners(listeners);
+ debugNtf.setTimestamp(QDateTime::currentMSecsSinceEpoch());
+ Q_FOREACH (NotificationSubscriber *subscriber, mSubscribers) {
+ mNotifyThreadPool->start(new NotifyRunnable(subscriber, { debugNtf }));
+ }
+}
diff --git a/src/server/notificationmanager.h b/src/server/notificationmanager.h
index 69bc0e4..8813c96 100644
--- a/src/server/notificationmanager.h
+++ b/src/server/notificationmanager.h
@@ -58,12 +58,16 @@ protected:
void init() Q_DECL_OVERRIDE;
void quit() Q_DECL_OVERRIDE;
+ void emitDebugNotification(const Protocol::ChangeNotification &ntf,
+ const QVector<QByteArray> &listeners);
+
private:
Protocol::ChangeNotification::List mNotifications;
QTimer *mTimer;
QThreadPool *mNotifyThreadPool;
QVector<NotificationSubscriber *> mSubscribers;
+ int mDebugNotifications;
friend class NotificationSubscriber;
friend class ::NotificationManagerTest;
diff --git a/src/server/notificationsubscriber.cpp b/src/server/notificationsubscriber.cpp
index 8dbd706..b1f1f22 100644
--- a/src/server/notificationsubscriber.cpp
+++ b/src/server/notificationsubscriber.cpp
@@ -39,6 +39,7 @@ NotificationSubscriber::NotificationSubscriber(NotificationManager *manager)
, mSocket(Q_NULLPTR)
, mAllMonitored(false)
, mExclusive(false)
+ , mNotificationDebugging(false)
{
}
@@ -60,6 +61,9 @@ NotificationSubscriber::NotificationSubscriber(NotificationManager *manager, qui
NotificationSubscriber::~NotificationSubscriber()
{
+ if (mNotificationDebugging) {
+ Q_EMIT notificationDebuggingChanged(false);
+ }
}
void NotificationSubscriber::socketReadyRead()
@@ -75,16 +79,16 @@ void NotificationSubscriber::socketReadyRead()
try {
cmd = Protocol::deserialize(mSocket);
} catch (const Akonadi::ProtocolException &e) {
- qDebug() << "ProtocolException:" << e.what();
+ qCWarning(AKONADISERVER_LOG) << "ProtocolException:" << e.what();
disconnectSubscriber();
return;
} catch (const std::exception &e) {
- qDebug() << "Unknown exception:" << e.what();
+ qCWarning(AKONADISERVER_LOG) << "Unknown exception:" << e.what();
disconnectSubscriber();
return;
}
if (cmd.type() == Protocol::Command::Invalid) {
- qDebug() << "Received an invalid command: resetting connection";
+ qCWarning(AKONADISERVER_LOG) << "Received an invalid command: resetting connection";
disconnectSubscriber();
return;
}
@@ -96,7 +100,7 @@ void NotificationSubscriber::socketReadyRead()
break;
case Protocol::Command::ModifySubscription:
if (mSubscriber.isEmpty()) {
- qDebug() << "Received ModifySubscription command before RegisterSubscriber";
+ qCWarning(AKONADISERVER_LOG) << "Received ModifySubscription command before RegisterSubscriber";
disconnectSubscriber();
return;
}
@@ -107,7 +111,7 @@ void NotificationSubscriber::socketReadyRead()
disconnectSubscriber();
break;
default:
- qDebug() << "Invalid command" << cmd.type() << "received by NotificationSubscriber" << mSubscriber;
+ qCWarning(AKONADISERVER_LOG) << "Invalid command" << cmd.type() << "received by NotificationSubscriber" << mSubscriber;
disconnectSubscriber();
break;
}
@@ -116,7 +120,7 @@ void NotificationSubscriber::socketReadyRead()
void NotificationSubscriber::socketDisconnected()
{
- qDebug() << "Subscriber" << mSubscriber << "disconnected from us!";
+ qCDebug(AKONADISERVER_LOG) << "Subscriber" << mSubscriber << "disconnected";
disconnectSubscriber();
}
@@ -144,7 +148,7 @@ void NotificationSubscriber::registerSubscriber(const Protocol::CreateSubscripti
{
QMutexLocker locker(&mLock);
- qDebug() << "Subscriber" << this << "identified as" << command.subscriberName();
+ qCDebug(AKONADISERVER_LOG) << "Subscriber" << this << "identified as" << command.subscriberName();
mSubscriber = command.subscriberName();
mSession = command.session();
@@ -230,20 +234,28 @@ void NotificationSubscriber::modifySubscription(const Protocol::ModifySubscripti
}
if (mManager) {
- // Did the caller just subscribed to subscription changes?
- if ((modifiedParts & Protocol::ModifySubscriptionCommand::Types)
- && command.startMonitoringTypes().contains(Protocol::ModifySubscriptionCommand::SubscriptionChanges))
- {
- // If yes, then send them list of all existing subscribers
- qDebug() << "========" << mSubscriber << "monitors subscriptions, will get" << mManager->mSubscribers.count() << "of them!";
- Protocol::ChangeNotification::List ntfs;
- ntfs.reserve(mManager->mSubscribers.count());
- Q_FOREACH (const NotificationSubscriber *subscriber, mManager->mSubscribers) {
- ntfs << subscriber->toChangeNotification();
+ if (modifiedParts & Protocol::ModifySubscriptionCommand::Types) {
+ // Did the caller just subscribed to subscription changes?
+ if (command.startMonitoringTypes().contains(Protocol::ModifySubscriptionCommand::SubscriptionChanges)) {
+ // If yes, then send them list of all existing subscribers
+ Q_FOREACH (const NotificationSubscriber *subscriber, mManager->mSubscribers) {
+ // Send them back to caller
+ QMetaObject::invokeMethod(this, "notify", Qt::QueuedConnection,
+ Q_ARG(Akonadi::Protocol::ChangeNotification,
+ subscriber->toChangeNotification()));
+ }
+ }
+ if (command.startMonitoringTypes().contains(Protocol::ModifySubscriptionCommand::ChangeNotifications)) {
+ if (!mNotificationDebugging) {
+ mNotificationDebugging = true;
+ Q_EMIT notificationDebuggingChanged(true);
+ }
+ } else if (command.stopMonitoringTypes().contains(Protocol::ModifySubscriptionCommand::ChangeNotifications)) {
+ if (mNotificationDebugging) {
+ mNotificationDebugging = false;
+ Q_EMIT notificationDebuggingChanged(false);
+ }
}
- // Send them back to caller
- QMetaObject::invokeMethod(this, "notify", Qt::QueuedConnection,
- Q_ARG(Akonadi::Protocol::ChangeNotification::List, ntfs));
}
// Emit subscription change notification
@@ -548,6 +560,25 @@ bool NotificationSubscriber::acceptsSubscriptionNotification(const Protocol::Sub
return mMonitoredTypes.contains(Protocol::ModifySubscriptionCommand::SubscriptionChanges);
}
+bool NotificationSubscriber::acceptsDebugChangeNotification(const Protocol::DebugChangeNotification &notification) const
+{
+ // Assumes mLock being locked by caller
+
+ Q_UNUSED(notification);
+
+ // We should never end up sending debug notification about a debug notification.
+ // This could get very messy very quickly...
+ Q_ASSERT(notification.notification().type() != Protocol::Command::DebugChangeNotification);
+ if (notification.notification().type() == Protocol::Command::DebugChangeNotification) {
+ return false;
+ }
+
+ // Unlike other types, debug change notifications must be explicitly enabled
+ // by caller and are excluded from "monitor all" as well
+ return mMonitoredTypes.contains(Protocol::ModifySubscriptionCommand::ChangeNotifications);
+}
+
+
bool NotificationSubscriber::acceptsNotification(const Protocol::ChangeNotification &notification) const
{
// Assumes mLock being locked
@@ -558,6 +589,7 @@ bool NotificationSubscriber::acceptsNotification(const Protocol::ChangeNotificat
}
// session is ignored
+ // TODO: Should this afect SubscriptionChangeNotification and DebugChangeNotification?
if (mIgnoredSessions.contains(notification.sessionId())) {
return false;
}
@@ -573,22 +605,25 @@ bool NotificationSubscriber::acceptsNotification(const Protocol::ChangeNotificat
return acceptsRelationNotification(notification);
case Protocol::Command::SubscriptionChangeNotification:
return acceptsSubscriptionNotification(notification);
+ case Protocol::Command::DebugChangeNotification:
+ return acceptsDebugChangeNotification(notification);
+
default:
qCDebug(AKONADISERVER_LOG) << "Received invalid change notification!";
return false;
}
}
-void NotificationSubscriber::notify(const Protocol::ChangeNotification::List &notifications)
+bool NotificationSubscriber::notify(const Protocol::ChangeNotification &notification)
{
QMutexLocker locker(&mLock);
- Q_FOREACH (const auto &notification, notifications) {
- if (acceptsNotification(notification)) {
- QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection,
- Q_ARG(Akonadi::Protocol::ChangeNotification, notification));
- }
+ if (acceptsNotification(notification)) {
+ QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection,
+ Q_ARG(Akonadi::Protocol::ChangeNotification, notification));
+ return true;
}
+ return false;
}
void NotificationSubscriber::writeNotification(const Protocol::ChangeNotification &notification)
diff --git a/src/server/notificationsubscriber.h b/src/server/notificationsubscriber.h
index ed7b407..d701cbc 100644
--- a/src/server/notificationsubscriber.h
+++ b/src/server/notificationsubscriber.h
@@ -43,13 +43,20 @@ public:
explicit NotificationSubscriber(NotificationManager *manager, quintptr socketDescriptor);
~NotificationSubscriber();
+ inline QByteArray subscriber() const {
+ return mSubscriber;
+ }
+
public Q_SLOTS:
- void notify(const Akonadi::Protocol::ChangeNotification::List &notifications);
+ bool notify(const Akonadi::Protocol::ChangeNotification &notification);
private Q_SLOTS:
void socketReadyRead();
void socketDisconnected();
+Q_SIGNALS:
+ void notificationDebuggingChanged(bool enabled);
+
protected:
void registerSubscriber(const Protocol::CreateSubscriptionCommand &command);
void modifySubscription(const Protocol::ModifySubscriptionCommand &command);
@@ -62,6 +69,7 @@ private:
bool acceptsTagNotification(const Protocol::TagChangeNotification &notification) const;
bool acceptsRelationNotification(const Protocol::RelationChangeNotification &notification) const;
bool acceptsSubscriptionNotification(const Protocol::SubscriptionChangeNotification &notification) const;
+ bool acceptsDebugChangeNotification(const Protocol::DebugChangeNotification &notification) const;
bool isCollectionMonitored(Entity::Id id) const;
bool isMimeTypeMonitored(const QString &mimeType) const;
@@ -92,6 +100,7 @@ protected:
QByteArray mSession;
bool mAllMonitored;
bool mExclusive;
+ bool mNotificationDebugging;
static QMimeDatabase sMimeDatabase;
};