1/*
2 Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#ifndef AKONADI_MONITOR_P_H
21#define AKONADI_MONITOR_P_H
22
23#include "akonadiprivate_export.h"
24#include "monitor.h"
25#include "collection.h"
26#include "collectionstatisticsjob.h"
27#include "collectionfetchscope.h"
28#include "item.h"
29#include "itemfetchscope.h"
30#include "tagfetchscope.h"
31#include "job.h"
32#include "entitycache_p.h"
33#include "servermanager.h"
34#include "changenotificationdependenciesfactory_p.h"
35#include "notificationsource_p.h"
36
37#include <akonadi/private/notificationmessagev3_p.h>
38
39#include <kmimetype.h>
40
41#include <QtCore/QObject>
42#include <QtCore/QTimer>
43
44namespace Akonadi {
45
46class Monitor;
47
48/**
49 * @internal
50 */
51class AKONADI_TESTS_EXPORT MonitorPrivate
52{
53public:
54 MonitorPrivate(ChangeNotificationDependenciesFactory *dependenciesFactory_, Monitor *parent);
55 virtual ~MonitorPrivate() {
56 delete dependenciesFactory;
57 delete collectionCache;
58 delete itemCache;
59 }
60 void init();
61
62 Monitor *q_ptr;
63 Q_DECLARE_PUBLIC(Monitor)
64 ChangeNotificationDependenciesFactory *dependenciesFactory;
65 NotificationSource *notificationSource;
66 Collection::List collections;
67 QSet<QByteArray> resources;
68 QSet<Item::Id> items;
69 QSet<Tag::Id> tags;
70 QSet<Monitor::Type> types;
71 QSet<QString> mimetypes;
72 bool monitorAll;
73 QList<QByteArray> sessions;
74 ItemFetchScope mItemFetchScope;
75 TagFetchScope mTagFetchScope;
76 CollectionFetchScope mCollectionFetchScope;
77 bool mFetchChangedOnly;
78 Session *session;
79 CollectionCache *collectionCache;
80 ItemListCache *itemCache;
81 TagListCache *tagCache;
82
83 // The waiting list
84 QQueue<NotificationMessageV3> pendingNotifications;
85 // The messages for which data is currently being fetched
86 QQueue<NotificationMessageV3> pipeline;
87 // In a pure Monitor, the pipeline contains items that were dequeued from pendingNotifications.
88 // The ordering [pipeline] [pendingNotifications] is kept at all times.
89 // [] [A B C] -> [A B] [C] -> [B] [C] -> [B C] [] -> [C] [] -> []
90 // In a ChangeRecorder, the pipeline contains one item only, and not dequeued yet.
91 // [] [A B C] -> [A] [A B C] -> [] [A B C] -> (changeProcessed) [] [B C] -> [B] [B C] etc...
92
93 bool fetchCollection;
94 bool fetchCollectionStatistics;
95 bool collectionMoveTranslationEnabled;
96
97 // Virtual methods for ChangeRecorder
98 virtual void notificationsEnqueued(int)
99 {
100 }
101 virtual void notificationsErased()
102 {
103 }
104
105 // Virtual so it can be overridden in FakeMonitor.
106 virtual bool connectToNotificationManager();
107 bool acceptNotification(const NotificationMessageV3 &msg) const;
108 void dispatchNotifications();
109 void flushPipeline();
110
111 // Called when the monitored item/collection changes, checks if the queued messages
112 // are still accepted, if not they are removed
113 void cleanOldNotifications();
114
115 bool ensureDataAvailable(const NotificationMessageV3 &msg);
116 /**
117 * Sends out the change notification @p msg.
118 * @param msg the change notification to send
119 * @return @c true if the notification was actually send to someone, @c false if no one was listening.
120 */
121 virtual bool emitNotification(const NotificationMessageV3 &msg);
122 void updatePendingStatistics(const NotificationMessageV3 &msg);
123 void invalidateCaches(const NotificationMessageV3 &msg);
124
125 /** Used by ResourceBase to inform us about collection changes before the notifications are emitted,
126 needed to avoid the missing RID race on change replay.
127 */
128 void invalidateCache(const Collection &col);
129
130 /// Virtual so that ChangeRecorder can set it to 0 and handle the pipeline itself
131 virtual int pipelineSize() const;
132
133 // private Q_SLOTS
134 void dataAvailable();
135 void slotSessionDestroyed(QObject *object);
136 void slotStatisticsChangedFinished(KJob *job);
137 void slotFlushRecentlyChangedCollections();
138
139 /**
140 Returns whether a message was appended to @p notificationQueue
141 */
142 int translateAndCompress(QQueue<NotificationMessageV3> &notificationQueue, const NotificationMessageV3 &msg);
143
144 virtual void slotNotify(const NotificationMessageV3::List &msgs);
145
146 /**
147 * Sends out a change notification for an item.
148 * @return @c true if the notification was actually send to someone, @c false if no one was listening.
149 */
150 bool emitItemsNotification(const NotificationMessageV3 &msg, const Item::List &items = Item::List(),
151 const Collection &collection = Collection(), const Collection &collectionDest = Collection());
152 /**
153 * Sends out a change notification for a collection.
154 * @return @c true if the notification was actually send to someone, @c false if no one was listening.
155 */
156 bool emitCollectionNotification(const NotificationMessageV3 &msg, const Collection &col = Collection(),
157 const Collection &par = Collection(), const Collection &dest = Collection());
158
159 bool emitTagsNotification(const NotificationMessageV3 &msg, const Tag::List &tags);
160
161 void serverStateChanged(Akonadi::ServerManager::State state);
162
163 /**
164 * This method is called by the ChangeMediator to enforce an invalidation of the passed collection.
165 */
166 void invalidateCollectionCache(qint64 collectionId);
167
168 /**
169 * This method is called by the ChangeMediator to enforce an invalidation of the passed item.
170 */
171 void invalidateItemCache(qint64 itemId);
172
173 /**
174 * This method is called by the ChangeMediator to enforce an invalidation of the passed tag.
175 */
176 void invalidateTagCache(qint64 tagId);
177
178 /**
179 @brief Class used to determine when to purge items in a Collection
180
181 The buffer method can be used to buffer a Collection. This may cause another Collection
182 to be purged if it is removed from the buffer.
183
184 The purge method is used to purge a Collection from the buffer, but not the model.
185 This is used for example, to not buffer Collections anymore if they get referenced,
186 and to ensure that one Collection does not appear twice in the buffer.
187
188 Check whether a Collection is buffered using the isBuffered method.
189 */
190 class AKONADI_TESTS_EXPORT PurgeBuffer
191 {
192 // Buffer the most recent 10 unreferenced Collections
193 static const int MAXBUFFERSIZE = 10;
194 public:
195 explicit PurgeBuffer()
196 {
197 }
198
199 /**
200 Adds @p id to the Collections to be buffered
201
202 @returns The collection id which was removed form the buffer or -1 if none.
203 */
204 Collection::Id buffer(Collection::Id id);
205
206 /**
207 Removes @p id from the Collections being buffered
208 */
209 void purge(Collection::Id id);
210
211 bool isBuffered(Collection::Id id) const
212 {
213 return m_buffer.contains(id);
214 }
215
216 static int buffersize();
217
218 private:
219 QQueue<Collection::Id> m_buffer;
220 } m_buffer;
221
222 QHash<Collection::Id, int> refCountMap;
223 bool useRefCounting;
224 void ref(Collection::Id id);
225 Collection::Id deref(Collection::Id id);
226
227 /**
228 * Returns true if the collection is monitored by monitor.
229 *
230 * A collection is always monitored if useRefCounting is false.
231 * If ref counting is used, the collection is only monitored,
232 * if the collection is either in refCountMap or m_buffer.
233 * If ref counting is used and the collection is not in refCountMap or m_buffer,
234 * no updates for the contained items are emitted, because they are lazily ignored.
235 */
236 bool isMonitored(Collection::Id colId) const;
237
238private:
239 // collections that need a statistics update
240 QSet<Collection::Id> recentlyChangedCollections;
241 QTimer statisticsCompressionTimer;
242
243 /**
244 @returns True if @p msg should be ignored. Otherwise appropriate signals are emitted for it.
245 */
246 bool isLazilyIgnored(const NotificationMessageV3 &msg, bool allowModifyFlagsConversion = false) const;
247
248 /**
249 Sets @p needsSplit to True when @p msg contains more than one item and there's at least one
250 listener that does not support batch operations. Sets @p batchSupported to True when
251 there's at least one listener that supports batch operations.
252 */
253 void checkBatchSupport(const NotificationMessageV3 &msg, bool &needsSplit, bool &batchSupported) const;
254
255 NotificationMessageV3::List splitMessage(const NotificationMessageV3 &msg, bool legacy) const;
256
257 bool isCollectionMonitored(Collection::Id collection) const
258 {
259 if (collection < 0) {
260 return false;
261 }
262 if (collections.contains(Collection(collection))) {
263 return true;
264 }
265 if (collections.contains(Collection::root())) {
266 return true;
267 }
268 return false;
269 }
270
271 bool isMimeTypeMonitored(const QString &mimetype) const
272 {
273 if (mimetypes.contains(mimetype)) {
274 return true;
275 }
276
277 KMimeType::Ptr mimeType = KMimeType::mimeType(mimetype, KMimeType::ResolveAliases);
278 if (mimeType.isNull()) {
279 return false;
280 }
281
282 foreach (const QString &mt, mimetypes) {
283 if (mimeType->is(mt)) {
284 return true;
285 }
286 }
287
288 return false;
289 }
290
291 bool isMoveDestinationResourceMonitored(const NotificationMessageV3 &msg) const
292 {
293 if (msg.operation() != NotificationMessageV2::Move) {
294 return false;
295 }
296 return resources.contains(msg.destinationResource());
297 }
298
299 void fetchStatistics(Collection::Id colId)
300 {
301 CollectionStatisticsJob *job = new CollectionStatisticsJob(Collection(colId), session);
302 QObject::connect(job, SIGNAL(result(KJob*)), q_ptr, SLOT(slotStatisticsChangedFinished(KJob*)));
303 }
304
305 void notifyCollectionStatisticsWatchers(Collection::Id collection, const QByteArray &resource);
306};
307
308}
309
310#endif
311